From a521ac1fdf0e5e004ab88546b5bf64ef45a90474 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 6 May 2026 12:18:42 +0100 Subject: [PATCH 001/144] Re-fork Deskflow 1.26 an add Synergy brand --- .gitignore | 11 +- .vscode/extensions.json | 9 + .vscode/launch.json | 193 ++++++++ .vscode/tasks.json | 250 ++++++++++ CLAUDE.md | 49 ++ CMakeLists.txt | 9 +- CMakePresets.json | 139 ++++++ extra/cmake/SaveVersion.cmake | 26 + extra/cmake/Synergy.cmake | 17 + extra/cmake/Version.cmake | 122 +++++ .../deploy/mac/bundle/Contents/Info.plist.in | 31 ++ extra/deploy/mac/bundle/Contents/PkgInfo.in | 1 + .../mac/bundle/Contents/Resources/App.icns | Bin 0 -> 162150 bytes .../bundle/Contents/Resources/Background.tiff | Bin 0 -> 72810 bytes .../mac/bundle/Contents/Resources/Volume.icns | Bin 0 -> 152977 bytes extra/deploy/mac/dmgbuild/settings.py | 69 +++ extra/deploy/wix/images/banner.png | Bin 0 -> 8593 bytes extra/deploy/wix/images/common_background.png | Bin 0 -> 18998 bytes extra/deploy/wix/images/dialog.png | Bin 0 -> 86987 bytes .../deploy/wix/images/welcome_background.png | Bin 0 -> 115370 bytes extra/src/apps/res/icons/16x16/padlock.png | Bin 0 -> 450 bytes extra/src/apps/res/icons/16x16/tray.png | Bin 0 -> 905 bytes extra/src/apps/res/icons/16x16/warning.png | Bin 0 -> 693 bytes extra/src/apps/res/icons/256x256/synergy.ico | Bin 0 -> 101014 bytes extra/src/apps/res/icons/64x64/folder.png | Bin 0 -> 919 bytes extra/src/apps/res/icons/64x64/tray-dark.png | Bin 0 -> 1032 bytes extra/src/apps/res/icons/64x64/tray-light.png | Bin 0 -> 1764 bytes extra/src/apps/res/icons/64x64/user-trash.png | Bin 0 -> 3815 bytes .../apps/res/icons/64x64/video-display.png | Bin 0 -> 2579 bytes extra/src/apps/res/image/logo-dark.png | Bin 0 -> 6206 bytes extra/src/apps/res/image/logo-light.png | Bin 0 -> 7530 bytes extra/src/apps/res/image/placeholder.png | Bin 0 -> 1605 bytes extra/src/apps/res/image/setup-blocker.png | Bin 0 -> 12202 bytes extra/src/apps/res/image/welcome.png | Bin 0 -> 11113 bytes extra/src/apps/res/synergy.ico | Bin 0 -> 147530 bytes extra/src/apps/res/synergy.png | Bin 0 -> 22516 bytes extra/src/apps/res/synergy.qrc | 17 + extra/src/apps/res/synergy.svg | 15 + extra/src/integtests/CMakeLists.txt | 19 + extra/src/lib/synergy/CMakeLists.txt | 17 + .../src/lib/synergy/gui/ActivationDialog.cpp | 231 +++++++++ extra/src/lib/synergy/gui/ActivationDialog.h | 68 +++ extra/src/lib/synergy/gui/ActivationDialog.ui | 153 ++++++ extra/src/lib/synergy/gui/AppTime.cpp | 53 ++ extra/src/lib/synergy/gui/AppTime.h | 39 ++ extra/src/lib/synergy/gui/CMakeLists.txt | 42 ++ .../synergy/gui/CancelActivationDialog.cpp | 35 ++ .../lib/synergy/gui/CancelActivationDialog.h | 36 ++ .../lib/synergy/gui/CancelActivationDialog.ui | 77 +++ extra/src/lib/synergy/gui/ExtraSettings.cpp | 50 ++ extra/src/lib/synergy/gui/ExtraSettings.h | 60 +++ extra/src/lib/synergy/gui/FeatureHandler.cpp | 129 +++++ extra/src/lib/synergy/gui/FeatureHandler.h | 43 ++ extra/src/lib/synergy/gui/constants.h | 40 ++ .../lib/synergy/gui/dialogs/UpgradeDialog.cpp | 47 ++ .../lib/synergy/gui/dialogs/UpgradeDialog.h | 32 ++ .../synergy/gui/license/LicenseActivator.cpp | 145 ++++++ .../synergy/gui/license/LicenseActivator.h | 66 +++ .../synergy/gui/license/LicenseHandler.cpp | 462 ++++++++++++++++++ .../lib/synergy/gui/license/LicenseHandler.h | 101 ++++ .../synergy/gui/license/license_notices.cpp | 78 +++ .../lib/synergy/gui/license/license_notices.h | 28 ++ .../lib/synergy/gui/license/license_utils.cpp | 56 +++ .../lib/synergy/gui/license/license_utils.h | 29 ++ extra/src/lib/synergy/hooks/gui_hook.h | 70 +++ .../lib/synergy/hooks/gui_hook_config.h.in | 29 ++ extra/src/lib/synergy/license/CMakeLists.txt | 25 + extra/src/lib/synergy/license/License.cpp | 129 +++++ extra/src/lib/synergy/license/License.h | 119 +++++ extra/src/lib/synergy/license/Product.cpp | 190 +++++++ extra/src/lib/synergy/license/Product.h | 89 ++++ extra/src/lib/synergy/license/SerialKey.h | 68 +++ .../src/lib/synergy/license/SerialKeyType.cpp | 37 ++ extra/src/lib/synergy/license/SerialKeyType.h | 44 ++ .../lib/synergy/license/parse_serial_key.cpp | 174 +++++++ .../lib/synergy/license/parse_serial_key.h | 69 +++ extra/src/unittests/CMakeLists.txt | 19 + .../gui/license/LicenseHandlerTests.cpp | 40 ++ .../gui/license/license_notices_tests.cpp | 144 ++++++ extra/src/unittests/license/LicenseTests.cpp | 198 ++++++++ extra/src/unittests/license/ProductTests.cpp | 90 ++++ .../unittests/license/SerialKeyTypeTests.cpp | 45 ++ src/apps/CMakeLists.txt | 6 +- src/apps/deskflow-core/CMakeLists.txt | 4 + src/apps/deskflow-daemon/CMakeLists.txt | 4 + src/apps/deskflow-gui/CMakeLists.txt | 14 +- src/lib/common/CMakeLists.txt | 4 +- 87 files changed, 4686 insertions(+), 19 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 CLAUDE.md create mode 100644 CMakePresets.json create mode 100644 extra/cmake/SaveVersion.cmake create mode 100644 extra/cmake/Synergy.cmake create mode 100644 extra/cmake/Version.cmake create mode 100644 extra/deploy/mac/bundle/Contents/Info.plist.in create mode 100644 extra/deploy/mac/bundle/Contents/PkgInfo.in create mode 100644 extra/deploy/mac/bundle/Contents/Resources/App.icns create mode 100644 extra/deploy/mac/bundle/Contents/Resources/Background.tiff create mode 100644 extra/deploy/mac/bundle/Contents/Resources/Volume.icns create mode 100644 extra/deploy/mac/dmgbuild/settings.py create mode 100644 extra/deploy/wix/images/banner.png create mode 100644 extra/deploy/wix/images/common_background.png create mode 100644 extra/deploy/wix/images/dialog.png create mode 100644 extra/deploy/wix/images/welcome_background.png create mode 100644 extra/src/apps/res/icons/16x16/padlock.png create mode 100644 extra/src/apps/res/icons/16x16/tray.png create mode 100644 extra/src/apps/res/icons/16x16/warning.png create mode 100644 extra/src/apps/res/icons/256x256/synergy.ico create mode 100644 extra/src/apps/res/icons/64x64/folder.png create mode 100644 extra/src/apps/res/icons/64x64/tray-dark.png create mode 100644 extra/src/apps/res/icons/64x64/tray-light.png create mode 100644 extra/src/apps/res/icons/64x64/user-trash.png create mode 100644 extra/src/apps/res/icons/64x64/video-display.png create mode 100644 extra/src/apps/res/image/logo-dark.png create mode 100644 extra/src/apps/res/image/logo-light.png create mode 100644 extra/src/apps/res/image/placeholder.png create mode 100644 extra/src/apps/res/image/setup-blocker.png create mode 100644 extra/src/apps/res/image/welcome.png create mode 100644 extra/src/apps/res/synergy.ico create mode 100644 extra/src/apps/res/synergy.png create mode 100644 extra/src/apps/res/synergy.qrc create mode 100644 extra/src/apps/res/synergy.svg create mode 100644 extra/src/integtests/CMakeLists.txt create mode 100644 extra/src/lib/synergy/CMakeLists.txt create mode 100644 extra/src/lib/synergy/gui/ActivationDialog.cpp create mode 100644 extra/src/lib/synergy/gui/ActivationDialog.h create mode 100644 extra/src/lib/synergy/gui/ActivationDialog.ui create mode 100644 extra/src/lib/synergy/gui/AppTime.cpp create mode 100644 extra/src/lib/synergy/gui/AppTime.h create mode 100644 extra/src/lib/synergy/gui/CMakeLists.txt create mode 100644 extra/src/lib/synergy/gui/CancelActivationDialog.cpp create mode 100644 extra/src/lib/synergy/gui/CancelActivationDialog.h create mode 100644 extra/src/lib/synergy/gui/CancelActivationDialog.ui create mode 100644 extra/src/lib/synergy/gui/ExtraSettings.cpp create mode 100644 extra/src/lib/synergy/gui/ExtraSettings.h create mode 100644 extra/src/lib/synergy/gui/FeatureHandler.cpp create mode 100644 extra/src/lib/synergy/gui/FeatureHandler.h create mode 100644 extra/src/lib/synergy/gui/constants.h create mode 100644 extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp create mode 100644 extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h create mode 100644 extra/src/lib/synergy/gui/license/LicenseActivator.cpp create mode 100644 extra/src/lib/synergy/gui/license/LicenseActivator.h create mode 100644 extra/src/lib/synergy/gui/license/LicenseHandler.cpp create mode 100644 extra/src/lib/synergy/gui/license/LicenseHandler.h create mode 100644 extra/src/lib/synergy/gui/license/license_notices.cpp create mode 100644 extra/src/lib/synergy/gui/license/license_notices.h create mode 100644 extra/src/lib/synergy/gui/license/license_utils.cpp create mode 100644 extra/src/lib/synergy/gui/license/license_utils.h create mode 100644 extra/src/lib/synergy/hooks/gui_hook.h create mode 100644 extra/src/lib/synergy/hooks/gui_hook_config.h.in create mode 100644 extra/src/lib/synergy/license/CMakeLists.txt create mode 100644 extra/src/lib/synergy/license/License.cpp create mode 100644 extra/src/lib/synergy/license/License.h create mode 100644 extra/src/lib/synergy/license/Product.cpp create mode 100644 extra/src/lib/synergy/license/Product.h create mode 100644 extra/src/lib/synergy/license/SerialKey.h create mode 100644 extra/src/lib/synergy/license/SerialKeyType.cpp create mode 100644 extra/src/lib/synergy/license/SerialKeyType.h create mode 100644 extra/src/lib/synergy/license/parse_serial_key.cpp create mode 100644 extra/src/lib/synergy/license/parse_serial_key.h create mode 100644 extra/src/unittests/CMakeLists.txt create mode 100644 extra/src/unittests/gui/license/LicenseHandlerTests.cpp create mode 100644 extra/src/unittests/gui/license/license_notices_tests.cpp create mode 100644 extra/src/unittests/license/LicenseTests.cpp create mode 100644 extra/src/unittests/license/ProductTests.cpp create mode 100644 extra/src/unittests/license/SerialKeyTypeTests.cpp diff --git a/.gitignore b/.gitignore index e10ad34f5..3c946a046 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ deskflow-config.toml .DS_Store *.code-workspace .env* -/scripts/*.egg-info +/scripts /*.user *.ui.autosave @@ -40,12 +40,3 @@ CMakeCache.txt CMakeUserPresets.json CMakeFiles/* -# vscode folder -/.vscode - -# scripts folder -/scripts - -# Ai helperfilers -**/[cC]laude.[mM][dD] -**/CLAUDE.[mM][dD] diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..0c264114d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "ms-vscode.cmake-tools", + "llvm-vs-code-extensions.vscode-clangd", + "ms-vscode.cpptools", + "llvm-vs-code-extensions.lldb-dap", + "jacqueslucke.gcov-viewer" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..84f7dbbb0 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,193 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "unix - gui", + "type": "lldb-dap", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergy", + "preLaunchTask": "build-silent", + "osx": { + "program": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy" + } + }, + { + "name": "unix - unittests", + "type": "lldb-dap", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/unittests", + "args": ["${input:gtest-args}"], + "preLaunchTask": "build-silent" + }, + { + "name": "unix - integtests", + "type": "lldb-dap", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/integtests", + "args": ["${input:gtest-args}"], + "preLaunchTask": "build-silent" + }, + { + "name": "unix - server", + "type": "lldb-dap", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergy-server", + "args": ["--config-toml", "synergy-config.toml"], + "preLaunchTask": "build-silent", + "osx": { + "program": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy-server" + } + }, + { + "name": "unix - client", + "type": "lldb-dap", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergy-client", + "args": ["--config-toml", "synergy-config.toml"], + "preLaunchTask": "build-silent", + "osx": { + "program": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy-client" + } + }, + { + "name": "unix - daemon", + "type": "lldb-dap", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin/synergy-daemon", + "args": ["-f"], + "preLaunchTask": "build-silent" + }, + { + "name": "unix - attach", + "type": "lldb-dap", + "request": "attach", + "pid": "${command:pickProcess}" + }, + { + "name": "windows - gui", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build-kill-gui" + }, + { + "name": "windows - version", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy-core", + "args": ["--version"], + "internalConsoleOptions": "openOnSessionStart" + }, + { + "name": "windows - unittests", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/unittests", + "args": ["${input:gtest-args}"], + "internalConsoleOptions": "openOnSessionStart" + }, + { + "name": "windows - integtests", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/integtests", + "args": ["${input:gtest-args}"], + "internalConsoleOptions": "openOnSessionStart" + }, + { + "name": "windows - server", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy-server", + "args": ["--config-toml", "synergy-config.toml"], + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build-kill-core" + }, + { + "name": "windows - client", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy-client", + "args": ["--config-toml", "synergy-config.toml"], + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build-kill-core" + }, + { + "name": "windows - legacy", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy-legacy", + "args": ["--get-profile-dir"], + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build" + }, + { + "name": "windows - active-desktop", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy-core", + "args": ["--active-desktop"], + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build-kill-core" + }, + { + "name": "windows - daemon foreground", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy-daemon", + "args": ["-f"], + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build-kill-core" + }, + { + "name": "windows - daemon install", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy-daemon", + "args": ["--install"], + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build" + }, + { + "name": "windows - daemon uninstall", + "type": "cppvsdbg", + "cwd": "${workspaceRoot}", + "request": "launch", + "program": "${workspaceFolder}/build/bin-copy/synergy-daemon", + "args": ["--uninstall"], + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "build" + }, + { + "name": "windows - attach", + "type": "cppvsdbg", + "request": "attach", + "processId": "${command:pickProcess}" + } + ], + "inputs": [ + { + "id": "gtest-args", + "type": "promptString", + "description": "Test arguments", + "default": "--gtest_filter=*" + } + ] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..0c14702a0 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,250 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "dependsOn": ["build-cmake"], + "windows": { + "command": "robocopy ${workspaceFolder}/build/bin ${workspaceFolder}/build/bin-copy /R:0 /E /S /NJS /NJH /NFL /NDL; exit 0" + }, + "group": { + "kind": "build", + "isDefault": true + }, + }, + { + "label": "build-cmake", + "type": "cmake", + "command": "build", + "targets": ["all"], + "preset": "${command:cmake.activeBuildPresetName}", + "group": "build", + "problemMatcher": { + "base": "$gcc", + "fileLocation": ["absolute"] + }, + }, + { + "label": "build-kill-core", + "dependsOn": ["kill-daemon", "kill-core", "build"], + "dependsOrder": "sequence" + }, + { + "label": "build-kill-gui", + "dependsOn": ["kill-gui", "kill-core", "build"], + "dependsOrder": "sequence" + }, + { + "label": "build-silent", + "type": "shell", + "dependsOn": ["build"], + "group": "build", + "presentation": { + "reveal": "silent", + } + }, + { + "label": "clean", + "type": "cmake", + "command": "build", + "targets": ["clean"], + "preset": "${command:cmake.activeBuildPresetName}", + "group": "build" + }, + { + "label": "clean-gcda", + "type": "shell", + "command": "find . -name '*.gcda' -delete", + "windows": { + "command": "$null" + }, + "presentation": { + "reveal": "silent" + } + }, + { + "label": "clean-qt", + "type": "shell", + "command": "rm -r build/src/gui build/src/lib/gui", + "windows": { + "command": "remove-item -recurse build/src/gui,build/src/lib/gui" + } + }, + { + "label": "clean-config", + "type": "shell", + "linux": { + "command": "rm -r ~/.config/Synergy/Synergy.conf" + }, + "windows": { + "command": "remove-item -recurse $env:APPDATA\\Synergy\\Synergy" + }, + "osx": { + "command": "rm -r ~/Library/Application\\ Support/Synergy/Synergy" + } + }, + { + "label": "tests", + "dependsOn": ["integtests", "unittests"], + "problemMatcher": [] + }, + { + "label": "kill-all", + "type": "shell", + "dependsOn": ["kill-gui", "kill-daemon", "kill-core"], + }, + { + "label": "kill-gui", + "type": "shell", + "command": "killall -9 synergy || true", + "windows": { + "command": "taskkill /F /IM synergy.exe; $true" + }, + }, + { + "label": "kill-daemon", + "type": "shell", + "command": "killall -9 synergy-daemon || true", + "windows": { + "command": "taskkill /F /IM synergy-daemon.exe; $true" + }, + "dependsOn": ["daemon-stop"], + }, + { + "label": "kill-core", + "type": "shell", + "command": "killall -9 synergy-core synergy-server synergy-client || true", + "windows": { + "command": "taskkill /F /IM synergy-core.exe; taskkill /F /IM synergy-server.exe; taskkill /F /IM synergy-client.exe; $true" + }, + }, + { + "label": "gui", + "type": "process", + "command": "${workspaceFolder}/build/bin/synergy", + "problemMatcher": [], + "dependsOn": ["build"], + "windows": { + "dependsOn": ["build-kill-gui"], + "command": "${workspaceFolder}/build/bin-copy/synergy.exe" + } + }, + { + "label": "daemon-stop", + "type": "shell", + "command": "echo noop > /dev/null", + "windows": { + "command": "Stop-Service -Name 'Synergy' -Force; while ((Get-Service -Name 'Synergy').Status -ne 'Stopped') { Start-Sleep -Seconds 1 }", + }, + }, + { + "label": "daemon-start", + "type": "shell", + "command": "echo noop > /dev/null", + "windows": { + "command": "Start-Service -Name 'Synergy'; while ((Get-Service -Name 'Synergy').Status -ne 'Running') { Start-Sleep -Seconds 1 }", + }, + }, + { + "label": "daemon-restart", + "type": "shell", + "command": "echo noop > /dev/null", + "windows": { + "command": "Restart-Service -Name 'Synergy'", + }, + }, + { + "label": "daemon-deploy", + "type": "shell", + "dependsOn": ["build-kill-core", "daemon-start"], + "dependsOrder": "sequence" + }, + { + "label": "daemon-install", + "type": "shell", + "command": "echo noop > /dev/null", + "windows": { + "command": "${workspaceFolder}/build/bin-copy/synergy-daemon.exe --install" + } + }, + { + "label": "daemon-uninstall", + "type": "shell", + "command": "echo noop > /dev/null", + "windows": { + "command": "${workspaceFolder}/build/bin-copy/synergy-daemon.exe --uninstall" + } + }, + { + "label": "daemon-reinstall", + "type": "shell", + "dependsOn": ["build-kill-core", "daemon-stop", "daemon-uninstall", "daemon-install"], + "dependsOrder": "sequence", + }, + { + "label": "unittests (current)", + "type": "shell", + "command": "python", + "args": [ + "./scripts/tests.py", + "--unit-tests", + "--ignore-return-code", + "--filter-file=${file}" + ], + "dependsOn": ["build", "clean-gcda"] + }, + { + "label": "integtests (current)", + "type": "shell", + "command": "python", + "args": [ + "./scripts/tests.py", + "--integ-tests", + "--ignore-return-code", + "--filter-file=${file}" + ], + "dependsOn": ["build", "clean-gcda"] + }, + { + "label": "unittests", + "type": "shell", + "command": "python", + "args": ["./scripts/tests.py", "--unit-tests", "--ignore-return-code"], + "dependsOn": ["build", "clean-gcda"] + }, + { + "label": "integtests", + "type": "shell", + "command": "python", + "args": ["./scripts/tests.py", "--integ-tests", "--ignore-return-code"], + "dependsOn": ["build", "clean-gcda"] + }, + { + "label": "unittests (current, valgrind)", + "type": "shell", + "command": "python", + "args": [ + "./scripts/tests.py", + "--unit-tests", + "--ignore-return-code", + "--filter-file=${file}", + "--valgrind" + ], + "dependsOn": ["build", "clean-gcda"] + }, + { + "label": "integtests (current, valgrind)", + "type": "shell", + "command": "python", + "args": [ + "./scripts/tests.py", + "--integ-tests", + "--ignore-return-code", + "--filter-file=${file}", + "--valgrind" + ], + "dependsOn": ["build", "clean-gcda"] + } + ] +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..f729f1ded --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,49 @@ +# AI instructions + +## Working style + +- **Never configure or build unless explicitly asked.** No `cmake`, `cmake --build`, `make`, `ninja`, etc. After code changes, stop at the edit and ask the user to build manually. + +## GitHub Actions + +- `matrix` context is NOT available in job-level `if` — the `if` runs before the matrix is expanded. Use step-level `if` conditions instead (e.g. set a `skip` output in a setup step and check it per-step). + +## Code style + +- Default to **no comments**. Only add one when the WHY is non-obvious — hidden constraints, subtle invariants, workarounds for specific bugs, behavior that would surprise a reader. Never add comments that explain WHAT or HOW (well-named identifiers do that). +- Don't reference the current task, fix, ticket, or CVE in comments. That belongs in the commit message. + +## Working with commits + +- **Never commit, amend, or rewrite history.** The user reviews staged changes and commits themselves. No `git commit`, no `git commit --amend`, no `git rebase`, no force-pushes. +- When cherry-picking, use `git cherry-pick --no-commit` so changes land in the index/working tree without producing a commit. Halt after conflict resolution and let the user commit. + +## Branches + +- **`master`** — stable 1.21.x line. Cherry-picks critical fixes from upstream Deskflow. Conservative. +- **`beta`** — re-forked from `deskflow/master` in 2026-05. Catches the fork up to upstream and re-establishes a clean base. Will replace master after months of stabilization. + +The `deskflow` git remote points at [deskflow/deskflow](https://github.com/deskflow/deskflow). Reference upstream as `deskflow/master`. + +## Backporting from upstream Deskflow + +Mechanics depend on which Synergy branch you're on: + +- **On `beta`**: paths align with `deskflow/master` since beta was forked from it. `git cherry-pick` should mostly work cleanly. Conflicts are usually genuine (overlapping changes), not path drift. +- **On `master`**: paths often diverge. Files may be at different locations than upstream (e.g. `DaemonApp.cpp` location may differ), and surrounding refactors may not be present. Plan to port hunks manually. + +In both cases: backport multi-commit PRs one commit at a time. Apply, surface conflicts, resolve together, wait for the user to review/commit, then move to the next. + +## Synergy overlay (`extra/`) + +Houses Synergy-only content that doesn't belong upstream: license/activation kernel, branding overrides, Synergy resources. Was a separate `ext/synergy-extra/` submodule until the re-fork; folded into the main repo for sanity (one PR per change, atomic refactors, no submodule init dance). + +The directory structure under `extra/` mirrors the root: `extra/src/lib/synergy/` parallels `src/lib/`, `extra/src/unittests/` parallels `src/unittests/`, `extra/deploy/` parallels `deploy/`, `extra/cmake/` parallels `cmake/`. This makes backport hygiene mechanical (anything under `extra/` is Synergy-only) and re-fork mapping positional (`extra/X/Y` ↔ `X/Y`). + +- Same license as the rest of the repo (GPLv2). Synergy is open source; commercial value is server-side activation infrastructure, signed builds, support, and brand — not closed binaries. Code linked into Synergy binaries must be GPLv2-compatible. +- Branding is driven by `extra/cmake/Synergy.cmake` overriding the `CMAKE_PROJECT_*` family (`CMAKE_PROJECT_PROPER_NAME`, `CMAKE_PROJECT_VENDOR`, `CMAKE_PROJECT_COPYRIGHT`, `CMAKE_PROJECT_CONTACT`, `CMAKE_PROJECT_REV_FQDN`, `CMAKE_PROJECT_HOMEPAGE_URL`) *after* deskflow's defaults are set. The old `DESKFLOW_APP_ID` / `DESKFLOW_GUI_HOOK_*` mechanism is gone. +- Binary names (`synergy-core`, `synergy-daemon`, `synergy`) come from `OUTPUT_NAME` overrides in `src/apps/*/CMakeLists.txt`. CMake target identifiers stay deskflow-flavored internally to keep file-path references aligned with upstream. + +## Planned + +- **Hook modernization**: replace the macro-string-based `DESKFLOW_GUI_HOOK_*` hook scheme with a `synergy::extra` namespace. Real C++ function calls from patched src/ files. Single macro guard `SYNERGY_EXTRA_HEADER` for the include. Adds Core-side hook callsites for license enforcement. diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e4f9b3ec..d1256247e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,9 @@ set(CMAKE_PROJECT_COPYRIGHT "(C) 2024-2026 ${CMAKE_PROJECT_VENDOR}") set(CMAKE_PROJECT_CONTACT "${CMAKE_PROJECT_PROPER_NAME} ") set(CMAKE_PROJECT_REV_FQDN "org.deskflow.deskflow") +# Pull in Synergy branding overrides. +include(${CMAKE_SOURCE_DIR}/extra/cmake/Synergy.cmake) + #Unset the vars used in the project call unset(DESKFLOW_VERSION_MAJOR) unset(DESKFLOW_VERSION_MINOR) @@ -177,8 +180,10 @@ elseif(BUILD_OSX_BUNDLE) set(CMAKE_INSTALL_BINDIR ${CMAKE_PROJECT_PROPER_NAME}.app/Contents/MacOS) set(CMAKE_INSTALL_LICENSE_DIR ${CMAKE_PROJECT_PROPER_NAME}.app/Contents/Resources) else() - set(CMAKE_INSTALL_LICENSE_DIR ${CMAKE_INSTALL_DATADIR}/licenses/${CMAKE_PROJECT_NAME}) - set(CMAKE_INSTALL_I18N_DIR ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}/translations) + string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _proj_lower) + set(CMAKE_INSTALL_LICENSE_DIR ${CMAKE_INSTALL_DATADIR}/licenses/${_proj_lower}) + set(CMAKE_INSTALL_I18N_DIR ${CMAKE_INSTALL_DATADIR}/${_proj_lower}/translations) + unset(_proj_lower) endif() add_subdirectory(docs) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 000000000..3f91b05c5 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,139 @@ +{ + "version": 2, + "configurePresets": [ + { + "name": "base", + "hidden": true, + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + } + }, + { + "name": "minimal", + "hidden": true, + "environment": { + "SYNERGY_BUILD_MINIMAL": "ON" + } + }, + { + "name": "windows", + "inherits": "base", + "hidden": true, + "generator": "Ninja", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe", + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "QT_PATH": "$env{QT_PATH}" + }, + "architecture": { + "value": "x64", + "strategy": "external" + }, + "toolset": { + "value": "host=x64", + "strategy": "external" + } + }, + { + "name": "linux", + "hidden": true, + "inherits": "base", + "generator": "Unix Makefiles" + }, + { + "name": "macos", + "hidden": true, + "inherits": "base", + "generator": "Unix Makefiles", + "cacheVariables": { + "CMAKE_OSX_SYSROOT": "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" + } + }, + { + "name": "windows-debug", + "inherits": "windows", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "windows-release", + "inherits": "windows", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "linux-debug", + "inherits": "linux", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "linux-release", + "inherits": "linux", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "macos-debug", + "inherits": "macos", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos-release", + "inherits": "macos", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "windows-debug-min", + "inherits": ["windows", "minimal"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "windows-release-min", + "inherits": ["windows", "minimal"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "linux-debug-min", + "inherits": ["linux", "minimal"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "linux-release-min", + "inherits": ["linux", "minimal"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "macos-debug-min", + "inherits": ["macos", "minimal"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "macos-release-min", + "inherits": ["macos", "minimal"], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ] +} diff --git a/extra/cmake/SaveVersion.cmake b/extra/cmake/SaveVersion.cmake new file mode 100644 index 000000000..aaf9e4376 --- /dev/null +++ b/extra/cmake/SaveVersion.cmake @@ -0,0 +1,26 @@ +# Synergy -- mouse and keyboard sharing utility +# Copyright (C) 2025 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 . + +include(${CMAKE_CURRENT_LIST_DIR}/Version.cmake) + +version_from_git_tags(VERSION VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_REVISION) +message(STATUS "Version: " ${VERSION}) + +if ("${VERSION_FILE}" STREQUAL "") + message(FATAL_ERROR "VERSION_FILE not set") +endif() + +message(STATUS "Writing to file: ${VERSION_FILE}") +file(WRITE ${VERSION_FILE} ${VERSION}) diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake new file mode 100644 index 000000000..492d82e19 --- /dev/null +++ b/extra/cmake/Synergy.cmake @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Symless Ltd +# SPDX-License-Identifier: MIT + +# Must be included after deskflow's project() call and the CMAKE_PROJECT_* +# defaults are set, so these overrides take effect. +set(CMAKE_PROJECT_PROPER_NAME "Synergy") +set(CMAKE_PROJECT_VENDOR "Symless Ltd") +set(CMAKE_PROJECT_COPYRIGHT "(C) 2012-2026 ${CMAKE_PROJECT_VENDOR}") +set(CMAKE_PROJECT_CONTACT "${CMAKE_PROJECT_PROPER_NAME} ") +set(CMAKE_PROJECT_REV_FQDN "com.symless.synergy") +set(CMAKE_PROJECT_HOMEPAGE_URL "https://symless.com/synergy") + +# Don't run unit tests as part of the build. Devs can opt back in with +# -DSKIP_BUILD_TESTS=OFF if they want post-build ctest invocation. +set(SKIP_BUILD_TESTS ON CACHE BOOL "Skip build time test") + +message(STATUS "Synergy branding applied: ${CMAKE_PROJECT_PROPER_NAME} ${CMAKE_PROJECT_VERSION}") diff --git a/extra/cmake/Version.cmake b/extra/cmake/Version.cmake new file mode 100644 index 000000000..0995014d5 --- /dev/null +++ b/extra/cmake/Version.cmake @@ -0,0 +1,122 @@ +# Synergy -- mouse and keyboard sharing utility +# Copyright (C) 2025 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 . + +# +# Partially based on back-ported upstream code but differs in two ways: +# +# 1. We use `git describe master` to count changes since last tag only on master. +# This is because we use squash merge instead of rebasing; using describe on the branch would +# give us a superficially higher revision number. +# +# 2. We use a different versioning strategy; we work on the version that is being released rather +# than a revision of the last version, so we use the `VERSION` file as the source of truth +# for the version number rather than the last tag. +# +function(version_from_git_tags VERSION VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_REVISION) + + option(SYNERGY_VERSION_RELEASE "Release version" OFF) + option(SYNERGY_VERSION_SNAPSHOT "Snapshot version" OFF) + + set(version_file "${CMAKE_CURRENT_SOURCE_DIR}/VERSION") + file(READ "${version_file}" version) + string(STRIP "${version}" version) + if ("${version}" STREQUAL "") + message(FATAL_ERROR "No version found in: ${version_file}") + endif() + message(VERBOSE "Version from file: ${version}") + + string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ "${version}") + set(match_major "${CMAKE_MATCH_1}") + set(minor_match "${CMAKE_MATCH_2}") + set(patch_match "${CMAKE_MATCH_3}") + + set(git_path "${CMAKE_CURRENT_SOURCE_DIR}/.git") + if(NOT EXISTS ${git_path}) + message(FATAL_ERROR "Not a Git repository: ${git_path}") + endif() + + find_package(Git) + if(NOT GIT_FOUND) + message(FATAL_ERROR "Git not found") + endif() + message(VERBOSE "Git repo: " ${CMAKE_CURRENT_SOURCE_DIR}) + + # Creating a release tag through the GitHub UI creates a lightweight tag, so use --tags + # to include lightweight tags in the search. + execute_process( + COMMAND ${GIT_EXECUTABLE} describe origin/master --tags --long --match "v[0-9]*" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE git_describe + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if (NOT git_describe) + message(FATAL_ERROR "No version tags found in the Git repository") + endif() + message(VERBOSE "Git describe: " ${git_describe}) + + string(REGEX REPLACE ".*-([0-9]+)-g.*" "\\1" rev_count ${git_describe}) + if ("${rev_count}" STREQUAL "") + message(FATAL_ERROR "No revision count found in Git describe output") + endif() + message(VERBOSE "Changes since last tag: " ${rev_count}) + + if (SYNERGY_VERSION_RELEASE) + + message(VERBOSE "Version is release") + set(rev_count 0) + + elseif(SYNERGY_VERSION_SNAPSHOT) + + message(VERBOSE "Version is snapshot") + + # The `snapshot` stage is set here regardless of what the stage is in the version file, + # which serves 2 purposes: + # 1. It lets QA know that this is a snapshot and not a release. + # 2. When uploading to the packages repo, files with `-snapshot` in the name are + # are uploaded to a separate directory, making packages easier for QA to find. + set(version "${match_major}.${minor_match}.${patch_match}-snapshot+r${rev_count}") + + else() + message(VERBOSE "Version is development") + + # Gotcha: GitHub checks out a detached head, so the local SHA is not the real head SHA. + if(NOT "${SYNERGY_VERSION_GIT_SHA}" STREQUAL "") + message(VERBOSE "Getting Git SHA from env var") + string(SUBSTRING ${SYNERGY_VERSION_GIT_SHA} 0 7 git_sha_short) + else() + message(VERBOSE "Getting local Git SHA") + execute_process( + COMMAND git rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE git_sha_short + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif() + + if ("${git_sha_short}" STREQUAL "") + message(FATAL_ERROR "No Git SHA found") + endif() + + message(STATUS "Git SHA: ${git_sha_short}") + set(version "${match_major}.${minor_match}.${patch_match}-dev+${git_sha_short}") + endif() + + set(${VERSION} "${version}" PARENT_SCOPE) + set(${VERSION_MAJOR} ${match_major} PARENT_SCOPE) + set(${VERSION_MINOR} ${minor_match} PARENT_SCOPE) + set(${VERSION_PATCH} ${patch_match} PARENT_SCOPE) + set(${VERSION_REVISION} ${rev_count} PARENT_SCOPE) + +endfunction() diff --git a/extra/deploy/mac/bundle/Contents/Info.plist.in b/extra/deploy/mac/bundle/Contents/Info.plist.in new file mode 100644 index 000000000..1c9dedb12 --- /dev/null +++ b/extra/deploy/mac/bundle/Contents/Info.plist.in @@ -0,0 +1,31 @@ + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + @DESKFLOW_APP_NAME@ + CFBundleExecutable + @DESKFLOW_APP_ID@ + CFBundleIconFile + @DESKFLOW_APP_NAME@.icns + CFBundleIdentifier + @DESKFLOW_APP_ID@ + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + @DESKFLOW_APP_NAME@ + CFBundlePackageType + APPL + CFBundleSignature + @DESKFLOW_MAC_BUNDLE_CODE@ + CFBundleShortVersionString + @DESKFLOW_VERSION@ + CFBundleVersion + @DESKFLOW_VERSION@ + NSHumanReadableCopyright + © 2012-@DESKFLOW_BUILD_YEAR@ Symless Ltd + LSMinimumSystemVersion + 10.9.0 + + diff --git a/extra/deploy/mac/bundle/Contents/PkgInfo.in b/extra/deploy/mac/bundle/Contents/PkgInfo.in new file mode 100644 index 000000000..c50e7b961 --- /dev/null +++ b/extra/deploy/mac/bundle/Contents/PkgInfo.in @@ -0,0 +1 @@ +APPL@DESKFLOW_MAC_BUNDLE_CODE@ \ No newline at end of file diff --git a/extra/deploy/mac/bundle/Contents/Resources/App.icns b/extra/deploy/mac/bundle/Contents/Resources/App.icns new file mode 100644 index 0000000000000000000000000000000000000000..8fc7147509b9abee451ab281a81c39ad706a6e5f GIT binary patch literal 162150 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�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 z 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 bCTH8 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 qQ*IR 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�e@i6ScjT_i;W<&^)`0=HcJf_J_SxX_Zh8W1D#b_DG_I#Bnmaht&mi? zaq_)RM;`P>ecb&(+HhG)vNYE{8F|i&;O-p;TZ8 z|AEh`zT&6TZS)?{C1e^3&|F;#KeRz(LCkWhBa9ZG;}FhC*PN9>;RFiQv_DAV7}XG6 z-vg6_9I6SZz>QY-I6=po*oo_Xr4;6bpyt%Ij`y?W9<-iKwjUX!`Kb1oxp(U%>Xsjd z_4_CI>}ZCwbpu$r(t&zyj$6w*@_79S#pN61`XGAYkSRmd_Jm0i>+4zeWyqr+U?rXv z*0-A}rz`56NC{VW|6!vo1Us`3Prabp6iN$~rOUi4Q>5M>lV2kQLYWT?iD_9Fjs3un zw9do_UzR%MSSlD#y&O|lP-2yZ6YmkA`LYJbEh_VlpMv?;PY&6yT$9i{wObYgpzWDk$R}y{a@P3w1U(* z0UM)w@qlQxd9DwF=B9i+J8MvAhZDi<*ljf))juPjG6~H_^d3$Op&7Ww^wAq+MuGmy zBN1@MvI*yIS8$)1Q#1g}SI0vxf1UNLAwfN@-3g&(^(i@q&*^s3&yf`7p|7h<&A-TX zZ*5{v09?vyt@y7cRzxR4Su!)Kgmu=OKtGsEwW@k=neZ>Z$gh&e-`f&sUMx-Z?ZH8I zvXCfDXWsOZN0~1ZxGH2RHmQ~2F8NP^j~QA1VnA+YlOK+d#t6muvcF=ZT$kVsLgwnJ z`?N~vd$<=E^c?T;;I@c1Da~56OMiy+XMnlbl5<&Ruk|VCNq;`FGp=dOUyaJ(>}!Y%# z31ZTcAodeRAM}SzUl|-06657w_Lf`wnZ+*m$_MLH2Ep%yup~YK$|=0*WGp|Mk%UEK zARN5b6(Ym-=t9J0kz$M5shG-sCZbB>tF~(eXp!fJOfoM6l2mRRb;X_9QXeuMYy0-( zHd;ut9-c`{QkK(b=7MlOyD8p7v4zg5rWMb)#-UVOaT#_z+b-51s@)Lxo8(JdV3c#5 z0<{u4l5G9@Fy2V=qM|bG%w|*BD2+hD>o7xUE}>0*3(`$8LV@M`ke%M?lh!j`)*?1o zI!SB&U=+;#RMbfw;>#sg>eiYUK|C{oH-9eY2sOk8P0^Ng`P^wO0H?eumdRh}ExYIV zW7Rz>s*r(~X%#mF)x@~!@fI8ntcGFR4?=}_=de;4chZzEl00>FziY^}m z#a%V#B7LKgsv#`)Q#QH>CoV)~UJGIoKd7JeIZuX7MZ83*koZA zspH&}a;T>!$E$3-drTlvtpw?_rDcUnUCc9y7u@+V80oa#CJ0}|G!^QHYc4&sGLcii zQ!d;)G88OH3AO1gAqLU2pS0hKUusO^2gFUl^2)vbg)ZcNo0`!Iq*UOis!r7AS~OQc z)@jHvEf*PQfZ}fx4ILV5kIjx>k~$bMk(bxdq~WlDL-*0B4L?0F=zCa8sxrj9OZtqA z{t4s&J3z$0sPKPmu^gv@L$yK=U5X>mmdpmJ_0=92(@7fpRGa@dM|G<-D~Qvh7d1voO+0vlj2>cigVlT4h}Td?3x_)Em%67L-{PQ1=ptdR z&D=-gQ=&EVmP}2Q%a*rOMVXFn{Et))i{;Z99+C_t*KkHcs~x_?5%)&h)>S#ush@SF zy1n?0IR}6TG5aeDVM92(0`VUszL-5$PX9v0KT8Tel~ImjhHUgD{~~_5$X$B5 zW-%y-YK5I1>L$y3EK?nRwcszdiL1xflCAr>a$}{y&g4WKqANoqvms(S0WnuD+GM0uOp>_r-5hg{?u|yH=0y z^?_Pb&`Yi5UpRot2s7zrKa0HcPMeVB`_TYRgulp1+D-7H$xMd6 z|50ti>5?v_=e4Y@5T0$+Fm}h@xW;|g1NW=x5Pg1(UUo2wv{Ih-!>MsqZff{$oIF8P z58xE}OG9QnqZ@KCw*&O|8Y`5yw$NQw6}JEFqv@F z;K;4+flwx9H4U3zHE|+6{A5unhVn#%2%icY^T9_9D*1_i)3uJ*EtO+6#O;G zB8XA-__<50k{0r_%JA!6G#@No8<8jRk^tC9W@y96 zi}T7f)<*CoO~@IB)-q2Bh^#e8#$yzA2-d5q^z=!{^;jhvEwQvDQ;N8K6Z3bXP=p8q zE3H`q-%e__PLZ#lpL)WE-$^lwD(~XmOC%=q5NMP%H4W#SA8NvwGudVpi+;$(vg4(ow9wRw3c zbz6{tQ-8}+vVQIC3}d04*$jC+t*N+mIA zREXdIf9`#b-Ss(L1vXEn8<9lb29fOLW;~!A1tz%*>z=dQXtgeqJBU8=W~6dUMeA5H zV?0Vm8gi-!BlQw+s6!T1mb+!#1*;f-02oLB*g=Ez7r-J1!+v7LsdhqBni{-hSvTjp&SpKA<2sE3QxjVV>$KVIc;w z!`RY{5ct2@+3uoLwXMhnbf*a`ki=8>BtZGXic#lbBAz29o_Fq&8;dZ@6ypG-cAw+H z`94Rfj-z+wYQ=+VqYgrhm2cn4A<0>66&>=w{UJvzJ(EyWAS@#MS_LKlG?-Za;^J~Q zb~YfTBgw#6q_ouS8GX9qMFwx>PV0D9VZVwd2Ch9-s8PqXb*rbUxHbn_gP~^b`9*gM z@M&V*wf>x+W!^Gbg2v0tf3KxubWn};UewV0tYJ2D8FNt)@Bd3?Nl18Y{0J!D^ZgS@ z_W4%pqKGO-&&`U9Pwk-C^BSumP?~*&Fm+^HS?cuacXAtQ^NjWv#tYl8>dOjpITM{5 zuAZqBpXg)Oq2ZWMv1Vdx!R7D9lEkA@P4x^kKW?@v$TLvJ*#d$`>+_c7%lMXn_Uz=e z&=Njj^V$zuOB5?+x#j;;FgqZgs)AhyYRAPt{6kphlvT^tQO}p(M7j`syRg16l5hU2 zIFLGOJ9bMeTGIH|2u{$MH%XTdWf4i$ddm~*NYerEa;k* z5(IJ!y%9IO7_TQXGqeceLSNlUTru$36)9Nws)^V*P~{(HYF34TPsdo^e!c%;jt=KW zwLjMF3}MO#EkB+=yn;i=yAwN&8zHh_T`Buct1o{+XZ%Uelf10urmvM)M! zGW)F*Zxy!r2>Bk`7OB)NorZ;#Byroh9p?io0?Th#q9i|S=FFo!iLrF)s)9ms(1wk9 zEHJ&d6cEQ5krN~^eC>UXD)BG9;|xdzj6@p9px^3@4>W$EskIR(hO!@9>OV>NsAeqQ zFge3DQ8GT=m@A;Lf&sFmOxomz(E~Z-)fUj4pgPV;GiwUHZ~X&=Bf)-B)Zf(UfouqO zP`F+cuYk&nE?DfWmo4k2AyjW62^uW69h~Tqs^RQEeR0EKaY>4AXXk{CWbb;RC$_@yEe_jri8%*-AOKyV~ zA(ri3IeU(izV~xH)DVdf zP2wE|NjyEIODAoWSCtRJeAvGtLHc#h{!tAMjnsG(cQrmsC=4GUW?Sec^)Kpn7y=*? z9Lc?|W80mrk`)9v#3s2e#CJ>BkoVYZnm0g1!>vn+e5h(CP92DQwJ{6H3)PaRl?K7# zJuk=yD!&HjRpKp_Pv^}UO%w;}37575eI=!HTs4 zZ@^y-`umke;2;dC{#Fo+ykx&8s42SYiLGQ(D$ThHr1_frcN_$muBe@Pbr6}YpW)Ko z02z^WleL$EhuQ5Gqs;9%ho+x;Y&GY_-}seyA;Q4@Q}_4KbRq+!96@9Mf44VI62Lq+ zZ(y*xE({Tb@-{CCyA>b-;{8eG?m0fh8FYG~bjkdC@gQ~rm1TuSs>nUmUf!d9@l~#T zVJ@J7bVEccQ|Or<=APP7RGKeAwP-BC3V!H7Yh>m%B?VaD|+y2#6(V7M*R zKX>E2gQAz+c1S|1u#A|KCD%%(W>}X>pxMLqDzV|!{@*MNEwF#dz!~Ui;L>#zdj=$K zNhd?zEMFYx_@*b{NU*^bAV&>~K9KtEKY9vXDY!`!us%c=85o&S63GqtL@2Tc77^><5ed;Dm z_`lrJS9&qTH>$V39x%OA3_COJKQPhxCx{R_>tj-$y%l;x0k?K^3gZO>_a6|J7|7zL zk@1aooqO!|R0-OABRj)Z!C_87P3#)f+aa}d+6%q8vv(53>}gf{&cvSppxX#c5m&h@ z&-dqBhLruR+X|O8jei?)Rp;Aw)lVAQFJ^i?ctuFK;wXZ2L(rg*c$8=dF1b<;frSmCH4vF4F7B0c6fJ84Xq{m`ep4Z6|`WV6z|4c1lD({;$;9y)bUx@yDt z5C9IKqq(S!v1Ed+y)`r2l(I2umNd#NTrC9h3Glpd(=6mcPB)#E+X3^{uCo3wmCmB9 zSTfdwgON(dIu*(=I7knAP}|PANA$PV)UP-AVySy&AS$W|jdgF3SKdWVTwG);Z~(`y zYb&!KoQEI^_pP&>E|PEJ-GENF)LGq$iezOUBHd;i;(%jtefMNFfIu@!L!Sv2zh^ZE z{*&potJunyPwW;mJ1GxWA+5{{e~#}7S9u{vf#&xD`@RXEZzfZ;)ZWV1bJfIe`!YKX zv!SWJFRpu3(;V<=s!KX1W~74yC`Z;nhZ}9LQMhj^U1iQQ zh(GxX>}v8xCgN3_^ny@MOs*M6RDYQzwVSzTdoQN;Rlf6MSt2tI#FVQqaWDzYkk1wk^u z`7ieLjIQH4JR_g6FE_PS?9*)GyV&JhjC zHth!BkUon)h8NegAKN=oXdM!2w2+n{0lXQdJ(|dq#Ub^}X3@5^ku@ACh)aTePL8Fy zH;~H)YfYrdO4+E@|9*K`PYIoFpo0w#_fl(|gn;}?gx+76EZ~>~8=&-dI z#${496}k9R9!Zlt)j?=@Q7pySqi-SH{&3#acGZ2LKZa!^>YOCVuDIF>kUkrod6Uye zE-{*@YtZ3Nhq zMU}xz+M)%c&!k63FywhPa8^_SVkZzT2h^ZX_przdNOWU$9s?^4zhGG{EuYYi)}iZn z=4+#XzCrhBooI<&t@^Mg=-Gzwr1Mz7)Ld}3>Rpd^CHyPPo_*m+!YCkw!aG72gH8*V z(0U&)aW~@mlSTAJfS|oGd6#DCO)97wC~bAu?OwN}9+6ycXD8>Gk7XyePD`I8djAQj zy?x8GGv^>P{$0vL&EGueZ!_?K7!YjUtd$~8zbTAewL(Gg&J)M z639CdB8cqDstNy1KEmS`8;T|K*~{UUr}CAgbHKl1Oy`hTvSpFgm4;(IYrU$+Ga|ja zRu&pl3uV<9r2$flPO#WZe95SGNNgN4lOU`gs!0vSE~yQeAeA02?#C%Pud$F+Jtf>j zD^=>J9YL_eE5euD+qhL`yo~_Z^tWR+dTV}?`lgeRcg!VSC503i&lQ%C$=@>R9S{ws zkBpoWHdj%EdEYgluvHsxovoVqmcPPb4aVAjU?X1c^S62JP_6>gYiu ze8A8Mc;vXBXDWC@dNN9afKk~yhc)TRFiPojD@k^J4<1LcTm523Wx+g%d1#+uXc17e zD_Hd*ru)HejAJS9Avdh(fPO`oRg>$?umFKY*z8fCSWBFgl99$F&p+Gxupgl>-yqgH z_xc4*zvXegD!rFqvm*H$$*#+0=iTs?} zR26~5V69{&!hwK0wK3iB{EGlimt-6gLK(*+NW>5>rtc8jnZC=BoQagJVDVlt!d31F z=5@hH%X2L8n!4?7|6z*um>gOpvo&#U@Ohbdg!~z7xY_S9FH4K^Sq(l1>OF&S`L|U> zhIJg@`dNR9eE=uaQQPS8{Sf(=`U{`leqMT_dtnciD+g+|(T8`^oq}k!LeeY&b>q6Q z%tBPrM9Zx&=0LZ6jY#-xKk7NoUD;Cry>Jp~>MD}>eTEiYJ0!c>k+$M%-&_~bBIwO3 zg{l@0P|M^e1gKm#DYY$OIcpakz-l+Gi#xx|u1HRg+4bBn4Tcst%Z&EDhl_ExbF|cI z$pBE!|A*hRSLZ3Zz>pOh=dKs}f*2K83jJ$FflCTlm6Q%MZagHEu$@q73WX=GV0maqUni8OK95?+jXX+yi`V)T3THkNG2DnA6%&Pc3M^=)z<++OKg z4Ooy)Wh5BHLP!CBowL$2%?s(_CGHvW(5gHvl70$#ZdnzClaIIaKoKMnIc~4GJ#f;( zOko&BRbp#g_X7w+YFj(EeF~+qRUOAWYy0NwTh=wcRdi3?1mWps?hYLLk zQr|}#HCcTG55~e)d+$Z4n7<(FxU2G zTLxl9jp3To=G!7@?jbOtC^9lLt_YV#??%4HGOy+!b(goJw+dD@S|`!rm|vk}JxIj= z3<&^^psIZZv3s0j<=gmic4)~dY75^HiC-WoX=ccqcsc$%K_SfAQ1>7k)do1eigXzc z?TgKU=FC>U_XBu2yWYl+wL1RG%HWv#(}#KbZfg$PIIUgKklL>CuB*-)h=s|B1@=I= zu@|4HTA!9Wm>3`L11A3(@|XD}?%=5$CdhJtbH2>07e*b?zIjvjRfa&?$P3k_2r@Mq zX3&}L5NcV#7T+%x?MZC%b}LbXerNAb{k53->+{mMCD0Up3n?yjG3M=+v(UVkvsm~+ z(QxkAJIeH^&jznwR=$yGKS_$`^bPobPc9S6dS5|L_#PyUtXzopt3ve!=dhX@QWh`O zCe~%V?2gE>%<8dSb@RH58{5e34RnvO)L4?_zr*u~*n~aPPUzI$N*JR_SLCQqReQM~ zFX#dD_%Vx4Hmc{MH*>#|bhDAYIF~*|VE=FEuiD9D-Gm{?L<>fuS}~o`P@L~E)+b_M z_P3u5a>IeX{^Xc<#`5hc<@SZ+a9EB`5+q(J$U(-J@=m?WjfQ$p6DxgVi%liSdjDx+ zn(^t^&u9n*aq6xr;|@zT197}B;3s-Sjo4)5;CK$#)eA-eVEzV88llY*WGwg$Q;ZLL zHswVKz$1yOaWIx&>g>2IqI2ApPoHOtFkl!g+hE05nP_)Rri8Eg&w1|W&DjZixx)!; z^bPq9Zt`|s=cwd7fHAZuVhhRbDV>__WR-W|uG&wKTXVMK-oFxBv7;gYtEuTi{rlYx z&Y;M*$#t4Iaxg0NR7SeAH0y)#l*o2V?L!qgu7upUHwIC`*1K8eM$A=j9OUhMs%%L3 zI}_FF;1`%~z(vqMb$>b_Zmg-*23PYfb-YBwjmP=9S&`6Fr#*A1Hp8Ob=l&J-sRX5J zVkZ>*N*!1%!?uire1yUAd(oWQw6h_Wyu?frS*FgI(|n;$ry^u8M%)@V6!G!5}GT z7<^T|)^TbOu9Gs(SWAww972Hh5aod~Lr#8=IGC`tkh$k_0MB&`)|)gur(Hi=II4S&I37t&B&$;+xb&{${@ z9{VHTe$raH+Gtn!tjDFAIObJyMYI5ZzE4s+r;;YbyPjfmA%J`0Vql2lj*V9$@5S}a zkd!6=3$*H(jtJtdjKYH!7r~HaKKhd3{dwkj)|u~2nLg`)XO0R$#yj+bZ~st>qAGn_ zq@EIX_5P^MCYp|&FU9IpJltgb>R*UbnlN^jg|5~tPl_rsLzjYci}#yERnHqX;9_aw*$?3l08WjxjNmt9CI z9m#sd=cjf_7wbFFXD19}7>nXp_)VLketlJ1KjO)WrwO z>(u1MOjrJi;(Fw+?^dF$d~%(&O`y3`1HB_b7lPN*3m2mhR47)j&Xb$s$Hx7U{x(NgI zc2a(tSk+hETsHNLGX8GubJRh5({*2+w6gg+M!#bp4ReD&2NS8c7#k#?I zhbEV1?0e~DR$J>iDu-9JUEL-Zj&LNf)~w|;lCV(ll|oMzp=y%O0A?kMK5>6%db2wD zq|-OM8pzi>-LLh$bTL^6$}lj88-yY8w(M^qlX{iwWrw2o~!o@L7WlQ@vj9S?SsU4oh^NJQwG>$<2)JZ^ZQwM%J0HpYiI z{{%I>2x=Pjrr=2i5FHu5ydCOMg3@ne8zM=8e6H$2EF31*ws$!rV5mXT4O{K$->mhL zK5ke^E+Q|uR(0fr7B9;?+3nwvjfY{V=FB#Tf2CO$GiP#d+NkFkk{~>Nd|xF){{;Eu zqg=McqO}vJx4Q@+kFc^$QE_vejR zh+zBxJPg@k1Necx#4q!pJ)Q59D=+Ev6ov?042jZiv$*v?+feW#7XDeBgZ0R@qY>fY z=4l1)fMaZPPi@Ht_IO9OF&6M``B&_U@q!)vp1pzTu?P#wqqqrIiTyvu`Mr=O{g8@s(D9}k^Q&>Ebdz4y+?B_|iJsWn%?K5deeYJqHaAykx}Af8wJESAX7`qBe#jEart@%>~?zhgvn zZzt_$R_2-p&Ha~P@?N+#Fi8qxwe9F9zvsvbr08A15C-uSWq28+Ns4|=W%>Zlb>&32 z|9#NCKS=pg@#dO$)GMlUD_otuAVe^6Hj`h`&b>SPrEUEH*jGHOOA&sV^hvi`(7aeN??m((X)-23=_4~Chi4^9I4Z(~XO=W&V$l)QAlN>)2G{^9I-YG~Ew zu{`zblKb61`ia8t5oOx~&#mAiX^Jex<(UyyN@%h-vB|f{?3wv5y68kXbuaLzyTt^& zcAchC{y-9zQ;P606;G%{E#vZ`lR_w5Y}@J!gVNk-2^)nnSTPj8tu5)+p79Ciylq1p zhLRz?hwToIWRLnTWWeZ)6R!SaeFWakPp@(oR>7-!;UaSK}%$g7o*?>iClP$&A%m|Y3uYRu!IXbqGQMshDnBP<3DMki?ch4(TI zkoEIz#bBM;Jf{`f7}K*_CqM+Wvg|yGk0E&cwsxmsEijE?nXRc7%y#GWX4e@OgF5y< zENlQtLWCXJNPseT>)1WWASF$b=qjaadC68Brgr@l8wBYk$6z>K0)dwR_u^T`Vn1O$ zT>;MRz_d#-;w$09V>;QB;@w*3OfAm3a110VTCzG}McX;;z|vrx1mif;Bhw!uaTe4$ z!vV}V3Xe=K!u7t8f%F0ou>B!bIEUcF8_*k+P8<;2lAL+aJqusfs9WwzUd}81iKU6{ zhJ+H<;TsIkq+OBA06$JfJME6SlMdXNmUBOP1JI>SM(R|JZ9s{h`FG7JUa9a*pPx(V zM7Jcl{lxi~GDSqWkS1to)_cx31jmG-!?5e)qc=p!Zdpk6T?z zP4K<;AndKKy2E7?`2=Sh8hhdq>^~YeDPYRxaI;f${Y6DDuRAMPe0dwjlaBhpIsbqE z5;QvqnLpb+!M=gOR?)^H1Mwc|#ZoIInF{@S%~F?csB*!}?| zUw~N{J`nAJgS1*W<3PO&+>;IK6$M`cHSk}d#+I_zYpTKrgsl`5jt~tU&;|Q^N`2M& zMX8&W1a!@l+=;EJP+~-fT;7Rq8(->f=5vHioVhVPoYPwXNEXpLjaX-h{y_@;_b2~! z%sN%Or)Bt@W4Hx+yt;KNlO&w>}S)x5ToJkDNQO05C2t@E-<&-wY!~1u>Or+ zayS;ARH$Iw$Om~zUwwxiib;_tIqj`F^kRbC8R1JM6L1Ff*vQGIwl8OIX9zL9k9f5V z>(nTJ=g11A=v}}PG;S+dXOTu^4B0~BseX&RpH$dSPL|$yx(q%7Z_cz4=q0U3OE%xQdqf!{Fq1GN1=%P!9ZTSKWWHk(S=+pzPJt7?Wa${Rz$d)vXyL~o9Kn%(}pzBY9ICaJSa@@!|a4o*vtbZqq{Yfo6E@2rc$OSYDD>Vh$3fb84ZlK+%DKIU0+&h;5CfCj55h!7N{YedS z8D+-1Id4Vv>Z~z(W|?MtYP1KY-;>U$^Ro7MdQZ}r-liX;3|Lx92o$#Ru1?WX=;Ga- z`&TmEAUVae+|LpY;Hd}?LL7v31(uO>Hc;?GekHS;o7#m7$%ML=u;lh5{5gsA{v-2~ zgtk*#0jK*t7{0I=WxQBI|27fb$Hd7QF_{d1y}nw+c8sbfD7@v)=1`T1N7pPtyQURy zQqH|w?N@%)Js;A2J#?nAv%#0`tWSTG?05s5AT|B*8r7}ey|UzbqF&Vnk(fTKH*+M% zxQvz>>AsL2U8{U+)qlvg4P5S^QW6KqL|zWck5mZyp{2*}v|lG8>xRLD33;8ToJrvg z`o#rC6~fppmKTP$(L#ygf(kvSNSp`b*-kTjS*@Qgm@z&||4&_)oymH{s`XAUUSB3k zn%;$KQk?T-)&?lp0WVafDTrYzwd1Z{eHcpL8jnYGB%^)_i^0_=>a|ZpQ^?oXA!$}2 zXxZv+aM1~i@fstgvmu*Bn5CKXw+@{w=OCLcrhpzbbz~W82;n*BHv*$;U32 zb8J{&55DG)u_sxtiIYX`QV+FydIvRo$bbKSVK>q%ibD@NQ1EQD6LB0$CbT~8s$q9$ zS^qIFN3d^4HrLT8TjRTQCOaebl=!n@g|XS%{cquE_?xYhfmqCyV|g+G^Ss<8Q=$V; z43{v+TWf`C!t84X0~Gpn)C!pXf&89?zuO^(9Wy7Hqs8dFZk0GK#elJpqUO6uEz0<> z-nTc_(4H$Y1nv>T;QueKBE;Vv7V3Z|bgmzLy$9F$aIfbS*jp9|Z;fjy!E2@ifsqjN z`t7BPqL6mD!9acsgi$K5xZtH%a9PEhXf#e~k6vxiIR45jykx{x-OnK1e^(#FH7atR z2;|0g2eb43Pno;yC7WS)$DzZwR`SfLJI9<8FdZk1uHuMf_^Yr;O4(|;7#&-gd1qf* zN$XM)94#K^d5vM(`OkPVlTPFWOY^0M?yIOwpIXuv@-0x2nNLz;w&*LPm5$s|adp20 zdpV5;Z>MP^lM9%&qR+w)pUIZeI5qCS!@q#gtVKc8%7SyU6=i-+o`Jye7faG6Vj89i zu{F=+WS@3~Rq2k#@Uy8ce%`vvQ>IwS+5&-8?OEYg&Vpws9_ehrERP$Ii${F}*Rb?U zg#@59D!ehU$hX0wG~1hb?hkfOB!i*Z9v{@d7B^GB|9|LzETN@*J&zB1l~0|Q^wx$0 zxuKqE#yVxNPKH0>$*b;oFeU8L;xR{NRTd8)4+Q7s`J-tOhd_O%AOdI37v*mw*z1fM#bq#W`-7D%kBrjI`2kLbEPrU}X+DY(rs=<7Owt}3l-Z_Bhu6@l`ix|1d1=TaP`$=2YKQh-pbhX)SI~>T>k$;B(YX!J zhix74c9#JAx{%Qx7@G(0INS@y8W?(uP&{kCY%Y9iE4`HMnBh?z>X8Lt+4f=PHDrf| z`^a}x10Z{`{KUAZn%Ze#Ukq?^MkK8QCk9?>eQrl#TrsM$bwR|_7(7y|vN|9HQN4KP zm&i-y%?Lw_x-Vshy|~x|=%S?rEqSdzhZ*TC1CxZ`YF4Ogf7u-p4xs)OYncchh0wF4$sqsHruc2Y{zU1PMNLawg}Zi@X);&UVF+-w75D$z5ZUR<8#y$qiD z^+RXb$rX;U0%JoTIsxjhx@NKU3s$n}fdz5e?f|Z*D{6HX4$3P_gc1J3L7^sXCzs+L zuLMFJf^+!MPSS*VF$i=!AZI-Bq;`o-l{M^nX%97%Vn}E6w3}Z3OLACXG^W_EV28ek zTT2<#7~&Ar!cr9QHpjRXt99S!jBo@CTT)0VSFcC!VuS^#5^&@A$cV@f_9Wks@S z!SV^P3L@d-FbL8E6_VRxHc_C{?<~EHb#lO&m^Rte;Ym0>qM7G|$o$L3WcgBVxp>;N zl*;abT}+Tt3YyS#&tor;1wyJxpHg-B<)M!~9WYBcal0>Fl&xHp_NrOS%Q(F8k(h@% zb6gAU8dT91OE%t}WP#e*09e>9#9?S+6T=J(un3>hOa{y`lU@@^gMs9C zjofCD8pyp_yKrn^VI0(aeHPYQXAk6CFsa8c7aIrel0vya*nrG#*(El&V(Re#Wl8dsmXh~WRwD?65(mCm3_vc z=X|3(CvKAmY2oLk)OFKPZ<1;}1c4y{sK8RdosnmgDH;2mn~cLg22=($0(5e{OhdlK zBY^D}^3{VTiqF2c$Uw9396;~KK}07(t#({Kj`wZ(TTN?XJ$`=ye*A*&!4E9po&9!#c^;W33;yb3eQ3 zn85sir0P_$I<>^0)bL;Nl}MKk{8KopiR=jS^+bW6b?h3)xF7aDo{PTHw&R{d_QwAk zH!7i;B&M9V9|A2YIMX)L@STBWN+DlUa2zl3uv!yYNSZ)I`0OT4(9U-@0#0bz<`~nt z=^gMo?!fx1M5X8J`yP&00gX|E+9lnEdr|v$_%KX$3Hl@l7g2jZ@rKAH2H<*@e9jIr zS}S?kCE68yOzF{Cv#5obgH4Neduk?<@%fvc#wc0`!+bNWRaw%S*TVcQNTEyuLu3JF z)0^4d2k=(!a#NQ|V5iPBCHF;6{5ahvxe*#Gt(DZF*2kex=TL&oeS~c5W$r(WJTlu* zOh3P6^=MaIg-z;2t@CB*zm0l>5sFy88aU;w-LS_dP`E^ckgtk$8nXp-J^ZcN4Q_DL z1$ZFUkwQ4G9&!omBs^hL71p?ywOm4zq!3Kpj;L$v5VYJ|yT2NM7;0Dz;4cebh`0V` z1i4a$1lr%ZaQESX1J8R5-^D4K1BgwYU~X^ySGxVzbCgg4CaW_g@T!2vtsVd*yqphL z4$9{xC#9fHk}ddu>WjMzS3oz>n=I-`zh@Z%P%&1{Nk*}3SqRwnawhwKn$F=I7 zcDxbt)kj8SovU$GicC{8|1JjJ_8e&|1-Q9>83?D6-3sDz_7M~y4x2E^Mree-y|qX)Vq)103xz|=Oy^7cF`XirLPnP7$%t#{FDk>fU^SQq~!S}I9eee^HMq7;zWS`e}6#N-je#E9F?^1MF<97PJ7 z7X2pf{xr~_F&yiiT)2P|W{97m8REgiAwwVX1)BiiJhs{H*E?w>*M}-PILaT>qcHH5 z0*2K);msi%{+e-gB%uah#Cqx5Pk-*U_};;7J`$5t-QO^f9)gu``wDCo#psDYg} zjR>&Jjs&{6zE56@ay?>h!!{0zGOU<-0^d~UB%7$tk#vwppZPFPYPOkDhmGH4txRSE z1iLS7Jorp13c#3R#>u=_fYTg`}bauvw>A=A8GVs)&~nmoABOG zy2Hv};#85TLwuKPC1Be_r!rZRYU@WN<@*>mlp<@x5kLMk1#tY;?k>1CVs80E^+X}~ zarjmPWaT# z%o!+{__#nJ7Tr6PqA-;O zMJG)X^m=p752Tcv%`5@?kJW=pp$@Xzi=3*4TJf(j{SpW5h=L7?@V$hD9{K9d6~I`5 zg(aNk)5WVID^Kda*t*b0=d?gaL?LwO@4MwiTH zL!lyhfy?5*F%mt6;&nKk=dBxvQj6c@Wjn%%fH~fQIz>&`ZZM~FLd4m? zQ>$EO+?Wy+VDT{V174s|<`LJ~8sm2m{2A_kZO+2dydAS(uJpCxc;Q?F$8>vS6T3QPKn5kAoTQlfH!d(hq(OLM^nJh9Ip`t6%H+%GxOFw!)P7C?sS9`x z{{hlBZaRZ*<2(E=KAjF{$}MUqh;ZW3`*5jt)YQi`18H%YcV=uqOM)}FwE=0vuC)Z= zf;yh)3IY3#>pL`xzw@8Q0f5y{WY`r_zvWAn5GWR&ryO0L|A1WX$Y=EDk-H8WSixK3 ziNJ?Bk<4r~xF=iNI&pMRox1B-QnWy<#)WX>GZxuU#&#WSME$8G4M=4Xw_P>_Q%!$o zT7aJ(c=WtdI~xj=$fnhrNIa4PTSpzP;`tl|9wXNV1z6@?Fznyd^I*I1zKnC_1)jRY zHLH$m@)NGNlz=q~Cn#V7ddtlgjqTR{4Tm!b;1Hn&C$SN3@G6P}L_k)k^SejmKztqX z7v*(wv^|kFY-rJ;Dyvr5Wi=scI;*Oa*zyxcU=)GNNmq_7cVEIhycC*u3Xmd@>Qngs zTf{2MxddM@c4*f{OPCxV(!-bKPB{qlls}q(t~K9r2!R)rfY%cG_VomAMGJNwF*D?* z?$V$Bgdb)CA`XC|5I3o$mO5EEc{2{PLBm1GzFBzQ_j=7tM6FIcP1(lKa*YuPSB6mm z&l0@|#>7EszI6@{t}1!0_#|5w2k(oV&+lpMapxwLzD!nU4vz?i&K9R}ovm8ai>`Ek zeog1x{)wUE(LPj;-vnO%bzaWKKd(Mc2&hP|4YwBz;*QzVGe%=ZG|L$v+Li;F znVQt*td>hT?5~L{5FUDbt57P*9@;Fw_Q}ol;3Dx0a+QOlBb6~Y(ka9-a8W-M8)Or$ zJR{RxBjl|+1)w4yANKAnqv3q}UUh5{GnO1NM86I;tFp{f7)T;eG?PcW;@}yhee%TB&gDwF*aD;h=^AVnE*{%?&jUXtiL8YC%`&4pY}} z>j<68t(Dj9ziVyczP^ISRo|-SHDA?i_bJH6vH*Hr8EbKFC*>5SsJn6jr02~{Fuc(m zdVb;iS6`IZC^uf5?6JQ)qJ5O2Umz?H%ck3|pqMX1bVfm?Zolpl7M**Jawq8%1;!u4RiX^lWcytlcsTe0q?GlSw?qK(|dg89&hAc*5TUmYH~{t2h~ zS!SJo8}I>j`|$J|b25p9CP9v_o`W+k&9t=(1hGuDY+3;{F5&^WM_$e18wr>W6vtO{t5TuxW2#m z9KKBV7ZvtOBsuGbXEsO}XoMi|xfk9!<2 z`{KK6q0^eicfCPE>~u6IU(uLlUrv0QPQs{0>&lO53wOqqbBo8G6=$16HMKwJNa?qx z^r&_k%9<=P)08SOJ~2lD*NB@rSB1XaiQt;TBJ3I(MNy7Hy-bF9KbrEP7c1QC`NBgA zEE$+f<)*M>Q^U~vb@WJDE(YOxq?YA0GseuidQ-rI)=J>?gi0v#iunK{T?hN`ZZ+y0 zw`q~D)^U~81JES+xK^zZDL|ZyrAt^=Gybuz0R6hu=d5*l0o8SoJb>yb>miJ_N_stE zA+qMd6ikCGo2R1sy}o@BM;Yx2_O{3uI zxfp#)2Aj)iP>YI>Aw8IKQuX>ZdHQ5(M!l^$^qEPX&}EA(j z;znr3QA^Y9VO#tPGapFG)t3}(dTVzn$ho~V>0kT83uo;jf5q5+^MyJd|1OnBBi~n{ zXZ^^fFViTT;x=9^(Q3Kv$F7FK8~+*!AiuVZtc04Xr?J9DSP(6^RE=(Xn>7JBidxt+ zU^b1FwgXfeJy42yrVgf=w_-V zbO}TToo|6Ht(fn0WU?4x0v;l9_h@A3oX`2%%!`}D*{wezUj{oul(9Dosw*>KMOu{R zyutvK>b=64-OFfor5z1K&yqNAv-eRsBmHomqGYO8#Lq~oxvGEILM6J2hbVU)wRBCx z_vG7S7+)FJ1gvF`FBK1Z`F+${Dy0>#v(ql4$UAZ5v4qi3>M2X!^Hh71PK2E0U1Q7$ zC(*H{>Vw8-fhD9>)SLlg+@j@yL%bCLdO{yNaJ!)gIVG^Im>sK)m?1HN9^KCSHM`_4 zHcz>sY?PLqbg+CPAhV9V!H@>8af?lJ3`2ZiuvwK;^#*zkFdCNFNW=ib=m+t~kGW@K zO=AFGk`iE~Y*5n}OKOD`Lsc**=x|i0U%(uZQpKeu;XRm8Zi~_8iKwqL!zb z>^2Jeyh1`>GwA{ucZ|8ZE-}J}p52$G&5(o`I4DWL^Fyr=c?U(M(P{`Re8L z*D=dZ0AZgiVRYJxsy3BDxGBYXC+-x%^iToGzbsH@svK+?42lJR9%d49;taVTUoYeQ9(| zkNK`P)t`V3)Y@k(6Fs;#y{dPXT!0=mtk*0Fh*rgLf^i<-IwpLezZNO*rD-xZXx%9& z+~>;>P<-j0cJqIahXg{YP!-nzZ$Oa0995n1J!q%AMPbjJCp8u&3PNEQ!$urexGnlu zzb_gSHRCnW2I=nn<*FWL>IK;Pt`yXKX?|ZJv#LLLywk74hgaas(I_PUV21f4)LYD+ z>;3k35?cxO5>aF!MQWncicfLDb9LmV`~}BOI7r|+(5~~dD%A6bJqD3+ynpmYYl_X+ z0~%$RxR~>Y;>>UziH3%(2`ZGSgW#A7=8V`}R2~0CNQJf2 zo>8zZ{+FixtIK?8J6AgSL$pF_I#!G-2wQ`=?)@?jaWtbPSOD={r3q31C1oYkm^~OZHI`RkT=tooO?;4;Ic>wj@j#_1AOQ#D4Zc8a)+^}gyRWJ zHf0k$m2x#3H{|llLC;AWfuzP3SOEiacy=Pq4ODod_b>(PSwu}^cl(R~jbubI)r_%SVNyKQi$WwN1-Dga_B z%r+A?tj8<2!;G9Q*5}yl|M?%Qu~JWAh&6h)t9=pk#jLP#4ghizGKi|^E;jn6uZkf*}x zHH46a9O}_6kh~(p8n&t*Af8nhXMLzys8cp#hql=ZgwL>fB-VrPwtb%1_`r|{oBErw zY`IIxlLiXIrw^ z7AL0p8ziCMh~R%a#p2NsLUB|J9l^#_$>hoEWGK_b>ap!cg2aI)#NC*5kY*OjhO%6G zf*Qc+m4IIdMY3NK!4~@h$?#dIy=$mbMJQ22YjN33r`qoSFS37n>{kF--;Tq-@w|pc z>YlH9@4N8DiyR=-GoCHEGS>n2M73h(OEj?Q?O?%>%MaJ|ujSw05 zl@-UA)`^8|Gr(&d@wTNN7>rWSzW|EA=s(#gv!^)VcyC62d0#HsHvN5N)1t92Ptpm{ z{T_I1_|4&RK9EEFL9NqUaz&uJ9M6(ulaD+IwQh601Qq3^_02DgvWEq<6tNij4-sJ2 z8yJl>fcd(SIKnDEuiB{Ey?zH?65AW(M(oLvpSPN%0Ap`t0VM?YTCe%+m~wxxt9mj$ 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 UO=)PIK8o0vSa-ey^E@s74_O3;3IG5A literal 0 HcmV?d00001 diff --git a/extra/deploy/mac/dmgbuild/settings.py b/extra/deploy/mac/dmgbuild/settings.py new file mode 100644 index 000000000..16ff4b8ad --- /dev/null +++ b/extra/deploy/mac/dmgbuild/settings.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +# Example: https://dmgbuild.readthedocs.io/en/latest/example.html + +from __future__ import unicode_literals + +import os.path + +app = defines.get("app") +app_basename = os.path.basename(app) +format = defines.get("format", "UDBZ") +size = defines.get("size", None) +files = [app] +symlinks = {"Applications": "/Applications"} +icon = os.path.join(app, "Contents/Resources/Volume.icns") +icon_locations = { + app_basename: (144, 190), + "Applications": (455, 190), +} +background = os.path.join(app, "Contents/Resources/Background.tiff") +show_status_bar = False +show_tab_view = False +show_toolbar = False +show_pathbar = False +show_sidebar = False +sidebar_width = 180 +window_rect = ((200, 120), (620, 420)) +default_view = "icon-view" +show_icon_preview = False +include_icon_view_settings = "auto" +include_list_view_settings = "auto" +arrange_by = None +grid_offset = (0, 0) +grid_spacing = 100 +scroll_position = (0, 0) +label_pos = "bottom" +text_size = 16 +icon_size = 100 +list_icon_size = 16 +list_text_size = 12 +list_scroll_position = (0, 0) +list_sort_by = "name" +list_use_relative_dates = True +list_calculate_all_sizes = (False,) +list_columns = ("name", "date-modified", "size", "kind", "date-added") +list_column_widths = { + "name": 300, + "date-modified": 181, + "date-created": 181, + "date-added": 181, + "date-last-opened": 181, + "size": 97, + "kind": 115, + "label": 100, + "version": 75, + "comments": 300, +} +list_column_sort_directions = { + "name": "ascending", + "date-modified": "descending", + "date-created": "descending", + "date-added": "descending", + "date-last-opened": "descending", + "size": "descending", + "kind": "ascending", + "label": "ascending", + "version": "ascending", + "comments": "ascending", +} diff --git a/extra/deploy/wix/images/banner.png b/extra/deploy/wix/images/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..cbf9b4675daac5bf6ea432666246c20bde3f946e GIT binary patch literal 8593 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 af6APkH@o2!`^)yQ>sL(wAn4!u^S=Ob2FI%a literal 0 HcmV?d00001 diff --git a/extra/deploy/wix/images/common_background.png b/extra/deploy/wix/images/common_background.png new file mode 100644 index 0000000000000000000000000000000000000000..647e8b40ce5bab287b874b4870fce3dc467ef619 GIT binary patch literal 18998 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

!-7O4Os21t?X&1Ujh{LY% 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

9jy39PrRdC#na;0G`~I3ATr>D9j{^vuZ-fJ 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 H)HBw>v^mT~KT}fq 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 literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/icons/16x16/padlock.png b/extra/src/apps/res/icons/16x16/padlock.png new file mode 100644 index 0000000000000000000000000000000000000000..e313e97e96215bfc299305c9976f269fc5f3561f GIT binary patch literal 450 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}LV!<*>-+cbFU}QxzR&CZ<-E^# z8;%}5`ub$-<1H4y9!~u8_0r#;kN$qYeWZ!+#umlDzu#V2D*pQQ>ytf#$B!TX^W#HhKi{mlvrXo3J@5DDlYZVRTf27ce=wL-w)8pBE`^dHzhH<6PLd%e zM*1yKEn||myNg^Amx4Bsy7G|u~ m?A?|@T14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>9EP3g+M90oYVH%7i=;v0i_@+A>`b1 z6Wh;*Ip&^67N2z|a^itG5Cz@W9lNhPK-lfqEP)7xeQ+(13lRrNZ8~GT>6G!wdB#VV z!3AOBK&jIpwD}}R>Z)V&#dTI&&Vsc7r9cR(<*JqEu7ju|_HboQR~+ij9|+lZ#d_;G zxFC=LaX-Ya|Ns9p&z%PhNrsG)AirP+>7r9lt{kltVXXUir%6C8C#!qcmx(* ztrNNbL4^O}v(8x8Tn5A^} zv;X(!7M8tU`R=Wq>Y~dRBr>?H&gv#zz9m-l|MIql@Yu(Qz*%MxU zcRn0d+n{>;$+>k;_x@#mvb4ZMPFC8^GW6)BnI|kyb{8$Js$4o{*R}T9(f96Z&u(}B z{Oik~q>o?U-i`kM0u_BCfEpM)UHx3vIVCg! E0NHet4FCWD literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/icons/16x16/warning.png b/extra/src/apps/res/icons/16x16/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..193e37733dcd9c3b4d38be7a6e77cc0a50117ba9 GIT binary patch literal 693 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 bK^p|rsL&TegMu0r3ZQ-H7&#v*gUbF7M}Ogd literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/icons/64x64/folder.png b/extra/src/apps/res/icons/64x64/folder.png new file mode 100644 index 0000000000000000000000000000000000000000..fb96ebdfe9f8ab1b0de979ff60afbcbee7764e3e GIT binary patch literal 919 zcmV;I18Dq-P)@~0drDELIAGL9O(c600d`2O+f$vv5yP*Al(5=FmwZS1;7d*k5!9ER8bzGD`;3j=>~$Azz)a`h*YXxOwQ?y z6GMDU6FZRDo=-v~zC7@04s&KaiNYApryq{idIzu$y=~yJ(5lsa|Duf&pn?}$CqIv1 zYzt3CNhJCLpIlx*2_!1#RrV<-{kk3Pqv@VoD}UiUF4)YkE>MfG5T7sm72B(sMcEY}i#I@alDSJ&&; z&gWk{$U#*F-lOfH!zC;-Qh9+4)&F0e*3CLHAOd}CqgJaq$bbkOVKWvKA}~PfSIB?} z%!vrhi3rSz2+WBH%!vrh$y~ZI*21NZ#CNSs-x$?H5n1ti$UqK(whx0ZW^i{l&-PH! z$R^NO7T2n>xPaLOS~RTDDR{y*;?uDSer zdV$8a>84KfFt>s9v(X^ERgvNExPw6MgW);~)!}rCCBI3go5BPp)D& znbk`O%*8~3%GmR0^wDF`-KCjkSSYs7!IdhIqpurD9RAXUD(oQ8T%jtAgDW))lfd}W zwe@|8zVNOjEdo34hwz5kk3Ne72%+Fg6quMe?CL25LS2jpS6m=h97&18+HD9FTz{M6 z??VFpZOh=U6TeLohYxk3qQMmv7>_=GUQZIoD7a!>C_R2bV&WJ!2`W!ga4`=86YAa3 z0D6^cU7w`j3J3(o>s{*Y(XU+yEbv|0Ih|VXLVbh4@SgZ8?WC4^cM`*TchVIqdSLj; zCGNI}K;9E~6B^0m#A;!wcZZ~P;iH81fl}{2kkm{`A|z1i-GFetJBj-x>y^B%e zXij^z3>QT(P|aCBd~+|5$263Ji^@WgXYA&*OE(}Cfdl33arQx_;2|m7Hs0NvVA^07 tRMNrWmrs;?D`O1$7}T%a*7F{6@DD%Je2PPL64U?y002ovPDHLkV1kKHqu>Al literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/icons/64x64/tray-dark.png b/extra/src/apps/res/icons/64x64/tray-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..e02e133ad55828d25aa83142f77406be6abb197b GIT binary patch literal 1032 zcmV+j1o!)iP)lzGMSIcj79*!5ntw9PPu$(u18G~R$P9}X0zY)UJ#|ZTyv?R z=feAc!R1lDlY9DK@DzVSJWq|aJl9owzb)TGH*ZDxQ?g<0-S6wnLS(}88I#bMyd`UYWTA7Qrc+`{<%NY; ze-ZJ&aXWzp&S8f*&6-QOBjXNnj58hJMyBr)S97`Katd#`>yUAcc%=SEytVc=-;z#B-M)3}L(h;#!p`sKZ&M663WQ5ceA@{{@(hS6n#Da{0n-W?o zvIKFXZ#MEB?a*3+trbM{{*n@d?)adKN>iUI9{}fFWnccMxk~STYy2-Tsn0L^h#NfW@+d&QS;;x?9K_G4?l&JDX~lFDaaD&lMuGO-^#y;kZ+%w zL!1=|W?mX`BcJYs2ys>*EQd4(uG8-kM@1=tdv7O(HQcgQM9Ap-n$7U|U(vX}hoe_? zSYb(Uh^g1;fB0I^7$E+5e7W=ga)6@ynA4ab?!2z>{&xX;Dw+c5oMP=g-*@`FJ>@;6 z<^iQ{y=OInl@^h?pn(idiSd40QqQvI-t2kAj|~d4L2?vlEc=cIV)CA#Bp$e&@a+w~ z?g&3j-q0|lGjAlwNqq*{8?@(z&(p&90kOMD-`t~KvzZ*U@A=etxeCPiff{aakWBGW zUYFkmE{Xb++8o2SDKfYJHrI#LxrIz7lgVTEp9Ri4N{E$OZFh~XEz7or z`uM^KYTkc{pRro;+QRyCWpT z$eo#m=i_79Z5;Jth{hBCQtACvTR*M@tO#XpQymZ^v2=ii^L6(2+(v|k_YlJFUMdCd z`NvcT&WvrU6Jj882i%=rSeBl-fnT17IFNF?dnpEYCfWyc!?G5ZA!>CHfjcvE&poNi zkw?9UgP*kC`zh`ZDVq~Ym7^{R142TLN2^_g7{QKwhPyzOu4#%3FiP5&1QaGSIrw^b zunr*z)d90J^NTn?e}@=A>+a&ZeuoWtT&umWbnj=!r%HH_x2&)eCbBuF(Ky(GfCSzK zX=G^42c7oRP*12$0kR@Jxqf&Gj_W$^MP)GS8^KaY>yWA0mx zhU+#OUp6PQH{al1I3>7C0faS?$%)3{K^2&=mw<1S&=CPP-n0Gs2fYzvFnfF9DR$-y zX7MxLqz?8B2x62J8MONQSd1irBl>P4dt(Pji!=MuDI?wF&H~m92sZlhjr@56%mnr< z;5kAqv%o2au4{QzjVJc&yCt$KjH0>p*yK}2Io>WHg?QqDSH3# zKpLwsVy-n)#gj2_8GVPLJCP9TXJEiw0=A9Yt8>wP@LEOe5d0v4q3UiCE&*$14(k+J z7Z{hplx`WYj38}GgYGvDzf>l&lh~0)c~H5yxnTuPdMH@zp$zasW{rdZ%wt!^=(E2{X$g8M8<#6ix?j0!8#rvym z!*mshH~0|+O&J?BXmi6*&7M?>hP=z^H_F2)F{!85+99p{cHd z25kpmCE5-!&5R9K0DjwW&r=wWL4$q?DA1;>Qj(YXi5ryAs`!0N;ESm}+AN_oU?sNPPfs2s)tT7i_Q3UmB!z+fr+$^~}`Mu_mtA`Y8Z z0m~7()|6$Y)3Ml=twsX{W!QLTK2WkUMJzqQMM*o8$zI1uu)rw(C~THy7?gy?by7P_Cq62Nmx!<@&STKb8J~W;4J7QEpOPF?D$p#r6LFc zx!6-TrvJc8_Oen^{8p>&aCWZFX9fgEmJUJ$i2xm(Ib~0ttZ@~j2*+%k{rcX$0Of<{ z20ba4q~~quRM|dk+Ovf+4q$+{NDZcVA|(w}a@8VE<9vX>@6*mf8V?~Lp$_m=6^bzX?X)JXWW3uL0D}AV~ zQ+2ucl6-wHR4Oj?36XZbZW4Wu;YN{Sa@>;qY)<>shpfcaaR2W|%tu60gfJW}($pKa zJ&L}gZj@q+Ys&XR-e}2~$dVYXJ2Z^sb2}G3KXcSgC>vryZKA<@P^;~gAtJGy6C~k* zwx5nd7%B38aKhNE1 zJKvJ67ov9%+Vup+@<95d+99>~_?RziB_t#yBqSsxgyCP!z|DYU)F+$(0000t<82XskIMF-ak77z^w%fa4D00006VoOIv0RI600RN!9r;`8x4s=OG zK~#9!&0E`VoJSQud%t^qi?6YrIH}S$4m5;{wy9b-g@>dPv`-+?g5U*-2gGB=9{{v3 zRB0b5ec**kAfZwqy`VNN&OOA1#;F5w>^gRQ+g;mhd%gD?=QoPZkyy>{PjcjCv3N!4=r7ECc}QbXt6?r?>Y_yTkdz=Z=3jPIpl3 zw8irNho?Qcbo#BUV`D#^o13%4Hmem}GojXfRnFCZH^^cWq>n~NpN>YB&cFWqKXh&j z0f~*ZU;X>q_&3n0D|9Iy7C3{^7 zxN!NSfw|e)A3^2tQ3>4uDWoacgZy(-XHd2DzFKEUVzHKg#U&7GhKOD2<)NF;`d=w#gp;HHIq zejhuXq!zLYP%02$x~IBLOL72%;1XsM%-YA#JE*(6CsbDrSYKPE{X+-n!&C2961dv2 z7F?s2ed8JHC}1U_su@0V_^^0mT?r`g2fMqvX#f5p8XLP(n*^&#RJMC9yhQ_~W2fRB9wX)plrIcvx6d$2tD@rWBlPK=<+`MDHp$yVfG**1STxY*^U*^OeT|MBIYNvAd#K~aM!%P0Wg+5Y|k+C8|NTAEwv=FAMu-kPJnzJ9j- zRzV(-AQaWM5}eQWC+bYjvrJ%U&vD?onc`& zS=EKgQ!KW^+O9GFbW8Pt51q*7kR&Yhwfsd z23VhIFab&%tW#qfiC&hCF5MXcN;Aq5nnY7Z$0kvthK&MN&ujG!P-AJw4E!L_9T9MQ zmM6`ENJ96raH8;~U_f4L_$VNqV8!f??m2>uWU)+mea)gS=!zenqZJ$7x)}h1_Hjg8X$Xbiv(!lL7x5@ouJkMNl=3bu>4*9JQ;lfY_|3Q zppqOCauCqru6BZ21@yJXYXE9>tge|IARwE&6p7OM`Z^Vg1(|wtQ9y*iAv~>vPYDm<<&R_PlVCR z2S_rs0pJ^2et3cZdoEzFlLlBx5a(o+$90?qpI=&{e6dWaT!F59GEN(b1nt??uLQF5 zq==WhD8OMauRS0{=6OY26D}3V-~;ee!GOB9jRa^1RV}k)LWy$O9L3kxd4l966oklL zpO4i)LA-*@XtF%VywT*;6b1Z#OqG=ekSP$7`fo4+TyCTWR7FYFkysSVUMi^96Q`g! zG0@j5>XIHV#T2=%yCPHck1E9fSR`ZtTFrgrS z(79uWCQa7J2_PAg+}zaKrAW{wAl;xjA#(RM0P5ex*<`QRN3tjr=oh`NOEBO!1WUnS zfSqHW=I7@`(j}cvRWku5^e&A8@?+*<5aYL+%wVMkc|>H0R^w};@olXwB6}-F2>Cqc z_Shi~s0cXmzyHdDE0L8Y7(>))WdE=GD4aJ2#VSQq_=qaT%^L5x%WcB z6tCkq^LZDK{SnrR4wb+lIfd@(>{R&xsQvi(HMW1J42Vj?Ot2TBv#ZM#1^|XYs9`?9 zydY}O0Az4FStvNa82A44^iAT8QM7l@Ae(^tti_2zx?7y=bh5G&4oy$yzv}*UZ4aNc^CyLMm4wToIpc000JX| ztEd4;iiSc>hIx;}=>!3_8mLIy+0!kDzak)rKtU?rP^=o%uu%X6RMG(K+cBmt$^uwV zz|u8sr69Z9-qxzL0Pk;ZZr0A%q=D06sR8Cu08S7qIZ6XiwX)~OWTzAi)SLyP z;Jtn(VQ}{@J}w}WjCUgSF28Sbf{?#~qkzE#sw72}26#Ol1VhO8tGq$A!aL7|^F+ZA ziuVcUmr7H2PmdtNQ1o0tPnW5&CSW!L#O}}nC1|KAgcTtKIfAgt1x|8<63LYM2A;t> z=mn0_}V6qW?w*qj|w_6bu+vML7y zK`MBQRA7(Z(b**g%klFd1ykd>`9%=|m-u>FYX{~H7!42|D*Lw12D#ReAl^}TA5gZ0 zyjByi!~@U*T!-+wl+j=a2B0731h z%?xG@utbN_d&`|2IcjTb!*vz=ODGb$J3E91pui^}>f1fgPrcn;2%z}A>Km4Y5&R6J zQ$BC2rwQ13IP&=Ye#1xPCZ>uS0HA1fF3DXT{XDd#kOdNbmzNtM+#WYMS@`ab4oYwc zy=QQM%KYrPbLTjZxDIo`d_bkVt*gVD1iUYmPP?RRg?bR+iN#`JjK3ytkH7?4pzPkd zWL8pHVN7>Z=@bGPOq5U?vc4dzn`epXnT(h$C9`Re28X5y*GWO^`~kqd`CJakSJ<47 zk@&nOfLDijm*@_&@UmA8W>5hjK_xj*gEVO#mK?vsVTUOg7ekW~XvSY!zr%s)k;+{qe%qC#ydAD3cyy2RDU@gHktrxeP0m{RS=E~XV09u zPDB|rw;lv+aC+<7+i$;p#oz<937EIG8+?OY95oP8yI6gIco!M8NPbUzUuoFKAOCau z%;}NKM6`-m)PsOc#M;!F8MEA=)6YNuyqmS*n-4zt;1D;$ZrY!tR9_aT zrP#|Sx4|(GvDvrm2gF24`T)!u;1sdrOzarVW`xa46DYrR^5l&dUijr(#X{i}5lu4E z^$LU_(FS82Z8~w{#5(8f4nOnEGY|6uW+zB61xQMDxG`v)X))6d(*Yr}-}FygtcogC z@7r1EZQ4Efd^j-lBQ~ExY$};*F+>@5~0T38H53@)nHw%|8 zU0V9{pWfizxu0AmA^>f>DOEqe9kaore0i3!!kE5sV zG}^h{|8Ww}I2c|=n9bW9d>|#qsNY7 zy+BTRwWkL-=qkjPW7(V2)5-Ja&m}k`v@&yZW@%wzeu;=8j0MKLtfS}@DR@B*d)rEY zl?a!NyF-$YRt*u2H8FyW0MGE4h>)+hx5v%N5(jIEo%aWqxpxb^43i0s`K)d? zt5>g#=Xu8W{R2fl8wocxPWVjCrP-n=8k41^C9}A=Xr4cRZf@SZ=_LYi?%X+Z`SN8` ztyax&I5e3|MvRp{Ha-LhHLrk}Acr*~I`s1}48>^a_hn^e#gs}V({8s9XF$K-H`#2~ z8W045ENTr#gAqieHkGBlRy8>tZseZ!OJ4_ z9*vE7ZZx(XJdFz$cWX@PBn`D+&6i*N+x+3Tzl!CDV*^m$_@3GL$xUYjY;pqQ0f-YQ z+hJ_p!?gl~Fi31i;b~_`(=Y&0LJ`GWNb;2w2yT+>JeVO9o?`{MIUczlKmbH-;JX9i z`QG#Z$o8%Mrk$NAR!%jFp`(43`xFqAaRdaTFjZNzYG#0RlioV5#%2 z06}20ouhpq>?FKx6e|RbNU_2r2cBI7U>%bY&?UCtO9y~3LIlW0BiR^YTW=WNHqeW_ z1H27ZAXga7$-Mwr8Fv&Ayc8G^Ne&1{-i9YF%?;>`P#{$W4$J}Yw!Ig6NRR+10!D6d zw1nWj3jn%Q5EuaZj^bFyHgvGzeM*zA4DMc~XPnUh+v>sr=)FWPV9n$*cvvF_AdGL+ z_K0nr$>BBbVGxXQ067Kd8C6PY1R6O#umyT1ffc@=P8FcYfWDx-wUjO9srYBI7F9Zx5w#oK;&}>kb#%MfOrA}I7WFJ#wc$?4>?6u3wIZo1^TP? zW9xwdsvR!`kOP47fz)@K&_+WKM#D(xX-0t++DI^qa19oKomP*$X#v1F05uukY(m^) z1JDB@F^3X~Bne|g2zuK4D7^?JtT$RK1qMVU0LTao(3FPvwK3yy>!zDCpGOccpe!akI2fiFp@=Oe0UK_a%?>XfLaf0iFOJhV94cw{j95V05%n=*fz0h zp-+bZOzUx{lBBv9>*08g^=yHZC*k(cq?7|fccEewJz^0#0D$5AaJZh%6*Q)20FFw! zLn13d^fm{C zAY^%X$7xVPJ$8CJ2M}T#>#3(fRbBKMB5>XgLstQGIZP`DP-yhC0KBL@tB=XjO~~#> z*LPB}t#W`|h9dOV+pHa96R9Uv?$RoQ_8gG#&=Nc0#Wtn%kVn82Bv9zNM2AyoAfBdp zh~Nh(`r!$=gVHF2^$suCiA*-b${Ej!kIlmv%)#3LD6ulgP$D=QC0!#1%N(gN!o!lM zPNe!kk3iJkK`Y^_1JORTu{NH#!+1`#l-}umrKN%Xrn(hN~|7?}j1}XMvnp<#S zbX;$=7Nv9)rbtjn$qBd*qe#cB5LQuK#<bs)kk-R8l=$SZu>A0-G zeQLcFa{%f+8j+PmFM=@#vO=-xh^)}-DF-N4$RG^{_{qP6J02^b2a0QNPh8L1q}RhU ziDE@6=YWLMR5B~DVeQ3fSfLF^j+wv;KjjcWdskK@mN0;$3{tF6fME>{PIPi1Fcm9M zI>$>GB^_+^DH4EbR-~5%Wu>VaSmBI0GAm$E5-U^~A;+ZjG$^OLbQ(fKvqG0J&5H5k zu6`EKVU+$kK(`ZNg-Jg3X;!ElqaVi7dKw&b%&_ssoP1L+T;TYOd9XqR*P{uQW5QmW zFAB#8;NioE`>(=%dZW+dK(4inza|HnG=E~R$!%|M+wXb){CQc+y&|_{-wb>4;)T2; zI01lD(4IYeW&!ASyQW+&n`_suC1{^Q0P6L++1lDNolZwy?wsyb*eD?pI0r&3R z%d%&SECo(*MTvmr<>j9V?Pq6ajRHbwZ(nO)Utc$`U%$S1_wL=F&&VRB|!-Z`ON;IgK7awgNRzv^FHd_zhT*CdCAw?EH62)yu5z>`VP<0)Bx~;{;gZL ze){p&)?ctFsJzf_`VG5|0*E1}^eBmKt4MxIYsKe%}D(vQ!7>w_Pb%BAlV3Z;(<#bQt_ z6g&C+VkQiOS>H3WGZ{a_8)i!9xYXr9^(o);XRlgd8;z!Ev^&FQqt$P<+dZ!BVW-{8 z^#_AI+gD|vzi4+m|7h&hKL6K0KY#k!XMe3a00O~@r3D}q03J6ED;#d0CXAXwp2I9Dny z%~vWPl$R^Zl}4jc=lcIuqh25IxL0pA`psIi$46Y2-H!AuUFS(H!Ngt!*F%;bONR(X ppZ(=1%NYP?0Gt7E2EcpUz6LP)Yz>xcqlh6>NL^Pwo=Ifs-cSpEf82_^45oNWuSF}0w&#u0&an5 znHF{tq}94;;UosiRp0tVy6#hfMkBPbU0X7##vh2Cnq$~;j5vyzIF>~&cW&<4xkC<@ zGxuxmEO)8<0|uh@-ua&S-*e7A_l^U7#-CXnS-%RkPN#a?V9JoCC1&qBtbXBTAP29|GjduTw=-*n%wpO77!xqGxY{38PSH)yzz%?x z*xDB_0-<8o#R{3lvg>id0LLEs{&f3Fb$O@63voW!2bJ&Ny7>bk^-9NGzWc&cr@&&d z>`Dq*VCXJZNOcZMjB%+D-THG_`@$>R3kZarYBX-GF^gq)p>2Vo)m4eAi!dPKg}6Vy zDZ9V{&st*^%kD!0E~xt;de+Tz^6S&$Zs zMII>u+Y~QSg8`;<=ljxit%zAH!%tPF(lCe@xqg)j*iV$!K@qET+UOFG!vI4FL6J=h z?TX_#*8R0y^9W;L7_i^T{4N<#yw)%i|Atl2!EvB@{+@>(fdPRD)&yOSL+rA98P!n0 zmgsVkE(=u90`KK8g{uKjI5j1t%On-DC9qg_ zIk^IM-$O4i%UD24P%F!EohQ~|y_F|eB5Kb)>A8ETTjjXaI?Qyf3g846?)|gA#>1G^ zg}G^UFbi5^vtY68VloBHxa-J-tGa)^88pX?1)uLe|76{?koD7*Wa5RXDP?t#SP`@A zYPJcOY1feo!ouDI>nBYLR~od)Di!K_x$YF0fx41=NBfcFN(Brpud+Kx#%+M#vRm1f zMp20>4I`86P^$~ToqhH@zgwy`vvjN*v#Q_y;o6Ia4Bg(f3&QC zJ{1%XV%O0VRBh4aOqK=3wt)h8LSj3!bQ=w@Saum(GV#0CS7AulaXVxE7BA$*&;I#R zv=(6yzOc37c1q2CYXDz#@c7a7JDKPH(ceS5;4$L)imvrEi?>-UyNV49EU~_-`>w#D z+JC{p+kOBy-trFYq5p5M9)L^l+zIdfe+}Mw?Y{i2?U3cw3tD24+nwsC2&KissFv5`#siZ+jb_P5`x4ENvZWfDa+|56#^&4+_8-D*!{s!(m`V1AY*Sq3{;J?3l#}d>& z--2Ub`X*d=<4^N8H@nl~g`g#dy@^na5qJHnaVw09jSqbBYO3aNvG~>`@GZ~7V6p5n zHU#X}k6)TP_NBjv12-1V0!xKhfC4y9)vX|Py!8C?N;YW~OmU7!D^w`ZFuWrHGD`@>f17aI3++5?wwL|wp>P$(Wo{MzD^ z9+<`Bl!Z))bV1Qh&KLg;=z`Y(dm#&nxy3SwbOr3K1OHSPSzf)MONnu^cq?v0H?zJ> zFn&cyq-D`0xa-C<G!?kOy2!3`uVZa4w)>>SB8T4BpMy0lE5H|rFs zW$ZKAZwT=$AVI&9gxN89uxY_B$)0oSd=A6bj-Iz5?!jIf*5}5>le@h3wg(zu&>yWY z7+{K9VWehw(nH>~u_NqdEd{#mQzW0l9Ts#O^EumIPN#8X9LHIf*Vene3D#qaJ*?OW z;mhZ-1^@Qxf=S2AdD~Dte!TwO?d9w<+3y6~?^$+kN~1t9iJ7gJ9QPEbu8(!-pO@)^ zJ?ryqnUmO0vTkxd7-FcWmQ!q7$#M9cV@)%WDnCo|nE*}by10yH>^tFKu%7$3FCVMv z?gV3I9nwAr15UKwO$gOxq`H1p`x<2+>R~MN%SjDR*(p*WSBz|0Nm+QXwdJq(l>2m9 zq}!;awuhrC=0TjKo8HE(pUW5Pi1(>7L{9nE2#P z8eJY!v%^TB#&)5pQYyb0Uy!HHx)!0(irhGO`TfB)Lqg*5_2zFmRGF~^Rf!K=vJ&VW z?=LUHkU${PKUT-wv_kAfUJ+Dluohk01ka%*DA#H+*~88g4cQo}&tBr9?rf7tx|8^_ zg4qG}jRFl&a{Xn7nQbE_ULF*q4j?_QSaffduLZh~z_-3gvR~J6^%ERv0Br$=1gC%n zW`5o=sa!7);(^$gS)8H4Vj9|30u#@GQ)NWc1*;3nTOdzSw*$dzBoiYlV;(l7p=o)H zFhTV|k(AYDDqbtqcw#R%@Ec?uD-5l#hdJe8GV`zt#L+(C%D#@sk{B2|?MURh<>NZ0 zv)I%5S|afN_`rh3bU{JGc+oryu&TPZqI|tNsKXOI)gHpE=P~K|3qc;AcWr1{9mJLw z?zhG2on#9(J~GeJoMvNF2R&58OXqs>5H-lso&>YLJkB~;VdEKgsd=F-V1Ie%i~6F_ zXV|nm10lF9FY6@elk|k45H2!zb56!>lCZ**T1+Mc9;Zr?1m^(aLG3rw1@qcQ>Hlwr z_}YaM>u0_M>^g~D8$9k&HlY3qmQRPv3|%nKZd`B-9+W%RTbTSIaSsrD=8|*TxSn)D zYX^s+1)z_pL7q|&b37j2y$K;Tsss~RJM3rGN)R^TLC z#!}^?`-R6i#?C41EyQOLuQg~skL)_}tT5FMqT`&hAbnq?zszkh51XUQl!ztNA-2k9 zIQtDiY=wCsK%yJH1$wkZ^Os2uVBh9KGC`?x)|q7jDMaEJa9>3dp##_)QIS}I3vB7` zQ^NWRA+};e!M>8`QFjsqkrno^NX(i0SO?4d!|CH6*M_zC{DZ)2l$T%`R9Wb{k>jdMo|aV^+G{;?{n&N>WX4&nK=9;-J3Mx zU=5i{5}hqt7O+H!A#c4E!9IB%M8e>G)^)CeJ|m6Sj(UyXc|L?8p!ULdYO#o`LmP*p zBBBJF&~RyU|J`(eWb9w>fLs|cus@dJN??o08|a>VY&Wkgk0QbQP^B{Nhqc~9m^;Z6 zlWEO7IAn#hZ4czdQN{HZ38R)EGcfVn2X*k7%DaY1c72fkk=;L@i@7CrB2ASREz!+- zpus-J#g{3BA)xAXMu?`z>;UPjfNk6Vrl7U(|73G^fjWp|Ac)86HhZ?aLbruhSYFH5 z9bDeq@}&ed57QkJrtk#iI_{nfc&%i`L}kPX?;eCIv9GN4K?8hN>GKglzW8-Jh%y>D zy!Ls5oGa=saR_gA1TN7!8HXVuavbf4lc+sZrNdGQ(8{Sp+ySs4v07g1Ch9%P61N#n z{Z;|%t89iL>`}yOGD#8f<27Jtg-vJtmN)JZt*?K~DWfJyM6@rM>kiL6>rS*+|5(Z3 z&XvSE0I(}x5;3%m#8!!Mk4SCorud!hBA>(6L_u&*HK|T@?WfIb}tk zD-Vjnv%+vRMPkKsdnZ9)(Yveb`wP9Cd-j_Da*t_T*y97KfG+2cd^G?=ka*I(I?I^H z2m3NHFqxo)C57C~Sy$A1l)MnUo}vZ}HX?BdOIQNX?~xV3JD(9EIU`37g}f^XE9~J^ z>O`J^ZA)D3#chlQD^Oq@lAL5GXvcj)e?GA^JS!~eb@VxPguU6_Zn=n% zA}z$oAX{N~&!4YT>G0s{%{r?C`RE2CttWf={hVS`Q%P&TD2{k?x@q92P7JzuA!S-D zeTUt%kW)o>ez!W9`94Dc`NxN@BkBe(crQ#Wj`IrE+yyAWpju%V`|4U>2wGqtehidY z-wV-C?`nY=LP5C+D@=3`mIdXa7L(Vaw_xzW9(SJAqTf>;&*8ok?*Q+6pPp)XLUN`K z6?o5-4}6PW_$||}fc^E@vv^Yj&}U@dzE$A_a{Z>_RvKOFE8&BWcST8xr~>5jp>S!^ z%#_&@t)*|k3KPA6Z}AFsGq7Q>Qzz13tDg5#phfTi=xj8O9vc`}@}6hTPlMxby>V2O;L>jcTop8w zC~{bD?bl3y&06GU&Fi``66p;gONbSrlY}~`iK|&D`n>1d*RWXdAI!x|uDu6weDf?V zdy^3TZ%)V{+32)L{3R$auL2WX{OVB|$E!NVGz7^n$!`c)du@Lc3J#f}P1vb#ynLk8 z8zw_-{)HMnmqun`hlAo1VPR zmL}Q!f{_R1AWGBeC6Mj-*bp%6jiil9(Ln4-$F0skzIfDw9RlNCl}ZK8V7{64(LpL? zzXk9feOBMSC;k_$E_5@yDHl%5l>3+>0xE6HajUW!j4025UfM=kda0#TeHdTQJb?Ly1T}_X)z(i8NaV z2RapE!s75?pL2F6EHSjaDwREpTjE}tbUJQXZczi|#+lV&^Mj*JHvfInYEGx}$YeeO zp%kN1u)>BRbz%z=R#>+h#IYlE0jCm+ED6*zcMC<}P2kU)7W6Bzm(Am4@?L~s78EcU zt4irv-6CL*c4w9#CN6?UEwQuxjEP~)t5R_deU-($^|i5O_4)99h+9c~K$BJz|E`1F zxrU$@qmp?J8Zew-EB_#fUs7}dTM%2sgsq2-20mvE;9vh_AAN(3jV}ITbAIvIHa)}Y>i!upGkwemZN^SN{>puGWxABL`NffY{{8$sJ!X8VjdWv|Xnl2I zt+MXay(-{+oYaX?N%S6tVBS5Fn-`}b%nHMlk}pCzV-N8>{$eL$gLI#0G!*@i$|RJ7 zNWhjZvq{ z??Ul=fNqjdyf9acv#*mX=Px4l;gu6?pXqlxggCJ`pv#%GaADjP@-z7{PNfnCN$%52 zke{ZpOt*Om6v>~F)r}@0a;S)z5s8)6Pr$n$e#To_y{*-bu7hL$@pr*m=lYBE(F)H+=&d5Gv!u)3`1ibq`uG_nont`C6QLNl+XL5TzAC^GtL*#+rwN9>k0P_3sAp3Zi zi}va=i+-hO{Eg*?`8mb2j^fvw1QtDB_(NK;T2TcmVofSyT7yDAY#)Sk?>-Dy+P@A1 z4S^>voVd4dJ*G0Grj5pFO)%f+_Ea&pfar$21tr9y1TGE+TE}3QlbWYJeGd?{kh)_k zi@4~fV9yE0mX5>LX_>Vf8MfWakZ(Lp$c$4=ZOa4xINUk9J*^&FsU*P1Up>ZC<6O@D z=wW#Oqff#hL!Z;7mrmTf>t9G&SYqrPr%HX;a=&&fOc<~uB1>qnVbcnU(CwlS&U30L z#Xrlkz7VOeT&%9{n*_y~Tf+~2Zz#koiYk4Ndkr|JIAzOLyaoy%@~tGlz<_d|$F-y= zULNqj#rbp*cUMZlFtuWJZSS~>h`syE&%hv)h}Y8O*wCf|m_{&Rp?09vQW_Y4<2}D~ zL~)8A5`PHU=TtcdYWR>ZvRbJVX{5h1rYd3sZhZw-yfP8RI#nNDONp@9C+~gqAf6n+ z0|{`58i+?%G(ipJkJYN6^r1u)u}44k>==o}s)?x*0|^G~AGp_wR|+=957P*EiH5^6 z=hwA{$STH82o$l?SwhCfc22WiN0B(S6y3)<@GbfV6htbBpT*;^9Bt4hAXi>9m&>*mOHp(v(7odp1MK`gmNt`Ngyn zS9{U>R5j(%Pd!fstlMgbS>+@9-hlxkh&q)qD_+|n;xEG>Ph0UCXc{2@L0l9pk1ZKptT(V+fW!d`?;6K&z!( zSS$<&V24l+vl_k6bxs-gYuhRm60n3#Yix5Wo==VhaA40R*fD6Vi&|Zy*6I?1?&H@u z<%I*SmhxfN5vBwgv1}M9jE`t!-Fr~(P_zxSV_E%EJT+|4EIj zl^N5Spf$ELW8Xexf1?2(xR&m)p)5)hur&Yu{!{+H{qVN>T6AdY!le(OC~<5HmQb{u zh%DsIWML80Gv+T1KFpyr#B+XGVwjlK*n(qf@&8G0WHPSyBE(ttM(#J;r`PlIis|vA zeG#xUI9mS5!ub(Wbq+%Wqc}wleTV1^>6YXvA*WStl3Qp}J%( cEsMqSD;{V2(c29_KL7v#07*qoM6N<$f`u9S#{d8T literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/image/logo-light.png b/extra/src/apps/res/image/logo-light.png new file mode 100644 index 0000000000000000000000000000000000000000..d0d6409821d2ecf308c877c6682d336e9ef5a527 GIT binary patch literal 7530 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 AZvX%Q literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/image/placeholder.png b/extra/src/apps/res/image/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..984d40b2877a35ab3cc5a49460fffed3b9ca2492 GIT binary patch literal 1605 zcmb7_|2NYM7{@=ewn>u}X^n8XPE=+^*~HjpH-?O`&^NYOXudA%M%wIBmTxzc#FA?h zS8^3g5fkO0Y;t|OT9&8@#Z_IAy1VxexaZvGocHs7&hwn}{P3LfI!_k$zk+eGnQdh4WG^jN0F|IN-N*q!y`i16i#D;zX+XH@}Q|k6>g0q5USd-ot z%b&0h!aFkQ0|Nth&*&R*(@k-SlP{!FsZ(LVd!4Xf%Vwy_Td{$4!*6a>R>pF_Md*7P zk;8a?e6Kr;CV!+=4Mwl9A|t;JxgCNOmz0d5x)vbnwPANmo*DDj+D_^70O#gvY_3~7 zeMIA>UbGQL=ks==(XAQdzN05^ASM90EAhfp&QQ+EY+sZnWqIP!MRN#eJ$toZv?`N@ z);<7<7vW`KTj{4Y^7Uhe3$-#UKsI_UBd(Ec#Yn+1bPoM`VmCKtjK9vdD;Cw=v@k9e zonxdvp>8JomoihSgC41$|Fp=RBAE6Hu$N&8I}IidnRV8?T%iAm==!fg;YYi}R%Cx8 zV1EX}?R+>kE(ISIy|=`0E_nsb5me46%7ioU+q@RS1r3Z@Te<|TCp zt+|(|Ct3HZaB}~{QzA5cby}RDey)unm$4+M1eVzsA~HkXU%=)nUK0{@7+c}uB%7Jz zn#ZrOc<;vw-!PaLzkI+32KR|kU48nxFS&VjlfaFEU2ocZn_XkE1;ZiQ?ulD0zU1Q2 z3Yd9*&hNT?avbHk9xiTF(S)kNmp6Gva^;r&yZ9hucZ#+m?aBaU5-6R@pxlU!V0d~U zue=8S$PtRvsYZ}!&O&`S28aMiG?dT|UWM$m$h?IS`ogOak~P_FJRh35^%TBB5f6P) z_mEb^gp8I(Qr99c${mOoHh1UkE`wONFJ1b;Qge;_@8t~o;mi#fFx)0`|Hv6IMZRP> zVkbSU+c7UfMA*+C%bk;?6G`2rk5sgo?1;*F;dI1jxX#MTs+krXY$>!;Yu9Hu3Ro=GUp7)a zI2!l$vd&S1bag;Ki^YC4V0}50~GA;V>&kRNKdqqPh~0IB$OG`3YM9{V(FZe8fb3;(Q#O1>P literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/image/setup-blocker.png b/extra/src/apps/res/image/setup-blocker.png new file mode 100644 index 0000000000000000000000000000000000000000..e1aa696a02fd2d583bd2ad6c481c4ecca93fae2f GIT binary patch literal 12202 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ⅆgDup0RLe8_yQces``f70aXlS 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 literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/synergy.ico b/extra/src/apps/res/synergy.ico new file mode 100644 index 0000000000000000000000000000000000000000..b35873027bb9ae32c78c0b8ddf76366dca4f5cda GIT binary patch literal 147530 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 S!bZga-s3a=7?yDu_x}Nw1e9a| literal 0 HcmV?d00001 diff --git a/extra/src/apps/res/synergy.png b/extra/src/apps/res/synergy.png new file mode 100644 index 0000000000000000000000000000000000000000..15da1fa2e85e8efc16f9ff864057d42d411641e7 GIT binary patch literal 22516 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 aaH(@cGPG_gorTnM1s + + icons/16x16/tray.png + icons/64x64/video-display.png + icons/64x64/user-trash.png + icons/16x16/warning.png + icons/256x256/synergy.ico + image/logo-light.png + image/logo-dark.png + icons/16x16/padlock.png + icons/64x64/tray-dark.png + icons/64x64/tray-light.png + image/welcome.png + icons/64x64/folder.png + image/placeholder.png + + diff --git a/extra/src/apps/res/synergy.svg b/extra/src/apps/res/synergy.svg new file mode 100644 index 000000000..6e8edd76e --- /dev/null +++ b/extra/src/apps/res/synergy.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/extra/src/integtests/CMakeLists.txt b/extra/src/integtests/CMakeLists.txt new file mode 100644 index 000000000..fc3a52eb3 --- /dev/null +++ b/extra/src/integtests/CMakeLists.txt @@ -0,0 +1,19 @@ +# Deskflow -- 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 . + +config_test() +set(target ${INTEG_TESTS_BIN}) +add_executable(${target} ${sources}) +target_link_libraries(${target} ${test_libs}) diff --git a/extra/src/lib/synergy/CMakeLists.txt b/extra/src/lib/synergy/CMakeLists.txt new file mode 100644 index 000000000..7f957f489 --- /dev/null +++ b/extra/src/lib/synergy/CMakeLists.txt @@ -0,0 +1,17 @@ +# 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 . + +add_subdirectory(license) +add_subdirectory(gui) diff --git a/extra/src/lib/synergy/gui/ActivationDialog.cpp b/extra/src/lib/synergy/gui/ActivationDialog.cpp new file mode 100644 index 000000000..1146fed5e --- /dev/null +++ b/extra/src/lib/synergy/gui/ActivationDialog.cpp @@ -0,0 +1,231 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#include "ActivationDialog.h" + +#include "CancelActivationDialog.h" +#include "gui/config/AppConfig.h" +#include "gui/styles.h" +#include "synergy/gui/constants.h" +#include "synergy/gui/license/LicenseHandler.h" +#include "synergy/gui/license/license_notices.h" +#include "synergy/license/parse_serial_key.h" +#include "ui_ActivationDialog.h" + +#include +#include +#include +#include +#include + +using namespace deskflow::gui; +using namespace synergy::gui; +using namespace synergy::license; + +const QString successTitle = "Serial key"; +const QString problemTitle = "Serial key problem"; + +ActivationDialog::ActivationDialog(QWidget *parent, AppConfig &appConfig, LicenseHandler &licenseHandler) + : QDialog(parent), + m_ui(new Ui::ActivationDialog), + m_pAppConfig(&appConfig), + m_licenseHandler(licenseHandler) +{ + m_ui->setupUi(this); + + m_ui->m_pLabelNotice->setStyleSheet(kStyleNoticeLabel); + + refreshSerialKey(); +} + +ActivationDialog::~ActivationDialog() +{ + delete m_ui; +} + +void ActivationDialog::refreshSerialKey() +{ + const QString envSerialKey = qEnvironmentVariable("SYNERGY_TEST_SERIAL_KEY"); + if (!envSerialKey.isEmpty()) { + qDebug("using serial key from env var"); + m_ui->m_pTextEditSerialKey->setText(envSerialKey); + } else { + qDebug("using serial key from config"); + const auto hexString = m_licenseHandler.license().serialKey().hexString; + m_ui->m_pTextEditSerialKey->setText(QString::fromStdString(hexString)); + } + + m_ui->m_pTextEditSerialKey->setFocus(); + m_ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); + + const auto &license = m_licenseHandler.license(); + if (license.isTimeLimited() && (license.isExpired() || license.isExpiringSoon())) { + m_ui->m_pLabelNotice->setText(licenseNotice(license, kColorWhite)); + m_ui->m_widgetNotice->show(); + } else { + m_ui->m_widgetNotice->hide(); + } +} + +void ActivationDialog::showEvent(QShowEvent *event) +{ + QDialog::showEvent(event); + + QTimer::singleShot(0, this, [this]() { + const auto &license = m_licenseHandler.license(); + if (license.isTimeLimited()) { + const auto notice = licenseNotice(license, kColorSecondary); + if (license.isExpired()) { + QMessageBox::warning( + this, "License expired", + tr("%1" + "

The application will now stop working. Please renew your license today to continue using the " + "application.

" + "

Once you have received your new serial key, you can enter it on the next screen.

") + .arg(notice) + ); + } else if (license.isExpiringSoon()) { + QMessageBox::warning( + this, "License expiring soon", + tr("%1" + "

If your license expires, the application will stop working. Please renew your license today to " + "avoid any interruptions.

" + "

Once you have received your new serial key, you can enter it on the next screen.

") + .arg(notice) + ); + } + } + }); +} + +void ActivationDialog::reject() +{ + // don't show the cancel confirmation dialog if they've already registered, + // since it's not relevant to customers who are changing their serial key. + const auto &license = m_licenseHandler.license(); + if (license.isValid() && !license.isExpired()) { + QDialog::reject(); + return; + } + + // the accept button should be labeled "Exit" on the cancel dialog. + CancelActivationDialog cancelActivationDialog(this); + if (cancelActivationDialog.exec() == QDialog::Accepted) { + QApplication::exit(); + } +} + +void ActivationDialog::accept() +{ + using Result = LicenseHandler::SetSerialKeyResult; + auto serialKey = m_ui->m_pTextEditSerialKey->toPlainText(); + + if (serialKey.isEmpty()) { + QMessageBox::information(this, "Activation", "Please enter a serial key."); + return; + } + + const auto result = m_licenseHandler.setLicense(serialKey); + if (result == Result::kUnchanged) { + qInfo() << "serial key did not change, nothing to do"; + QDialog::accept(); + return; + } + + if (result != Result::kSuccess) { + showResultDialog(result); + return; + } + + m_serialKeyChanged = true; + showSuccessDialog(); + QDialog::accept(); +} + +void ActivationDialog::showResultDialog(LicenseHandler::SetSerialKeyResult result) +{ + switch (result) { + using enum LicenseHandler::SetSerialKeyResult; + + case kInvalid: + QMessageBox::warning( + this, problemTitle, + QString( + "Invalid serial key. " + R"(Please
contact us for help.)" + ) + .arg(kUrlContact) + .arg(kColorSecondary) + ); + break; + + case kExpired: + QMessageBox::warning( + this, problemTitle, + QString( + "Sorry, that serial key has expired. " + R"(Please renew your license.)" + ) + .arg(kUrlContact) + .arg(kColorSecondary) + ); + break; + + default: + qFatal("unexpected change serial key result: %d", static_cast(result)); + } +} + +void ActivationDialog::showSuccessDialog() +{ + const auto &license = m_licenseHandler.license(); + + QString title = successTitle; + QString message = tr("

Thanks for entering your serial key for %1.

").arg(m_licenseHandler.productName()); + + const auto tlsAvailable = m_licenseHandler.license().isTlsAvailable(); + if (tlsAvailable && m_pAppConfig->tlsEnabled()) { + message += "

To ensure that TLS encryption works correctly, " + "please use the same serial key on all of your computers.

"; + } + + if (license.isTimeLimited()) { + auto daysLeft = license.daysLeft().count(); + if (license.isTrial()) { + title = "Trial started"; + message += QString("Your trial will expire in %1 %2.").arg(daysLeft).arg((daysLeft == 1) ? "day" : "days"); + } else if (license.isSubscription()) { + message += QString("Your license will expire in %1 %2.").arg(daysLeft).arg((daysLeft == 1) ? "day" : "days"); + } + } + + QMessageBox::information(this, title, message); +} + +void ActivationDialog::showErrorDialog(const QString &message) +{ + QString fullMessage = QString( + "

There was a problem with your serial key.

" + R"(

Please contact us )" + "and provide the following information:

" + "%3" + ) + .arg(kUrlContact) + .arg(kColorSecondary) + .arg(message); + QMessageBox::warning(this, problemTitle, fullMessage); +} diff --git a/extra/src/lib/synergy/gui/ActivationDialog.h b/extra/src/lib/synergy/gui/ActivationDialog.h new file mode 100644 index 000000000..0abfa6307 --- /dev/null +++ b/extra/src/lib/synergy/gui/ActivationDialog.h @@ -0,0 +1,68 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#pragma once + +#include "synergy/gui/license/LicenseHandler.h" + +#include + +namespace Ui { +class ActivationDialog; +} + +class AppConfig; + +class ActivationDialog : public QDialog +{ + Q_OBJECT + +public: + ActivationDialog(QWidget *parent, AppConfig &appConfig, LicenseHandler &licenseHandler); + ~ActivationDialog() override; + + class ActivationMessageError : public std::runtime_error + { + public: + ActivationMessageError() : std::runtime_error("could not show activation message") + { + } + }; + + bool serialKeyChanged() const + { + return m_serialKeyChanged; + } + +public slots: + void reject() override; + void accept() override; + +protected: + void refreshSerialKey(); + +private: + void showResultDialog(LicenseHandler::SetSerialKeyResult result); + void showSuccessDialog(); + void showErrorDialog(const QString &message); + void showEvent(QShowEvent *) override; + + Ui::ActivationDialog *m_ui; + AppConfig *m_pAppConfig; + LicenseHandler &m_licenseHandler; + bool m_serialKeyChanged = false; +}; diff --git a/extra/src/lib/synergy/gui/ActivationDialog.ui b/extra/src/lib/synergy/gui/ActivationDialog.ui new file mode 100644 index 000000000..b8ac11b1f --- /dev/null +++ b/extra/src/lib/synergy/gui/ActivationDialog.ui @@ -0,0 +1,153 @@ + + + ActivationDialog + + + + 0 + 0 + 541 + 241 + + + + Serial key + + + + + + + true + + + + Enter your serial key + + + + + + + <p>Your serial key is on your <a href="https://symless.com/synergy/account?source=gui" style="color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://symless.com/synergy/contact?source=gui" style="color:#4285f4;">Contact us</span></a></p> + + + true + + + + + + + true + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style="-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;"><br /></p></body></html> + + + false + + + + + + + + 2 + + + 0 + + + 0 + + + 8 + + + + + m_pLabelNotice + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + m_pTextEditSerialKey + + + + + buttonBox + accepted() + ActivationDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ActivationDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/extra/src/lib/synergy/gui/AppTime.cpp b/extra/src/lib/synergy/gui/AppTime.cpp new file mode 100644 index 000000000..5343db9f1 --- /dev/null +++ b/extra/src/lib/synergy/gui/AppTime.cpp @@ -0,0 +1,53 @@ +/* + * 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 . + */ + +#include "AppTime.h" + +#include + +using system_clock = std::chrono::system_clock; +using time_point = system_clock::time_point; + +namespace synergy::gui { + +const auto kTestTimeEnvVar = "SYNERGY_TEST_START_TIME"; + +AppTime::AppTime() +{ + m_realStartTime = system_clock::now(); + if (qEnvironmentVariableIsSet(kTestTimeEnvVar)) { + const auto testTime = qEnvironmentVariable(kTestTimeEnvVar).toLongLong(); + qDebug("setting test time to: %lld", testTime); + m_testStartTime = std::chrono::seconds{testTime}; + } +} + +bool AppTime::hasTestTime() const +{ + return m_testStartTime.has_value(); +} + +time_point AppTime::now() +{ + if (m_testStartTime.has_value()) { + const auto runtime = system_clock::now() - m_realStartTime; + return time_point{m_testStartTime.value()} + runtime; + } + return system_clock::now(); +} + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/AppTime.h b/extra/src/lib/synergy/gui/AppTime.h new file mode 100644 index 000000000..62faab118 --- /dev/null +++ b/extra/src/lib/synergy/gui/AppTime.h @@ -0,0 +1,39 @@ +/* + * 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 . + */ + +#pragma once + +#include +#include + +namespace synergy::gui { + +class AppTime +{ + using time_point = std::chrono::system_clock::time_point; + +public: + AppTime(); + time_point now(); + bool hasTestTime() const; + +private: + std::optional m_testStartTime = std::nullopt; + time_point m_realStartTime = std::chrono::system_clock::now(); +}; + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/CMakeLists.txt b/extra/src/lib/synergy/gui/CMakeLists.txt new file mode 100644 index 000000000..5e2d7f52b --- /dev/null +++ b/extra/src/lib/synergy/gui/CMakeLists.txt @@ -0,0 +1,42 @@ +# Synergy -- mouse and keyboard sharing utility +# Copyright (C) 2024 Symless Ltd. +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file LICENSE that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +set(target synergy-gui) + +set(res_dir ${GUI_RES_DIR}) +set(qrc_file ${GUI_QRC_FILE}) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +file(GLOB_RECURSE sources *.cpp) +file(GLOB_RECURSE headers *.h) +file(GLOB_RECURSE ui_files *.ui) + +if(ADD_HEADERS_TO_SOURCES) + list(APPEND sources ${headers}) +endif() + +add_library(${target} STATIC ${sources} ${ui_files} ${qrc_file}) + +target_link_libraries( + ${target} + gui + license + Qt6::Core + Qt6::Widgets + Qt6::Network) diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.cpp b/extra/src/lib/synergy/gui/CancelActivationDialog.cpp new file mode 100644 index 000000000..242fd58c2 --- /dev/null +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.cpp @@ -0,0 +1,35 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#include "CancelActivationDialog.h" + +#include "ui_CancelActivationDialog.h" + +#include "QPushButton" + +CancelActivationDialog::CancelActivationDialog(QWidget *parent) : QDialog(parent), ui(new Ui::CancelActivationDialog) +{ + ui->setupUi(this); + + ui->m_pButtonBox->button(QDialogButtonBox::Cancel)->setText("&Back"); + ui->m_pButtonBox->button(QDialogButtonBox::Ok)->setText("&Exit"); +} + +CancelActivationDialog::~CancelActivationDialog() +{ + delete ui; +} diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.h b/extra/src/lib/synergy/gui/CancelActivationDialog.h new file mode 100644 index 000000000..e22729e42 --- /dev/null +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.h @@ -0,0 +1,36 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#pragma once + +#include + +namespace Ui { +class CancelActivationDialog; +} + +class CancelActivationDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CancelActivationDialog(QWidget *parent = 0); + ~CancelActivationDialog(); + +private: + Ui::CancelActivationDialog *ui; +}; diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.ui b/extra/src/lib/synergy/gui/CancelActivationDialog.ui new file mode 100644 index 000000000..2f349ac83 --- /dev/null +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.ui @@ -0,0 +1,77 @@ + + + CancelActivationDialog + + + + 0 + 0 + 429 + 273 + + + + Cancel Activation + + + + + + <html><head/><body><p>You'll need to purchase a license to use Synergy.</p><p><a href="https://symless.com/synergy/contact?source=gui"><span style=" text-decoration: underline; color:#007af4;">Contact us</span></a></p><p>The application will now exit.</p></body></html> + + + true + + + true + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok + + + + + + + + + m_pButtonBox + accepted() + CancelActivationDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + m_pButtonBox + rejected() + CancelActivationDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/extra/src/lib/synergy/gui/ExtraSettings.cpp b/extra/src/lib/synergy/gui/ExtraSettings.cpp new file mode 100644 index 000000000..8ec39809e --- /dev/null +++ b/extra/src/lib/synergy/gui/ExtraSettings.cpp @@ -0,0 +1,50 @@ +/* + * 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 . + */ + +#include "ExtraSettings.h" + +#include +#include + +using namespace deskflow::gui::proxy; + +namespace synergy::gui { + +const auto kSerialKeySettingKey = "serialKey"; +const auto kActivatedSettingKey = "activated"; + +void ExtraSettings::load() +{ + const auto &settings = getActiveSettings(); + m_serialKey = settings.value(kSerialKeySettingKey).toString(); + m_activated = settings.value(kActivatedSettingKey).toBool(); +} + +void ExtraSettings::sync() +{ + auto &settings = getActiveSettings(); + if (!settings.isWritable()) { + qCritical() << "unable to save to settings, file not writable:" << settings.fileName(); + return; + } + + settings.setValue(kSerialKeySettingKey, m_serialKey); + settings.setValue(kActivatedSettingKey, m_activated); + settings.sync(); +} + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/ExtraSettings.h b/extra/src/lib/synergy/gui/ExtraSettings.h new file mode 100644 index 000000000..95dd604f4 --- /dev/null +++ b/extra/src/lib/synergy/gui/ExtraSettings.h @@ -0,0 +1,60 @@ +/* + * 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 . + */ + +#pragma once + +#include "gui/config/Settings.h" + +#include +#include +#include + +namespace synergy::gui { + +class ExtraSettings : public deskflow::gui::Settings +{ + Q_OBJECT +public: + ExtraSettings() = default; + virtual ~ExtraSettings() = default; + void load(); + void sync(); + + QString serialKey() const + { + return m_serialKey; + } + void setSerialKey(const QString &serialKey) + { + m_serialKey = serialKey; + } + + bool activated() const + { + return m_activated; + } + void setActivated(bool activated) + { + m_activated = activated; + } + +private: + QString m_serialKey; + bool m_activated = false; +}; + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp new file mode 100644 index 000000000..eca5ddfa0 --- /dev/null +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -0,0 +1,129 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2025 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 . + */ + +#include "FeatureHandler.h" + +#include "dialogs/UpgradeDialog.h" +#include "gui/config/AppConfig.h" +#include "gui/constants.h" +#include "gui/diagnostic.h" +#include "license/LicenseHandler.h" +#include "synergy/gui/constants.h" + +#include +#include +#include +#include +#include + +using namespace deskflow::gui; +using namespace synergy::gui; + +void FeatureHandler::handleMainWindow(AppConfig *appConfig) +{ + m_appConfig = appConfig; +} + +void FeatureHandler::handleSettings(QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope) const +{ + const auto onSystemScopeToggle = [this, parent, systemScope, userScope](bool checked) { + qDebug("system scope radio button toggled"); + systemScope->blockSignals(true); + userScope->blockSignals(true); + handleSettingsScopeRadioButton(parent, systemScope, userScope, checked); + systemScope->blockSignals(false); + userScope->blockSignals(false); + }; + QObject::connect(systemScope, &QRadioButton::toggled, onSystemScopeToggle); + + checkSettingsScopeLicense(parent, systemScope, userScope, false); +} + +bool FeatureHandler::checkSettingsScopeLicense( + QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope, bool showDialog +) const +{ + const auto &licenseHandler = LicenseHandler::instance(); + if (!licenseHandler.isEnabled()) { + qDebug("license handler disabled, skipping settings scope check"); + return true; + } + + const auto &license = licenseHandler.license(); + if (!license.isSettingsScopeAvailable() && systemScope->isChecked()) { + qDebug("settings scope not available, showing upgrade dialog"); + userScope->setChecked(true); + + if (showDialog) { + UpgradeDialog dialog(parent); + dialog.showDialog( + QString("Settings Scope"), + QString("Please upgrade to %1 to enable the settings scope feature.").arg(kBusinessProductName), kUrlContact + ); + } + + return false; + } + + return true; +} + +void FeatureHandler::handleSettingsScopeRadioButton( + QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope, bool checked +) const +{ + if (!checkSettingsScopeLicense(parent, systemScope, userScope, true)) { + qDebug("settings scope not available, skipping feature handler"); + return; + } + + const auto userScopeText = QStringLiteral("current user"); + const auto systemScopeText = QStringLiteral("all users"); + const auto from = checked ? userScopeText : systemScopeText; + const auto to = checked ? systemScopeText : userScopeText; + const auto result = QMessageBox::information( + parent, "Switch settings profile", + QString("Switching settings from %1 to %2 requires %3 to restart.\n\n" + "Would you like to restart the application now?") + .arg(from, to, QApplication::applicationName()), + QMessageBox::Yes | QMessageBox::Cancel + ); + + if (result == QMessageBox::Yes) { + auto &systemSettings = m_appConfig->settings().getSystemSettings(); + systemSettings.loadSystem(); + + if (systemSettings.isEmpty()) { + qDebug("system settings are empty, copying user settings"); + systemSettings.copyFrom(m_appConfig->settings().getUserSettings()); + } + + systemSettings.setValue(kSystemScopeSetting, checked); + systemSettings.sync(); + + // This seems rather clumsy and un-elegant at first glance, but actually when you consider + // the complexities of hot-switching the settings scope while the application is running, + // restarting the applocation is actually the lowest maintenance solution. + deskflow::gui::diagnostic::restart(); + } else { + if (checked) { + userScope->setChecked(true); + } else { + systemScope->setChecked(true); + } + } +} diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h new file mode 100644 index 000000000..e737bae22 --- /dev/null +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -0,0 +1,43 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2025 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 . + */ + +#pragma once + +class AppConfig; +class QDialog; +class QRadioButton; + +class FeatureHandler +{ +public: + static FeatureHandler &instance() + { + static FeatureHandler instance; + return instance; + } + + void handleMainWindow(AppConfig *appConfig); + void handleSettings(QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope) const; + +private: + void handleSettingsScopeRadioButton(QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope, bool checked) + const; + bool + checkSettingsScopeLicense(QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope, bool showDialog) const; + + AppConfig *m_appConfig = nullptr; +}; diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h new file mode 100644 index 000000000..e25a38f49 --- /dev/null +++ b/extra/src/lib/synergy/gui/constants.h @@ -0,0 +1,40 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +namespace synergy::gui { + +const auto kProProductName = "Synergy 1 Pro"; +const auto kBusinessProductName = "Synergy 1 Business"; + +const auto kUrlApi = "https://symless.com/api"; +const auto kUrlWebsite = DESKFLOW_WEBSITE_URL; +const auto kUrlSourceQuery = "source=gui"; + +const auto kLinkBuy = R"(Buy now)"; +const auto kLinkRenew = R"(Renew now)"; +const auto kLinkDownload = R"(Download now)"; + +const auto kUrlPersonalUpgrade = QString("%1/purchase/upgrade?%2").arg(kUrlWebsite, kUrlSourceQuery); +const auto kUrlContact = QString("%1/contact?%2").arg(kUrlWebsite, kUrlSourceQuery); + +const auto kUrlApiLicenseActivate = QString("%1/product/activate").arg(kUrlApi); + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp new file mode 100644 index 000000000..a3af9cd8b --- /dev/null +++ b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp @@ -0,0 +1,47 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2022 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 . + */ + +#include "UpgradeDialog.h" + +#include "synergy/gui/constants.h" + +#include +#include + +UpgradeDialog::UpgradeDialog(QWidget *parent) : QMessageBox(parent) +{ + m_cancel = addButton("Cancel", QMessageBox::RejectRole); + m_upgrade = addButton("Upgrade", QMessageBox::AcceptRole); +} + +void UpgradeDialog::showDialog(const QString &title, const QString &body, const QString &link) +{ + setWindowTitle(title); + setText(body); + exec(); + + if (clickedButton() == m_upgrade) { + const auto url = QUrl(link); + if (QDesktopServices::openUrl(url)) { + qDebug("opened url: %s", qUtf8Printable(url.toString())); + } else { + qCritical("failed to open url: %s", qUtf8Printable(url.toString())); + } + } else { + qDebug("upgrade was declined"); + } +} diff --git a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h new file mode 100644 index 000000000..ecdda43fe --- /dev/null +++ b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h @@ -0,0 +1,32 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2022 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 . + */ + +#pragma once + +#include +#include + +class UpgradeDialog : public QMessageBox +{ +public: + explicit UpgradeDialog(QWidget *parent = nullptr); + void showDialog(const QString &title, const QString &body, const QString &link); + +private: + QPushButton *m_upgrade = nullptr; + QPushButton *m_cancel = nullptr; +}; diff --git a/extra/src/lib/synergy/gui/license/LicenseActivator.cpp b/extra/src/lib/synergy/gui/license/LicenseActivator.cpp new file mode 100644 index 000000000..83b54ec6b --- /dev/null +++ b/extra/src/lib/synergy/gui/license/LicenseActivator.cpp @@ -0,0 +1,145 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022-2025 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 . + */ + +#include "LicenseActivator.h" + +#include "synergy/gui/constants.h" + +#include +#include +#include +#include +#include + +namespace synergy::gui::license { + +QString activateUrl() +{ + const auto envVar = qEnvironmentVariable("SYNERGY_TEST_API_URL_ACTIVATE"); + return envVar.isEmpty() ? kUrlApiLicenseActivate : envVar; +} + +LicenseActivator::LicenseActivator() +{ + connect(&m_manager, &QNetworkAccessManager::finished, this, &LicenseActivator::handleResponse); +} + +void LicenseActivator::activate(Data activateData) +{ + m_isBusy = true; + + const auto url = QUrl(activateUrl()); + qDebug().noquote() << "activating with url:" << url.toString(); + + auto request = QNetworkRequest(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + m_manager.post(request, getRequestData(activateData)); +} + +void LicenseActivator::handleResponse(QNetworkReply *reply) +{ + m_isBusy = false; + + if (!reply) { + qWarning("no activation reply"); + Q_EMIT activationFailed("License activation failed, empty network reply."); + return; + } + + const auto response = reply->readAll(); + + if (reply->error() != QNetworkReply::NoError) { + const auto kLimit = 200; + const auto responseSliced = response.length() > kLimit ? response.sliced(0, kLimit) + "..." : response; + qWarning().noquote() << "activation error:" << reply->error() << reply->errorString() << responseSliced; + Q_EMIT activationFailed("License activation failed, there was a network error."); + reply->deleteLater(); + return; + } + + qDebug().noquote() << "activation response:" << response; + const auto jsonDoc = QJsonDocument::fromJson(response); + if (response.isNull()) { + qWarning("empty activation response"); + Q_EMIT activationFailed("License activation failed, the server sent an empty response."); + reply->deleteLater(); + return; + } + + const auto json = jsonDoc.object(); + if (json["status"].toString() != "success") { + const auto status = json["status"].toString(); + const auto message = json["message"].toString(); + + if (!status.isEmpty()) { + qWarning().noquote() << "activation status:" << status; + } else { + qWarning("activation status was empty"); + } + + if (!message.isEmpty()) { + qWarning().noquote() << "activation message:" << message; + Q_EMIT activationFailed(message); + } else { + qWarning("activation message was empty"); + Q_EMIT activationFailed("License activation failed, unknown error."); + } + + reply->deleteLater(); + return; + } + + qInfo().noquote() << "activation successful"; + Q_EMIT activationSucceeded(); + reply->deleteLater(); +} + +QByteArray LicenseActivator::getRequestData(Data activateData) +{ + if (activateData.machineSignature.isEmpty()) { + qFatal("cannot create activation request, no machine id"); + } + + if (activateData.hostnameSignature.isEmpty()) { + qFatal("cannot create activation request, no hostname"); + } + + if (activateData.serialKey.isEmpty()) { + qFatal("cannot create activation request, no serial key"); + } + + if (activateData.appVersion.isEmpty()) { + qFatal("cannot create activation request, no app version"); + } + + if (activateData.osName.isEmpty()) { + qFatal("cannot create activation request, no os name"); + } + + QJsonObject requestData; + requestData["machineSignature"] = activateData.machineSignature; + requestData["hostnameSignature"] = activateData.hostnameSignature; + requestData["serialKey"] = activateData.serialKey; + requestData["appVersion"] = activateData.appVersion; + requestData["osName"] = activateData.osName; + requestData["isServer"] = activateData.isServer; + + return QJsonDocument(requestData).toJson(); +} + +}; // namespace synergy::gui::license diff --git a/extra/src/lib/synergy/gui/license/LicenseActivator.h b/extra/src/lib/synergy/gui/license/LicenseActivator.h new file mode 100644 index 000000000..b623f8d70 --- /dev/null +++ b/extra/src/lib/synergy/gui/license/LicenseActivator.h @@ -0,0 +1,66 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022-2025 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 . + */ + +#pragma once + +#include +#include +#include + +class QNetworkReply; + +namespace synergy::gui::license { + +class LicenseActivator : public QObject +{ + Q_OBJECT + +public: + struct Data + { + QString machineSignature; + QString hostnameSignature; + QString serialKey; + QString appVersion; + QString osName; + bool isServer; + }; + + explicit LicenseActivator(); + + void activate(Data activateData); + + bool isBusy() + { + return m_isBusy; + } + +signals: + void activationFailed(const QString &message); + void activationSucceeded(); + +private slots: + void handleResponse(QNetworkReply *reply); + +private: + QByteArray getRequestData(Data activateData); + + QNetworkAccessManager m_manager; + bool m_isBusy = false; +}; + +} // namespace synergy::gui::license diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp new file mode 100644 index 000000000..7fc81d7e4 --- /dev/null +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -0,0 +1,462 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "LicenseHandler.h" + +#include "ActivationDialog.h" +#include "dialogs/UpgradeDialog.h" +#include "gui/config/AppConfig.h" +#include "gui/core/CoreProcess.h" +#include "gui/styles.h" +#include "synergy/gui/constants.h" +#include "synergy/gui/license/license_utils.h" +#include "synergy/license/Product.h" +#include "version.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono; +using namespace synergy::gui::license; +using namespace synergy::gui; +using namespace deskflow::gui; +using License = synergy::license::License; + +LicenseHandler::LicenseHandler() +{ + m_enabled = synergy::gui::license::isActivationEnabled(); + + connect(&m_activator, &LicenseActivator::activationFailed, this, [this](const QString &message) { + QString fullMessage = QString( + "

There was a problem activating your license.

" + "%3" + R"(

Please contact us )" + "if there is anything we can do to help.

" + ) + .arg(kUrlContact) + .arg(kColorSecondary) + .arg(message); + QMessageBox::warning(m_pMainWindow, "Activation failed", fullMessage); + + qWarning("activation failed, showing serial key dialog"); + return showSerialKeyDialog(); + }); + + connect(&m_activator, &LicenseActivator::activationSucceeded, this, [this] { + qDebug("license activation succeeded, saving settings"); + m_settings.setActivated(true); + m_settings.sync(); + + if (m_pCoreProcess == nullptr) { + qFatal("core process not set"); + } + + qDebug("resuming core process after activation"); + m_pCoreProcess->start(); + }); +} + +void LicenseHandler::handleMainWindow( + QMainWindow *mainWindow, AppConfig *appConfig, deskflow::gui::CoreProcess *coreProcess +) +{ + // Must still be set as these are used when not enabled. + m_pMainWindow = mainWindow; + m_pAppConfig = appConfig; + m_pCoreProcess = coreProcess; + + if (!m_enabled) { + qDebug("license handler disabled, skipping main window handler"); + return; + } + + qDebug("main window create handled"); + + if (!loadSettings()) { + qFatal("failed to load license settings"); + } +} + +bool LicenseHandler::handleAppStart() +{ + if (m_pMainWindow == nullptr) { + qFatal("main window not set"); + } + + if (m_pAppConfig == nullptr) { + qFatal("app config not set"); + } + + if (!m_enabled) { + qDebug("license handler disabled, skipping start handler"); + m_pMainWindow->setWindowTitle(SYNERGY_PRODUCT_NAME); + return true; + } + + updateWindowTitle(); + + const auto serialKeyAction = new QAction("Change serial key", m_pMainWindow); + QObject::connect(serialKeyAction, &QAction::triggered, [this] { showSerialKeyDialog(); }); + + const auto licenseMenu = new QMenu("License"); + licenseMenu->addAction(serialKeyAction); + m_pMainWindow->menuBar()->addAction(licenseMenu->menuAction()); + + const auto checkResult = check(); + if (!checkResult) { + return false; + } + + qDebug("license is valid, continuing with start"); + updateWindowTitle(); + clampFeatures(); + return true; +} + +void LicenseHandler::handleSettings( + QDialog *parent, QCheckBox *enableTls, QCheckBox *invertConnection, QRadioButton *systemScope, + QRadioButton *userScope +) const +{ + if (!m_enabled) { + qDebug("license handler disabled, skipping settings handler"); + return; + } + + const auto onTlsToggle = [this, parent, enableTls] { + qDebug("license handler, tls checkbox toggled"); + checkTlsCheckBox(parent, enableTls, true); + }; + QObject::connect(enableTls, &QCheckBox::toggled, onTlsToggle); + + const auto onInvertConnectionToggle = [this, parent, invertConnection] { + qDebug("license handler, invert connection checkbox toggled"); + checkInvertConnectionCheckBox(parent, invertConnection, true); + }; + QObject::connect(invertConnection, &QCheckBox::toggled, onInvertConnectionToggle); + + checkTlsCheckBox(parent, enableTls, false); + checkInvertConnectionCheckBox(parent, invertConnection, false); +} + +void LicenseHandler::handleVersionCheck(QString &versionUrl) +{ + if (!m_enabled) { + qDebug("license handler disabled, skipping version check handler"); + return; + } + + const auto edition = license().productEdition(); + if (edition == Product::Edition::kBusiness) { + versionUrl.append("/business"); + } else { + versionUrl.append("/personal"); + } +} + +bool LicenseHandler::handleCoreStart() +{ + // HACK: For some reason, the core start trigger gets called twice when clicking the 'start' button. + // If the activator is called twice in quick succession, the core is started twice. + if (m_activator.isBusy()) { + qDebug("activator is busy, skipping core start handler"); + return false; + } + + if (!m_enabled) { + qDebug("license handler disabled, skipping core start handler"); + return true; + } + + if (m_pAppConfig == nullptr) { + qFatal("app config not set"); + } + + if (m_pMainWindow == nullptr) { + qFatal("main window not set"); + } + + if (m_pCoreProcess == nullptr) { + qFatal("core process not set"); + } + + if (m_settings.activated()) { + qDebug("license is activated, starting core"); + return true; + } + + if (m_license.serialKey().isOffline) { + qDebug("offline serial key, starting core"); + return true; + } + + if (!m_license.isValid()) { + qWarning("no valid license, skipping core start"); + return false; + } + + qInfo("activating license"); + + const auto machineId = QSysInfo::machineUniqueId(); + const auto hostname = QHostInfo::localHostName(); + + // Protect the customer by anonymizing the machine ID and hostname so that if leaked accidentally, + // the information cannot be used as an attack vector on the customer by a bad actor. + const QString machineSignature = QCryptographicHash::hash(machineId, QCryptographicHash::Sha256).toHex(); + const auto hostnameSignature = QCryptographicHash::hash(hostname.toUtf8(), QCryptographicHash::Sha256).toHex(); + + const auto serialKey = QString::fromStdString(m_license.serialKey().hexString); + const auto osName = QSysInfo::prettyProductName(); + const auto isServer = m_pAppConfig->serverGroupChecked(); + + m_activator.activate({machineSignature, hostnameSignature, serialKey, kVersion, osName, isServer}); + + return false; +} + +bool LicenseHandler::loadSettings() +{ + using enum SetSerialKeyResult; + + m_settings.load(); + + const auto serialKey = m_settings.serialKey(); + if (!serialKey.isEmpty()) { + const auto result = setLicense(m_settings.serialKey(), true); + if (result != kSuccess && result != kUnchanged) { + qWarning("set serial key failed, showing activation dialog"); + return showSerialKeyDialog(); + } + } + + return true; +} + +void LicenseHandler::saveSettings() +{ + const auto hexString = m_license.serialKey().hexString; + m_settings.setSerialKey(QString::fromStdString(hexString)); + m_settings.sync(); +} + +bool LicenseHandler::showSerialKeyDialog() +{ + if (!m_settings.isWritable()) { + QMessageBox::warning( + m_pMainWindow, "Write access required", + tr("

The settings file is not writable:

" + "

%1

" + "

Please check the file permissions and try again.

") + .arg(m_settings.fileName()) + ); + return false; + } + + ActivationDialog dialog(m_pMainWindow, *m_pAppConfig, *this); + const auto result = dialog.exec(); + if (result != QDialog::Accepted) { + qWarning("license serial key dialog declined"); + return false; + } + + if (dialog.serialKeyChanged()) { + // Reset activation so new serial key can be activated. + qDebug("serial key changed, updating settings"); + m_settings.setActivated(false); + m_settings.sync(); + } + + saveSettings(); + updateWindowTitle(); + clampFeatures(); + + if (dialog.serialKeyChanged() && m_pCoreProcess->isStarted()) { + qDebug("restarting core on serial key change"); + m_pCoreProcess->restart(); + } + + qDebug("license serial key dialog accepted"); + return true; +} + +void LicenseHandler::updateWindowTitle() const +{ + const auto productName = QString::fromStdString(m_license.productName()); + qDebug("updating main window title: %s", qPrintable(productName)); + m_pMainWindow->setWindowTitle(productName); +} + +void LicenseHandler::checkTlsCheckBox(QDialog *parent, QCheckBox *checkBoxEnableTls, bool showDialog) const +{ + if (!m_license.isTlsAvailable() && checkBoxEnableTls->isChecked()) { + qDebug("tls not available, showing upgrade dialog"); + checkBoxEnableTls->setChecked(false); + + if (showDialog) { + UpgradeDialog dialog(parent); + dialog.showDialog( + QString("TLS Encryption"), + QString("Please upgrade to %1 to enable TLS encryption.").arg(synergy::gui::kProProductName), + synergy::gui::kUrlPersonalUpgrade + ); + } + } +} + +void LicenseHandler::checkInvertConnectionCheckBox( + QDialog *parent, QCheckBox *checkBoxInvertConnection, bool showDialog +) const +{ + if (!m_license.isInvertConnectionAvailable() && checkBoxInvertConnection->isChecked()) { + qDebug("invert connection not available, showing upgrade dialog"); + checkBoxInvertConnection->setChecked(false); + + if (showDialog) { + UpgradeDialog dialog(parent); + dialog.showDialog( + QString("Invert Connection"), + QString("Please upgrade to %1 to enable the invert connection feature.") + .arg(synergy::gui::kBusinessProductName), + synergy::gui::kUrlContact + ); + } + } +} + +const synergy::license::License &LicenseHandler::license() const +{ + return m_license; +} + +Product::Edition LicenseHandler::productEdition() const +{ + return m_license.productEdition(); +} + +QString LicenseHandler::productName() const +{ + return QString::fromStdString(m_license.productName()); +} + +/// @param allowExpired If true, allow expired licenses to be set. +/// Useful for passing an expired license to the activation dialog. +LicenseHandler::SetSerialKeyResult LicenseHandler::setLicense(const QString &hexString, bool allowExpired) +{ + using enum LicenseHandler::SetSerialKeyResult; + + if (hexString.isEmpty()) { + qFatal("serial key is empty"); + } + + qDebug() << "changing serial key to:" << hexString; + auto serialKey = parseSerialKey(hexString); + + if (!serialKey.isValid) { + qWarning() << "invalid serial key, ignoring:" << hexString; + return kInvalid; + } + + auto license = License(serialKey); + if (m_time.hasTestTime()) { + license.setNowFunc([this]() { return m_time.now(); }); + } + + if (!allowExpired && license.isExpired()) { + qDebug("license is expired, ignoring"); + return kExpired; + } + + const auto oldSerialKey = m_license.serialKey(); + m_license = license; + + // This delayed check logic seems really complex. Is it really worth the maintenance and testing cost? + // Condition must run *after* the license member is set, since it's async callback uses this member. + if (!m_license.isExpired() && m_license.isTimeLimited()) { + auto secondsLeft = m_license.secondsLeft(); + if (secondsLeft.count() < INT_MAX) { + const auto validateAt = secondsLeft + seconds{1}; + const auto interval = duration_cast(validateAt); + QTimer::singleShot(interval, this, &LicenseHandler::check); + } else { + qDebug("license expiry too distant to schedule timer"); + } + } + + if (serialKey == oldSerialKey) { + qDebug("serial key did not change, ignoring"); + return kUnchanged; + } + + return kSuccess; +} + +bool LicenseHandler::check() +{ + if (!m_license.isValid()) { + qDebug("license validation failed, license invalid"); + return showSerialKeyDialog(); + } else if (m_license.isExpired()) { + qDebug("license validation failed, license expired"); + return showSerialKeyDialog(); + } else if (m_license.isExpiringSoon()) { + qDebug("license is expiring soon, skipping activation dialog"); + showSerialKeyDialog(); + // Return true even if dialog cancelled, since expiring soon licenses are still valid. + return true; + } else { + qDebug("license validation succeeded"); + return true; + } +} + +void LicenseHandler::clampFeatures() +{ + if (m_pAppConfig->tlsEnabled() && !m_license.isTlsAvailable()) { + qWarning("tls not available, disabling tls"); + m_pAppConfig->setTlsEnabled(false); + } + + if (m_pAppConfig->invertConnection() && !m_license.isInvertConnectionAvailable()) { + qWarning("invert connection not available, disabling invert connection"); + m_pAppConfig->setInvertConnection(false); + } + + if (m_pAppConfig->isSystemScope() && !m_license.isSettingsScopeAvailable()) { + qFatal("settings scope not available"); + } + + qDebug("committing default feature settings"); + m_pAppConfig->commit(); +} + +void LicenseHandler::disable() +{ + qDebug("disabling license handler"); + m_enabled = false; +} diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h new file mode 100644 index 000000000..d5e8bf24f --- /dev/null +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -0,0 +1,101 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "synergy/gui/AppTime.h" +#include "synergy/gui/ExtraSettings.h" +#include "synergy/gui/license/LicenseActivator.h" +#include "synergy/license/License.h" +#include "synergy/license/Product.h" + +class AppConfig; +class QMainWindow; +class QDialog; +class QCheckBox; +class QRadioButton; + +namespace deskflow::gui { +class CoreProcess; +} + +/** + * @brief A convenience wrapper for `License` that provides Qt signals, etc. + */ +class LicenseHandler : public QObject +{ + Q_OBJECT + + using License = synergy::license::License; + using SerialKey = synergy::license::SerialKey; + +public: + enum class SetSerialKeyResult + { + kSuccess, + kFatal, + kUnchanged, + kInvalid, + kExpired + }; + + explicit LicenseHandler(); + + static LicenseHandler &instance() + { + static LicenseHandler instance; + return instance; + } + + void handleMainWindow(QMainWindow *mainWindow, AppConfig *appConfig, deskflow::gui::CoreProcess *coreProcess); + bool handleAppStart(); + void handleSettings( + QDialog *parent, QCheckBox *enableTls, QCheckBox *invertConnection, QRadioButton *systemScope, + QRadioButton *userScope + ) const; + void handleVersionCheck(QString &versionUrl); + bool handleCoreStart(); + bool loadSettings(); + void saveSettings(); + const License &license() const; + Product::Edition productEdition() const; + QString productName() const; + SetSerialKeyResult setLicense(const QString &hexString, bool allowExpired = false); + void clampFeatures(); + void disable(); + + bool isEnabled() const + { + return m_enabled; + } + +private: + void checkTlsCheckBox(QDialog *parent, QCheckBox *checkBoxEnableTls, bool showDialog) const; + void checkInvertConnectionCheckBox(QDialog *parent, QCheckBox *checkBoxInvertConnection, bool showDialog) const; + void updateWindowTitle() const; + bool showSerialKeyDialog(); + bool check(); + + bool m_enabled = true; + synergy::gui::AppTime m_time; + License m_license = License::invalid(); + synergy::gui::ExtraSettings m_settings; + synergy::gui::license::LicenseActivator m_activator; + QMainWindow *m_pMainWindow = nullptr; + AppConfig *m_pAppConfig = nullptr; + deskflow::gui::CoreProcess *m_pCoreProcess = nullptr; +}; diff --git a/extra/src/lib/synergy/gui/license/license_notices.cpp b/extra/src/lib/synergy/gui/license/license_notices.cpp new file mode 100644 index 000000000..c26417557 --- /dev/null +++ b/extra/src/lib/synergy/gui/license/license_notices.cpp @@ -0,0 +1,78 @@ +/* + * 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 . + */ + +#include "license_notices.h" + +#include "constants.h" +#include "synergy/license/License.h" + +using License = synergy::license::License; + +namespace synergy::gui { + +QString trialLicenseNotice(const License &license, const QString &linkColor); +QString subscriptionLicenseNotice(const License &license, const QString &linkColor); + +QString licenseNotice(const License &license, const QString &linkColor) +{ + if (license.isTrial()) { + return trialLicenseNotice(license, linkColor); + } else if (license.isSubscription()) { + return subscriptionLicenseNotice(license, linkColor); + } else { + qFatal("license notice only for time limited licenses"); + return ""; // Workaround for no return warning on Windows. + } +} + +QString trialLicenseNotice(const License &license, const QString &linkColor) +{ + const QString buyLink = QString(kLinkBuy).arg(kUrlContact).arg(linkColor); + if (license.isExpired()) { + return QString("

Your trial has ended. %1

").arg(buyLink); + } else { + auto daysLeft = license.daysLeft().count(); + if (daysLeft <= 0) { + return QString("

Your trial ends today. %1

").arg(buyLink); + } else { + return QString("

Your trial ends in %1 %2. %3

") + .arg(daysLeft) + .arg((daysLeft == 1) ? "day" : "days") + .arg(buyLink); + } + } +} + +QString subscriptionLicenseNotice(const License &license, const QString &linkColor) +{ + const QString renewLink = QString(kLinkRenew).arg(kUrlContact).arg(linkColor); + if (license.isExpired()) { + return QString("

Your license has expired. %1

").arg(renewLink); + } else { + auto daysLeft = license.daysLeft().count(); + if (daysLeft <= 0) { + return QString("

Your license expires today. %1

").arg(renewLink); + } else { + return QString("

Your license expires in %1 %2. %3

") + .arg(daysLeft) + .arg((daysLeft == 1) ? "day" : "days") + .arg(renewLink); + } + } +} + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/license/license_notices.h b/extra/src/lib/synergy/gui/license/license_notices.h new file mode 100644 index 000000000..c1ba0efb0 --- /dev/null +++ b/extra/src/lib/synergy/gui/license/license_notices.h @@ -0,0 +1,28 @@ +/* + * 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 . + */ + +#pragma once + +#include "synergy/license/License.h" + +#include + +namespace synergy::gui { + +QString licenseNotice(const synergy::license::License &license, const QString &linkColor); + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/license/license_utils.cpp b/extra/src/lib/synergy/gui/license/license_utils.cpp new file mode 100644 index 000000000..86f4f3827 --- /dev/null +++ b/extra/src/lib/synergy/gui/license/license_utils.cpp @@ -0,0 +1,56 @@ +/* + * 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 . + */ + +#include "license_utils.h" + +#include "gui/string_utils.h" +#include "synergy/license/parse_serial_key.h" + +#include +#include + +namespace synergy::gui::license { + +#ifdef SYNERGY_ENABLE_ACTIVATION +const bool kEnableActivation = true; +#else +const bool kEnableActivation = false; +#endif // SYNERGY_ENABLE_ACTIVATION + +bool isActivationEnabled() +{ + if (strToTrue(qEnvironmentVariable("SYNERGY_ENABLE_ACTIVATION"))) { + return true; + } else { + return kEnableActivation; + } +} + +synergy::license::SerialKey parseSerialKey(const QString &hexString) +{ + try { + return synergy::license::parseSerialKey(hexString.toStdString()); + } catch (const std::exception &e) { + qWarning("failed to parse serial key: %s", e.what()); + return synergy::license::SerialKey::invalid(); + } catch (...) { + qWarning("failed to parse serial key, unknown error"); + return synergy::license::SerialKey::invalid(); + } +} + +} // namespace synergy::gui::license diff --git a/extra/src/lib/synergy/gui/license/license_utils.h b/extra/src/lib/synergy/gui/license/license_utils.h new file mode 100644 index 000000000..fcf704663 --- /dev/null +++ b/extra/src/lib/synergy/gui/license/license_utils.h @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +#pragma once + +#include + +#include "synergy/license/SerialKey.h" + +namespace synergy::gui::license { + +bool isActivationEnabled(); +synergy::license::SerialKey parseSerialKey(const QString &hexString); + +} // namespace synergy::gui::license diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h new file mode 100644 index 000000000..51c450851 --- /dev/null +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -0,0 +1,70 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 - 2025 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 . + */ + +#pragma once + +#include "synergy/gui/FeatureHandler.h" +#include "synergy/gui/license/LicenseHandler.h" +#include "synergy/hooks/gui_hook_config.h" // IWYU pragma: keep + +#include +#include +#include +#include + +namespace deskflow::gui { +class CoreProcess; +} + +namespace synergy::hooks { + +inline void onMainWindow(QMainWindow *mainWindow, AppConfig *appConfig, deskflow::gui::CoreProcess *coreProcess) +{ + LicenseHandler::instance().handleMainWindow(mainWindow, appConfig, coreProcess); + FeatureHandler::instance().handleMainWindow(appConfig); +} + +inline bool onAppStart() +{ + return LicenseHandler::instance().handleAppStart(); +} + +inline void onSettings( + QDialog *parent, QCheckBox *enableTls, QCheckBox *invertConnection, QRadioButton *systemScope, + QRadioButton *userScope +) +{ + LicenseHandler::instance().handleSettings(parent, enableTls, invertConnection, systemScope, userScope); + FeatureHandler::instance().handleSettings(parent, systemScope, userScope); +} + +inline void onVersionCheck(QString &versionUrl) +{ + return LicenseHandler::instance().handleVersionCheck(versionUrl); +} + +inline bool onCoreStart() +{ + return LicenseHandler::instance().handleCoreStart(); +} + +inline void onTestStart() +{ + LicenseHandler::instance().disable(); +} + +} // namespace synergy::hooks diff --git a/extra/src/lib/synergy/hooks/gui_hook_config.h.in b/extra/src/lib/synergy/hooks/gui_hook_config.h.in new file mode 100644 index 000000000..f96e5800d --- /dev/null +++ b/extra/src/lib/synergy/hooks/gui_hook_config.h.in @@ -0,0 +1,29 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2024 - 2025 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 . + */ + +#pragma once + +// clang-format off + +#cmakedefine DESKFLOW_GUI_HOOK_MAIN_WINDOW ${DESKFLOW_GUI_HOOK_MAIN_WINDOW} +#cmakedefine DESKFLOW_GUI_HOOK_APP_START ${DESKFLOW_GUI_HOOK_APP_START} +#cmakedefine DESKFLOW_GUI_HOOK_SETTINGS ${DESKFLOW_GUI_HOOK_SETTINGS} +#cmakedefine DESKFLOW_GUI_HOOK_VERSION ${DESKFLOW_GUI_HOOK_VERSION} +#cmakedefine DESKFLOW_GUI_HOOK_CORE_START ${DESKFLOW_GUI_HOOK_CORE_START} +#cmakedefine DESKFLOW_GUI_HOOK_TEST_START ${DESKFLOW_GUI_HOOK_TEST_START} + +// clang-format on diff --git a/extra/src/lib/synergy/license/CMakeLists.txt b/extra/src/lib/synergy/license/CMakeLists.txt new file mode 100644 index 000000000..ce92ad413 --- /dev/null +++ b/extra/src/lib/synergy/license/CMakeLists.txt @@ -0,0 +1,25 @@ +# 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 . + +file(GLOB headers "*.h") +file(GLOB sources "*.cpp") + +if(ADD_HEADERS_TO_SOURCES) + list(APPEND sources ${headers}) +endif() + +add_library(license STATIC ${sources}) + +target_link_libraries(license arch base) diff --git a/extra/src/lib/synergy/license/License.cpp b/extra/src/lib/synergy/license/License.cpp new file mode 100644 index 000000000..e72bccdc4 --- /dev/null +++ b/extra/src/lib/synergy/license/License.cpp @@ -0,0 +1,129 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#include "License.h" + +#include "Product.h" +#include "synergy/license/SerialKey.h" +#include "synergy/license/parse_serial_key.h" + +#include +#include + +using namespace std::chrono; + +namespace synergy::license { + +License::License(const std::string &hexString) : m_serialKey(parseSerialKey(hexString)) +{ +} + +License::License(const SerialKey &serialKey) : m_serialKey(serialKey) +{ + if (!m_serialKey.isValid) { + throw InvalidSerialKey(); + } +} + +bool License::isTrial() const +{ + return m_serialKey.type.isTrial(); +} + +bool License::isSubscription() const +{ + return m_serialKey.type.isSubscription(); +} + +bool License::isTimeLimited() const +{ + return m_serialKey.type.isSubscription() || m_serialKey.type.isTrial(); +} + +bool License::isTlsAvailable() const +{ + return m_serialKey.product.isFeatureAvailable(Product::Feature::kTls); +} + +bool License::isInvertConnectionAvailable() const +{ + return m_serialKey.product.isFeatureAvailable(Product::Feature::kInvertConnection); +} + +bool License::isSettingsScopeAvailable() const +{ + return m_serialKey.product.isFeatureAvailable(Product::Feature::kSettingsScope); +} + +Product::Edition License::productEdition() const +{ + return m_serialKey.product.edition(); +} + +bool License::isExpiringSoon() const +{ + if (!isTimeLimited()) { + return false; + } + + if (!m_serialKey.warnTime.has_value()) { + throw NoTimeLimitError(); + } + + return m_nowFunc() >= m_serialKey.warnTime.value(); +} + +bool License::isExpired() const +{ + if (!isTimeLimited()) { + return false; + } + + if (!m_serialKey.expireTime.has_value()) { + throw NoTimeLimitError(); + } + + return m_nowFunc() >= m_serialKey.expireTime.value(); +} + +seconds License::secondsLeft() const +{ + if (!m_serialKey.expireTime.has_value()) { + throw NoTimeLimitError(); + } + + auto expireTime = m_serialKey.expireTime.value(); + + auto timeLeft = expireTime - m_nowFunc(); + return duration_cast(timeLeft); +} + +days License::daysLeft() const +{ + return duration_cast(secondsLeft()); +} + +std::string License::productName() const +{ + auto name = m_serialKey.product.name(); + if (m_serialKey.type.isTrial()) { + name += " (Trial)"; + } + return name; +} + +} // namespace synergy::license diff --git a/extra/src/lib/synergy/license/License.h b/extra/src/lib/synergy/license/License.h new file mode 100644 index 000000000..ac01c2202 --- /dev/null +++ b/extra/src/lib/synergy/license/License.h @@ -0,0 +1,119 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#pragma once + +#include "SerialKey.h" + +#include +#include +#include +#include + +class Server; +class LicenseHandler; +class LicenseTests; + +namespace synergy::license { + +class License +{ + friend class ::Server; + friend class ::LicenseHandler; + friend class ::LicenseTests; + + using days = std::chrono::days; + using system_clock = std::chrono::system_clock; + using time_point = system_clock::time_point; + using NowFunc = std::function; + using LicenseError = std::runtime_error; + +public: + explicit License(const SerialKey &serialKey); + explicit License(const std::string &hexString); + ~License() = default; + + friend bool operator==(License const &lhs, License const &rhs) + { + return lhs.m_serialKey == rhs.m_serialKey; + } + + bool isTlsAvailable() const; + bool isInvertConnectionAvailable() const; + bool isSettingsScopeAvailable() const; + bool isValid() const + { + return m_serialKey.isValid; + } + bool isExpiringSoon() const; + bool isExpired() const; + bool isTrial() const; + bool isSubscription() const; + bool isTimeLimited() const; + std::chrono::days daysLeft() const; + std::chrono::seconds secondsLeft() const; + Product::Edition productEdition() const; + std::string productName() const; + const SerialKey &serialKey() const + { + return m_serialKey; + } + void invalidate() + { + m_serialKey = SerialKey::invalid(); + } + + class InvalidSerialKey : public LicenseError + { + public: + explicit InvalidSerialKey() : LicenseError("invalid serial key") + { + } + }; + + class NoTimeLimitError : public LicenseError + { + public: + explicit NoTimeLimitError() : LicenseError("serial key has no time limit") + { + } + }; + +protected: + void setNowFunc(const NowFunc &nowFunc) + { + m_nowFunc = nowFunc; + } + +private: + // for intentionality, force use of `invalid()` static function. + License() = default; + + // prevent copy, so that changes can be reflected in one instance. + License(const License &) = default; + License &operator=(const License &) = default; + + static License invalid() + { + return License(); + } + + SerialKey m_serialKey = SerialKey::invalid(); + NowFunc m_nowFunc = []() { return system_clock::now(); }; +}; + +} // namespace synergy::license diff --git a/extra/src/lib/synergy/license/Product.cpp b/extra/src/lib/synergy/license/Product.cpp new file mode 100644 index 000000000..32790b661 --- /dev/null +++ b/extra/src/lib/synergy/license/Product.cpp @@ -0,0 +1,190 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#include + +#include "Product.h" + +using SKE = Product::SerialKeyEditionID; + +const char *const kLicensedProductName = "Synergy 1"; + +const std::string SKE::Pro = "pro"; +const std::string SKE::Basic = "basic"; +const std::string SKE::Business = "business"; + +using Edition = Product::Edition; + +const std::map> kSerialKeyEditions{ + {SKE::Basic, Edition::kBasic}, + {SKE::Pro, Edition::kPro}, + {SKE::Business, Edition::kBusiness}, +}; + +Product::Product(Edition edition) : m_edition(edition) +{ +} + +Product::Product(const std::string &serialKeyEditionID) +{ + setEdition(serialKeyEditionID); +} + +Edition Product::edition() const +{ + return m_edition; +} + +std::string Product::serialKeyId() const +{ + switch (edition()) { + using enum Edition; + + case kPro: + return SKE::Pro; + + case kBasic: + return SKE::Basic; + + case kBusiness: + return SKE::Business; + + default: + throw InvalidProductEdition(); + } +} + +std::string Product::name() const +{ + + const std::string nameBase = kLicensedProductName; + switch (edition()) { + using enum Edition; + + case kUnregistered: + return nameBase + " (unregistered)"; + + case kBasic: + return nameBase + " Basic"; + + case kPro: + return nameBase + " Pro"; + + case kBusiness: + return nameBase + " Business"; + + default: + throw InvalidProductEdition(); + } +} + +void Product::setEdition(Edition edition) +{ + m_edition = edition; +} + +void Product::setEdition(const std::string &name) +{ + const auto &pType = kSerialKeyEditions.find(name); + + if (pType != kSerialKeyEditions.end()) { + m_edition = pType->second; + } else { + throw InvalidProductEdition(); + } +} + +bool Product::isValid() const +{ + if (m_edition == Edition::kUnregistered) { + return false; + } + return kSerialKeyEditions.contains(serialKeyId()); +} + +bool Product::isTlsAvailable() const +{ + switch (edition()) { + using enum Edition; + + case kPro: + case kBusiness: + return true; + + case kBasic: + case kUnregistered: + return false; + + default: + throw InvalidProductEdition(); + } +} + +bool Product::isInvertConnectionAvailable() const +{ + switch (edition()) { + using enum Edition; + + case kBusiness: + return true; + + case kBasic: + case kPro: + case kUnregistered: + return false; + + default: + throw InvalidProductEdition(); + } +} + +bool Product::isSettingsScopeAvailable() const +{ + switch (edition()) { + using enum Edition; + + case kBusiness: + return true; + + case kBasic: + case kPro: + case kUnregistered: + return false; + + default: + throw InvalidProductEdition(); + } +} + +bool Product::isFeatureAvailable(Product::Feature feature) const +{ + switch (feature) { + using enum Product::Feature; + + case kTls: + return isTlsAvailable(); + + case kInvertConnection: + return isInvertConnectionAvailable(); + + case kSettingsScope: + return isSettingsScopeAvailable(); + + default: + throw InvalidFeature(); + } +} diff --git a/extra/src/lib/synergy/license/Product.h b/extra/src/lib/synergy/license/Product.h new file mode 100644 index 000000000..d02f9d100 --- /dev/null +++ b/extra/src/lib/synergy/license/Product.h @@ -0,0 +1,89 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#pragma once + +#include +#include + +class Product +{ + friend bool operator==(Product const &, Product const &) = default; + +public: + class InvalidProductEdition : public std::runtime_error + { + public: + explicit InvalidProductEdition() : std::runtime_error("invalid product edition") + { + } + }; + + class InvalidFeature : public std::runtime_error + { + public: + explicit InvalidFeature() : std::runtime_error("invalid feature") + { + } + }; + + enum class Edition + { + kUnregistered = -1, + kBasic = 0, + kPro = 1, + kBusiness = 4, + }; + + enum class Feature + { + kTls = 0, + kInvertConnection = 1, + kSettingsScope = 2, + }; + + /** + * @brief Product edition IDs found in a decoded serial key. + */ + class SerialKeyEditionID + { + public: + static const std::string Basic; + static const std::string Pro; + static const std::string Business; + }; + + Product() = default; + explicit Product(Edition edition); + explicit Product(const std::string &serialKeyEditionID); + + bool isValid() const; + Edition edition() const; + std::string serialKeyId() const; + std::string name() const; + bool isFeatureAvailable(Feature feature) const; + + void setEdition(Edition type); + void setEdition(const std::string &serialKeyId); + +private: + bool isTlsAvailable() const; + bool isInvertConnectionAvailable() const; + bool isSettingsScopeAvailable() const; + + Edition m_edition = Edition::kUnregistered; +}; diff --git a/extra/src/lib/synergy/license/SerialKey.h b/extra/src/lib/synergy/license/SerialKey.h new file mode 100644 index 000000000..ea443f5a2 --- /dev/null +++ b/extra/src/lib/synergy/license/SerialKey.h @@ -0,0 +1,68 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022 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 . + */ + +#pragma once + +#include "Product.h" +#include "SerialKeyType.h" + +#include +#include +#include +#include + +namespace synergy::license { + +struct SerialKey +{ + using time_point = std::chrono::system_clock::time_point; + + friend bool operator==(const SerialKey &lhs, const SerialKey &rhs) + { + return (lhs.hexString == rhs.hexString) && (lhs.warnTime == rhs.warnTime) && (lhs.expireTime == rhs.expireTime) && + (lhs.product == rhs.product) && (lhs.type == rhs.type); + } + + explicit SerialKey(const std::string &key) : hexString(key) + { + } + + static SerialKey invalid() + { + return SerialKey(Product::Edition::kUnregistered); + } + + const std::string &toString() const + { + return hexString; + } + + bool isValid = false; + std::string hexString = ""; + Product product; + SerialKeyType type; + std::optional warnTime = std::nullopt; + std::optional expireTime = std::nullopt; + bool isOffline = false; + +private: + explicit SerialKey(Product::Edition edition) : product(edition) + { + } +}; + +} // namespace synergy::license diff --git a/extra/src/lib/synergy/license/SerialKeyType.cpp b/extra/src/lib/synergy/license/SerialKeyType.cpp new file mode 100644 index 000000000..1f608d76c --- /dev/null +++ b/extra/src/lib/synergy/license/SerialKeyType.cpp @@ -0,0 +1,37 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2015 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "SerialKeyType.h" + +const std::string SerialKeyType::Trial = "trial"; +const std::string SerialKeyType::Subscription = "subscription"; + +void SerialKeyType::setType(const std::string_view &type) +{ + m_isTrial = (type == SerialKeyType::Trial); + m_isSubscription = (type == SerialKeyType::Subscription); +} + +bool SerialKeyType::isTrial() const +{ + return m_isTrial; +} + +bool SerialKeyType::isSubscription() const +{ + return m_isSubscription; +} diff --git a/extra/src/lib/synergy/license/SerialKeyType.h b/extra/src/lib/synergy/license/SerialKeyType.h new file mode 100644 index 000000000..396cf89aa --- /dev/null +++ b/extra/src/lib/synergy/license/SerialKeyType.h @@ -0,0 +1,44 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#pragma once + +#include + +class SerialKeyType +{ +private: + friend bool operator==(SerialKeyType const &lhs, SerialKeyType const &rhs) = default; + +public: + static const std::string Trial; + static const std::string Subscription; + + explicit SerialKeyType() = default; + explicit SerialKeyType(const std::string_view &type) + { + setType(type); + } + + void setType(const std::string_view &type); + bool isTrial() const; + bool isSubscription() const; + +private: + bool m_isTrial = false; + bool m_isSubscription = false; +}; diff --git a/extra/src/lib/synergy/license/parse_serial_key.cpp b/extra/src/lib/synergy/license/parse_serial_key.cpp new file mode 100644 index 000000000..db70c43c7 --- /dev/null +++ b/extra/src/lib/synergy/license/parse_serial_key.cpp @@ -0,0 +1,174 @@ +/* + * 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 . + */ + +#include "parse_serial_key.h" + +#include "SerialKey.h" +#include "SerialKeyType.h" +#include "utils/string_utils.h" + +#include +#include +#include +#include +#include + +using Parts = std::vector; +using system_clock = std::chrono::system_clock; +using time_point = system_clock::time_point; + +namespace synergy::license { + +std::string decode(const std::string &hexString); +Parts tokenize(const std::string &plainText); +SerialKey parseV1(const std::string &hexString, const Parts &parts); +SerialKey parseV2(const std::string &hexString, const Parts &parts); +SerialKey parseV3(const std::string &hexString, const Parts &parts); +std::optional parseDate(const std::string &unixTimeString); + +SerialKey parseSerialKey(const std::string &hexString) +{ + const auto &trimmed = deskflow::utils::trim(hexString); + const auto &plainText = decode(trimmed); + const auto &parts = tokenize(plainText); + const auto &version = parts.at(0); + + if (version == "v1") { + return parseV1(trimmed, parts); + } else if (version == "v2") { + return parseV2(trimmed, parts); + } else if (version == "v3") { + return parseV3(trimmed, parts); + } else { + throw InvalidSerialKeyVersion(version); + } +} + +std::string decode(const std::string &hexString) +{ + if (hexString.length() % 2 != 0) { + throw InvalidHexString(); + } + + std::string plainText; + plainText.reserve(hexString.length() / 2); + + for (size_t i = 0; i < hexString.length(); i += 2) { + std::string byteString = hexString.substr(i, 2); + auto byte = static_cast(std::stoi(byteString, nullptr, 16)); + plainText.push_back(byte); + } + + return plainText; +} + +SerialKey parseV1(const std::string &hexString, const Parts &parts) +{ + if (parts.size() < 8) { + throw InvalidSerialKeyFormat(); + } + + // e.g.: {v1;basic;name;seats;email;company;1398297600;1398384000} + SerialKey serialKey(hexString); + serialKey.product = Product(parts.at(1)); + serialKey.warnTime = parseDate(parts.at(6)); + serialKey.expireTime = parseDate(parts.at(7)); + serialKey.isValid = true; + return serialKey; +} + +SerialKey parseV2(const std::string &hexString, const Parts &parts) +{ + if (parts.size() < 9) { + throw InvalidSerialKeyFormat(); + } + // e.g.: {v2;trial;basic;name;seats;email;company;1398297600;1398384000} + SerialKey serialKey(hexString); + serialKey.type = SerialKeyType(parts.at(1)); + serialKey.product = Product(parts.at(2)); + serialKey.warnTime = parseDate(parts.at(7)); + serialKey.expireTime = parseDate(parts.at(8)); + serialKey.isValid = true; + return serialKey; +} + +SerialKey parseV3(const std::string &hexString, const Parts &parts) +{ + if (parts.size() < 10) { + throw InvalidSerialKeyFormat(); + } + // e.g.: {v3;offline;trial;basic;name;seats;email;company;1398297600;1398384000} + SerialKey serialKey(hexString); + serialKey.isOffline = (parts.at(1) == "offline"); + serialKey.type = SerialKeyType(parts.at(2)); + serialKey.product = Product(parts.at(3)); + serialKey.warnTime = parseDate(parts.at(8)); + serialKey.expireTime = parseDate(parts.at(9)); + serialKey.isValid = true; + return serialKey; +} + +Parts tokenize(const std::string &plainText) +{ + if (plainText.front() != '{' || plainText.back() != '}') { + throw InvalidSerialKeyFormat(); + } + + const auto serialData = plainText.substr(1, plainText.length() - 2); + + Parts parts; + std::stringstream ss(serialData); + std::string item; + + while (std::getline(ss, item, ';')) { + parts.push_back(item); + } + + // it's possible that the last character is a delimiter, so add an empty part + if (!serialData.empty() && serialData.back() == ';') { + parts.emplace_back(""); + } + + return parts; +} + +std::optional parseDate(const std::string &unixTimeString) +{ + auto clean = deskflow::utils::trim(unixTimeString); + if (clean.empty()) { + return std::nullopt; + } + + try { + auto seconds = std::stoll(clean); + if (seconds <= 0) { + return std::nullopt; + } else { + return time_point{std::chrono::seconds{seconds}}; + } + } catch (std::invalid_argument &) { + throw InvalidSerialKeyDate(unixTimeString, "invalid argument"); + } catch (std::out_of_range &) { + throw InvalidSerialKeyDate(unixTimeString, "out of range"); + } catch (std::exception &ex) { + throw InvalidSerialKeyDate(unixTimeString, ex.what()); + } catch (...) { // NOSONAR + throw InvalidSerialKeyDate(unixTimeString, "unknown error"); + } +} + +} // namespace synergy::license diff --git a/extra/src/lib/synergy/license/parse_serial_key.h b/extra/src/lib/synergy/license/parse_serial_key.h new file mode 100644 index 000000000..14e2bb637 --- /dev/null +++ b/extra/src/lib/synergy/license/parse_serial_key.h @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +#include "SerialKey.h" + +#include +#include + +namespace synergy::license { + +class SerialKeyParseError : public std::runtime_error +{ +public: + explicit SerialKeyParseError(const std::string &message) : std::runtime_error(message) + { + } +}; + +class InvalidHexString : public SerialKeyParseError +{ +public: + explicit InvalidHexString() : SerialKeyParseError("invalid hex string") + { + } +}; + +class InvalidSerialKeyFormat : public SerialKeyParseError +{ +public: + explicit InvalidSerialKeyFormat() : SerialKeyParseError("invalid serial key format") + { + } +}; + +class InvalidSerialKeyDate : public SerialKeyParseError +{ +public: + explicit InvalidSerialKeyDate(const std::string &date, const std::string &cause) + : SerialKeyParseError("invalid serial key date: " + date + "\n" + cause) + { + } +}; + +class InvalidSerialKeyVersion : public SerialKeyParseError +{ +public: + explicit InvalidSerialKeyVersion(const std::string &version) + : SerialKeyParseError("invalid serial key version: " + version) + { + } +}; + +SerialKey parseSerialKey(const std::string &hexString); + +} // namespace synergy::license diff --git a/extra/src/unittests/CMakeLists.txt b/extra/src/unittests/CMakeLists.txt new file mode 100644 index 000000000..675f28fd6 --- /dev/null +++ b/extra/src/unittests/CMakeLists.txt @@ -0,0 +1,19 @@ +# Deskflow -- 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 . + +config_test() +set(target ${UNIT_TESTS_BIN}) +add_executable(${target} ${sources}) +target_link_libraries(${target} ${test_libs}) diff --git a/extra/src/unittests/gui/license/LicenseHandlerTests.cpp b/extra/src/unittests/gui/license/LicenseHandlerTests.cpp new file mode 100644 index 000000000..c1c5cf564 --- /dev/null +++ b/extra/src/unittests/gui/license/LicenseHandlerTests.cpp @@ -0,0 +1,40 @@ +/* + * Deskflow -- 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 . + */ + +#include "gui/license/LicenseHandler.h" + +#include +#include + +using namespace synergy::license; +using namespace std::chrono; + +const auto kPast = system_clock::now() - hours(1); +const auto kFuture = system_clock::now() + hours(1); + +TEST(LicenseHandlerTests, changeSerialKey_validExpiredLicense_returnsTrue) +{ + LicenseHandler licenseHandler; + licenseHandler.setEnabled(true); + auto hexString = // + "7B76313B70726F3B6E69636B20626F6C746F6E3B313B6" + "E69636B4073796D6C6573732E636F6D3B203B303B307D"; + + auto result = licenseHandler.changeSerialKey(hexString); + + ASSERT_EQ(LicenseHandler::ChangeSerialKeyResult::kSuccess, result); +} diff --git a/extra/src/unittests/gui/license/license_notices_tests.cpp b/extra/src/unittests/gui/license/license_notices_tests.cpp new file mode 100644 index 000000000..7c9d1eb14 --- /dev/null +++ b/extra/src/unittests/gui/license/license_notices_tests.cpp @@ -0,0 +1,144 @@ +/* + * Deskflow -- 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 . + */ + +#include "gui/license/license_notices.h" + +#include +#include +#include + +using namespace std::chrono; +using namespace synergy::license; +using namespace synergy::gui; +using ::testing::HasSubstr; + +const auto kPast = system_clock::now() - hours(1); +const auto kFutureOneHour = system_clock::now() + hours(1); +const auto kFutureOneDay = system_clock::now() + days(1) + hours(1); +const auto kFutureOneWeek = system_clock::now() + days(7) + hours(1); + +TEST(license_notices_tests, licenseNotice_trialExpired_correctText) +{ + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kPast; + serialKey.expireTime = kPast; + serialKey.type.setType("trial"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial has expired")); +} + +TEST(license_notices_tests, licenseNotice_trialExpiringInOneHour_correctText) +{ + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneHour; + serialKey.expireTime = kFutureOneHour; + serialKey.type.setType("trial"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires today")); +} + +TEST(license_notices_tests, licenseNotice_trialExpiringInOneDay_correctText) +{ + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneDay; + serialKey.expireTime = kFutureOneDay; + serialKey.type.setType("trial"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires in 1 day")); +} + +TEST(license_notices_tests, licenseNotice_trialExpiringInOneWeek_correctText) +{ + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneWeek; + serialKey.expireTime = kFutureOneWeek; + serialKey.type.setType("trial"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires in 7 days")); +} + +TEST(license_notices_tests, licenseNotice_subscriptionExpired_correctText) +{ + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kPast; + serialKey.expireTime = kPast; + serialKey.type.setType("subscription"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your license has expired")); +} + +TEST(license_notices_tests, licenseNotice_subscriptionExpiringInOneHour_correctText) +{ + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneHour; + serialKey.expireTime = kFutureOneHour; + serialKey.type.setType("subscription"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your license expires today")); +} + +TEST(license_notices_tests, licenseNotice_subscriptionExpiringInOneDay_correctText) +{ + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneDay; + serialKey.expireTime = kFutureOneDay; + serialKey.type.setType("subscription"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your license expires in 1 day")); +} + +TEST(license_notices_tests, licenseNotice_subscriptionExpiringInOneWeek_correctText) +{ + SerialKey serialKey(""); + serialKey.isValid = true; + serialKey.warnTime = kFutureOneWeek; + serialKey.expireTime = kFutureOneWeek; + serialKey.type.setType("subscription"); + License license(serialKey); + + QString notice = licenseNotice(license); + + EXPECT_THAT(notice.toStdString(), HasSubstr("Your license expires in 7 days")); +} diff --git a/extra/src/unittests/license/LicenseTests.cpp b/extra/src/unittests/license/LicenseTests.cpp new file mode 100644 index 000000000..3a1de70ca --- /dev/null +++ b/extra/src/unittests/license/LicenseTests.cpp @@ -0,0 +1,198 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#include +#define TEST_ENV + +#include "synergy/license/License.h" +#include "synergy/license/Product.h" + +#include +#include + +using enum Product::Edition; +using namespace synergy::license; +using time_point = std::chrono::system_clock::time_point; +using seconds = std::chrono::seconds; + +class LicenseTests : public ::testing::Test +{ +protected: + void setNow(License &license, int unixTime) const + { + license.setNowFunc([unixTime]() { return time_point{seconds{unixTime}}; }); + } +}; + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isTrial) +{ + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_TRUE(license.isTrial()); +} + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isTimeLimited) +{ + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_TRUE(license.isTimeLimited()); +} + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isNotSubscription) +{ + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_FALSE(license.isSubscription()); +} + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isExpiring) +{ + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_FALSE(license.isExpiringSoon()); +} + +TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isBasicEdition) +{ + // {v2;trial;basic;Bob;1;email;company name;1;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_EQ(kBasic, license.productEdition()); +} + +TEST_F(LicenseTests, isExpiring_expiringV2TrialBasicSerial_returnTrue) +{ + // {v2;trial;basic;Bob;1;email;company name;86400;0} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B38363430303B307D"); + setNow(license, 86401); + + EXPECT_TRUE(license.isTrial()); + EXPECT_TRUE(license.isExpiringSoon()); +} + +TEST_F(LicenseTests, isExpired_validV2TrialBasicSerial_returnFalse) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 0); + + EXPECT_TRUE(license.isTrial()); + EXPECT_FALSE(license.isExpired()); +} + +TEST_F(LicenseTests, isExpired_expiringV2TrialBasicSerial_returnFalse) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 1); + + EXPECT_TRUE(license.isTrial()); + EXPECT_FALSE(license.isExpired()); +} + +TEST_F(LicenseTests, isExpired_expiredV2TrialBasicSerial_returnTrue) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 86401); + + EXPECT_TRUE(license.isTrial()); + EXPECT_TRUE(license.isExpired()); +} + +TEST_F(LicenseTests, daysLeft_validExactlyOneDayV2TrialBasicSerial_returnOne) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 0); + + EXPECT_EQ(1, license.secondsLeft().count()); +} + +TEST_F(LicenseTests, daysLeft_validWithinOneDayV2TrialBasicSerial_returnOne) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 0); + + EXPECT_EQ(1, license.secondsLeft().count()); +} + +TEST_F(LicenseTests, daysLeft_expiredV2TrialBasicSerial_returnZero) +{ + // {v2;trial;basic;Bob;1;email;company name;0;86400} + License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" + "F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 86401); + + EXPECT_EQ(0, license.secondsLeft().count()); +} + +// Subscription license tests +TEST_F(LicenseTests, isExpiring_validV2SubscriptionBasicSerial_returnFalse) +{ + // {v2;subscription;basic;Bob;1;email;company name;1;86400} + License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" + "56D61696C3B636F6D70616E79206E616D653B313B38363430307D"); + setNow(license, 0); + + EXPECT_TRUE(license.isSubscription()); + EXPECT_FALSE(license.isExpiringSoon()); + EXPECT_EQ(kBasic, license.productEdition()); +} + +TEST_F(LicenseTests, isExpiring_expiringV2SubscriptionBasicSerial_returnTrue) +{ + // {v2;subscription;basic;Bob;1;email;company name;86400;0} + License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" + "56D61696C3B636F6D70616E79206E616D653B38363430303B307D"); + setNow(license, 86401); + + EXPECT_TRUE(license.isSubscription()); + EXPECT_TRUE(license.isExpiringSoon()); +} + +TEST_F(LicenseTests, isExpired_expiredV2SubscriptionBasicSerial_returnTrue) +{ + // {v2;subscription;basic;Bob;1;email;company name;0;86400} + License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" + "56D61696C3B636F6D70616E79206E616D653B303B38363430307D"); + setNow(license, 86401); + + EXPECT_TRUE(license.isSubscription()); + EXPECT_TRUE(license.isExpired()); +} diff --git a/extra/src/unittests/license/ProductTests.cpp b/extra/src/unittests/license/ProductTests.cpp new file mode 100644 index 000000000..00d4a8653 --- /dev/null +++ b/extra/src/unittests/license/ProductTests.cpp @@ -0,0 +1,90 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +// TODO: Move these tests and the code under test downstream to Synergy + +#define TEST_ENV + +#include "synergy/license/Product.h" + +#include + +using enum Product::Edition; + +TEST(ProductTests, equal_operator) +{ + Product product1(kPro); + Product product2(kPro); + + EXPECT_EQ(product1, product2); +} + +TEST(ProductTests, ctor_businessName_isValid) +{ + Product product(Product::SerialKeyEditionID::Business); + + EXPECT_EQ(kBusiness, product.edition()); + EXPECT_TRUE(product.isValid()); +} + +TEST(ProductTests, ctor_basicType_isValid) +{ + Product product(kBasic); + + EXPECT_TRUE(product.isValid()); +} + +TEST(ProductTests, setEdition_invalidType_throws) +{ + Product product; + + EXPECT_THROW(product.setEdition("test"), Product::InvalidType); +} + +TEST(ProductTests, setEdition_pro_isValid) +{ + Product product; + + product.setEdition(kPro); + + EXPECT_EQ(kPro, product.edition()); + EXPECT_EQ(Product::SerialKeyEditionID::Pro, product.serialKeyId()); + EXPECT_EQ("Deskflow Pro", product.name()); + EXPECT_TRUE(product.isValid()); +} + +TEST(ProductTests, setEdition_basic_isValid) +{ + Product product; + + product.setEdition(kBasic); + + EXPECT_EQ(kBasic, product.edition()); + EXPECT_EQ(Product::SerialKeyEditionID::Basic, product.serialKeyId()); + EXPECT_EQ("Deskflow Basic", product.name()); +} + +TEST(ProductTests, setEdition_business_isValid) +{ + Product product; + + product.setEdition(kBusiness); + + EXPECT_EQ(kBusiness, product.edition()); + EXPECT_EQ(Product::SerialKeyEditionID::Business, product.serialKeyId()); + EXPECT_EQ("Deskflow Business", product.name()); +} diff --git a/extra/src/unittests/license/SerialKeyTypeTests.cpp b/extra/src/unittests/license/SerialKeyTypeTests.cpp new file mode 100644 index 000000000..81037a546 --- /dev/null +++ b/extra/src/unittests/license/SerialKeyTypeTests.cpp @@ -0,0 +1,45 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2016 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 . + */ + +#define TEST_ENV + +#include "synergy/license/SerialKeyType.h" + +#include + +TEST(SerialKeyTypeTests, TrialTemporaryKeyType_false) +{ + SerialKeyType KeyType; + EXPECT_FALSE(KeyType.isTrial()); + EXPECT_FALSE(KeyType.isSubscription()); +} + +TEST(SerialKeyTypeTests, TrialTemporaryKeyType_true) +{ + SerialKeyType KeyType; + KeyType.setType("trial"); + EXPECT_TRUE(KeyType.isTrial()); + EXPECT_FALSE(KeyType.isSubscription()); +} + +TEST(SerialKeyTypeTests, TimeLimitedKeyType_true) +{ + SerialKeyType KeyType; + KeyType.setType("subscription"); + EXPECT_FALSE(KeyType.isTrial()); + EXPECT_TRUE(KeyType.isSubscription()); +} diff --git a/src/apps/CMakeLists.txt b/src/apps/CMakeLists.txt index 2f7669de9..62e56def7 100644 --- a/src/apps/CMakeLists.txt +++ b/src/apps/CMakeLists.txt @@ -18,11 +18,11 @@ function(generate_app_man TARGET NAME) --name ${NAME} --include ${CMAKE_SOURCE_DIR}/src/apps/res/manpage.txt --no-info - ${target} - -o $/${target}.1 + $ + -o $/$.1 ) install( - FILES $/${target}.1 + FILES $/$.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 ) endif() diff --git a/src/apps/deskflow-core/CMakeLists.txt b/src/apps/deskflow-core/CMakeLists.txt index d1715a3d9..87e56f124 100644 --- a/src/apps/deskflow-core/CMakeLists.txt +++ b/src/apps/deskflow-core/CMakeLists.txt @@ -12,6 +12,10 @@ add_executable(${target} CoreArgParser.cpp ) +string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _basename) +set_target_properties(${target} PROPERTIES OUTPUT_NAME "${_basename}-core") +unset(_basename) + if(WIN32) # Generate rc file set(EXE_DESCRIPTION "${CMAKE_PROJECT_PROPER_NAME} combined server and client application") diff --git a/src/apps/deskflow-daemon/CMakeLists.txt b/src/apps/deskflow-daemon/CMakeLists.txt index 69cfea48e..84c177267 100644 --- a/src/apps/deskflow-daemon/CMakeLists.txt +++ b/src/apps/deskflow-daemon/CMakeLists.txt @@ -19,6 +19,10 @@ if(WIN32) DaemonApp.h ${CMAKE_CURRENT_BINARY_DIR}/${target}.rc) + string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _basename) + set_target_properties(${target} PROPERTIES OUTPUT_NAME "${_basename}-daemon") + unset(_basename) + target_link_libraries( ${target} arch diff --git a/src/apps/deskflow-gui/CMakeLists.txt b/src/apps/deskflow-gui/CMakeLists.txt index b349c2385..054128ef1 100644 --- a/src/apps/deskflow-gui/CMakeLists.txt +++ b/src/apps/deskflow-gui/CMakeLists.txt @@ -17,9 +17,9 @@ if(WIN32) set(platform_extra deskflow.rc) elseif(BUILD_OSX_BUNDLE) #setup our bundle plist file - set(BUNDLE_EXECUTABLE_NAME "${target}") - set(BUNDLE_BUNDLE_NAME "${target}") - set(BUNDLE_DISPLAY_NAME "${target}") + set(BUNDLE_EXECUTABLE_NAME "${CMAKE_PROJECT_PROPER_NAME}") + set(BUNDLE_BUNDLE_NAME "${CMAKE_PROJECT_PROPER_NAME}") + set(BUNDLE_DISPLAY_NAME "${CMAKE_PROJECT_PROPER_NAME}") set(BUNDLE_GUI_IDENTIFIER "${CMAKE_PROJECT_REV_FQDN}") set(BUNDLE_ICON_FILE ${target}.icns) set(BUNDLE_INFO_STRING "${CMAKE_PROJECT_DESCRIPTION}") @@ -41,6 +41,14 @@ add_executable(${target} WIN32 MACOSX_BUNDLE deskflow-gui.cpp ) +if(APPLE) + set_target_properties(${target} PROPERTIES OUTPUT_NAME "${CMAKE_PROJECT_PROPER_NAME}") +else() + string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _basename) + set_target_properties(${target} PROPERTIES OUTPUT_NAME "${_basename}") + unset(_basename) +endif() + target_link_libraries( ${target} gui diff --git a/src/lib/common/CMakeLists.txt b/src/lib/common/CMakeLists.txt index b93a6fc87..89e3c4ad3 100644 --- a/src/lib/common/CMakeLists.txt +++ b/src/lib/common/CMakeLists.txt @@ -1,10 +1,12 @@ # SPDX-FileCopyrightText: (C) 2024 - 2025 Chris Rizzitello # SPDX-License-Identifier: MIT -set(CORE_BINARY "${CMAKE_PROJECT_NAME}-core") +string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _basename) +set(CORE_BINARY "${_basename}-core") if(WIN32) string(APPEND CORE_BINARY ".exe") endif() +unset(_basename) configure_file(Constants.h.in Constants.h @ONLY) configure_file(VersionInfo.h.in VersionInfo.h @ONLY) From a21afffafab637cd670a42d498cbfb27ebf5eece Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 6 May 2026 12:53:02 +0100 Subject: [PATCH 002/144] Port activation changes from extra --- extra/src/lib/synergy/gui/ExtraSettings.cpp | 3 + extra/src/lib/synergy/gui/ExtraSettings.h | 10 + extra/src/lib/synergy/gui/constants.h | 5 + .../synergy/gui/license/LicenseActivator.cpp | 145 -------------- .../synergy/gui/license/LicenseApiClient.cpp | 184 ++++++++++++++++++ ...{LicenseActivator.h => LicenseApiClient.h} | 22 ++- .../synergy/gui/license/LicenseHandler.cpp | 177 +++++++++++++++-- .../lib/synergy/gui/license/LicenseHandler.h | 12 +- 8 files changed, 388 insertions(+), 170 deletions(-) delete mode 100644 extra/src/lib/synergy/gui/license/LicenseActivator.cpp create mode 100644 extra/src/lib/synergy/gui/license/LicenseApiClient.cpp rename extra/src/lib/synergy/gui/license/{LicenseActivator.h => LicenseApiClient.h} (71%) diff --git a/extra/src/lib/synergy/gui/ExtraSettings.cpp b/extra/src/lib/synergy/gui/ExtraSettings.cpp index 8ec39809e..f02d8c984 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.cpp +++ b/extra/src/lib/synergy/gui/ExtraSettings.cpp @@ -26,12 +26,14 @@ namespace synergy::gui { const auto kSerialKeySettingKey = "serialKey"; const auto kActivatedSettingKey = "activated"; +const auto kGraceStartSettingKey = "graceStartEpochSecs"; void ExtraSettings::load() { const auto &settings = getActiveSettings(); m_serialKey = settings.value(kSerialKeySettingKey).toString(); m_activated = settings.value(kActivatedSettingKey).toBool(); + m_graceStartEpochSecs = settings.value(kGraceStartSettingKey).toLongLong(); } void ExtraSettings::sync() @@ -44,6 +46,7 @@ void ExtraSettings::sync() settings.setValue(kSerialKeySettingKey, m_serialKey); settings.setValue(kActivatedSettingKey, m_activated); + settings.setValue(kGraceStartSettingKey, m_graceStartEpochSecs); settings.sync(); } diff --git a/extra/src/lib/synergy/gui/ExtraSettings.h b/extra/src/lib/synergy/gui/ExtraSettings.h index 95dd604f4..38ded1a1d 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.h +++ b/extra/src/lib/synergy/gui/ExtraSettings.h @@ -52,9 +52,19 @@ class ExtraSettings : public deskflow::gui::Settings m_activated = activated; } + qint64 graceStartEpochSecs() const + { + return m_graceStartEpochSecs; + } + void setGraceStartEpochSecs(qint64 epochSecs) + { + m_graceStartEpochSecs = epochSecs; + } + private: QString m_serialKey; bool m_activated = false; + qint64 m_graceStartEpochSecs = 0; }; } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index e25a38f49..1d7693ca1 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -19,6 +19,8 @@ #include +#include + namespace synergy::gui { const auto kProProductName = "Synergy 1 Pro"; @@ -36,5 +38,8 @@ const auto kUrlPersonalUpgrade = QString("%1/purchase/upgrade?%2").arg(kUrlWebsi const auto kUrlContact = QString("%1/contact?%2").arg(kUrlWebsite, kUrlSourceQuery); const auto kUrlApiLicenseActivate = QString("%1/product/activate").arg(kUrlApi); +const auto kUrlApiLicenseCheck = QString("%1/product/check").arg(kUrlApi); + +constexpr auto kLicenseGracePeriod = std::chrono::days{14}; } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/license/LicenseActivator.cpp b/extra/src/lib/synergy/gui/license/LicenseActivator.cpp deleted file mode 100644 index 83b54ec6b..000000000 --- a/extra/src/lib/synergy/gui/license/LicenseActivator.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022-2025 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 . - */ - -#include "LicenseActivator.h" - -#include "synergy/gui/constants.h" - -#include -#include -#include -#include -#include - -namespace synergy::gui::license { - -QString activateUrl() -{ - const auto envVar = qEnvironmentVariable("SYNERGY_TEST_API_URL_ACTIVATE"); - return envVar.isEmpty() ? kUrlApiLicenseActivate : envVar; -} - -LicenseActivator::LicenseActivator() -{ - connect(&m_manager, &QNetworkAccessManager::finished, this, &LicenseActivator::handleResponse); -} - -void LicenseActivator::activate(Data activateData) -{ - m_isBusy = true; - - const auto url = QUrl(activateUrl()); - qDebug().noquote() << "activating with url:" << url.toString(); - - auto request = QNetworkRequest(url); - request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - - m_manager.post(request, getRequestData(activateData)); -} - -void LicenseActivator::handleResponse(QNetworkReply *reply) -{ - m_isBusy = false; - - if (!reply) { - qWarning("no activation reply"); - Q_EMIT activationFailed("License activation failed, empty network reply."); - return; - } - - const auto response = reply->readAll(); - - if (reply->error() != QNetworkReply::NoError) { - const auto kLimit = 200; - const auto responseSliced = response.length() > kLimit ? response.sliced(0, kLimit) + "..." : response; - qWarning().noquote() << "activation error:" << reply->error() << reply->errorString() << responseSliced; - Q_EMIT activationFailed("License activation failed, there was a network error."); - reply->deleteLater(); - return; - } - - qDebug().noquote() << "activation response:" << response; - const auto jsonDoc = QJsonDocument::fromJson(response); - if (response.isNull()) { - qWarning("empty activation response"); - Q_EMIT activationFailed("License activation failed, the server sent an empty response."); - reply->deleteLater(); - return; - } - - const auto json = jsonDoc.object(); - if (json["status"].toString() != "success") { - const auto status = json["status"].toString(); - const auto message = json["message"].toString(); - - if (!status.isEmpty()) { - qWarning().noquote() << "activation status:" << status; - } else { - qWarning("activation status was empty"); - } - - if (!message.isEmpty()) { - qWarning().noquote() << "activation message:" << message; - Q_EMIT activationFailed(message); - } else { - qWarning("activation message was empty"); - Q_EMIT activationFailed("License activation failed, unknown error."); - } - - reply->deleteLater(); - return; - } - - qInfo().noquote() << "activation successful"; - Q_EMIT activationSucceeded(); - reply->deleteLater(); -} - -QByteArray LicenseActivator::getRequestData(Data activateData) -{ - if (activateData.machineSignature.isEmpty()) { - qFatal("cannot create activation request, no machine id"); - } - - if (activateData.hostnameSignature.isEmpty()) { - qFatal("cannot create activation request, no hostname"); - } - - if (activateData.serialKey.isEmpty()) { - qFatal("cannot create activation request, no serial key"); - } - - if (activateData.appVersion.isEmpty()) { - qFatal("cannot create activation request, no app version"); - } - - if (activateData.osName.isEmpty()) { - qFatal("cannot create activation request, no os name"); - } - - QJsonObject requestData; - requestData["machineSignature"] = activateData.machineSignature; - requestData["hostnameSignature"] = activateData.hostnameSignature; - requestData["serialKey"] = activateData.serialKey; - requestData["appVersion"] = activateData.appVersion; - requestData["osName"] = activateData.osName; - requestData["isServer"] = activateData.isServer; - - return QJsonDocument(requestData).toJson(); -} - -}; // namespace synergy::gui::license diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp new file mode 100644 index 000000000..f06189d34 --- /dev/null +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -0,0 +1,184 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2022-2026 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 . + */ + +#include "LicenseApiClient.h" + +#include "synergy/gui/constants.h" + +#include +#include +#include +#include +#include + +namespace synergy::gui::license { + +QString activateUrl() +{ + const auto envVar = qEnvironmentVariable("SYNERGY_TEST_API_URL_ACTIVATE"); + return envVar.isEmpty() ? kUrlApiLicenseActivate : envVar; +} + +QString checkUrl() +{ + const auto envVar = qEnvironmentVariable("SYNERGY_TEST_API_URL_CHECK"); + return envVar.isEmpty() ? kUrlApiLicenseCheck : envVar; +} + +LicenseApiClient::LicenseApiClient() +{ + connect(&m_manager, &QNetworkAccessManager::finished, this, &LicenseApiClient::handleResponse); +} + +void LicenseApiClient::activate(Data data) +{ + post(RequestKind::kActivate, QUrl(activateUrl()), data); +} + +void LicenseApiClient::check(Data data) +{ + post(RequestKind::kCheck, QUrl(checkUrl()), data); +} + +void LicenseApiClient::post(RequestKind kind, const QUrl &url, const Data &data) +{ + m_isBusy = true; + m_pendingKind = kind; + + qDebug().noquote() << "license api request:" << url.toString(); + + auto request = QNetworkRequest(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + m_manager.post(request, getRequestData(data)); +} + +void LicenseApiClient::handleResponse(QNetworkReply *reply) +{ + m_isBusy = false; + const auto kind = m_pendingKind; + + const auto emitFailed = [this, kind](const QString &message) { + if (kind == RequestKind::kActivate) { + Q_EMIT activationFailed(message); + } else { + Q_EMIT checkFailed(message); + } + }; + + const auto emitSucceeded = [this, kind] { + if (kind == RequestKind::kActivate) { + Q_EMIT activationSucceeded(); + } else { + Q_EMIT checkSucceeded(); + } + }; + + if (!reply) { + qWarning("no license api reply"); + emitFailed("License request failed, empty network reply."); + return; + } + + const auto response = reply->readAll(); + + if (reply->error() != QNetworkReply::NoError) { + const auto kLimit = 200; + const auto responseSliced = response.length() > kLimit ? response.sliced(0, kLimit) + "..." : response; + qWarning().noquote() << "license api error:" << reply->error() << reply->errorString() << responseSliced; + emitFailed("License request failed, there was a network error."); + reply->deleteLater(); + return; + } + + qDebug().noquote() << "license api response:" << response; + const auto jsonDoc = QJsonDocument::fromJson(response); + if (response.isNull()) { + qWarning("empty license api response"); + emitFailed("License request failed, the server sent an empty response."); + reply->deleteLater(); + return; + } + + const auto json = jsonDoc.object(); + if (json["status"].toString() != "success") { + const auto status = json["status"].toString(); + const auto message = json["message"].toString(); + + if (!status.isEmpty()) { + qWarning().noquote() << "license api status:" << status; + } else { + qWarning("license api status was empty"); + } + + if (!message.isEmpty()) { + qWarning().noquote() << "license api message:" << message; + } else { + qWarning("license api message was empty"); + } + + if (status == "disabled") { + Q_EMIT licenseDisabled(message.isEmpty() ? QStringLiteral("License has been disabled.") : message); + } else if (!message.isEmpty()) { + emitFailed(message); + } else { + emitFailed("License request failed, unknown error."); + } + + reply->deleteLater(); + return; + } + + qInfo().noquote() << "license api request successful"; + emitSucceeded(); + reply->deleteLater(); +} + +QByteArray LicenseApiClient::getRequestData(const Data &data) const +{ + if (data.machineSignature.isEmpty()) { + qFatal("cannot create license request, no machine id"); + } + + if (data.hostnameSignature.isEmpty()) { + qFatal("cannot create license request, no hostname"); + } + + if (data.serialKey.isEmpty()) { + qFatal("cannot create license request, no serial key"); + } + + if (data.appVersion.isEmpty()) { + qFatal("cannot create license request, no app version"); + } + + if (data.osName.isEmpty()) { + qFatal("cannot create license request, no os name"); + } + + QJsonObject requestData; + requestData["machineSignature"] = data.machineSignature; + requestData["hostnameSignature"] = data.hostnameSignature; + requestData["serialKey"] = data.serialKey; + requestData["appVersion"] = data.appVersion; + requestData["osName"] = data.osName; + requestData["isServer"] = data.isServer; + + return QJsonDocument(requestData).toJson(); +} + +}; // namespace synergy::gui::license diff --git a/extra/src/lib/synergy/gui/license/LicenseActivator.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h similarity index 71% rename from extra/src/lib/synergy/gui/license/LicenseActivator.h rename to extra/src/lib/synergy/gui/license/LicenseApiClient.h index b623f8d70..f14674f35 100644 --- a/extra/src/lib/synergy/gui/license/LicenseActivator.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022-2025 Synergy Ltd. + * Copyright (C) 2022-2026 Synergy Ltd. * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -25,7 +25,7 @@ class QNetworkReply; namespace synergy::gui::license { -class LicenseActivator : public QObject +class LicenseApiClient : public QObject { Q_OBJECT @@ -40,9 +40,10 @@ class LicenseActivator : public QObject bool isServer; }; - explicit LicenseActivator(); + explicit LicenseApiClient(); - void activate(Data activateData); + void activate(Data data); + void check(Data data); bool isBusy() { @@ -52,15 +53,26 @@ class LicenseActivator : public QObject signals: void activationFailed(const QString &message); void activationSucceeded(); + void checkFailed(const QString &message); + void checkSucceeded(); + void licenseDisabled(const QString &message); private slots: void handleResponse(QNetworkReply *reply); private: - QByteArray getRequestData(Data activateData); + enum class RequestKind + { + kActivate, + kCheck + }; + + void post(RequestKind kind, const QUrl &url, const Data &data); + QByteArray getRequestData(const Data &data) const; QNetworkAccessManager m_manager; bool m_isBusy = false; + RequestKind m_pendingKind = RequestKind::kActivate; }; } // namespace synergy::gui::license diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 7fc81d7e4..18bc69b00 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -31,13 +31,17 @@ #include #include #include +#include #include #include +#include #include #include +#include #include #include #include +#include #include #include #include @@ -52,7 +56,7 @@ LicenseHandler::LicenseHandler() { m_enabled = synergy::gui::license::isActivationEnabled(); - connect(&m_activator, &LicenseActivator::activationFailed, this, [this](const QString &message) { + connect(&m_apiClient, &LicenseApiClient::activationFailed, this, [this](const QString &message) { QString fullMessage = QString( "

There was a problem activating your license.

" "%3" @@ -68,10 +72,12 @@ LicenseHandler::LicenseHandler() return showSerialKeyDialog(); }); - connect(&m_activator, &LicenseActivator::activationSucceeded, this, [this] { + connect(&m_apiClient, &LicenseApiClient::activationSucceeded, this, [this] { qDebug("license activation succeeded, saving settings"); m_settings.setActivated(true); + m_settings.setGraceStartEpochSecs(0); m_settings.sync(); + m_warnedAboutGrace = false; if (m_pCoreProcess == nullptr) { qFatal("core process not set"); @@ -80,6 +86,10 @@ LicenseHandler::LicenseHandler() qDebug("resuming core process after activation"); m_pCoreProcess->start(); }); + + connect(&m_apiClient, &LicenseApiClient::checkSucceeded, this, &LicenseHandler::handleRemoteCheckSucceeded); + connect(&m_apiClient, &LicenseApiClient::checkFailed, this, &LicenseHandler::handleRemoteCheckFailed); + connect(&m_apiClient, &LicenseApiClient::licenseDisabled, this, &LicenseHandler::disableLicenseRemotely); } void LicenseHandler::handleMainWindow( @@ -136,6 +146,7 @@ bool LicenseHandler::handleAppStart() qDebug("license is valid, continuing with start"); updateWindowTitle(); clampFeatures(); + runRemoteCheck(); return true; } @@ -184,7 +195,7 @@ bool LicenseHandler::handleCoreStart() { // HACK: For some reason, the core start trigger gets called twice when clicking the 'start' button. // If the activator is called twice in quick succession, the core is started twice. - if (m_activator.isBusy()) { + if (m_apiClient.isBusy()) { qDebug("activator is busy, skipping core start handler"); return false; } @@ -222,20 +233,7 @@ bool LicenseHandler::handleCoreStart() } qInfo("activating license"); - - const auto machineId = QSysInfo::machineUniqueId(); - const auto hostname = QHostInfo::localHostName(); - - // Protect the customer by anonymizing the machine ID and hostname so that if leaked accidentally, - // the information cannot be used as an attack vector on the customer by a bad actor. - const QString machineSignature = QCryptographicHash::hash(machineId, QCryptographicHash::Sha256).toHex(); - const auto hostnameSignature = QCryptographicHash::hash(hostname.toUtf8(), QCryptographicHash::Sha256).toHex(); - - const auto serialKey = QString::fromStdString(m_license.serialKey().hexString); - const auto osName = QSysInfo::prettyProductName(); - const auto isServer = m_pAppConfig->serverGroupChecked(); - - m_activator.activate({machineSignature, hostnameSignature, serialKey, kVersion, osName, isServer}); + m_apiClient.activate(buildApiData()); return false; } @@ -289,6 +287,8 @@ bool LicenseHandler::showSerialKeyDialog() // Reset activation so new serial key can be activated. qDebug("serial key changed, updating settings"); m_settings.setActivated(false); + m_settings.setGraceStartEpochSecs(0); + m_warnedAboutGrace = false; m_settings.sync(); } @@ -301,6 +301,14 @@ bool LicenseHandler::showSerialKeyDialog() m_pCoreProcess->restart(); } + // If the user accepted the dialog while not activated (e.g. recovering from a + // remote disable), retry activation so something visible happens regardless of + // whether the serial key changed. + if (!m_settings.activated() && m_license.isValid() && !m_license.serialKey().isOffline && !m_apiClient.isBusy()) { + qInfo("retrying activation after dialog accept"); + m_apiClient.activate(buildApiData()); + } + qDebug("license serial key dialog accepted"); return true; } @@ -425,7 +433,11 @@ bool LicenseHandler::check() qDebug("license validation failed, license expired"); return showSerialKeyDialog(); } else if (m_license.isExpiringSoon()) { - qDebug("license is expiring soon, skipping activation dialog"); + if (isInGracePeriod()) { + qDebug("license expiring soon but in remote grace period, suppressing renew nag"); + return true; + } + qDebug("license is expiring soon, showing serial key dialog"); showSerialKeyDialog(); // Return true even if dialog cancelled, since expiring soon licenses are still valid. return true; @@ -460,3 +472,132 @@ void LicenseHandler::disable() qDebug("disabling license handler"); m_enabled = false; } + +bool LicenseHandler::isInGracePeriod() const +{ + return m_settings.graceStartEpochSecs() > 0; +} + +bool LicenseHandler::isGracePeriodExpired() const +{ + if (!isInGracePeriod()) { + return false; + } + const auto now = QDateTime::currentSecsSinceEpoch(); + const auto elapsed = seconds{now - m_settings.graceStartEpochSecs()}; + return elapsed >= duration_cast(kLicenseGracePeriod); +} + +LicenseApiClient::Data LicenseHandler::buildApiData() const +{ + const auto machineId = QSysInfo::machineUniqueId(); + const auto hostname = QHostInfo::localHostName(); + + // Anonymise so a leak doesn't reveal which customer machine the data belongs to. + const auto machineSignature = QCryptographicHash::hash(machineId, QCryptographicHash::Sha256).toHex(); + const auto hostnameSignature = QCryptographicHash::hash(hostname.toUtf8(), QCryptographicHash::Sha256).toHex(); + + return { + machineSignature, + hostnameSignature, + QString::fromStdString(m_license.serialKey().hexString), + kVersion, + QSysInfo::prettyProductName(), + m_pAppConfig->serverGroupChecked() + }; +} + +void LicenseHandler::runRemoteCheck() +{ + if (!m_settings.activated() || !m_license.isValid() || m_license.serialKey().isOffline) { + qDebug("license not activated or offline, skipping remote check"); + return; + } + + if (m_apiClient.isBusy()) { + qDebug("license activator busy, skipping remote check"); + return; + } + + qInfo("running remote license check"); + m_apiClient.check(buildApiData()); +} + +void LicenseHandler::handleRemoteCheckSucceeded() +{ + qInfo("remote license check succeeded"); + + const bool wasInGrace = isInGracePeriod(); + m_settings.setGraceStartEpochSecs(0); + m_settings.sync(); + m_warnedAboutGrace = false; + + if (wasInGrace && m_pMainWindow != nullptr) { + QMessageBox::information( + m_pMainWindow, "License restored", tr("Your license is valid again. Thanks for your patience.") + ); + } +} + +void LicenseHandler::handleRemoteCheckFailed(const QString &message) +{ + qWarning().noquote() << "remote license check failed:" << message; + + if (!isInGracePeriod()) { + m_settings.setGraceStartEpochSecs(QDateTime::currentSecsSinceEpoch()); + m_settings.sync(); + } + + if (isGracePeriodExpired()) { + disableLicenseRemotely(message); + return; + } + + if (!m_warnedAboutGrace && m_pMainWindow != nullptr) { + m_warnedAboutGrace = true; + const auto graceDays = static_cast(kLicenseGracePeriod.count()); + QMessageBox::warning( + m_pMainWindow, "License check failed", + tr("

We could not verify your license:

" + "

%1

" + "

%2 will keep working for %3 days. " + R"(If the problem persists, please contact us.)" + "

") + .arg(message.toHtmlEscaped()) + .arg(productName()) + .arg(graceDays) + .arg(kUrlContact) + .arg(kColorSecondary) + ); + } +} + +void LicenseHandler::disableLicenseRemotely(const QString &reason) +{ + qWarning().noquote() << "license grace period expired, disabling:" << reason; + + if (m_pCoreProcess != nullptr && m_pCoreProcess->isStarted()) { + qDebug("stopping core process due to disabled license"); + m_pCoreProcess->stop(); + } + + // Keep the serial key + in-memory license so the next activation attempt can succeed + // automatically if the server re-enables the license (e.g. after the customer pays). + m_settings.setActivated(false); + m_settings.setGraceStartEpochSecs(0); + m_settings.sync(); + m_warnedAboutGrace = false; + + if (m_pMainWindow != nullptr) { + QMessageBox::warning( + m_pMainWindow, "License disabled", + tr("

Your license has been disabled and could not be verified within the grace period:

" + "

%1

" + R"(

Please contact us to restore access. )" + "Once your license is reinstated, the app will resume automatically.

") + .arg(reason.toHtmlEscaped()) + .arg(kUrlContact) + .arg(kColorSecondary) + ); + } +} diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index d5e8bf24f..21f864d76 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -19,7 +19,7 @@ #include "synergy/gui/AppTime.h" #include "synergy/gui/ExtraSettings.h" -#include "synergy/gui/license/LicenseActivator.h" +#include "synergy/gui/license/LicenseApiClient.h" #include "synergy/license/License.h" #include "synergy/license/Product.h" @@ -89,12 +89,20 @@ class LicenseHandler : public QObject void updateWindowTitle() const; bool showSerialKeyDialog(); bool check(); + void runRemoteCheck(); + void handleRemoteCheckSucceeded(); + void handleRemoteCheckFailed(const QString &message); + bool isInGracePeriod() const; + bool isGracePeriodExpired() const; + void disableLicenseRemotely(const QString &reason); + synergy::gui::license::LicenseApiClient::Data buildApiData() const; bool m_enabled = true; synergy::gui::AppTime m_time; License m_license = License::invalid(); synergy::gui::ExtraSettings m_settings; - synergy::gui::license::LicenseActivator m_activator; + synergy::gui::license::LicenseApiClient m_apiClient; + bool m_warnedAboutGrace = false; QMainWindow *m_pMainWindow = nullptr; AppConfig *m_pAppConfig = nullptr; deskflow::gui::CoreProcess *m_pCoreProcess = nullptr; From 9f5f638df207274401f813aaba0089288225be30 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 6 May 2026 13:18:51 +0100 Subject: [PATCH 003/144] build: update minor version to 21 in CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1256247e..e44db2143 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Fallback for when git can not be found set(DESKFLOW_VERSION_MAJOR 1) -set(DESKFLOW_VERSION_MINOR 26) +set(DESKFLOW_VERSION_MINOR 21) set(DESKFLOW_VERSION_PATCH 0) set(DESKFLOW_VERSION_TWEAK 0) From 9deafd6b0a208e0a3993525169cc6f757794bd9c Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 6 May 2026 13:20:12 +0100 Subject: [PATCH 004/144] refactor: remove unused launch configurations and test tasks from VSCode setup --- .vscode/launch.json | 54 --------------------------------- .vscode/tasks.json | 73 ++------------------------------------------- 2 files changed, 2 insertions(+), 125 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 84f7dbbb0..8e0d516ce 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -30,30 +30,6 @@ "args": ["${input:gtest-args}"], "preLaunchTask": "build-silent" }, - { - "name": "unix - server", - "type": "lldb-dap", - "cwd": "${workspaceRoot}", - "request": "launch", - "program": "${workspaceFolder}/build/bin/synergy-server", - "args": ["--config-toml", "synergy-config.toml"], - "preLaunchTask": "build-silent", - "osx": { - "program": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy-server" - } - }, - { - "name": "unix - client", - "type": "lldb-dap", - "cwd": "${workspaceRoot}", - "request": "launch", - "program": "${workspaceFolder}/build/bin/synergy-client", - "args": ["--config-toml", "synergy-config.toml"], - "preLaunchTask": "build-silent", - "osx": { - "program": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy-client" - } - }, { "name": "unix - daemon", "type": "lldb-dap", @@ -105,36 +81,6 @@ "args": ["${input:gtest-args}"], "internalConsoleOptions": "openOnSessionStart" }, - { - "name": "windows - server", - "type": "cppvsdbg", - "cwd": "${workspaceRoot}", - "request": "launch", - "program": "${workspaceFolder}/build/bin-copy/synergy-server", - "args": ["--config-toml", "synergy-config.toml"], - "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": "build-kill-core" - }, - { - "name": "windows - client", - "type": "cppvsdbg", - "cwd": "${workspaceRoot}", - "request": "launch", - "program": "${workspaceFolder}/build/bin-copy/synergy-client", - "args": ["--config-toml", "synergy-config.toml"], - "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": "build-kill-core" - }, - { - "name": "windows - legacy", - "type": "cppvsdbg", - "cwd": "${workspaceRoot}", - "request": "launch", - "program": "${workspaceFolder}/build/bin-copy/synergy-legacy", - "args": ["--get-profile-dir"], - "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": "build" - }, { "name": "windows - active-desktop", "type": "cppvsdbg", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0c14702a0..f21a2ff01 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -84,11 +84,6 @@ "command": "rm -r ~/Library/Application\\ Support/Synergy/Synergy" } }, - { - "label": "tests", - "dependsOn": ["integtests", "unittests"], - "problemMatcher": [] - }, { "label": "kill-all", "type": "shell", @@ -114,9 +109,9 @@ { "label": "kill-core", "type": "shell", - "command": "killall -9 synergy-core synergy-server synergy-client || true", + "command": "killall -9 synergy-core || true", "windows": { - "command": "taskkill /F /IM synergy-core.exe; taskkill /F /IM synergy-server.exe; taskkill /F /IM synergy-client.exe; $true" + "command": "taskkill /F /IM synergy-core.exe; $true" }, }, { @@ -181,70 +176,6 @@ "type": "shell", "dependsOn": ["build-kill-core", "daemon-stop", "daemon-uninstall", "daemon-install"], "dependsOrder": "sequence", - }, - { - "label": "unittests (current)", - "type": "shell", - "command": "python", - "args": [ - "./scripts/tests.py", - "--unit-tests", - "--ignore-return-code", - "--filter-file=${file}" - ], - "dependsOn": ["build", "clean-gcda"] - }, - { - "label": "integtests (current)", - "type": "shell", - "command": "python", - "args": [ - "./scripts/tests.py", - "--integ-tests", - "--ignore-return-code", - "--filter-file=${file}" - ], - "dependsOn": ["build", "clean-gcda"] - }, - { - "label": "unittests", - "type": "shell", - "command": "python", - "args": ["./scripts/tests.py", "--unit-tests", "--ignore-return-code"], - "dependsOn": ["build", "clean-gcda"] - }, - { - "label": "integtests", - "type": "shell", - "command": "python", - "args": ["./scripts/tests.py", "--integ-tests", "--ignore-return-code"], - "dependsOn": ["build", "clean-gcda"] - }, - { - "label": "unittests (current, valgrind)", - "type": "shell", - "command": "python", - "args": [ - "./scripts/tests.py", - "--unit-tests", - "--ignore-return-code", - "--filter-file=${file}", - "--valgrind" - ], - "dependsOn": ["build", "clean-gcda"] - }, - { - "label": "integtests (current, valgrind)", - "type": "shell", - "command": "python", - "args": [ - "./scripts/tests.py", - "--integ-tests", - "--ignore-return-code", - "--filter-file=${file}", - "--valgrind" - ], - "dependsOn": ["build", "clean-gcda"] } ] } From e33071f38d697f8478a84a5c0c07a9d0f7070731 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 6 May 2026 13:20:23 +0100 Subject: [PATCH 005/144] refactor: update icon paths and improve error message handling in Windows applications --- src/apps/deskflow-core/CMakeLists.txt | 4 ++-- src/apps/deskflow-daemon/CMakeLists.txt | 2 +- src/apps/deskflow-daemon/deskflow-daemon.cpp | 4 +++- src/apps/deskflow-gui/CMakeLists.txt | 3 ++- src/lib/common/Constants.h.in | 1 + src/lib/net/SecureUtils.cpp | 3 ++- src/lib/platform/MSWindowsWatchdog.cpp | 4 +--- src/lib/platform/OSXPowerManager.cpp | 7 ++++++- 8 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/apps/deskflow-core/CMakeLists.txt b/src/apps/deskflow-core/CMakeLists.txt index 87e56f124..90ab308ef 100644 --- a/src/apps/deskflow-core/CMakeLists.txt +++ b/src/apps/deskflow-core/CMakeLists.txt @@ -19,12 +19,12 @@ unset(_basename) if(WIN32) # Generate rc file set(EXE_DESCRIPTION "${CMAKE_PROJECT_PROPER_NAME} combined server and client application") - set(EXE_ICON "IDI_DESKFLOW ICON DISCARDABLE \"${CMAKE_SOURCE_DIR}/src/apps/res/deskflow.ico\"") + set(EXE_ICON "IDI_DESKFLOW ICON DISCARDABLE \"${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.ico\"") configure_file(${CMAKE_SOURCE_DIR}/src/apps/res/windows.rc.in ${target}.rc) target_sources(${target} PRIVATE ${target}.exe.manifest - ${CMAKE_SOURCE_DIR}/src/apps/res/deskflow.ico + ${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.ico ${CMAKE_CURRENT_BINARY_DIR}/${target}.rc ) endif() diff --git a/src/apps/deskflow-daemon/CMakeLists.txt b/src/apps/deskflow-daemon/CMakeLists.txt index 84c177267..52826beb9 100644 --- a/src/apps/deskflow-daemon/CMakeLists.txt +++ b/src/apps/deskflow-daemon/CMakeLists.txt @@ -9,7 +9,7 @@ if(WIN32) # Generate rc file set(EXE_DESCRIPTION "${CMAKE_PROJECT_PROPER_NAME} Daemon for handling secure desktops (UAC prompts, login screen, etc)") - set(EXE_ICON "IDI_DESKFLOW ICON DISCARDABLE \"${CMAKE_SOURCE_DIR}/src/apps/res/deskflow.ico\"") + set(EXE_ICON "IDI_DESKFLOW ICON DISCARDABLE \"${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.ico\"") configure_file(${CMAKE_SOURCE_DIR}/src/apps/res/windows.rc.in ${target}.rc) add_executable( diff --git a/src/apps/deskflow-daemon/deskflow-daemon.cpp b/src/apps/deskflow-daemon/deskflow-daemon.cpp index c88912d42..82dc0d101 100644 --- a/src/apps/deskflow-daemon/deskflow-daemon.cpp +++ b/src/apps/deskflow-daemon/deskflow-daemon.cpp @@ -10,6 +10,7 @@ #include "arch/Arch.h" #include "base/EventQueue.h" #include "base/Log.h" +#include "common/Constants.h" #include "common/ExitCodes.h" #include "common/Settings.h" #include "common/VersionInfo.h" @@ -131,6 +132,7 @@ void handleError(const char *message) #if defined(Q_OS_WIN) // Show a message box for when run from MSI in Win32 subsystem. - MessageBoxA(nullptr, message, "Deskflow daemon error", MB_OK | MB_ICONERROR); + const std::string title = std::string(kAppName) + " daemon error"; + MessageBoxA(nullptr, message, title.c_str(), MB_OK | MB_ICONERROR); #endif } diff --git a/src/apps/deskflow-gui/CMakeLists.txt b/src/apps/deskflow-gui/CMakeLists.txt index 054128ef1..e504279eb 100644 --- a/src/apps/deskflow-gui/CMakeLists.txt +++ b/src/apps/deskflow-gui/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) if(WIN32) set(EXE_DESCRIPTION "${CMAKE_PROJECT_PROPER_NAME} GUI configuration tool") - set(EXE_ICON "IDI_ICON1 ICON DISCARDABLE \"${CMAKE_SOURCE_DIR}/src/apps/res/deskflow.ico\" ") + set(EXE_ICON "IDI_ICON1 ICON DISCARDABLE \"${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.ico\" ") configure_file(${CMAKE_SOURCE_DIR}/src/apps/res/windows.rc.in deskflow.rc) set(platform_extra deskflow.rc) elseif(BUILD_OSX_BUNDLE) @@ -38,6 +38,7 @@ endif() add_executable(${target} WIN32 MACOSX_BUNDLE ${platform_extra} ../res/deskflow.qrc + ${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.qrc deskflow-gui.cpp ) diff --git a/src/lib/common/Constants.h.in b/src/lib/common/Constants.h.in index 1aa97f1c0..22d7a98f9 100644 --- a/src/lib/common/Constants.h.in +++ b/src/lib/common/Constants.h.in @@ -30,6 +30,7 @@ const auto kWindowsRuntimeMajor = @REQUIRED_MSVC_RUNTIME_MAJOR@; const auto kWindowsRuntimeMinor = @REQUIRED_MSVC_RUNTIME_MINOR@; // clang-format on +constexpr auto kCoreBinNameW = L"@CORE_BINARY@"; constexpr auto kCloseEventName = L"Global\\@CMAKE_PROJECT_PROPER_NAME@Close"; constexpr auto kSendSasEventName = L"Global\\@CMAKE_PROJECT_PROPER_NAME@SendSAS"; diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index f47e4379a..f1833bc53 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -8,6 +8,7 @@ #include "SecureUtils.h" #include "base/FinalAction.h" +#include "common/Constants.h" #include #include @@ -85,7 +86,7 @@ void generatePemSelfSignedCert(const QString &path, int keyLength) X509_set_pubkey(cert, privateKey); auto *name = X509_get_subject_name(cert); - X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast("Deskflow"), -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, reinterpret_cast(kAppName), -1, -1, 0); X509_set_issuer_name(cert, name); X509_sign(cert, privateKey, EVP_sha256()); diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 8f56b9a69..a9f8be337 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -37,9 +37,7 @@ HANDLE openProcessForKill(const PROCESSENTRY32 &entry) if (entry.th32ProcessID == 0) return nullptr; - if (_wcsicmp(entry.szExeFile, L"deskflow-client.exe") != 0 && // - _wcsicmp(entry.szExeFile, L"deskflow-server.exe") != 0 && // - _wcsicmp(entry.szExeFile, L"deskflow-core.exe") != 0) { + if (_wcsicmp(entry.szExeFile, kCoreBinNameW) != 0) { return nullptr; } diff --git a/src/lib/platform/OSXPowerManager.cpp b/src/lib/platform/OSXPowerManager.cpp index c17c99906..3ed168d46 100644 --- a/src/lib/platform/OSXPowerManager.cpp +++ b/src/lib/platform/OSXPowerManager.cpp @@ -7,6 +7,9 @@ #include "OSXPowerManager.h" #include "base/Log.h" +#include "common/Constants.h" + +#include OSXPowerManager::~OSXPowerManager() { @@ -16,10 +19,12 @@ OSXPowerManager::~OSXPowerManager() void OSXPowerManager::disableSleep() { if (!m_sleepPreventionAssertionID) { - CFStringRef reasonForActivity = CFSTR("Deskflow application"); + const std::string reason = std::string(kAppName) + " application"; + CFStringRef reasonForActivity = CFStringCreateWithCString(nullptr, reason.c_str(), kCFStringEncodingUTF8); IOReturn result = IOPMAssertionCreateWithName( kIOPMAssertPreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &m_sleepPreventionAssertionID ); + CFRelease(reasonForActivity); if (result != kIOReturnSuccess) { m_sleepPreventionAssertionID = 0; LOG_ERR("failed to disable system idle sleep"); From b23cd19ac01d102dd8502e80cff668cea392bfa8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 6 May 2026 17:16:40 +0100 Subject: [PATCH 006/144] refactor: reorganize project structure and enhance CMake configuration for Synergy branding --- .gitignore | 3 ++ CLAUDE.md | 49 ------------------- CMakeLists.txt | 11 +++++ extra/CMakeLists.txt | 8 +++ extra/cmake/Synergy.cmake | 4 ++ extra/src/CMakeLists.txt | 4 ++ extra/src/lib/CMakeLists.txt | 4 ++ extra/src/lib/synergy/CMakeLists.txt | 8 ++- .../lib/synergy/license/parse_serial_key.cpp | 17 +++++-- src/lib/arch/win32/ArchDaemonWindows.cpp | 2 +- src/lib/common/CMakeLists.txt | 3 +- src/lib/common/Constants.h.in | 4 ++ src/lib/common/Settings.cpp | 2 +- .../{UrlConstants.h => UrlConstants.h.in} | 4 +- src/lib/gui/MainWindow.h | 3 +- src/lib/platform/MSWindowsClipboard.cpp | 5 +- src/lib/platform/MSWindowsDesks.cpp | 5 +- 17 files changed, 73 insertions(+), 63 deletions(-) delete mode 100644 CLAUDE.md create mode 100644 extra/CMakeLists.txt create mode 100644 extra/src/CMakeLists.txt create mode 100644 extra/src/lib/CMakeLists.txt rename src/lib/common/{UrlConstants.h => UrlConstants.h.in} (87%) diff --git a/.gitignore b/.gitignore index 3c946a046..685035b71 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ CMakeCache.txt CMakeUserPresets.json CMakeFiles/* +# AI +/.claude + diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index f729f1ded..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,49 +0,0 @@ -# AI instructions - -## Working style - -- **Never configure or build unless explicitly asked.** No `cmake`, `cmake --build`, `make`, `ninja`, etc. After code changes, stop at the edit and ask the user to build manually. - -## GitHub Actions - -- `matrix` context is NOT available in job-level `if` — the `if` runs before the matrix is expanded. Use step-level `if` conditions instead (e.g. set a `skip` output in a setup step and check it per-step). - -## Code style - -- Default to **no comments**. Only add one when the WHY is non-obvious — hidden constraints, subtle invariants, workarounds for specific bugs, behavior that would surprise a reader. Never add comments that explain WHAT or HOW (well-named identifiers do that). -- Don't reference the current task, fix, ticket, or CVE in comments. That belongs in the commit message. - -## Working with commits - -- **Never commit, amend, or rewrite history.** The user reviews staged changes and commits themselves. No `git commit`, no `git commit --amend`, no `git rebase`, no force-pushes. -- When cherry-picking, use `git cherry-pick --no-commit` so changes land in the index/working tree without producing a commit. Halt after conflict resolution and let the user commit. - -## Branches - -- **`master`** — stable 1.21.x line. Cherry-picks critical fixes from upstream Deskflow. Conservative. -- **`beta`** — re-forked from `deskflow/master` in 2026-05. Catches the fork up to upstream and re-establishes a clean base. Will replace master after months of stabilization. - -The `deskflow` git remote points at [deskflow/deskflow](https://github.com/deskflow/deskflow). Reference upstream as `deskflow/master`. - -## Backporting from upstream Deskflow - -Mechanics depend on which Synergy branch you're on: - -- **On `beta`**: paths align with `deskflow/master` since beta was forked from it. `git cherry-pick` should mostly work cleanly. Conflicts are usually genuine (overlapping changes), not path drift. -- **On `master`**: paths often diverge. Files may be at different locations than upstream (e.g. `DaemonApp.cpp` location may differ), and surrounding refactors may not be present. Plan to port hunks manually. - -In both cases: backport multi-commit PRs one commit at a time. Apply, surface conflicts, resolve together, wait for the user to review/commit, then move to the next. - -## Synergy overlay (`extra/`) - -Houses Synergy-only content that doesn't belong upstream: license/activation kernel, branding overrides, Synergy resources. Was a separate `ext/synergy-extra/` submodule until the re-fork; folded into the main repo for sanity (one PR per change, atomic refactors, no submodule init dance). - -The directory structure under `extra/` mirrors the root: `extra/src/lib/synergy/` parallels `src/lib/`, `extra/src/unittests/` parallels `src/unittests/`, `extra/deploy/` parallels `deploy/`, `extra/cmake/` parallels `cmake/`. This makes backport hygiene mechanical (anything under `extra/` is Synergy-only) and re-fork mapping positional (`extra/X/Y` ↔ `X/Y`). - -- Same license as the rest of the repo (GPLv2). Synergy is open source; commercial value is server-side activation infrastructure, signed builds, support, and brand — not closed binaries. Code linked into Synergy binaries must be GPLv2-compatible. -- Branding is driven by `extra/cmake/Synergy.cmake` overriding the `CMAKE_PROJECT_*` family (`CMAKE_PROJECT_PROPER_NAME`, `CMAKE_PROJECT_VENDOR`, `CMAKE_PROJECT_COPYRIGHT`, `CMAKE_PROJECT_CONTACT`, `CMAKE_PROJECT_REV_FQDN`, `CMAKE_PROJECT_HOMEPAGE_URL`) *after* deskflow's defaults are set. The old `DESKFLOW_APP_ID` / `DESKFLOW_GUI_HOOK_*` mechanism is gone. -- Binary names (`synergy-core`, `synergy-daemon`, `synergy`) come from `OUTPUT_NAME` overrides in `src/apps/*/CMakeLists.txt`. CMake target identifiers stay deskflow-flavored internally to keep file-path references aligned with upstream. - -## Planned - -- **Hook modernization**: replace the macro-string-based `DESKFLOW_GUI_HOOK_*` hook scheme with a `synergy::extra` namespace. Real C++ function calls from patched src/ files. Single macro guard `SYNERGY_EXTRA_HEADER` for the include. Adds Core-side hook callsites for license enforcement. diff --git a/CMakeLists.txt b/CMakeLists.txt index e44db2143..f2f01c83c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,16 @@ set(CMAKE_PROJECT_REV_FQDN "org.deskflow.deskflow") # Pull in Synergy branding overrides. include(${CMAKE_SOURCE_DIR}/extra/cmake/Synergy.cmake) +# Derive CMAKE_PROJECT_DOMAIN from CMAKE_PROJECT_REV_FQDN. +# org.deskflow.deskflow -> deskflow.org; com.symless.synergy -> symless.com +string(REPLACE "." ";" _fqdn_parts "${CMAKE_PROJECT_REV_FQDN}") +list(GET _fqdn_parts 0 _tld) +list(GET _fqdn_parts 1 _domain) +set(CMAKE_PROJECT_DOMAIN "${_domain}.${_tld}") +unset(_fqdn_parts) +unset(_tld) +unset(_domain) + #Unset the vars used in the project call unset(DESKFLOW_VERSION_MAJOR) unset(DESKFLOW_VERSION_MINOR) @@ -192,6 +202,7 @@ add_subdirectory(docs) add_subdirectory(translations) add_subdirectory(src) +add_subdirectory(extra) option(BUILD_INSTALLER "Build installer" ON) if(BUILD_INSTALLER) diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt new file mode 100644 index 000000000..41c54f8eb --- /dev/null +++ b/extra/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Symless Ltd +# SPDX-License-Identifier: MIT + +# Make synergy/... headers under this tree resolvable from anywhere that descends +# from this directory (and from upstream code once hooks are wired). +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/lib") + +add_subdirectory(src) diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index 492d82e19..78be810a7 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -14,4 +14,8 @@ set(CMAKE_PROJECT_HOMEPAGE_URL "https://symless.com/synergy") # -DSKIP_BUILD_TESTS=OFF if they want post-build ctest invocation. set(SKIP_BUILD_TESTS ON CACHE BOOL "Skip build time test") +# Resource paths consumed by extra/src/lib/synergy/gui/CMakeLists.txt. +set(GUI_RES_DIR "${CMAKE_SOURCE_DIR}/extra/src/apps/res") +set(GUI_QRC_FILE "${GUI_RES_DIR}/synergy.qrc") + message(STATUS "Synergy branding applied: ${CMAKE_PROJECT_PROPER_NAME} ${CMAKE_PROJECT_VERSION}") diff --git a/extra/src/CMakeLists.txt b/extra/src/CMakeLists.txt new file mode 100644 index 000000000..38928b1e3 --- /dev/null +++ b/extra/src/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Symless Ltd +# SPDX-License-Identifier: MIT + +add_subdirectory(lib) diff --git a/extra/src/lib/CMakeLists.txt b/extra/src/lib/CMakeLists.txt new file mode 100644 index 000000000..507a7b03a --- /dev/null +++ b/extra/src/lib/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Symless Ltd +# SPDX-License-Identifier: MIT + +add_subdirectory(synergy) diff --git a/extra/src/lib/synergy/CMakeLists.txt b/extra/src/lib/synergy/CMakeLists.txt index 7f957f489..7f513fff6 100644 --- a/extra/src/lib/synergy/CMakeLists.txt +++ b/extra/src/lib/synergy/CMakeLists.txt @@ -14,4 +14,10 @@ # along with this program. If not, see . add_subdirectory(license) -add_subdirectory(gui) + +# gui subdirectory disabled until hook modernization rewires it. +# The synergy GUI code was written against an older deskflow gui API +# (gui/string_utils.h, gui/styles.h, gui/diagnostic.h, gui/config/AppConfig.h, +# gui/config/Settings.h — all refactored away upstream). Re-enable when hook +# wiring lands and the gui integration is ported. +# add_subdirectory(gui) diff --git a/extra/src/lib/synergy/license/parse_serial_key.cpp b/extra/src/lib/synergy/license/parse_serial_key.cpp index db70c43c7..42931b97a 100644 --- a/extra/src/lib/synergy/license/parse_serial_key.cpp +++ b/extra/src/lib/synergy/license/parse_serial_key.cpp @@ -19,7 +19,6 @@ #include "SerialKey.h" #include "SerialKeyType.h" -#include "utils/string_utils.h" #include #include @@ -31,6 +30,18 @@ using Parts = std::vector; using system_clock = std::chrono::system_clock; using time_point = system_clock::time_point; +namespace { +std::string trim(const std::string &s) +{ + const auto first = s.find_first_not_of(" \t\n\r\f\v"); + if (first == std::string::npos) { + return ""; + } + const auto last = s.find_last_not_of(" \t\n\r\f\v"); + return s.substr(first, last - first + 1); +} +} // namespace + namespace synergy::license { std::string decode(const std::string &hexString); @@ -42,7 +53,7 @@ std::optional parseDate(const std::string &unixTimeString); SerialKey parseSerialKey(const std::string &hexString) { - const auto &trimmed = deskflow::utils::trim(hexString); + const auto &trimmed = trim(hexString); const auto &plainText = decode(trimmed); const auto &parts = tokenize(plainText); const auto &version = parts.at(0); @@ -148,7 +159,7 @@ Parts tokenize(const std::string &plainText) std::optional parseDate(const std::string &unixTimeString) { - auto clean = deskflow::utils::trim(unixTimeString); + auto clean = trim(unixTimeString); if (clean.empty()) { return std::nullopt; } diff --git a/src/lib/arch/win32/ArchDaemonWindows.cpp b/src/lib/arch/win32/ArchDaemonWindows.cpp index 9e840caf4..d8285919a 100644 --- a/src/lib/arch/win32/ArchDaemonWindows.cpp +++ b/src/lib/arch/win32/ArchDaemonWindows.cpp @@ -22,7 +22,7 @@ ArchDaemonWindows *ArchDaemonWindows::s_daemon = nullptr; ArchDaemonWindows::ArchDaemonWindows() : m_daemonThreadID(0) { - m_quitMessage = RegisterWindowMessage(L"DeskflowDaemonExit"); + m_quitMessage = RegisterWindowMessage(kDaemonExitMsgNameW); } int ArchDaemonWindows::runDaemon(RunFunc runFunc) diff --git a/src/lib/common/CMakeLists.txt b/src/lib/common/CMakeLists.txt index 89e3c4ad3..8d15abbbc 100644 --- a/src/lib/common/CMakeLists.txt +++ b/src/lib/common/CMakeLists.txt @@ -10,6 +10,7 @@ unset(_basename) configure_file(Constants.h.in Constants.h @ONLY) configure_file(VersionInfo.h.in VersionInfo.h @ONLY) +configure_file(UrlConstants.h.in UrlConstants.h @ONLY) add_library(common STATIC Coordinate.h @@ -24,8 +25,8 @@ add_library(common STATIC Settings.cpp QSettingsProxy.cpp QSettingsProxy.h - UrlConstants.h ${CMAKE_CURRENT_BINARY_DIR}/Constants.h + ${CMAKE_CURRENT_BINARY_DIR}/UrlConstants.h ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h ) diff --git a/src/lib/common/Constants.h.in b/src/lib/common/Constants.h.in index 22d7a98f9..9ad664ab0 100644 --- a/src/lib/common/Constants.h.in +++ b/src/lib/common/Constants.h.in @@ -22,6 +22,7 @@ const auto kCopyright = // const auto kCoreBinName = "@CORE_BINARY@"; const auto kCoreIpcName = "@CMAKE_PROJECT_NAME@-core"; +const auto kGuiSocketName = "@CMAKE_PROJECT_NAME@-gui"; #ifdef _WIN32 @@ -33,5 +34,8 @@ const auto kWindowsRuntimeMinor = @REQUIRED_MSVC_RUNTIME_MINOR@; constexpr auto kCoreBinNameW = L"@CORE_BINARY@"; constexpr auto kCloseEventName = L"Global\\@CMAKE_PROJECT_PROPER_NAME@Close"; constexpr auto kSendSasEventName = L"Global\\@CMAKE_PROJECT_PROPER_NAME@SendSAS"; +constexpr auto kClipboardOwnershipFormatW = L"@CMAKE_PROJECT_PROPER_NAME@ Ownership"; +constexpr auto kDeskClassNameW = L"@CMAKE_PROJECT_PROPER_NAME@Desk"; +constexpr auto kDaemonExitMsgNameW = L"@CMAKE_PROJECT_PROPER_NAME@DaemonExit"; #endif diff --git a/src/lib/common/Settings.cpp b/src/lib/common/Settings.cpp index d35b4d38a..789ae76a5 100644 --- a/src/lib/common/Settings.cpp +++ b/src/lib/common/Settings.cpp @@ -8,7 +8,7 @@ #include "LogLevel.h" #include "NetworkProtocol.h" -#include "UrlConstants.h" +#include "common/UrlConstants.h" #include #include diff --git a/src/lib/common/UrlConstants.h b/src/lib/common/UrlConstants.h.in similarity index 87% rename from src/lib/common/UrlConstants.h rename to src/lib/common/UrlConstants.h.in index 332db4201..d916ae365 100644 --- a/src/lib/common/UrlConstants.h +++ b/src/lib/common/UrlConstants.h.in @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2024 Symless Ltd. + * SPDX-FileCopyrightText: (C) 2024 - 2026 Symless Ltd. * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception */ @@ -11,7 +11,7 @@ // important: this is used for settings paths on some platforms, // and must not be a url. qt automatically converts this to reverse domain // notation (rdn), e.g. org.deskflow -const auto kOrgDomain = QStringLiteral("deskflow.org"); +const auto kOrgDomain = QStringLiteral("@CMAKE_PROJECT_DOMAIN@"); const auto kUrlSourceQuery = QStringLiteral("source=gui"); const auto kUrlApp = QStringLiteral("https://%1").arg(kOrgDomain); diff --git a/src/lib/gui/MainWindow.h b/src/lib/gui/MainWindow.h index 1ec5d97f6..3c34f706e 100644 --- a/src/lib/gui/MainWindow.h +++ b/src/lib/gui/MainWindow.h @@ -15,6 +15,7 @@ #include #include "VersionChecker.h" +#include "common/Constants.h" #include "config/ServerConfig.h" #include "gui/core/CoreProcess.h" #include "gui/core/NetworkMonitor.h" @@ -160,7 +161,7 @@ class MainWindow : public QMainWindow void serverClientsChanged(const QStringList &clients); - inline static const auto m_guiSocketName = QStringLiteral("deskflow-gui"); + inline static const auto m_guiSocketName = QString::fromUtf8(kGuiSocketName); inline static const auto m_nameRegEx = QRegularExpression(QStringLiteral("^[\\w\\-_\\.]{0,255}$")); VersionChecker m_versionChecker; diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index eab6155df..3cf2bf89c 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -9,6 +9,7 @@ #include "platform/MSWindowsClipboard.h" #include "base/Log.h" +#include "common/Constants.h" #include "platform/MSWindowsClipboardBitmapConverter.h" #include "platform/MSWindowsClipboardFacade.h" #include "platform/MSWindowsClipboardHTMLConverter.h" @@ -207,7 +208,7 @@ bool MSWindowsClipboard::isOwnedByDeskflow() { // create ownership format if we haven't yet if (s_ownershipFormat == 0) { - s_ownershipFormat = RegisterClipboardFormat(TEXT("Deskflow Ownership")); + s_ownershipFormat = RegisterClipboardFormatW(kClipboardOwnershipFormatW); } return (IsClipboardFormatAvailable(getOwnershipFormat()) != 0); } @@ -216,7 +217,7 @@ UINT MSWindowsClipboard::getOwnershipFormat() { // create ownership format if we haven't yet if (s_ownershipFormat == 0) { - s_ownershipFormat = RegisterClipboardFormat(TEXT("Deskflow Ownership")); + s_ownershipFormat = RegisterClipboardFormatW(kClipboardOwnershipFormatW); } // return the format diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 143425a8b..786429f51 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -13,6 +13,7 @@ #include "base/IJob.h" #include "base/Log.h" #include "base/TMethodJob.h" +#include "common/Constants.h" #include "deskflow/IScreenSaver.h" #include "deskflow/ScreenException.h" #include "deskflow/win32/AppUtilWindows.h" @@ -353,7 +354,7 @@ ATOM MSWindowsDesks::createDeskWindowClass(bool isPrimary) const classInfo.hCursor = m_cursor; classInfo.hbrBackground = nullptr; classInfo.lpszMenuName = nullptr; - classInfo.lpszClassName = L"DeskflowDesk"; + classInfo.lpszClassName = kDeskClassNameW; classInfo.hIconSm = nullptr; return RegisterClassEx(&classInfo); } @@ -615,7 +616,7 @@ void MSWindowsDesks::deskThread(const void *vdesk) // create a window. we use this window to hide the cursor. try { - desk->m_window = createWindow(m_deskClass, L"DeskflowDesk"); + desk->m_window = createWindow(m_deskClass, kDeskClassNameW); LOG_DEBUG("desk %ls window is 0x%08x", desk->m_name.c_str(), desk->m_window); } catch (...) { // ignore From 71867f5981553cd030ad66c022945485114b29a5 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 09:23:59 +0100 Subject: [PATCH 007/144] refactor: update project domain handling and remove unused UrlConstants.h --- CMakeLists.txt | 11 +---------- extra/cmake/Synergy.cmake | 1 + src/lib/common/CMakeLists.txt | 3 +-- src/lib/common/Constants.h.in | 5 +++-- src/lib/common/{UrlConstants.h.in => UrlConstants.h} | 4 +++- 5 files changed, 9 insertions(+), 15 deletions(-) rename src/lib/common/{UrlConstants.h.in => UrlConstants.h} (91%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f2f01c83c..251c47ce8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,20 +87,11 @@ set(CMAKE_PROJECT_VENDOR "${CMAKE_PROJECT_PROPER_NAME} Devs") set(CMAKE_PROJECT_COPYRIGHT "(C) 2024-2026 ${CMAKE_PROJECT_VENDOR}") set(CMAKE_PROJECT_CONTACT "${CMAKE_PROJECT_PROPER_NAME} ") set(CMAKE_PROJECT_REV_FQDN "org.deskflow.deskflow") +set(CMAKE_PROJECT_DOMAIN "deskflow.org") # Pull in Synergy branding overrides. include(${CMAKE_SOURCE_DIR}/extra/cmake/Synergy.cmake) -# Derive CMAKE_PROJECT_DOMAIN from CMAKE_PROJECT_REV_FQDN. -# org.deskflow.deskflow -> deskflow.org; com.symless.synergy -> symless.com -string(REPLACE "." ";" _fqdn_parts "${CMAKE_PROJECT_REV_FQDN}") -list(GET _fqdn_parts 0 _tld) -list(GET _fqdn_parts 1 _domain) -set(CMAKE_PROJECT_DOMAIN "${_domain}.${_tld}") -unset(_fqdn_parts) -unset(_tld) -unset(_domain) - #Unset the vars used in the project call unset(DESKFLOW_VERSION_MAJOR) unset(DESKFLOW_VERSION_MINOR) diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index 78be810a7..0cfe2b32b 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -8,6 +8,7 @@ set(CMAKE_PROJECT_VENDOR "Symless Ltd") set(CMAKE_PROJECT_COPYRIGHT "(C) 2012-2026 ${CMAKE_PROJECT_VENDOR}") set(CMAKE_PROJECT_CONTACT "${CMAKE_PROJECT_PROPER_NAME} ") set(CMAKE_PROJECT_REV_FQDN "com.symless.synergy") +set(CMAKE_PROJECT_DOMAIN "symless.com") set(CMAKE_PROJECT_HOMEPAGE_URL "https://symless.com/synergy") # Don't run unit tests as part of the build. Devs can opt back in with diff --git a/src/lib/common/CMakeLists.txt b/src/lib/common/CMakeLists.txt index 8d15abbbc..89e3c4ad3 100644 --- a/src/lib/common/CMakeLists.txt +++ b/src/lib/common/CMakeLists.txt @@ -10,7 +10,6 @@ unset(_basename) configure_file(Constants.h.in Constants.h @ONLY) configure_file(VersionInfo.h.in VersionInfo.h @ONLY) -configure_file(UrlConstants.h.in UrlConstants.h @ONLY) add_library(common STATIC Coordinate.h @@ -25,8 +24,8 @@ add_library(common STATIC Settings.cpp QSettingsProxy.cpp QSettingsProxy.h + UrlConstants.h ${CMAKE_CURRENT_BINARY_DIR}/Constants.h - ${CMAKE_CURRENT_BINARY_DIR}/UrlConstants.h ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h ) diff --git a/src/lib/common/Constants.h.in b/src/lib/common/Constants.h.in index 9ad664ab0..e8ea0b3e1 100644 --- a/src/lib/common/Constants.h.in +++ b/src/lib/common/Constants.h.in @@ -1,7 +1,7 @@ /* * Deskflow -- mouse and keyboard sharing utility * SPDX-FileCopyrightText: (C) 2024 - 2025 Chris Rizzitello - * SPDX-FileCopyrightText: (C) 2025 Symless Ltd. + * SPDX-FileCopyrightText: (C) 2025 - 2026 Symless Ltd. * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception */ @@ -10,13 +10,14 @@ const auto kAppName = "@CMAKE_PROJECT_PROPER_NAME@"; const auto kAppId = "@CMAKE_PROJECT_NAME@"; const auto kAppDescription = "@CMAKE_PROJECT_DESCRIPTION@"; +const auto kAppDomain = "@CMAKE_PROJECT_DOMAIN@"; const auto kDaemonBinName = "@CMAKE_PROJECT_NAME@-daemon"; const auto kDaemonIpcName = "@CMAKE_PROJECT_NAME@-daemon"; const auto kRevFqdnName = "@CMAKE_PROJECT_REV_FQDN@"; const auto kCopyright = // "Copyright @CMAKE_PROJECT_COPYRIGHT@\n" - "Copyright (C) 2012-2025 Symless Ltd.\n" + "Copyright (C) 2012-2026 Symless Ltd.\n" "Copyright (C) 2009-2012 Nick Bolton\n" "Copyright (C) 2002-2009 Chris Schoeneman"; diff --git a/src/lib/common/UrlConstants.h.in b/src/lib/common/UrlConstants.h similarity index 91% rename from src/lib/common/UrlConstants.h.in rename to src/lib/common/UrlConstants.h index d916ae365..1324d6ab7 100644 --- a/src/lib/common/UrlConstants.h.in +++ b/src/lib/common/UrlConstants.h @@ -6,12 +6,14 @@ #pragma once +#include "common/Constants.h" + #include // important: this is used for settings paths on some platforms, // and must not be a url. qt automatically converts this to reverse domain // notation (rdn), e.g. org.deskflow -const auto kOrgDomain = QStringLiteral("@CMAKE_PROJECT_DOMAIN@"); +const auto kOrgDomain = QString::fromUtf8(kAppDomain); const auto kUrlSourceQuery = QStringLiteral("source=gui"); const auto kUrlApp = QStringLiteral("https://%1").arg(kOrgDomain); From 8ec8fbbad4d7077be30336ff61800cddace652c8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 10:18:28 +0100 Subject: [PATCH 008/144] refactor: update project structure and naming conventions across CMake files --- CMakeLists.txt | 33 +------ deploy/CMakeLists.txt | 1 - extra/cmake/SaveVersion.cmake | 26 ----- extra/cmake/Synergy.cmake | 26 ++++- extra/cmake/Version.cmake | 122 ------------------------ src/apps/deskflow-core/CMakeLists.txt | 9 +- src/apps/deskflow-daemon/CMakeLists.txt | 7 +- src/apps/deskflow-gui/CMakeLists.txt | 21 ++-- src/lib/common/CMakeLists.txt | 4 +- translations/CMakeLists.txt | 22 ++--- 10 files changed, 53 insertions(+), 218 deletions(-) delete mode 100644 extra/cmake/SaveVersion.cmake delete mode 100644 extra/cmake/Version.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 251c47ce8..8a273c9f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,13 +21,13 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Fallback for when git can not be found +# Project version. Synergy.cmake adds the dev/snapshot/release suffix later. set(DESKFLOW_VERSION_MAJOR 1) set(DESKFLOW_VERSION_MINOR 21) set(DESKFLOW_VERSION_PATCH 0) set(DESKFLOW_VERSION_TWEAK 0) -# Get the version from git if it's a git repository +# Git short SHA, consumed by VersionInfo.h.in (kVersionGitSha). if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) find_package(Git) if(GIT_FOUND) @@ -37,29 +37,6 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) OUTPUT_VARIABLE GIT_SHA_SHORT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) - execute_process( - COMMAND ${GIT_EXECUTABLE} describe --long --match v* --always - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - OUTPUT_VARIABLE GITREV - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - ) - string(FIND "${GITREV}" "v" isRev) - if(NOT ${isRev} EQUAL -1) - string(REGEX MATCH [0-9]+ MAJOR ${GITREV}) - string(REGEX MATCH \\.[0-9]+ MINOR ${GITREV}) - string(REPLACE "." "" MINOR "${MINOR}") - string(REGEX MATCH [0-9]+\- PATCH ${GITREV}) - string(REPLACE "-" "" PATCH "${PATCH}") - string(REGEX MATCH \-[0-9]+\- TWEAK ${GITREV}) - string(REPLACE "-" "" TWEAK "${TWEAK}") - set(DESKFLOW_VERSION_MAJOR ${MAJOR}) - set(DESKFLOW_VERSION_MINOR ${MINOR}) - set(DESKFLOW_VERSION_PATCH ${PATCH}) - set(DESKFLOW_VERSION_TWEAK ${TWEAK}) - else() - set(DESKFLOW_VERSION_TWEAK "9999") - endif() - unset(GITREV) endif() endif() @@ -181,10 +158,8 @@ elseif(BUILD_OSX_BUNDLE) set(CMAKE_INSTALL_BINDIR ${CMAKE_PROJECT_PROPER_NAME}.app/Contents/MacOS) set(CMAKE_INSTALL_LICENSE_DIR ${CMAKE_PROJECT_PROPER_NAME}.app/Contents/Resources) else() - string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _proj_lower) - set(CMAKE_INSTALL_LICENSE_DIR ${CMAKE_INSTALL_DATADIR}/licenses/${_proj_lower}) - set(CMAKE_INSTALL_I18N_DIR ${CMAKE_INSTALL_DATADIR}/${_proj_lower}/translations) - unset(_proj_lower) + set(CMAKE_INSTALL_LICENSE_DIR ${CMAKE_INSTALL_DATADIR}/licenses/${CMAKE_PROJECT_NAME}) + set(CMAKE_INSTALL_I18N_DIR ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}/translations) endif() add_subdirectory(docs) diff --git a/deploy/CMakeLists.txt b/deploy/CMakeLists.txt index c7b0ca781..d155d1309 100644 --- a/deploy/CMakeLists.txt +++ b/deploy/CMakeLists.txt @@ -33,7 +33,6 @@ else() message(STATUS "UNKNOWN System: ${CMAKE_SYSTEM_NAME}") endif() -# Always use "deskflow" for start of name set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PACKAGE_VERSION_LABEL}-${OS_STRING}") message(STATUS "Package Basename: ${CPACK_PACKAGE_FILE_NAME}") diff --git a/extra/cmake/SaveVersion.cmake b/extra/cmake/SaveVersion.cmake deleted file mode 100644 index aaf9e4376..000000000 --- a/extra/cmake/SaveVersion.cmake +++ /dev/null @@ -1,26 +0,0 @@ -# Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2025 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 . - -include(${CMAKE_CURRENT_LIST_DIR}/Version.cmake) - -version_from_git_tags(VERSION VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_REVISION) -message(STATUS "Version: " ${VERSION}) - -if ("${VERSION_FILE}" STREQUAL "") - message(FATAL_ERROR "VERSION_FILE not set") -endif() - -message(STATUS "Writing to file: ${VERSION_FILE}") -file(WRITE ${VERSION_FILE} ${VERSION}) diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index 0cfe2b32b..a1327b2e4 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -19,4 +19,28 @@ set(SKIP_BUILD_TESTS ON CACHE BOOL "Skip build time test") set(GUI_RES_DIR "${CMAKE_SOURCE_DIR}/extra/src/apps/res") set(GUI_QRC_FILE "${GUI_RES_DIR}/synergy.qrc") -message(STATUS "Synergy branding applied: ${CMAKE_PROJECT_PROPER_NAME} ${CMAKE_PROJECT_VERSION}") +# Override deskflow's project name. This cascades into binary names +# (${CMAKE_PROJECT_NAME}-core, etc.), install paths, package names, +# translation file naming, and CPack metadata. Source files in src/apps/*/ +# are patched to use literal filenames since they previously assumed +# target name == source basename. +set(CMAKE_PROJECT_NAME synergy) + +# Synergy version mode suffix. Base version (MAJOR.MINOR.PATCH) is set in +# the root CMakeLists.txt. Default mode is dev; flip with -DSYNERGY_VERSION_RELEASE=ON +# or -DSYNERGY_VERSION_SNAPSHOT=ON for CI/release builds. +option(SYNERGY_VERSION_RELEASE "Release version" OFF) +option(SYNERGY_VERSION_SNAPSHOT "Snapshot version" OFF) + +set(_base "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}") +if(SYNERGY_VERSION_RELEASE) + set(CMAKE_PROJECT_VERSION "${_base}") + set(CMAKE_PROJECT_VERSION_TWEAK 0) +elseif(SYNERGY_VERSION_SNAPSHOT) + set(CMAKE_PROJECT_VERSION "${_base}-snapshot") + set(CMAKE_PROJECT_VERSION_TWEAK 1) +else() + set(CMAKE_PROJECT_VERSION "${_base}-dev") + set(CMAKE_PROJECT_VERSION_TWEAK 1) +endif() +unset(_base) diff --git a/extra/cmake/Version.cmake b/extra/cmake/Version.cmake deleted file mode 100644 index 0995014d5..000000000 --- a/extra/cmake/Version.cmake +++ /dev/null @@ -1,122 +0,0 @@ -# Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2025 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 . - -# -# Partially based on back-ported upstream code but differs in two ways: -# -# 1. We use `git describe master` to count changes since last tag only on master. -# This is because we use squash merge instead of rebasing; using describe on the branch would -# give us a superficially higher revision number. -# -# 2. We use a different versioning strategy; we work on the version that is being released rather -# than a revision of the last version, so we use the `VERSION` file as the source of truth -# for the version number rather than the last tag. -# -function(version_from_git_tags VERSION VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_REVISION) - - option(SYNERGY_VERSION_RELEASE "Release version" OFF) - option(SYNERGY_VERSION_SNAPSHOT "Snapshot version" OFF) - - set(version_file "${CMAKE_CURRENT_SOURCE_DIR}/VERSION") - file(READ "${version_file}" version) - string(STRIP "${version}" version) - if ("${version}" STREQUAL "") - message(FATAL_ERROR "No version found in: ${version_file}") - endif() - message(VERBOSE "Version from file: ${version}") - - string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" _ "${version}") - set(match_major "${CMAKE_MATCH_1}") - set(minor_match "${CMAKE_MATCH_2}") - set(patch_match "${CMAKE_MATCH_3}") - - set(git_path "${CMAKE_CURRENT_SOURCE_DIR}/.git") - if(NOT EXISTS ${git_path}) - message(FATAL_ERROR "Not a Git repository: ${git_path}") - endif() - - find_package(Git) - if(NOT GIT_FOUND) - message(FATAL_ERROR "Git not found") - endif() - message(VERBOSE "Git repo: " ${CMAKE_CURRENT_SOURCE_DIR}) - - # Creating a release tag through the GitHub UI creates a lightweight tag, so use --tags - # to include lightweight tags in the search. - execute_process( - COMMAND ${GIT_EXECUTABLE} describe origin/master --tags --long --match "v[0-9]*" - WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - OUTPUT_VARIABLE git_describe - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if (NOT git_describe) - message(FATAL_ERROR "No version tags found in the Git repository") - endif() - message(VERBOSE "Git describe: " ${git_describe}) - - string(REGEX REPLACE ".*-([0-9]+)-g.*" "\\1" rev_count ${git_describe}) - if ("${rev_count}" STREQUAL "") - message(FATAL_ERROR "No revision count found in Git describe output") - endif() - message(VERBOSE "Changes since last tag: " ${rev_count}) - - if (SYNERGY_VERSION_RELEASE) - - message(VERBOSE "Version is release") - set(rev_count 0) - - elseif(SYNERGY_VERSION_SNAPSHOT) - - message(VERBOSE "Version is snapshot") - - # The `snapshot` stage is set here regardless of what the stage is in the version file, - # which serves 2 purposes: - # 1. It lets QA know that this is a snapshot and not a release. - # 2. When uploading to the packages repo, files with `-snapshot` in the name are - # are uploaded to a separate directory, making packages easier for QA to find. - set(version "${match_major}.${minor_match}.${patch_match}-snapshot+r${rev_count}") - - else() - message(VERBOSE "Version is development") - - # Gotcha: GitHub checks out a detached head, so the local SHA is not the real head SHA. - if(NOT "${SYNERGY_VERSION_GIT_SHA}" STREQUAL "") - message(VERBOSE "Getting Git SHA from env var") - string(SUBSTRING ${SYNERGY_VERSION_GIT_SHA} 0 7 git_sha_short) - else() - message(VERBOSE "Getting local Git SHA") - execute_process( - COMMAND git rev-parse --short HEAD - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - OUTPUT_VARIABLE git_sha_short - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif() - - if ("${git_sha_short}" STREQUAL "") - message(FATAL_ERROR "No Git SHA found") - endif() - - message(STATUS "Git SHA: ${git_sha_short}") - set(version "${match_major}.${minor_match}.${patch_match}-dev+${git_sha_short}") - endif() - - set(${VERSION} "${version}" PARENT_SCOPE) - set(${VERSION_MAJOR} ${match_major} PARENT_SCOPE) - set(${VERSION_MINOR} ${minor_match} PARENT_SCOPE) - set(${VERSION_PATCH} ${patch_match} PARENT_SCOPE) - set(${VERSION_REVISION} ${rev_count} PARENT_SCOPE) - -endfunction() diff --git a/src/apps/deskflow-core/CMakeLists.txt b/src/apps/deskflow-core/CMakeLists.txt index 90ab308ef..2e2804add 100644 --- a/src/apps/deskflow-core/CMakeLists.txt +++ b/src/apps/deskflow-core/CMakeLists.txt @@ -4,18 +4,15 @@ # SPDX-License-Identifier: MIT set(target ${CMAKE_PROJECT_NAME}-core) +set(filename deskflow-core) add_executable(${target} - "${target}.cpp" + "${filename}.cpp" CoreArgs.h CoreArgParser.h CoreArgParser.cpp ) -string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _basename) -set_target_properties(${target} PROPERTIES OUTPUT_NAME "${_basename}-core") -unset(_basename) - if(WIN32) # Generate rc file set(EXE_DESCRIPTION "${CMAKE_PROJECT_PROPER_NAME} combined server and client application") @@ -23,7 +20,7 @@ if(WIN32) configure_file(${CMAKE_SOURCE_DIR}/src/apps/res/windows.rc.in ${target}.rc) target_sources(${target} PRIVATE - ${target}.exe.manifest + ${filename}.exe.manifest ${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.ico ${CMAKE_CURRENT_BINARY_DIR}/${target}.rc ) diff --git a/src/apps/deskflow-daemon/CMakeLists.txt b/src/apps/deskflow-daemon/CMakeLists.txt index 52826beb9..ebfebc9f4 100644 --- a/src/apps/deskflow-daemon/CMakeLists.txt +++ b/src/apps/deskflow-daemon/CMakeLists.txt @@ -6,6 +6,7 @@ # Daemon is only needed on Windows for elevating processes to deal with UAC. if(WIN32) set(target ${CMAKE_PROJECT_NAME}-daemon) + set(filename deskflow-daemon) # Generate rc file set(EXE_DESCRIPTION "${CMAKE_PROJECT_PROPER_NAME} Daemon for handling secure desktops (UAC prompts, login screen, etc)") @@ -14,15 +15,11 @@ if(WIN32) add_executable( ${target} WIN32 - ${target}.cpp + ${filename}.cpp DaemonApp.cpp DaemonApp.h ${CMAKE_CURRENT_BINARY_DIR}/${target}.rc) - string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _basename) - set_target_properties(${target} PROPERTIES OUTPUT_NAME "${_basename}-daemon") - unset(_basename) - target_link_libraries( ${target} arch diff --git a/src/apps/deskflow-gui/CMakeLists.txt b/src/apps/deskflow-gui/CMakeLists.txt index e504279eb..5b67cd1b3 100644 --- a/src/apps/deskflow-gui/CMakeLists.txt +++ b/src/apps/deskflow-gui/CMakeLists.txt @@ -3,10 +3,11 @@ # SPDX-License-Identifier: MIT if(APPLE) - set(target Deskflow) + set(target ${CMAKE_PROJECT_PROPER_NAME}) else() - set(target deskflow) + set(target ${CMAKE_PROJECT_NAME}) endif() +set(filename deskflow-gui) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -17,9 +18,9 @@ if(WIN32) set(platform_extra deskflow.rc) elseif(BUILD_OSX_BUNDLE) #setup our bundle plist file - set(BUNDLE_EXECUTABLE_NAME "${CMAKE_PROJECT_PROPER_NAME}") - set(BUNDLE_BUNDLE_NAME "${CMAKE_PROJECT_PROPER_NAME}") - set(BUNDLE_DISPLAY_NAME "${CMAKE_PROJECT_PROPER_NAME}") + set(BUNDLE_EXECUTABLE_NAME "${target}") + set(BUNDLE_BUNDLE_NAME "${target}") + set(BUNDLE_DISPLAY_NAME "${target}") set(BUNDLE_GUI_IDENTIFIER "${CMAKE_PROJECT_REV_FQDN}") set(BUNDLE_ICON_FILE ${target}.icns) set(BUNDLE_INFO_STRING "${CMAKE_PROJECT_DESCRIPTION}") @@ -39,17 +40,9 @@ add_executable(${target} WIN32 MACOSX_BUNDLE ${platform_extra} ../res/deskflow.qrc ${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.qrc - deskflow-gui.cpp + ${filename}.cpp ) -if(APPLE) - set_target_properties(${target} PROPERTIES OUTPUT_NAME "${CMAKE_PROJECT_PROPER_NAME}") -else() - string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _basename) - set_target_properties(${target} PROPERTIES OUTPUT_NAME "${_basename}") - unset(_basename) -endif() - target_link_libraries( ${target} gui diff --git a/src/lib/common/CMakeLists.txt b/src/lib/common/CMakeLists.txt index 89e3c4ad3..b93a6fc87 100644 --- a/src/lib/common/CMakeLists.txt +++ b/src/lib/common/CMakeLists.txt @@ -1,12 +1,10 @@ # SPDX-FileCopyrightText: (C) 2024 - 2025 Chris Rizzitello # SPDX-License-Identifier: MIT -string(TOLOWER "${CMAKE_PROJECT_PROPER_NAME}" _basename) -set(CORE_BINARY "${_basename}-core") +set(CORE_BINARY "${CMAKE_PROJECT_NAME}-core") if(WIN32) string(APPEND CORE_BINARY ".exe") endif() -unset(_basename) configure_file(Constants.h.in Constants.h @ONLY) configure_file(VersionInfo.h.in VersionInfo.h @ONLY) diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index 6e8a7bc87..aab16608f 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -5,13 +5,13 @@ option(CLEAN_TRS "Clean obsolete translations from tr files" OFF) find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS LinguistTools) # To add a new language Add 639 shortname -set (${CMAKE_PROJECT_NAME}_TRS - ${CMAKE_PROJECT_NAME}_es.ts - ${CMAKE_PROJECT_NAME}_it.ts - ${CMAKE_PROJECT_NAME}_ja.ts - ${CMAKE_PROJECT_NAME}_zh_CN.ts - ${CMAKE_PROJECT_NAME}_ru.ts - ${CMAKE_PROJECT_NAME}_ko.ts +set (deskflow_TRS + deskflow_es.ts + deskflow_it.ts + deskflow_ja.ts + deskflow_zh_CN.ts + deskflow_ru.ts + deskflow_ko.ts ) set(TR_OPTIONS -no-ui-lines -locations none -silent) @@ -20,10 +20,10 @@ if(CLEAN_TRS) endif() # English will have only plurals -qt_create_translation(TRS ${CMAKE_SOURCE_DIR}/src ${CMAKE_PROJECT_NAME}_en.ts OPTIONS -pluralonly ${TR_OPTIONS}) +qt_create_translation(TRS ${CMAKE_SOURCE_DIR}/src deskflow_en.ts OPTIONS -pluralonly ${TR_OPTIONS}) # Other languages contain the full set of strings. -qt_create_translation(TRS ${CMAKE_SOURCE_DIR}/src ${${CMAKE_PROJECT_NAME}_TRS} OPTIONS ${TR_OPTIONS}) +qt_create_translation(TRS ${CMAKE_SOURCE_DIR}/src ${deskflow_TRS} OPTIONS ${TR_OPTIONS}) #ensure that the targets are built always add_custom_target(app_translations ALL DEPENDS ${TRS}) @@ -50,8 +50,8 @@ else() ) # install the other Qt lang files renamed to qt_lang.qm - foreach(LANG ${${CMAKE_PROJECT_NAME}_TRS}) - string(REPLACE "${CMAKE_PROJECT_NAME}_" "" LANG ${LANG}) + foreach(LANG ${deskflow_TRS}) + string(REPLACE "deskflow_" "" LANG ${LANG}) string(REPLACE ".ts" "" LANG ${LANG}) add_custom_command( TARGET app_translations POST_BUILD From 762b2a69af0d21e004a3a64d215de89f18905b81 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 10:21:29 +0100 Subject: [PATCH 009/144] refactor: standardize translation filenames using a variable for consistency --- translations/CMakeLists.txt | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/translations/CMakeLists.txt b/translations/CMakeLists.txt index aab16608f..c142f66f0 100644 --- a/translations/CMakeLists.txt +++ b/translations/CMakeLists.txt @@ -4,14 +4,16 @@ set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM 1) option(CLEAN_TRS "Clean obsolete translations from tr files" OFF) find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE COMPONENTS LinguistTools) +set(filename "deskflow") + # To add a new language Add 639 shortname -set (deskflow_TRS - deskflow_es.ts - deskflow_it.ts - deskflow_ja.ts - deskflow_zh_CN.ts - deskflow_ru.ts - deskflow_ko.ts +set (translations + ${filename}_es.ts + ${filename}_it.ts + ${filename}_ja.ts + ${filename}_zh_CN.ts + ${filename}_ru.ts + ${filename}_ko.ts ) set(TR_OPTIONS -no-ui-lines -locations none -silent) @@ -20,10 +22,10 @@ if(CLEAN_TRS) endif() # English will have only plurals -qt_create_translation(TRS ${CMAKE_SOURCE_DIR}/src deskflow_en.ts OPTIONS -pluralonly ${TR_OPTIONS}) +qt_create_translation(TRS ${CMAKE_SOURCE_DIR}/src ${filename}_en.ts OPTIONS -pluralonly ${TR_OPTIONS}) # Other languages contain the full set of strings. -qt_create_translation(TRS ${CMAKE_SOURCE_DIR}/src ${deskflow_TRS} OPTIONS ${TR_OPTIONS}) +qt_create_translation(TRS ${CMAKE_SOURCE_DIR}/src ${translations} OPTIONS ${TR_OPTIONS}) #ensure that the targets are built always add_custom_target(app_translations ALL DEPENDS ${TRS}) @@ -50,8 +52,8 @@ else() ) # install the other Qt lang files renamed to qt_lang.qm - foreach(LANG ${deskflow_TRS}) - string(REPLACE "deskflow_" "" LANG ${LANG}) + foreach(LANG ${translations}) + string(REPLACE "${filename}_" "" LANG ${LANG}) string(REPLACE ".ts" "" LANG ${LANG}) add_custom_command( TARGET app_translations POST_BUILD From 18ae562743cebcb9b3634dde9e70fc372b18aa5b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 11:39:22 +0100 Subject: [PATCH 010/144] refactor: enhance versioning logic to compute revision count from Git tags --- extra/cmake/Synergy.cmake | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index a1327b2e4..15128b789 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -32,15 +32,34 @@ set(CMAKE_PROJECT_NAME synergy) option(SYNERGY_VERSION_RELEASE "Release version" OFF) option(SYNERGY_VERSION_SNAPSHOT "Snapshot version" OFF) +# Compute revision count once. Used as both the +rN suffix in snapshot version +# strings and as CMAKE_PROJECT_VERSION_TWEAK (consumed by VersionInfo.h.in's +# kDisplayVersion ternary and src/apps/res/windows.rc.in's VER_VERSION 4th +# digit, which Windows update mechanisms key off for installer recognition). +set(_rev_count 0) +if(GIT_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} describe HEAD --tags --long --match "v[0-9]*" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + OUTPUT_VARIABLE _git_describe + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(_git_describe MATCHES "-([0-9]+)-g") + set(_rev_count "${CMAKE_MATCH_1}") + endif() +endif() + set(_base "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}") if(SYNERGY_VERSION_RELEASE) set(CMAKE_PROJECT_VERSION "${_base}") set(CMAKE_PROJECT_VERSION_TWEAK 0) elseif(SYNERGY_VERSION_SNAPSHOT) - set(CMAKE_PROJECT_VERSION "${_base}-snapshot") - set(CMAKE_PROJECT_VERSION_TWEAK 1) + set(CMAKE_PROJECT_VERSION "${_base}-snapshot+r${_rev_count}") + set(CMAKE_PROJECT_VERSION_TWEAK ${_rev_count}) else() set(CMAKE_PROJECT_VERSION "${_base}-dev") - set(CMAKE_PROJECT_VERSION_TWEAK 1) + set(CMAKE_PROJECT_VERSION_TWEAK ${_rev_count}) endif() unset(_base) +unset(_git_describe) +unset(_rev_count) From 9144cf97f3c481af9f2dedde1abb76738b6ddfa6 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 11:41:30 +0100 Subject: [PATCH 011/144] Enhance application dialogs and translations - Updated AboutDialog to display the application name dynamically. - Improved ServerConfigDialog by adding context-sensitive help for the protocol label. - Modified SettingsDialog to include the application name in clipboard-related messages. - Adjusted UI files to reflect changes in the protocol help text and clipboard warnings. - Updated translations for Spanish, Italian, Japanese, Korean, Russian, and Chinese to include dynamic application name references in protocol descriptions and clipboard warnings. --- src/lib/gui/dialogs/AboutDialog.cpp | 3 +++ src/lib/gui/dialogs/ServerConfigDialog.cpp | 2 ++ src/lib/gui/dialogs/ServerConfigDialog.ui | 2 +- src/lib/gui/dialogs/SettingsDialog.cpp | 3 +++ src/lib/gui/dialogs/SettingsDialog.ui | 2 +- translations/deskflow_es.ts | 8 ++++---- translations/deskflow_it.ts | 8 ++++---- translations/deskflow_ja.ts | 8 ++++---- translations/deskflow_ko.ts | 8 ++++---- translations/deskflow_ru.ts | 4 ++-- translations/deskflow_zh_CN.ts | 8 ++++---- 11 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/lib/gui/dialogs/AboutDialog.cpp b/src/lib/gui/dialogs/AboutDialog.cpp index 4b1fda8c9..5d1be6521 100644 --- a/src/lib/gui/dialogs/AboutDialog.cpp +++ b/src/lib/gui/dialogs/AboutDialog.cpp @@ -19,6 +19,9 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui{std::make_unique { ui->setupUi(this); + setWindowTitle(QStringLiteral("About %1").arg(kAppName)); + ui->lblName->setText(kAppName); + const int px = (fontMetrics().height() * 6); const QSize pixmapSize(px, px); ui->lblIcon->setFixedSize(pixmapSize); diff --git a/src/lib/gui/dialogs/ServerConfigDialog.cpp b/src/lib/gui/dialogs/ServerConfigDialog.cpp index 7ac8c3737..35b0adb64 100644 --- a/src/lib/gui/dialogs/ServerConfigDialog.cpp +++ b/src/lib/gui/dialogs/ServerConfigDialog.cpp @@ -33,6 +33,8 @@ ServerConfigDialog::ServerConfigDialog(QWidget *parent, ServerConfig &config) { ui->setupUi(this); + ui->labelProtocol->setWhatsThis(ui->labelProtocol->whatsThis().arg(kAppName)); + m_originalProtocol = Settings::value(Settings::Server::Protocol).value(); connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &ServerConfigDialog::accept); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &ServerConfigDialog::reject); diff --git a/src/lib/gui/dialogs/ServerConfigDialog.ui b/src/lib/gui/dialogs/ServerConfigDialog.ui index 523b32db6..e6cb3a887 100644 --- a/src/lib/gui/dialogs/ServerConfigDialog.ui +++ b/src/lib/gui/dialogs/ServerConfigDialog.ui @@ -614,7 +614,7 @@ - <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A Deskflow client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> + <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A %1 client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> Network protocol diff --git a/src/lib/gui/dialogs/SettingsDialog.cpp b/src/lib/gui/dialogs/SettingsDialog.cpp index 66672cbd0..45d264ad5 100644 --- a/src/lib/gui/dialogs/SettingsDialog.cpp +++ b/src/lib/gui/dialogs/SettingsDialog.cpp @@ -11,6 +11,7 @@ #include "common/PlatformInfo.h" #include "ui_SettingsDialog.h" +#include "common/Constants.h" #include "common/I18N.h" #include "common/Settings.h" #include "gui/Messages.h" @@ -32,6 +33,8 @@ SettingsDialog::SettingsDialog(QWidget *parent, const ServerConfig &serverConfig ui->setupUi(this); + ui->lblWlClipboard->setText(ui->lblWlClipboard->text().arg(kAppName)); + // these are enabled by the control next to them ui->lineCommandEnter->setEnabled(false); ui->lineCommandExit->setEnabled(false); diff --git a/src/lib/gui/dialogs/SettingsDialog.ui b/src/lib/gui/dialogs/SettingsDialog.ui index ae33488fc..ee403285b 100644 --- a/src/lib/gui/dialogs/SettingsDialog.ui +++ b/src/lib/gui/dialogs/SettingsDialog.ui @@ -820,7 +820,7 @@ - <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> + <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make %1 harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> true diff --git a/translations/deskflow_es.ts b/translations/deskflow_es.ts index d29fe9e3f..c127b9aea 100644 --- a/translations/deskflow_es.ts +++ b/translations/deskflow_es.ts @@ -1065,8 +1065,8 @@ Además, verifique que puede %1 el archivo de configuración del servidor: %2ms - <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A Deskflow client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> - <html><head/><body><p>Habilita la compatibilidad con programas que usan los protocolos Synergy o Barrier:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 usa el protocolo Synergy.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap y Synergy 1 usan el protocolo Barrier.</li></ul><p>Un cliente de Deskflow usará automáticamente el Protocolo Synergy o Barrier según el protocolo del servidor.</p></body></html> + <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A %1 client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> + <html><head/><body><p>Habilita la compatibilidad con programas que usan los protocolos Synergy o Barrier:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 usa el protocolo Synergy.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap y Synergy 1 usan el protocolo Barrier.</li></ul><p>Un cliente de %1 usará automáticamente el Protocolo Synergy o Barrier según el protocolo del servidor.</p></body></html> Network protocol @@ -1265,8 +1265,8 @@ Al habilitar esta opción, se deshabilitará la interfaz gráfica de usuario (GU Habilitar la compatibilidad con wl-clipboard - <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> - <html><head/><body><p>Requiere el paquete wl-clipboard</p><p>Al usar wl-clipboard v2.2.1, existe un error que provoca la pérdida del foco y que puede dificultar el uso de Deskflow. Este error se ha corregido al usar la rama principal de wl-clipboard, a menos que su Compositor no sea compatible con el protocolo wlroots-data-control.</p></body></html> + <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make %1 harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> + <html><head/><body><p>Requiere el paquete wl-clipboard</p><p>Al usar wl-clipboard v2.2.1, existe un error que provoca la pérdida del foco y que puede dificultar el uso de %1. Este error se ha corregido al usar la rama principal de wl-clipboard, a menos que su Compositor no sea compatible con el protocolo wlroots-data-control.</p></body></html> Automatic diff --git a/translations/deskflow_it.ts b/translations/deskflow_it.ts index 733484120..e7335fd89 100644 --- a/translations/deskflow_it.ts +++ b/translations/deskflow_it.ts @@ -1065,8 +1065,8 @@ Inoltre, verifica di poter %1 il file di configurazione del server: %2ms - <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A Deskflow client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> - <html><head/><body><p>Abilita la compatibilità con programmi che utilizzano i protocolli Synergy o Barrier:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 usa il protocollo Synergy.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap e Synergy 1 usano il protocollo Barrier.</li></ul><p>Un client Deskflow utilizzerà automaticamente il protocollo Synergy o Barrier a seconda del protocollo del server.</p></body></html> + <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A %1 client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> + <html><head/><body><p>Abilita la compatibilità con programmi che utilizzano i protocolli Synergy o Barrier:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 usa il protocollo Synergy.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap e Synergy 1 usano il protocollo Barrier.</li></ul><p>Un client %1 utilizzerà automaticamente il protocollo Synergy o Barrier a seconda del protocollo del server.</p></body></html> Network protocol @@ -1265,8 +1265,8 @@ L'abilitazione di questa impostazione disabiliterà l'interfaccia graf Abilita il supporto wl-clipboard - <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> - <html><head/><body><p>Richiede il pacchetto wl-clipboard</p><p>Quando si utilizza wl-clipboard v2.2.1, si verifica un bug di furto del focus che potrebbe rendere Deskflow più difficile da usare. Questo problema è stato risolto quando si utilizza il ramo master di wl-clipboard, a meno che il proprio compositore non supporti il ​​protocollo wlroots-data-control.</p></body></html> + <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make %1 harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> + <html><head/><body><p>Richiede il pacchetto wl-clipboard</p><p>Quando si utilizza wl-clipboard v2.2.1, si verifica un bug di furto del focus che potrebbe rendere %1 più difficile da usare. Questo problema è stato risolto quando si utilizza il ramo master di wl-clipboard, a meno che il proprio compositore non supporti il ​​protocollo wlroots-data-control.</p></body></html> Automatic diff --git a/translations/deskflow_ja.ts b/translations/deskflow_ja.ts index e93b0d591..6085685e8 100644 --- a/translations/deskflow_ja.ts +++ b/translations/deskflow_ja.ts @@ -1067,8 +1067,8 @@ Additionally, check you are able to %1 the server config file: %2 ms - <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A Deskflow client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> - <html><head/><body><p>Synergy もしくは Barrier プロトコルを利用するプログラムとの互換性を有効にします。</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 は Synergy プロトコルを利用します。</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier、Input-Leap、Synergy 1 は Barrier プロトコルを利用します。</li></ul><p>Deskflow クライアントはサーバー側のプロトコルに従って自動的に Synergy か Barrier プロトコルを選択します。</p></body></html> + <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A %1 client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> + <html><head/><body><p>Synergy もしくは Barrier プロトコルを利用するプログラムとの互換性を有効にします。</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 は Synergy プロトコルを利用します。</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier、Input-Leap、Synergy 1 は Barrier プロトコルを利用します。</li></ul><p>%1 クライアントはサーバー側のプロトコルに従って自動的に Synergy か Barrier プロトコルを選択します。</p></body></html> Network protocol @@ -1267,8 +1267,8 @@ Enabling this setting will disable the server config GUI. wl-clipboard によるクリップボード対応を有効にする - <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> - <html><head/><body><p>wl-clipboard パッケージが必要です。</p><p>wl-clipboard v2.2.1 を使用すると、フォーカス盗用のバグにより Deskflow の使い勝手が悪くなる可能性があります。この問題は wl-clipboard のマスターブランチで修正されていますが、使用しているコンポジターが wlroots-data-control プロトコルに対応している必要があります。</p></body></html> + <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make %1 harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> + <html><head/><body><p>wl-clipboard パッケージが必要です。</p><p>wl-clipboard v2.2.1 を使用すると、フォーカス盗用のバグにより %1 の使い勝手が悪くなる可能性があります。この問題は wl-clipboard のマスターブランチで修正されていますが、使用しているコンポジターが wlroots-data-control プロトコルに対応している必要があります。</p></body></html> Automatic diff --git a/translations/deskflow_ko.ts b/translations/deskflow_ko.ts index 4df41ed3c..5759e8816 100644 --- a/translations/deskflow_ko.ts +++ b/translations/deskflow_ko.ts @@ -1065,8 +1065,8 @@ Additionally, check you are able to %1 the server config file: %2 ms - <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A Deskflow client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> - <html><head/><body><p>Synergy 또는 Barrier 프로토콜을 사용하는 프로그램과의 호환성을 활성화합니다:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3는 Synergy 프로토콜을 사용합니다.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap, Synergy 1은 Barrier 프로토콜을 사용합니다.</li></ul><p>Deskflow 클라이언트는 서버 프로토콜에 따라 Synergy 또는 Barrier 프로토콜을 자동으로 사용합니다.</p></body></html> + <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A %1 client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> + <html><head/><body><p>Synergy 또는 Barrier 프로토콜을 사용하는 프로그램과의 호환성을 활성화합니다:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3는 Synergy 프로토콜을 사용합니다.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap, Synergy 1은 Barrier 프로토콜을 사용합니다.</li></ul><p>%1 클라이언트는 서버 프로토콜에 따라 Synergy 또는 Barrier 프로토콜을 자동으로 사용합니다.</p></body></html> Network protocol @@ -1265,8 +1265,8 @@ Enabling this setting will disable the server config GUI. wl-clipboard 지원 사용 - <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> - <html><head/><body><p>wl-clipboard 패키지가 필요합니다.</p><p>wl-clipboard v2.2.1 사용 시 포커스 탈취 버그로 인해 Deskflow 사용이 어려워질 수 있습니다. 이 문제는 wl-clipboard master 브랜치를 사용하면 해결되지만, 사용 중인 컴포지터가 wlroots-data-control 프로토콜을 지원하지 않으면 해결이 안 될 수 있습니다.</p></body></html> + <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make %1 harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> + <html><head/><body><p>wl-clipboard 패키지가 필요합니다.</p><p>wl-clipboard v2.2.1 사용 시 포커스 탈취 버그로 인해 %1 사용이 어려워질 수 있습니다. 이 문제는 wl-clipboard master 브랜치를 사용하면 해결되지만, 사용 중인 컴포지터가 wlroots-data-control 프로토콜을 지원하지 않으면 해결이 안 될 수 있습니다.</p></body></html> Automatic diff --git a/translations/deskflow_ru.ts b/translations/deskflow_ru.ts index fc5d6ab22..d3db66e86 100644 --- a/translations/deskflow_ru.ts +++ b/translations/deskflow_ru.ts @@ -1065,7 +1065,7 @@ Additionally, check you are able to %1 the server config file: %2 мс - <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A Deskflow client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> + <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A %1 client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> <html><head/><body><p>Включает совместимость с протоколами Synergy или Barrier.</p></body></html> @@ -1263,7 +1263,7 @@ Enabling this setting will disable the server config GUI. Сбросить до значений по умолчанию - <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> + <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make %1 harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> <html><head/><body><p>Требуется пакет wl-clipboard. В версии 2.2.1 есть ошибка перехвата фокуса.</p></body></html> diff --git a/translations/deskflow_zh_CN.ts b/translations/deskflow_zh_CN.ts index 667bd7c00..ddb45f2ca 100644 --- a/translations/deskflow_zh_CN.ts +++ b/translations/deskflow_zh_CN.ts @@ -1067,8 +1067,8 @@ Additionally, check you are able to %1 the server config file: %2 毫秒 - <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A Deskflow client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> - <html><head/><body><p>启用与使用 Synergy 或 Barrier 协议的程序的兼容性:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 使用 Synergy 协议。</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap 和 Synergy 1 使用 Barrier 协议。</li></ul><p>Deskflow 客户端将根据服务器协议自动使用 Synergy 或 Barrier 协议。</p></body></html> + <html><head/><body><p>Enables compatibility with programs that use either the Synergy or Barrier protocols:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 uses the Synergy protocol.</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap and Synergy 1 uses the Barrier protocol.</li></ul><p>A %1 client will automatically use either the Synergy or Barrier protocol depending on the server protocol.</p></body></html> + <html><head/><body><p>启用与使用 Synergy 或 Barrier 协议的程序的兼容性:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Synergy 3 使用 Synergy 协议。</li><li style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Barrier, Input-Leap 和 Synergy 1 使用 Barrier 协议。</li></ul><p>%1 客户端将根据服务器协议自动使用 Synergy 或 Barrier 协议。</p></body></html> Network protocol @@ -1267,8 +1267,8 @@ Enabling this setting will disable the server config GUI. 启用 wl-clipboard 支持 - <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> - <html><head/><body><p>需要 wl-clipboard 包</p><p>使用 wl-clipboard v2.2.1 时存在一个焦点抢夺 Bug,可能导致 Deskflow 使用不便。该问题已在 wl-clipboard 的 master 分支中修复,除非您的合成器不支持 wlroots-data-control 协议。</p></body></html> + <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make %1 harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html> + <html><head/><body><p>需要 wl-clipboard 包</p><p>使用 wl-clipboard v2.2.1 时存在一个焦点抢夺 Bug,可能导致 %1 使用不便。该问题已在 wl-clipboard 的 master 分支中修复,除非您的合成器不支持 wlroots-data-control 协议。</p></body></html> Automatic From 6de422a70f7f2c508ef76adb72c207737a3308f7 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 12:52:04 +0100 Subject: [PATCH 012/144] Add README and update copyright information to Synergy App Ltd - Created a new README.md file with project description, support links, and FAQs. - Updated copyright information from "Symless Ltd" to "Synergy App Ltd" in various source files, including CMakeLists.txt, Info.plist, and multiple .cpp and .h files. - Changed URLs in the GUI and license files to reflect the new domain "synergyapp.io". - Modified translation files to update the "About" dialog to use the new project name. --- .github/README.md | 187 ------------------ README.md | 49 +++++ extra/CMakeLists.txt | 2 +- extra/cmake/Synergy.cmake | 10 +- .../deploy/mac/bundle/Contents/Info.plist.in | 2 +- extra/src/CMakeLists.txt | 2 +- extra/src/integtests/CMakeLists.txt | 2 +- extra/src/lib/CMakeLists.txt | 2 +- extra/src/lib/synergy/CMakeLists.txt | 2 +- .../src/lib/synergy/gui/ActivationDialog.cpp | 2 +- extra/src/lib/synergy/gui/ActivationDialog.h | 2 +- extra/src/lib/synergy/gui/ActivationDialog.ui | 2 +- extra/src/lib/synergy/gui/AppTime.cpp | 2 +- extra/src/lib/synergy/gui/AppTime.h | 2 +- extra/src/lib/synergy/gui/CMakeLists.txt | 2 +- .../synergy/gui/CancelActivationDialog.cpp | 2 +- .../lib/synergy/gui/CancelActivationDialog.h | 2 +- .../lib/synergy/gui/CancelActivationDialog.ui | 2 +- extra/src/lib/synergy/gui/ExtraSettings.cpp | 2 +- extra/src/lib/synergy/gui/ExtraSettings.h | 2 +- extra/src/lib/synergy/gui/FeatureHandler.cpp | 2 +- extra/src/lib/synergy/gui/FeatureHandler.h | 2 +- extra/src/lib/synergy/gui/constants.h | 4 +- .../lib/synergy/gui/dialogs/UpgradeDialog.cpp | 2 +- .../lib/synergy/gui/dialogs/UpgradeDialog.h | 2 +- .../synergy/gui/license/LicenseHandler.cpp | 2 +- .../lib/synergy/gui/license/LicenseHandler.h | 2 +- .../synergy/gui/license/license_notices.cpp | 2 +- .../lib/synergy/gui/license/license_notices.h | 2 +- .../lib/synergy/gui/license/license_utils.cpp | 2 +- .../lib/synergy/gui/license/license_utils.h | 2 +- extra/src/lib/synergy/hooks/gui_hook.h | 2 +- .../lib/synergy/hooks/gui_hook_config.h.in | 2 +- extra/src/lib/synergy/license/CMakeLists.txt | 2 +- extra/src/lib/synergy/license/License.cpp | 2 +- extra/src/lib/synergy/license/License.h | 2 +- extra/src/lib/synergy/license/Product.cpp | 2 +- extra/src/lib/synergy/license/Product.h | 2 +- extra/src/lib/synergy/license/SerialKey.h | 2 +- .../src/lib/synergy/license/SerialKeyType.cpp | 2 +- extra/src/lib/synergy/license/SerialKeyType.h | 2 +- .../lib/synergy/license/parse_serial_key.cpp | 2 +- .../lib/synergy/license/parse_serial_key.h | 2 +- extra/src/unittests/CMakeLists.txt | 2 +- .../gui/license/LicenseHandlerTests.cpp | 2 +- .../gui/license/license_notices_tests.cpp | 2 +- extra/src/unittests/license/LicenseTests.cpp | 2 +- extra/src/unittests/license/ProductTests.cpp | 2 +- .../unittests/license/SerialKeyTypeTests.cpp | 2 +- src/lib/common/Constants.h.in | 1 - src/lib/gui/dialogs/AboutDialog.cpp | 8 +- src/lib/gui/dialogs/AboutDialog.ui | 2 +- translations/deskflow_es.ts | 4 +- translations/deskflow_it.ts | 4 +- translations/deskflow_ja.ts | 4 +- translations/deskflow_ko.ts | 4 +- translations/deskflow_ru.ts | 4 +- translations/deskflow_zh_CN.ts | 4 +- 58 files changed, 120 insertions(+), 255 deletions(-) delete mode 100644 .github/README.md create mode 100644 README.md diff --git a/.github/README.md b/.github/README.md deleted file mode 100644 index 667ed74b9..000000000 --- a/.github/README.md +++ /dev/null @@ -1,187 +0,0 @@ - - - - Deskflow - - -**Deskflow** is a free and open source keyboard and mouse sharing app. -Use the keyboard, mouse, or trackpad of one computer to control nearby computers, -and work seamlessly between them. -It's like a software KVM (but without the video). -TLS encryption is enabled by default. Wayland is supported. Clipboard sharing is supported. - -> [!TIP] -> -> **Chat with us** -> -> - Main discussion on Matrix: [`#deskflow:matrix.org`](https://matrix.to/#/#deskflow:matrix.org) ([Matrix clients](https://matrix.org/ecosystem/clients/)) -> - Discussion also happens on IRC: `#deskflow` or `#deskflow-dev` on [Libera Chat](https://libera.chat/) -> - Start a [new discussion](https://github.com/deskflow/deskflow/discussions) on our GitHub project. - -## Download - -[![Downloads: Stable Release](https://img.shields.io/github/downloads/deskflow/deskflow/latest/total?style=for-the-badge&logo=github&label=Download%20Stable)](https://github.com/deskflow/deskflow/releases/latest)      [![Downloads: Continuous Build](https://img.shields.io/github/downloads/deskflow/deskflow/continuous/total?style=for-the-badge&logo=github&label=Download%20Continuous)](https://github.com/deskflow/deskflow/releases/continuous)      [![Download From Flathub](https://img.shields.io/flathub/downloads/org.deskflow.deskflow?style=for-the-badge&logo=flathub&label=Download%20from%20flathub)](https://flathub.org/apps/org.deskflow.deskflow) - -> [!NOTE] -> On Windows, you will need to install the -> [Microsoft Visual C++ Redistributable](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-microsoft-visual-c-redistributable-version). -> Download latest: [`vc_redist.x64.exe`](https://aka.ms/vc14/vc_redist.x64.exe) [`vc_redist.arm64.exe`](https://aka.ms/vc14/vc_redist.arm64.exe) - -> [!TIP] -> For macOS users, the easiest way to install and stay up to date is to use [Homebrew](https://brew.sh) with our [homebrew-tap](https://github.com/deskflow/homebrew-tap). -> macOS reports unsigned apps as damaged. This occurs because we do not use an Apple certificate for notarization. Clear the quarantine attribute to run the app: `xattr -c Deskflow.app` - -To use Deskflow, download one of our [packages](https://github.com/deskflow/deskflow/releases), install `deskflow` (from your package repository), or [build it](https://github.com/deskflow/deskflow/wiki/Building) from source. - -## Stats - -[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/deskflow/deskflow?logo=github)](https://github.com/deskflow/deskflow/commits/master/) -[![GitHub top language](https://img.shields.io/github/languages/top/deskflow/deskflow?logo=github)](https://github.com/deskflow/deskflow/commits/master/) -[![GitHub License](https://img.shields.io/github/license/deskflow/deskflow?logo=github)](LICENSE) -[![REUSE status](https://api.reuse.software/badge/github.com/deskflow/deskflow)](https://api.reuse.software/info/github.com/deskflow/deskflow) - -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=deskflow_deskflow&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=deskflow_deskflow) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=deskflow_deskflow&metric=coverage)](https://sonarcloud.io/summary/new_code?id=deskflow_deskflow) -[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=deskflow_deskflow&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=deskflow_deskflow) -[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=deskflow_deskflow&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=deskflow_deskflow) - -[![CI](https://github.com/deskflow/deskflow/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/deskflow/deskflow/actions/workflows/continuous-integration.yml) -[![CodeQL Analysis](https://github.com/deskflow/deskflow/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/deskflow/deskflow/actions/workflows/codeql-analysis.yml) -[![SonarCloud Analysis](https://github.com/deskflow/deskflow/actions/workflows/sonarcloud-analysis.yml/badge.svg)](https://github.com/deskflow/deskflow/actions/workflows/sonarcloud-analysis.yml) - -## Contribute - -[![Good first issues](https://img.shields.io/github/issues/deskflow/deskflow/good%20first%20issue?label=good%20first%20issues&color=%2344cc11)](https://github.com/deskflow/deskflow/labels/good%20first%20issue) - -There are many ways to contribute to the Deskflow project. - -We're a friendly, active, and welcoming community focused on building a great app. - -Read our [Contributing](https://github.com/deskflow/deskflow/wiki/Contributing) page to get started. - -For instructions on building Deskflow, use the wiki page: [Building](https://github.com/deskflow/deskflow/wiki/Building) - -## Operating Systems - -We support all major operating systems, including Windows, macOS, Linux, and Unix-like BSD-derived. - -Windows 10 v1809 or higher is required. - -macOS 13 or higher is required to use our CI builds for Apple Silicon machines. macOS 12 or higher is required for Intel macs or local builds. - -Linux requires libei 1.3+ and libportal 0.8+ for the server/client. Additionally, Qt 6.7+ is required for the GUI. -Linux users with systems not meeting these requirements should use flatpak in place of a native package. - -We officially support FreeBSD, and would also like to support: OpenBSD, NetBSD, DragonFly, Solaris. - -## Repology - -Repology monitors a huge number of package repositories and other sources comparing package -versions across them and gathering other information. - -[![Repology](https://repology.org/badge/vertical-allrepos/deskflow.svg?columns=2&exclude_unsupported)](https://repology.org/project/deskflow/versions) - -## Installing on macOS - -When you install Deskflow on macOS, you need to allow accessibility access (Privacy & Security) to both the `Deskflow` app and the `deskflow` process. - -If using Sequoia, you may also need to allow `Deskflow` under Local Network‍ settings (Privacy & Security). -When prompted by the OS, go to the settings and enable the access. - -If you are upgrading and you already have `Deskflow` or `deskflow` -on the allowed list you will need to manually remove them before accessibility access can be granted to the new version. - -macOS users who download directly from releases may need to run `xattr -c /Applications/Deskflow.app` after copying the app to the `Applications` dir. - -It is recommended to install Deskflow using [Homebrew](https://brew.sh) from our [homebrew-tap](https://github.com/deskflow/homebrew-tap) - -To add our tap, run: - -``` -brew tap deskflow/tap -``` - -Then install either: - -- Stable: `brew install deskflow` -- Continuous: `brew install deskflow-dev` - -## Similar Projects - -In the open source developer community, similar projects collaborate for the improvement of all -mouse and keyboard sharing tools. We aim for idea sharing and interoperability. - -- [**Lan Mouse**](https://github.com/feschber/lan-mouse) - - Rust implementation with the goal of having native front-ends and interoperability with - Deskflow/Synergy. -- [**Synergy**](https://symless.com/synergy) - - Downstream commercial fork. Synergy sponsors Deskflow with financial support and contributes code ([learn more](https://github.com/deskflow/deskflow/wiki/Relationship-with-Synergy)). -- [**Input Leap**](https://github.com/input-leap/input-leap) - - Inactive Deskflow/Synergy-derivative with the goal continuing Barrier development (now a dead fork). - -## FAQ - -### Is Deskflow compatible with Synergy, Input Leap, or Barrier? - -Yes, Deskflow has network compatibility with all forks: - -- Requires Deskflow >= v1.17.0.96 -- Deskflow will _just work_ with Input Leap and Barrier (server or client). -- Connecting a Deskflow client to a Synergy 1 server will also _just work_. -- To connect a Synergy 1 client, you need to select the Synergy protocol in the Deskflow server settings. - -_Note:_ Only Synergy 1 is compatible with Deskflow (Synergy 3 is not yet compatible). - -### Is Deskflow compatible with Lan Mouse? - -We would love to see compatibility with Lan Mouse. This may be quite an effort as currently the way they handle the generated input is very different. - -### If I want to solve issues in Deskflow do I need to contribute to a fork? - -We welcome PRs (pull requests) from the community. If you'd like to make a change, please feel -free to [start a discussion](https://github.com/deskflow/deskflow/discussions) or -[open a PR](https://github.com/deskflow/deskflow/wiki/Contributing). - -### Is clipboard sharing supported? - -Absolutely. The clipboard-sharing feature is a cornerstone feature of the product and we are -committed to maintaining and improving that feature. - -### Is Wayland for Linux supported? - -Yes! Wayland (the Linux display server protocol aimed to become the successor of the X Window -System) is an important platform for us. -The [`libei`](https://gitlab.freedesktop.org/libinput/libei) and -[`libportal`](https://github.com/flatpak/libportal) libraries enable -Wayland support for Deskflow. We would like to give special thanks to Peter Hutterer, -who is the author of `libei`, a major contributor to `libportal`, and the author of the Wayland -implementation in Deskflow. Others such as Olivier Fourdan and Povilas Kanapickas helped with the -Wayland implementation. - -Some features _may_ be unavailable or broken on Wayland. Please see the [known Wayland issues](https://github.com/deskflow/deskflow/discussions/7499). - -### Where did it all start? - -Deskflow was first created as Synergy in 2001 by Chris Schoeneman. -Read about the [history of the project](https://github.com/deskflow/deskflow/wiki/History) on our -wiki. - -## Meow'Dib (our mascot) - -![Meow'Dib](https://github.com/user-attachments/assets/726f695c-3dfb-4abd-875d-ed658f6c610f) - -## Deskflow Contributors - -[![Sponsored by Synergy](https://raw.githubusercontent.com/deskflow/deskflow-artwork/b2c72a3e60a42dee793bd47efc275b5ee0bdaa5f/misc/synergy-sponsor.svg)](https://symless.com/synergy) - -[Synergy](https://symless.com/synergy) sponsors the Deskflow project by contributing code and providing financial support ([learn more](https://github.com/deskflow/deskflow/wiki/Relationship-with-Synergy)). - -Deskflow is made by possible by these contributors. - - - - - -## License - -This project is licensed under [GPL-2.0](LICENSE) with an [OpenSSL exception](../LICENSES/LicenseRef-OpenSSL-Exception.txt). diff --git a/README.md b/README.md new file mode 100644 index 000000000..157537874 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Synergy + +[![CodeQL Analysis](https://github.com/symless/synergy/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/symless/synergy/actions/workflows/codeql-analysis.yml) +[![SonarCloud Analysis](https://github.com/symless/synergy/actions/workflows/sonarcloud-analysis.yml/badge.svg)](https://github.com/symless/synergy/actions/workflows/sonarcloud-analysis.yml) + +Use the keyboard, mouse, or trackpad of one computer to control nearby computers, and work seamlessly between them. + +- [Get Synergy](https://synergyapp.io) +- [Technical support](https://synergyapp.io/contact) + +This repository contains the source code used to build Synergy 1 and the Core for Synergy 3. +It's based on the upstream Deskflow community project, which is sponsored by Synergy. + +- [Contibute to Deskflow](https://deskflow.org) + +## FAQ + +### How do I build the source code? +If you're a customer, you generally don’t need to build Synergy yourself as we provide pre-built, tested releases. +However, if you're a customer looking to build Synergy from source, [contact us](https://synergyapp.io/contact) so we can help with that. +If you're a developer looking to contribute to an open source community, join us in our [Deskflow](https://deskflow.org) project. + +### What’s the difference between Synergy and Deskflow? +Synergy is a stable, supported commercial product. It is quality assurance tested, has a warranty, and is maintained by a team of full-time engineers. +Deskflow is the upstream project where the open source community, including Synergy engineers, prototype and iterate on new features. +Synergy is your business-ready solution; Deskflow is for open source contributors and early adopters. + +### Where should I file bugs or feature requests? +For supported customers, reach out to our [support team](https://synergyapp.io/contact) and we’ll triage and track issues internally. +If you're contributing to the community project, use [Deskflow issues](https://github.com/deskflow/deskflow/issues) to report bugs or request features. + +### Can I contribute code to Synergy? +We welcome contributions, but our community development happens upstream in Deskflow. +That’s the best place to propose changes and collaborate with the wider community. +Changes flow downstream to Synergy once they have matured enough and are ready for customer usage. + +### How often does Deskflow merge into Synergy? +We regularly port stable features and fixes from Deskflow into Synergy. +This involves QA, integration testing, and compliance review. Critical bug fixes are fast-tracked. +For specific timelines on particular bug fixes and features, please [get in touch](https://synergyapp.io/contact). + +### Is Deskflow stable? +Deskflow is intended for developers and contributors who can self-support and fix issues. +It’s not suitable for production or business-critical environments requiring stability guarantees. +For those cases, we recommend using Synergy. + +### Why have two projects? +This model lets us move fast without breaking things. Deskflow empowers rapid community-driven innovation. +Synergy delivers a stable, supported experience to customers. diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index 41c54f8eb..a23f10561 100644 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: (C) 2012 - 2026 Symless Ltd +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd # SPDX-License-Identifier: MIT # Make synergy/... headers under this tree resolvable from anywhere that descends diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index 15128b789..9f9bc83e5 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -1,15 +1,15 @@ -# SPDX-FileCopyrightText: (C) 2012 - 2026 Symless Ltd +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd # SPDX-License-Identifier: MIT # Must be included after deskflow's project() call and the CMAKE_PROJECT_* # defaults are set, so these overrides take effect. set(CMAKE_PROJECT_PROPER_NAME "Synergy") -set(CMAKE_PROJECT_VENDOR "Symless Ltd") +set(CMAKE_PROJECT_VENDOR "Synergy App Ltd") set(CMAKE_PROJECT_COPYRIGHT "(C) 2012-2026 ${CMAKE_PROJECT_VENDOR}") -set(CMAKE_PROJECT_CONTACT "${CMAKE_PROJECT_PROPER_NAME} ") +set(CMAKE_PROJECT_CONTACT "${CMAKE_PROJECT_PROPER_NAME} ") set(CMAKE_PROJECT_REV_FQDN "com.symless.synergy") -set(CMAKE_PROJECT_DOMAIN "symless.com") -set(CMAKE_PROJECT_HOMEPAGE_URL "https://symless.com/synergy") +set(CMAKE_PROJECT_DOMAIN "synergyapp.io") +set(CMAKE_PROJECT_HOMEPAGE_URL "https://synergyapp.io") # Don't run unit tests as part of the build. Devs can opt back in with # -DSKIP_BUILD_TESTS=OFF if they want post-build ctest invocation. diff --git a/extra/deploy/mac/bundle/Contents/Info.plist.in b/extra/deploy/mac/bundle/Contents/Info.plist.in index 1c9dedb12..90f4d5ad5 100644 --- a/extra/deploy/mac/bundle/Contents/Info.plist.in +++ b/extra/deploy/mac/bundle/Contents/Info.plist.in @@ -24,7 +24,7 @@ CFBundleVersion @DESKFLOW_VERSION@ NSHumanReadableCopyright - © 2012-@DESKFLOW_BUILD_YEAR@ Symless Ltd + © 2012-@DESKFLOW_BUILD_YEAR@ Synergy App Ltd LSMinimumSystemVersion 10.9.0 diff --git a/extra/src/CMakeLists.txt b/extra/src/CMakeLists.txt index 38928b1e3..9c2b296e9 100644 --- a/extra/src/CMakeLists.txt +++ b/extra/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: (C) 2012 - 2026 Symless Ltd +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd # SPDX-License-Identifier: MIT add_subdirectory(lib) diff --git a/extra/src/integtests/CMakeLists.txt b/extra/src/integtests/CMakeLists.txt index fc3a52eb3..dcb66dfbf 100644 --- a/extra/src/integtests/CMakeLists.txt +++ b/extra/src/integtests/CMakeLists.txt @@ -1,5 +1,5 @@ # Deskflow -- mouse and keyboard sharing utility -# Copyright (C) 2024 Symless Ltd. +# Copyright (C) 2024 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/CMakeLists.txt b/extra/src/lib/CMakeLists.txt index 507a7b03a..5b0c49c31 100644 --- a/extra/src/lib/CMakeLists.txt +++ b/extra/src/lib/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: (C) 2012 - 2026 Symless Ltd +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd # SPDX-License-Identifier: MIT add_subdirectory(synergy) diff --git a/extra/src/lib/synergy/CMakeLists.txt b/extra/src/lib/synergy/CMakeLists.txt index 7f513fff6..52b8ca516 100644 --- a/extra/src/lib/synergy/CMakeLists.txt +++ b/extra/src/lib/synergy/CMakeLists.txt @@ -1,5 +1,5 @@ # Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2024 Symless Ltd. +# Copyright (C) 2024 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/ActivationDialog.cpp b/extra/src/lib/synergy/gui/ActivationDialog.cpp index 1146fed5e..4a0f8acf6 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/ActivationDialog.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/ActivationDialog.h b/extra/src/lib/synergy/gui/ActivationDialog.h index 0abfa6307..7cd242d0f 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.h +++ b/extra/src/lib/synergy/gui/ActivationDialog.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/ActivationDialog.ui b/extra/src/lib/synergy/gui/ActivationDialog.ui index b8ac11b1f..909b9ab7b 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.ui +++ b/extra/src/lib/synergy/gui/ActivationDialog.ui @@ -29,7 +29,7 @@ - <p>Your serial key is on your <a href="https://symless.com/synergy/account?source=gui" style="color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://symless.com/synergy/contact?source=gui" style="color:#4285f4;">Contact us</span></a></p> + <p>Your serial key is on your <a href="https://synergyapp.io/account?source=gui" style="color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://synergyapp.io/contact?source=gui" style="color:#4285f4;">Contact us</span></a></p> true diff --git a/extra/src/lib/synergy/gui/AppTime.cpp b/extra/src/lib/synergy/gui/AppTime.cpp index 5343db9f1..9c5494797 100644 --- a/extra/src/lib/synergy/gui/AppTime.cpp +++ b/extra/src/lib/synergy/gui/AppTime.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/AppTime.h b/extra/src/lib/synergy/gui/AppTime.h index 62faab118..5876934b0 100644 --- a/extra/src/lib/synergy/gui/AppTime.h +++ b/extra/src/lib/synergy/gui/AppTime.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/CMakeLists.txt b/extra/src/lib/synergy/gui/CMakeLists.txt index 5e2d7f52b..9b6518254 100644 --- a/extra/src/lib/synergy/gui/CMakeLists.txt +++ b/extra/src/lib/synergy/gui/CMakeLists.txt @@ -1,5 +1,5 @@ # Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2024 Symless Ltd. +# Copyright (C) 2024 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.cpp b/extra/src/lib/synergy/gui/CancelActivationDialog.cpp index 242fd58c2..21e217c9f 100644 --- a/extra/src/lib/synergy/gui/CancelActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.h b/extra/src/lib/synergy/gui/CancelActivationDialog.h index e22729e42..05362214a 100644 --- a/extra/src/lib/synergy/gui/CancelActivationDialog.h +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.h @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.ui b/extra/src/lib/synergy/gui/CancelActivationDialog.ui index 2f349ac83..7640407ea 100644 --- a/extra/src/lib/synergy/gui/CancelActivationDialog.ui +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.ui @@ -17,7 +17,7 @@ - <html><head/><body><p>You'll need to purchase a license to use Synergy.</p><p><a href="https://symless.com/synergy/contact?source=gui"><span style=" text-decoration: underline; color:#007af4;">Contact us</span></a></p><p>The application will now exit.</p></body></html> + <html><head/><body><p>You'll need to purchase a license to use Synergy.</p><p><a href="https://synergyapp.io/contact?source=gui"><span style=" text-decoration: underline; color:#007af4;">Contact us</span></a></p><p>The application will now exit.</p></body></html> true diff --git a/extra/src/lib/synergy/gui/ExtraSettings.cpp b/extra/src/lib/synergy/gui/ExtraSettings.cpp index f02d8c984..0a8fa8cf4 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.cpp +++ b/extra/src/lib/synergy/gui/ExtraSettings.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/ExtraSettings.h b/extra/src/lib/synergy/gui/ExtraSettings.h index 38ded1a1d..f44ceecca 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.h +++ b/extra/src/lib/synergy/gui/ExtraSettings.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index eca5ddfa0..fa981662f 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2025 Symless Ltd. + * Copyright (C) 2025 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index e737bae22..bb0a0ade5 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2025 Symless Ltd. + * Copyright (C) 2025 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 1d7693ca1..5763066fd 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,7 +26,7 @@ namespace synergy::gui { const auto kProProductName = "Synergy 1 Pro"; const auto kBusinessProductName = "Synergy 1 Business"; -const auto kUrlApi = "https://symless.com/api"; +const auto kUrlApi = "https://synergyapp.io/api"; const auto kUrlWebsite = DESKFLOW_WEBSITE_URL; const auto kUrlSourceQuery = "source=gui"; diff --git a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp index a3af9cd8b..dad7fbd14 100644 --- a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp +++ b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. + * Copyright (C) 2022 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h index ecdda43fe..77d6ba7e6 100644 --- a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h +++ b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. + * Copyright (C) 2022 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 18bc69b00..fbc7d0b00 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Symless Ltd. + * Copyright (C) 2015 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index 21f864d76..eac638760 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Symless Ltd. + * Copyright (C) 2015 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/license_notices.cpp b/extra/src/lib/synergy/gui/license/license_notices.cpp index c26417557..635ee8701 100644 --- a/extra/src/lib/synergy/gui/license/license_notices.cpp +++ b/extra/src/lib/synergy/gui/license/license_notices.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/license_notices.h b/extra/src/lib/synergy/gui/license/license_notices.h index c1ba0efb0..72c0f98a7 100644 --- a/extra/src/lib/synergy/gui/license/license_notices.h +++ b/extra/src/lib/synergy/gui/license/license_notices.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/license_utils.cpp b/extra/src/lib/synergy/gui/license/license_utils.cpp index 86f4f3827..a524d92ed 100644 --- a/extra/src/lib/synergy/gui/license/license_utils.cpp +++ b/extra/src/lib/synergy/gui/license/license_utils.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/license_utils.h b/extra/src/lib/synergy/gui/license/license_utils.h index fcf704663..448724a1c 100644 --- a/extra/src/lib/synergy/gui/license/license_utils.h +++ b/extra/src/lib/synergy/gui/license/license_utils.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index 51c450851..ea793dcfa 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 - 2025 Symless Ltd. + * Copyright (C) 2024 - 2025 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/hooks/gui_hook_config.h.in b/extra/src/lib/synergy/hooks/gui_hook_config.h.in index f96e5800d..b1eda78c6 100644 --- a/extra/src/lib/synergy/hooks/gui_hook_config.h.in +++ b/extra/src/lib/synergy/hooks/gui_hook_config.h.in @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 - 2025 Symless Ltd. + * Copyright (C) 2024 - 2025 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/CMakeLists.txt b/extra/src/lib/synergy/license/CMakeLists.txt index ce92ad413..cc8f71243 100644 --- a/extra/src/lib/synergy/license/CMakeLists.txt +++ b/extra/src/lib/synergy/license/CMakeLists.txt @@ -1,5 +1,5 @@ # Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2024 Symless Ltd. +# Copyright (C) 2024 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/License.cpp b/extra/src/lib/synergy/license/License.cpp index e72bccdc4..9048a6df7 100644 --- a/extra/src/lib/synergy/license/License.cpp +++ b/extra/src/lib/synergy/license/License.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/License.h b/extra/src/lib/synergy/license/License.h index ac01c2202..529ba33a5 100644 --- a/extra/src/lib/synergy/license/License.h +++ b/extra/src/lib/synergy/license/License.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/Product.cpp b/extra/src/lib/synergy/license/Product.cpp index 32790b661..d05ea10e0 100644 --- a/extra/src/lib/synergy/license/Product.cpp +++ b/extra/src/lib/synergy/license/Product.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/Product.h b/extra/src/lib/synergy/license/Product.h index d02f9d100..cddc873d7 100644 --- a/extra/src/lib/synergy/license/Product.h +++ b/extra/src/lib/synergy/license/Product.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/SerialKey.h b/extra/src/lib/synergy/license/SerialKey.h index ea443f5a2..b930fd24c 100644 --- a/extra/src/lib/synergy/license/SerialKey.h +++ b/extra/src/lib/synergy/license/SerialKey.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. + * Copyright (C) 2022 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/SerialKeyType.cpp b/extra/src/lib/synergy/license/SerialKeyType.cpp index 1f608d76c..e819b5adb 100644 --- a/extra/src/lib/synergy/license/SerialKeyType.cpp +++ b/extra/src/lib/synergy/license/SerialKeyType.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Symless Ltd. + * Copyright (C) 2015 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/SerialKeyType.h b/extra/src/lib/synergy/license/SerialKeyType.h index 396cf89aa..78b538b24 100644 --- a/extra/src/lib/synergy/license/SerialKeyType.h +++ b/extra/src/lib/synergy/license/SerialKeyType.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/parse_serial_key.cpp b/extra/src/lib/synergy/license/parse_serial_key.cpp index 42931b97a..759f5ffe3 100644 --- a/extra/src/lib/synergy/license/parse_serial_key.cpp +++ b/extra/src/lib/synergy/license/parse_serial_key.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/parse_serial_key.h b/extra/src/lib/synergy/license/parse_serial_key.h index 14e2bb637..971df756f 100644 --- a/extra/src/lib/synergy/license/parse_serial_key.h +++ b/extra/src/lib/synergy/license/parse_serial_key.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/CMakeLists.txt b/extra/src/unittests/CMakeLists.txt index 675f28fd6..dba1a08e4 100644 --- a/extra/src/unittests/CMakeLists.txt +++ b/extra/src/unittests/CMakeLists.txt @@ -1,5 +1,5 @@ # Deskflow -- mouse and keyboard sharing utility -# Copyright (C) 2024 Symless Ltd. +# Copyright (C) 2024 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/gui/license/LicenseHandlerTests.cpp b/extra/src/unittests/gui/license/LicenseHandlerTests.cpp index c1c5cf564..9f4853880 100644 --- a/extra/src/unittests/gui/license/LicenseHandlerTests.cpp +++ b/extra/src/unittests/gui/license/LicenseHandlerTests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/gui/license/license_notices_tests.cpp b/extra/src/unittests/gui/license/license_notices_tests.cpp index 7c9d1eb14..92d8a480e 100644 --- a/extra/src/unittests/gui/license/license_notices_tests.cpp +++ b/extra/src/unittests/gui/license/license_notices_tests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. + * Copyright (C) 2024 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/license/LicenseTests.cpp b/extra/src/unittests/license/LicenseTests.cpp index 3a1de70ca..f5a523aea 100644 --- a/extra/src/unittests/license/LicenseTests.cpp +++ b/extra/src/unittests/license/LicenseTests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/license/ProductTests.cpp b/extra/src/unittests/license/ProductTests.cpp index 00d4a8653..63a8e89c2 100644 --- a/extra/src/unittests/license/ProductTests.cpp +++ b/extra/src/unittests/license/ProductTests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/license/SerialKeyTypeTests.cpp b/extra/src/unittests/license/SerialKeyTypeTests.cpp index 81037a546..db90b8492 100644 --- a/extra/src/unittests/license/SerialKeyTypeTests.cpp +++ b/extra/src/unittests/license/SerialKeyTypeTests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. + * Copyright (C) 2016 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/src/lib/common/Constants.h.in b/src/lib/common/Constants.h.in index e8ea0b3e1..8e6d3bc83 100644 --- a/src/lib/common/Constants.h.in +++ b/src/lib/common/Constants.h.in @@ -17,7 +17,6 @@ const auto kRevFqdnName = "@CMAKE_PROJECT_REV_FQDN@"; const auto kCopyright = // "Copyright @CMAKE_PROJECT_COPYRIGHT@\n" - "Copyright (C) 2012-2026 Symless Ltd.\n" "Copyright (C) 2009-2012 Nick Bolton\n" "Copyright (C) 2002-2009 Chris Schoeneman"; diff --git a/src/lib/gui/dialogs/AboutDialog.cpp b/src/lib/gui/dialogs/AboutDialog.cpp index 5d1be6521..66c03b11f 100644 --- a/src/lib/gui/dialogs/AboutDialog.cpp +++ b/src/lib/gui/dialogs/AboutDialog.cpp @@ -12,6 +12,7 @@ #include "common/Constants.h" #include "common/Settings.h" #include "common/VersionInfo.h" +#include "gui/StyleUtils.h" #include @@ -19,8 +20,11 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui{std::make_unique { ui->setupUi(this); - setWindowTitle(QStringLiteral("About %1").arg(kAppName)); - ui->lblName->setText(kAppName); + setWindowTitle(windowTitle().arg(kAppName)); + ui->lblIcon->hide(); + ui->lblName->setPixmap(QPixmap(QStringLiteral(":/image/logo-%1.png").arg(deskflow::gui::iconMode()))); + ui->lblName->setContentsMargins(0, 0, 0, 10); + ui->linkContributors->hide(); const int px = (fontMetrics().height() * 6); const QSize pixmapSize(px, px); diff --git a/src/lib/gui/dialogs/AboutDialog.ui b/src/lib/gui/dialogs/AboutDialog.ui index 173823351..1b1becb3a 100644 --- a/src/lib/gui/dialogs/AboutDialog.ui +++ b/src/lib/gui/dialogs/AboutDialog.ui @@ -20,7 +20,7 @@ - About Deskflow + About %1 diff --git a/translations/deskflow_es.ts b/translations/deskflow_es.ts index c127b9aea..d5eac4289 100644 --- a/translations/deskflow_es.ts +++ b/translations/deskflow_es.ts @@ -4,8 +4,8 @@ AboutDialog - About Deskflow - Acerca de Deskflow + About %1 + Acerca de %1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> diff --git a/translations/deskflow_it.ts b/translations/deskflow_it.ts index e7335fd89..881803de8 100644 --- a/translations/deskflow_it.ts +++ b/translations/deskflow_it.ts @@ -4,8 +4,8 @@ AboutDialog - About Deskflow - Informazioni su Deskflow + About %1 + Informazioni su %1 Version: diff --git a/translations/deskflow_ja.ts b/translations/deskflow_ja.ts index 6085685e8..fce0b41cd 100644 --- a/translations/deskflow_ja.ts +++ b/translations/deskflow_ja.ts @@ -4,8 +4,8 @@ AboutDialog - About Deskflow - Deskflow について + About %1 + %1 について <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> diff --git a/translations/deskflow_ko.ts b/translations/deskflow_ko.ts index 5759e8816..efde4ed14 100644 --- a/translations/deskflow_ko.ts +++ b/translations/deskflow_ko.ts @@ -4,8 +4,8 @@ AboutDialog - About Deskflow - Deskflow 정보 + About %1 + %1 정보 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> diff --git a/translations/deskflow_ru.ts b/translations/deskflow_ru.ts index d3db66e86..08216c8a6 100644 --- a/translations/deskflow_ru.ts +++ b/translations/deskflow_ru.ts @@ -4,8 +4,8 @@ AboutDialog - About Deskflow - О Deskflow + About %1 + О %1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> diff --git a/translations/deskflow_zh_CN.ts b/translations/deskflow_zh_CN.ts index ddb45f2ca..7ca728833 100644 --- a/translations/deskflow_zh_CN.ts +++ b/translations/deskflow_zh_CN.ts @@ -4,8 +4,8 @@ AboutDialog - About Deskflow - 关于 Deskflow + About %1 + 关于 %1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> From 678833bf499f6382fa49308a53e3b0984a1b088e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 14:45:46 +0100 Subject: [PATCH 013/144] Refactor settings management and introduce test settings - Updated AppTime class to use a more descriptive type alias for time points. - Refactored ExtraSettings to utilize a new static Settings API for loading and syncing settings. - Removed unnecessary dependencies and streamlined the ExtraSettings class. - Simplified FeatureHandler by removing unused settings scope handling and integrating with the new Settings API. - Introduced TestSettings class for managing test configurations, allowing overrides via environment variables or a dedicated configuration file. - Updated LicenseHandler to remove deprecated features and integrate with the new Settings API. - Removed unused methods related to invert connection feature from License and Product classes. - Cleaned up GUI hooks to streamline integration with the new settings management. - Added styles header for UI styling constants. --- extra/CMakeLists.txt | 26 ++++ extra/src/lib/synergy/CMakeLists.txt | 8 +- .../src/lib/synergy/gui/ActivationDialog.cpp | 18 +-- extra/src/lib/synergy/gui/ActivationDialog.h | 7 +- extra/src/lib/synergy/gui/AppTime.cpp | 22 ++-- extra/src/lib/synergy/gui/AppTime.h | 6 +- extra/src/lib/synergy/gui/ExtraSettings.cpp | 41 +++--- extra/src/lib/synergy/gui/ExtraSettings.h | 14 +-- extra/src/lib/synergy/gui/FeatureHandler.cpp | 115 +++++++---------- extra/src/lib/synergy/gui/FeatureHandler.h | 19 ++- extra/src/lib/synergy/gui/TestSettings.cpp | 117 ++++++++++++++++++ extra/src/lib/synergy/gui/TestSettings.h | 92 ++++++++++++++ extra/src/lib/synergy/gui/constants.h | 4 +- .../synergy/gui/license/LicenseApiClient.cpp | 9 +- .../synergy/gui/license/LicenseApiClient.h | 4 +- .../synergy/gui/license/LicenseHandler.cpp | 107 +++------------- .../lib/synergy/gui/license/LicenseHandler.h | 13 +- .../lib/synergy/gui/license/license_utils.cpp | 17 ++- extra/src/lib/synergy/gui/styles.h | 34 +++++ extra/src/lib/synergy/hooks/gui_hook.h | 18 +-- .../lib/synergy/hooks/gui_hook_config.h.in | 29 ----- extra/src/lib/synergy/license/License.cpp | 5 - extra/src/lib/synergy/license/License.h | 1 - extra/src/lib/synergy/license/Product.cpp | 21 ---- extra/src/lib/synergy/license/Product.h | 2 - src/apps/deskflow-gui/deskflow-gui.cpp | 10 ++ src/lib/gui/MainWindow.cpp | 14 +++ src/lib/gui/VersionChecker.cpp | 9 +- src/lib/gui/dialogs/SettingsDialog.cpp | 8 ++ 29 files changed, 466 insertions(+), 324 deletions(-) create mode 100644 extra/src/lib/synergy/gui/TestSettings.cpp create mode 100644 extra/src/lib/synergy/gui/TestSettings.h create mode 100644 extra/src/lib/synergy/gui/styles.h delete mode 100644 extra/src/lib/synergy/hooks/gui_hook_config.h.in diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index a23f10561..b7a9325b3 100644 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -6,3 +6,29 @@ include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/lib") add_subdirectory(src) + +# Wire the synergy::hooks::* callsites in upstream src/. The synergy-gui sublib +# provides the inline functions in synergy/hooks/gui_hook.h that upstream calls +# behind the SYNERGY_EXTRA_HEADER guard. Setting the define + include path on +# the upstream gui target lets MainWindow.cpp / SettingsDialog.cpp / etc. find +# the header without editing upstream's CMakeLists. +if(TARGET gui) + target_compile_definitions(gui PRIVATE SYNERGY_EXTRA_HEADER) + target_include_directories(gui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/lib) + # Cascade synergy-gui to anything that links gui (the synergy executable, + # but also unit tests that link libgui.a — they pull in MainWindow.cpp.o + # which now references synergy::hooks::* symbols). Plain signature matches + # upstream's gui CMakeLists. + target_link_libraries(gui synergy-gui) +endif() + +# Pick the gui app target name (Apple uses the proper-cased brand name). +if(APPLE) + set(_synergy_gui_app ${CMAKE_PROJECT_PROPER_NAME}) +else() + set(_synergy_gui_app ${CMAKE_PROJECT_NAME}) +endif() +if(TARGET ${_synergy_gui_app}) + target_compile_definitions(${_synergy_gui_app} PRIVATE SYNERGY_EXTRA_HEADER) + target_include_directories(${_synergy_gui_app} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/lib) +endif() diff --git a/extra/src/lib/synergy/CMakeLists.txt b/extra/src/lib/synergy/CMakeLists.txt index 52b8ca516..a59c853c6 100644 --- a/extra/src/lib/synergy/CMakeLists.txt +++ b/extra/src/lib/synergy/CMakeLists.txt @@ -14,10 +14,4 @@ # along with this program. If not, see . add_subdirectory(license) - -# gui subdirectory disabled until hook modernization rewires it. -# The synergy GUI code was written against an older deskflow gui API -# (gui/string_utils.h, gui/styles.h, gui/diagnostic.h, gui/config/AppConfig.h, -# gui/config/Settings.h — all refactored away upstream). Re-enable when hook -# wiring lands and the gui integration is ported. -# add_subdirectory(gui) +add_subdirectory(gui) diff --git a/extra/src/lib/synergy/gui/ActivationDialog.cpp b/extra/src/lib/synergy/gui/ActivationDialog.cpp index 4a0f8acf6..f1e8f2dc0 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/ActivationDialog.cpp @@ -18,11 +18,12 @@ #include "ActivationDialog.h" #include "CancelActivationDialog.h" -#include "gui/config/AppConfig.h" -#include "gui/styles.h" +#include "common/Settings.h" +#include "synergy/gui/TestSettings.h" #include "synergy/gui/constants.h" #include "synergy/gui/license/LicenseHandler.h" #include "synergy/gui/license/license_notices.h" +#include "synergy/gui/styles.h" #include "synergy/license/parse_serial_key.h" #include "ui_ActivationDialog.h" @@ -39,10 +40,9 @@ using namespace synergy::license; const QString successTitle = "Serial key"; const QString problemTitle = "Serial key problem"; -ActivationDialog::ActivationDialog(QWidget *parent, AppConfig &appConfig, LicenseHandler &licenseHandler) +ActivationDialog::ActivationDialog(QWidget *parent, LicenseHandler &licenseHandler) : QDialog(parent), m_ui(new Ui::ActivationDialog), - m_pAppConfig(&appConfig), m_licenseHandler(licenseHandler) { m_ui->setupUi(this); @@ -59,10 +59,10 @@ ActivationDialog::~ActivationDialog() void ActivationDialog::refreshSerialKey() { - const QString envSerialKey = qEnvironmentVariable("SYNERGY_TEST_SERIAL_KEY"); - if (!envSerialKey.isEmpty()) { - qDebug("using serial key from env var"); - m_ui->m_pTextEditSerialKey->setText(envSerialKey); + const QString testSerialKey = TestSettings::instance().serialKey(); + if (!testSerialKey.isEmpty()) { + qDebug("using serial key from test settings"); + m_ui->m_pTextEditSerialKey->setText(testSerialKey); } else { qDebug("using serial key from config"); const auto hexString = m_licenseHandler.license().serialKey().hexString; @@ -198,7 +198,7 @@ void ActivationDialog::showSuccessDialog() QString message = tr("

Thanks for entering your serial key for %1.

").arg(m_licenseHandler.productName()); const auto tlsAvailable = m_licenseHandler.license().isTlsAvailable(); - if (tlsAvailable && m_pAppConfig->tlsEnabled()) { + if (tlsAvailable && Settings::value(Settings::Security::TlsEnabled).toBool()) { message += "

To ensure that TLS encryption works correctly, " "please use the same serial key on all of your computers.

"; } diff --git a/extra/src/lib/synergy/gui/ActivationDialog.h b/extra/src/lib/synergy/gui/ActivationDialog.h index 7cd242d0f..2e1389dae 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.h +++ b/extra/src/lib/synergy/gui/ActivationDialog.h @@ -25,14 +25,12 @@ namespace Ui { class ActivationDialog; } -class AppConfig; - class ActivationDialog : public QDialog { Q_OBJECT public: - ActivationDialog(QWidget *parent, AppConfig &appConfig, LicenseHandler &licenseHandler); + ActivationDialog(QWidget *parent, LicenseHandler &licenseHandler); ~ActivationDialog() override; class ActivationMessageError : public std::runtime_error @@ -48,7 +46,7 @@ class ActivationDialog : public QDialog return m_serialKeyChanged; } -public slots: +public Q_SLOTS: void reject() override; void accept() override; @@ -62,7 +60,6 @@ public slots: void showEvent(QShowEvent *) override; Ui::ActivationDialog *m_ui; - AppConfig *m_pAppConfig; LicenseHandler &m_licenseHandler; bool m_serialKeyChanged = false; }; diff --git a/extra/src/lib/synergy/gui/AppTime.cpp b/extra/src/lib/synergy/gui/AppTime.cpp index 9c5494797..7f27594ff 100644 --- a/extra/src/lib/synergy/gui/AppTime.cpp +++ b/extra/src/lib/synergy/gui/AppTime.cpp @@ -17,21 +17,17 @@ #include "AppTime.h" -#include +#include "synergy/gui/TestSettings.h" -using system_clock = std::chrono::system_clock; -using time_point = system_clock::time_point; +#include namespace synergy::gui { -const auto kTestTimeEnvVar = "SYNERGY_TEST_START_TIME"; - AppTime::AppTime() { - m_realStartTime = system_clock::now(); - if (qEnvironmentVariableIsSet(kTestTimeEnvVar)) { - const auto testTime = qEnvironmentVariable(kTestTimeEnvVar).toLongLong(); - qDebug("setting test time to: %lld", testTime); + m_realStartTime = std::chrono::system_clock::now(); + if (const auto testTime = TestSettings::instance().startTimeEpochSecs(); testTime != 0) { + qDebug("setting test time to: %lld", static_cast(testTime)); m_testStartTime = std::chrono::seconds{testTime}; } } @@ -41,13 +37,13 @@ bool AppTime::hasTestTime() const return m_testStartTime.has_value(); } -time_point AppTime::now() +AppTime::TimePoint AppTime::now() { if (m_testStartTime.has_value()) { - const auto runtime = system_clock::now() - m_realStartTime; - return time_point{m_testStartTime.value()} + runtime; + const auto runtime = std::chrono::system_clock::now() - m_realStartTime; + return TimePoint{m_testStartTime.value()} + runtime; } - return system_clock::now(); + return std::chrono::system_clock::now(); } } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/AppTime.h b/extra/src/lib/synergy/gui/AppTime.h index 5876934b0..97aeac4a7 100644 --- a/extra/src/lib/synergy/gui/AppTime.h +++ b/extra/src/lib/synergy/gui/AppTime.h @@ -24,16 +24,16 @@ namespace synergy::gui { class AppTime { - using time_point = std::chrono::system_clock::time_point; + using TimePoint = std::chrono::system_clock::time_point; public: AppTime(); - time_point now(); + TimePoint now(); bool hasTestTime() const; private: std::optional m_testStartTime = std::nullopt; - time_point m_realStartTime = std::chrono::system_clock::now(); + TimePoint m_realStartTime = std::chrono::system_clock::now(); }; } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/ExtraSettings.cpp b/extra/src/lib/synergy/gui/ExtraSettings.cpp index 0a8fa8cf4..41eb6dae1 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.cpp +++ b/extra/src/lib/synergy/gui/ExtraSettings.cpp @@ -17,37 +17,44 @@ #include "ExtraSettings.h" -#include -#include +#include "common/Settings.h" -using namespace deskflow::gui::proxy; +#include namespace synergy::gui { -const auto kSerialKeySettingKey = "serialKey"; -const auto kActivatedSettingKey = "activated"; -const auto kGraceStartSettingKey = "graceStartEpochSecs"; +const auto kSerialKeySettingKey = QStringLiteral("synergy/serialKey"); +const auto kActivatedSettingKey = QStringLiteral("synergy/activated"); +const auto kGraceStartSettingKey = QStringLiteral("synergy/graceStartEpochSecs"); void ExtraSettings::load() { - const auto &settings = getActiveSettings(); - m_serialKey = settings.value(kSerialKeySettingKey).toString(); - m_activated = settings.value(kActivatedSettingKey).toBool(); - m_graceStartEpochSecs = settings.value(kGraceStartSettingKey).toLongLong(); + m_serialKey = Settings::value(kSerialKeySettingKey).toString(); + m_activated = Settings::value(kActivatedSettingKey).toBool(); + m_graceStartEpochSecs = Settings::value(kGraceStartSettingKey).toLongLong(); } void ExtraSettings::sync() { - auto &settings = getActiveSettings(); - if (!settings.isWritable()) { - qCritical() << "unable to save to settings, file not writable:" << settings.fileName(); + if (!Settings::isWritable()) { + qCritical() << "unable to save synergy settings, file not writable:" << Settings::settingsFile(); return; } - settings.setValue(kSerialKeySettingKey, m_serialKey); - settings.setValue(kActivatedSettingKey, m_activated); - settings.setValue(kGraceStartSettingKey, m_graceStartEpochSecs); - settings.sync(); + Settings::setValue(kSerialKeySettingKey, m_serialKey); + Settings::setValue(kActivatedSettingKey, m_activated); + Settings::setValue(kGraceStartSettingKey, m_graceStartEpochSecs); + Settings::save(); +} + +QString ExtraSettings::fileName() const +{ + return Settings::settingsFile(); +} + +bool ExtraSettings::isWritable() const +{ + return Settings::isWritable(); } } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/ExtraSettings.h b/extra/src/lib/synergy/gui/ExtraSettings.h index f44ceecca..ce737cc76 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.h +++ b/extra/src/lib/synergy/gui/ExtraSettings.h @@ -17,20 +17,17 @@ #pragma once -#include "gui/config/Settings.h" - -#include #include -#include namespace synergy::gui { -class ExtraSettings : public deskflow::gui::Settings +// Synergy license state (serial key, activation, grace period). Stored as +// synergy/* keys in the upstream Settings file via the new static Settings API. +class ExtraSettings { - Q_OBJECT public: ExtraSettings() = default; - virtual ~ExtraSettings() = default; + void load(); void sync(); @@ -61,6 +58,9 @@ class ExtraSettings : public deskflow::gui::Settings m_graceStartEpochSecs = epochSecs; } + QString fileName() const; + bool isWritable() const; + private: QString m_serialKey; bool m_activated = false; diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index fa981662f..30fd8ae09 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -17,84 +17,46 @@ #include "FeatureHandler.h" -#include "dialogs/UpgradeDialog.h" -#include "gui/config/AppConfig.h" -#include "gui/constants.h" -#include "gui/diagnostic.h" +#include "common/Settings.h" #include "license/LicenseHandler.h" #include "synergy/gui/constants.h" #include +#include #include #include #include -#include +#include +#include -using namespace deskflow::gui; using namespace synergy::gui; -void FeatureHandler::handleMainWindow(AppConfig *appConfig) +void FeatureHandler::handleMainWindow(QMainWindow *mainWindow) { - m_appConfig = appConfig; + Q_UNUSED(mainWindow); } -void FeatureHandler::handleSettings(QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope) const +void FeatureHandler::handleSettings(QDialog *parent) const { - const auto onSystemScopeToggle = [this, parent, systemScope, userScope](bool checked) { - qDebug("system scope radio button toggled"); - systemScope->blockSignals(true); - userScope->blockSignals(true); - handleSettingsScopeRadioButton(parent, systemScope, userScope, checked); - systemScope->blockSignals(false); - userScope->blockSignals(false); - }; - QObject::connect(systemScope, &QRadioButton::toggled, onSystemScopeToggle); + Q_UNUSED(parent); - checkSettingsScopeLicense(parent, systemScope, userScope, false); + // Synergy-specific settings UI (scope radios, etc.) lives in extra/. To be + // wired when the synergy widget-injection layer lands; until then, the + // scope-switching helper below is callable directly when needed. } -bool FeatureHandler::checkSettingsScopeLicense( - QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope, bool showDialog -) const +void FeatureHandler::switchScope(QDialog *parent, bool toSystemScope) { const auto &licenseHandler = LicenseHandler::instance(); - if (!licenseHandler.isEnabled()) { - qDebug("license handler disabled, skipping settings scope check"); - return true; - } - - const auto &license = licenseHandler.license(); - if (!license.isSettingsScopeAvailable() && systemScope->isChecked()) { - qDebug("settings scope not available, showing upgrade dialog"); - userScope->setChecked(true); - - if (showDialog) { - UpgradeDialog dialog(parent); - dialog.showDialog( - QString("Settings Scope"), - QString("Please upgrade to %1 to enable the settings scope feature.").arg(kBusinessProductName), kUrlContact - ); - } - - return false; - } - - return true; -} - -void FeatureHandler::handleSettingsScopeRadioButton( - QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope, bool checked -) const -{ - if (!checkSettingsScopeLicense(parent, systemScope, userScope, true)) { - qDebug("settings scope not available, skipping feature handler"); + if (licenseHandler.isEnabled() && !licenseHandler.license().isSettingsScopeAvailable() && toSystemScope) { + qWarning("settings scope not available for this license tier"); return; } const auto userScopeText = QStringLiteral("current user"); const auto systemScopeText = QStringLiteral("all users"); - const auto from = checked ? userScopeText : systemScopeText; - const auto to = checked ? systemScopeText : userScopeText; + const auto from = toSystemScope ? userScopeText : systemScopeText; + const auto to = toSystemScope ? systemScopeText : userScopeText; const auto result = QMessageBox::information( parent, "Switch settings profile", QString("Switching settings from %1 to %2 requires %3 to restart.\n\n" @@ -103,27 +65,36 @@ void FeatureHandler::handleSettingsScopeRadioButton( QMessageBox::Yes | QMessageBox::Cancel ); - if (result == QMessageBox::Yes) { - auto &systemSettings = m_appConfig->settings().getSystemSettings(); - systemSettings.loadSystem(); + if (result != QMessageBox::Yes) { + return; + } - if (systemSettings.isEmpty()) { - qDebug("system settings are empty, copying user settings"); - systemSettings.copyFrom(m_appConfig->settings().getUserSettings()); + const auto destFile = toSystemScope ? Settings::SystemSettingFile : Settings::UserSettingFile; + const auto sourceFile = toSystemScope ? Settings::UserSettingFile : Settings::SystemSettingFile; + + QSettings dest(destFile, QSettings::IniFormat); + if (dest.allKeys().isEmpty()) { + qDebug() << "destination settings empty, copying from:" << sourceFile; + QSettings source(sourceFile, QSettings::IniFormat); + for (const auto &key : source.allKeys()) { + dest.setValue(key, source.value(key)); } + dest.sync(); + } - systemSettings.setValue(kSystemScopeSetting, checked); - systemSettings.sync(); + Settings::setSettingsFile(destFile); - // This seems rather clumsy and un-elegant at first glance, but actually when you consider - // the complexities of hot-switching the settings scope while the application is running, - // restarting the applocation is actually the lowest maintenance solution. - deskflow::gui::diagnostic::restart(); - } else { - if (checked) { - userScope->setChecked(true); - } else { - systemScope->setChecked(true); - } + // Hot-switching the active settings file mid-run touches enough state + // (signal/slot wiring, cached UI defaults) that a clean restart is the + // safest path. Inlined because upstream's `deskflow::gui::diagnostic::restart()` + // is defined in Diagnostic.cpp but not declared in the public header. + const auto program = QCoreApplication::applicationFilePath(); + auto arguments = QCoreApplication::arguments(); + if (const auto resetIndex = arguments.indexOf(QStringLiteral("--reset")); resetIndex != -1) { + arguments.remove(resetIndex); } + qInfo("launching new process: %s", qPrintable(program)); + QProcess::startDetached(program, arguments); + qDebug("exiting current process"); + QCoreApplication::exit(); } diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index bb0a0ade5..e40f04fbf 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -17,9 +17,8 @@ #pragma once -class AppConfig; class QDialog; -class QRadioButton; +class QMainWindow; class FeatureHandler { @@ -30,14 +29,12 @@ class FeatureHandler return instance; } - void handleMainWindow(AppConfig *appConfig); - void handleSettings(QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope) const; + void handleMainWindow(QMainWindow *mainWindow); + void handleSettings(QDialog *parent) const; -private: - void handleSettingsScopeRadioButton(QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope, bool checked) - const; - bool - checkSettingsScopeLicense(QDialog *parent, QRadioButton *systemScope, QRadioButton *userScope, bool showDialog) const; - - AppConfig *m_appConfig = nullptr; + // Switches the active settings file between system and user scope and + // restarts the application. The community removed system/user scope from + // upstream's UI; Synergy keeps the feature downstream as a license-tier + // capability. Caller is responsible for license-tier gating. + static void switchScope(QDialog *parent, bool toSystemScope); }; diff --git a/extra/src/lib/synergy/gui/TestSettings.cpp b/extra/src/lib/synergy/gui/TestSettings.cpp new file mode 100644 index 000000000..4241387e3 --- /dev/null +++ b/extra/src/lib/synergy/gui/TestSettings.cpp @@ -0,0 +1,117 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#include "TestSettings.h" + +#include "common/Constants.h" +#include "common/Settings.h" + +#include +#include +#include + +namespace synergy::gui { + +namespace { + +QString envOrFile(const char *envName, const QString &fileValue, bool fileEnabled) +{ + const auto env = qEnvironmentVariable(envName); + if (!env.isEmpty()) { + return env; + } + return fileEnabled ? fileValue : QString(); +} + +} // namespace + +TestSettings &TestSettings::instance() +{ + static TestSettings inst; + return inst; +} + +TestSettings::TestSettings() + : m_fileName(QStringLiteral("%1/%2.test.conf").arg(Settings::UserDir, kAppName)) +{ + load(); +} + +void TestSettings::load() +{ + m_enabled = false; + m_serialKey.clear(); + m_apiUrlActivate.clear(); + m_apiUrlCheck.clear(); + m_startTimeEpochSecs = 0; + m_verbose = false; + m_skipRemoteCheck = false; + m_allowExpiredLicenses = false; + + if (!QFile::exists(m_fileName)) { + qDebug().noquote() << "test settings file not found, test mode off:" << m_fileName; + return; + } + + QSettings ini(m_fileName, QSettings::IniFormat); + m_enabled = ini.value(QStringLiteral("test/enabled"), false).toBool(); + if (!m_enabled) { + qDebug().noquote() << "test settings file present but test/enabled=false:" << m_fileName; + return; + } + m_serialKey = ini.value(QStringLiteral("test/serialKey")).toString(); + m_apiUrlActivate = ini.value(QStringLiteral("test/apiUrlActivate")).toString(); + m_apiUrlCheck = ini.value(QStringLiteral("test/apiUrlCheck")).toString(); + m_startTimeEpochSecs = ini.value(QStringLiteral("test/startTime"), 0).toLongLong(); + + m_verbose = ini.value(QStringLiteral("features/verbose"), false).toBool(); + m_skipRemoteCheck = ini.value(QStringLiteral("features/skipRemoteCheck"), false).toBool(); + m_allowExpiredLicenses = ini.value(QStringLiteral("features/allowExpiredLicenses"), false).toBool(); + + qInfo().noquote() << "test mode enabled, loaded:" << m_fileName; +} + +void TestSettings::reload() +{ + load(); +} + +QString TestSettings::serialKey() const +{ + return envOrFile("SYNERGY_TEST_SERIAL_KEY", m_serialKey, m_enabled); +} + +QString TestSettings::apiUrlActivate() const +{ + return envOrFile("SYNERGY_TEST_API_URL_ACTIVATE", m_apiUrlActivate, m_enabled); +} + +QString TestSettings::apiUrlCheck() const +{ + return envOrFile("SYNERGY_TEST_API_URL_CHECK", m_apiUrlCheck, m_enabled); +} + +qint64 TestSettings::startTimeEpochSecs() const +{ + const auto env = qEnvironmentVariable("SYNERGY_TEST_START_TIME"); + if (!env.isEmpty()) { + return env.toLongLong(); + } + return m_enabled ? m_startTimeEpochSecs : 0; +} + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/TestSettings.h b/extra/src/lib/synergy/gui/TestSettings.h new file mode 100644 index 000000000..95150d442 --- /dev/null +++ b/extra/src/lib/synergy/gui/TestSettings.h @@ -0,0 +1,92 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#pragma once + +#include + +namespace synergy::gui { + +// QA/test overrides for Synergy. Lives at `${UserDir}/${kAppName}.test.conf`, +// sibling to the main Synergy.conf settings file. Sourcing values from a file +// (rather than env vars) sidesteps the pain of getting env vars into GUI +// launches on macOS / Windows / VS Code F5. +// +// All accessors fall back to the corresponding SYNERGY_TEST_* env var when +// the env var is set, so existing CI/automation flows are unchanged. +class TestSettings +{ +public: + static TestSettings &instance(); + + // True iff the test config file exists and `[test]/enabled=true`. The + // value is cached at construction; call reload() to re-read. + bool isEnabled() const + { + return m_enabled; + } + + // Test overrides. Each accessor returns the env var when set, otherwise + // the file value when isEnabled(), otherwise empty / 0. + QString serialKey() const; + QString apiUrlActivate() const; + QString apiUrlCheck() const; + qint64 startTimeEpochSecs() const; + + // Feature toggles read from the [features] section. File-only — no env + // var fallback for these (no precedent). + bool verbose() const + { + return m_verbose; + } + bool skipRemoteCheck() const + { + return m_skipRemoteCheck; + } + bool allowExpiredLicenses() const + { + return m_allowExpiredLicenses; + } + + // Path to the test config file (whether or not it exists). + QString fileName() const + { + return m_fileName; + } + + // Re-read the file from disk (e.g., from a Test menu "Reload" action). + void reload(); + +private: + TestSettings(); + TestSettings(const TestSettings &) = delete; + TestSettings &operator=(const TestSettings &) = delete; + + void load(); + + QString m_fileName; + bool m_enabled = false; + QString m_serialKey; + QString m_apiUrlActivate; + QString m_apiUrlCheck; + qint64 m_startTimeEpochSecs = 0; + bool m_verbose = false; + bool m_skipRemoteCheck = false; + bool m_allowExpiredLicenses = false; +}; + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 5763066fd..caccd4d1f 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -17,6 +17,8 @@ #pragma once +#include "common/Constants.h" + #include #include @@ -27,7 +29,7 @@ const auto kProProductName = "Synergy 1 Pro"; const auto kBusinessProductName = "Synergy 1 Business"; const auto kUrlApi = "https://synergyapp.io/api"; -const auto kUrlWebsite = DESKFLOW_WEBSITE_URL; +const auto kUrlWebsite = QStringLiteral("https://%1").arg(kAppDomain); const auto kUrlSourceQuery = "source=gui"; const auto kLinkBuy = R"(Buy now)"; diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index f06189d34..2f723846a 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -17,6 +17,7 @@ #include "LicenseApiClient.h" +#include "synergy/gui/TestSettings.h" #include "synergy/gui/constants.h" #include @@ -29,14 +30,14 @@ namespace synergy::gui::license { QString activateUrl() { - const auto envVar = qEnvironmentVariable("SYNERGY_TEST_API_URL_ACTIVATE"); - return envVar.isEmpty() ? kUrlApiLicenseActivate : envVar; + const auto testUrl = TestSettings::instance().apiUrlActivate(); + return testUrl.isEmpty() ? kUrlApiLicenseActivate : testUrl; } QString checkUrl() { - const auto envVar = qEnvironmentVariable("SYNERGY_TEST_API_URL_CHECK"); - return envVar.isEmpty() ? kUrlApiLicenseCheck : envVar; + const auto testUrl = TestSettings::instance().apiUrlCheck(); + return testUrl.isEmpty() ? kUrlApiLicenseCheck : testUrl; } LicenseApiClient::LicenseApiClient() diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h index f14674f35..126e63514 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -50,14 +50,14 @@ class LicenseApiClient : public QObject return m_isBusy; } -signals: +Q_SIGNALS: void activationFailed(const QString &message); void activationSucceeded(); void checkFailed(const QString &message); void checkSucceeded(); void licenseDisabled(const QString &message); -private slots: +private Q_SLOTS: void handleResponse(QNetworkReply *reply); private: diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index fbc7d0b00..5eb9b6527 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -18,17 +18,16 @@ #include "LicenseHandler.h" #include "ActivationDialog.h" +#include "common/Settings.h" +#include "common/VersionInfo.h" #include "dialogs/UpgradeDialog.h" -#include "gui/config/AppConfig.h" #include "gui/core/CoreProcess.h" -#include "gui/styles.h" #include "synergy/gui/constants.h" #include "synergy/gui/license/license_utils.h" +#include "synergy/gui/styles.h" #include "synergy/license/Product.h" -#include "version.h" #include -#include #include #include #include @@ -40,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -92,13 +90,10 @@ LicenseHandler::LicenseHandler() connect(&m_apiClient, &LicenseApiClient::licenseDisabled, this, &LicenseHandler::disableLicenseRemotely); } -void LicenseHandler::handleMainWindow( - QMainWindow *mainWindow, AppConfig *appConfig, deskflow::gui::CoreProcess *coreProcess -) +void LicenseHandler::handleMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) { // Must still be set as these are used when not enabled. m_pMainWindow = mainWindow; - m_pAppConfig = appConfig; m_pCoreProcess = coreProcess; if (!m_enabled) { @@ -119,13 +114,9 @@ bool LicenseHandler::handleAppStart() qFatal("main window not set"); } - if (m_pAppConfig == nullptr) { - qFatal("app config not set"); - } - if (!m_enabled) { qDebug("license handler disabled, skipping start handler"); - m_pMainWindow->setWindowTitle(SYNERGY_PRODUCT_NAME); + m_pMainWindow->setWindowTitle(kAppName); return true; } @@ -150,30 +141,14 @@ bool LicenseHandler::handleAppStart() return true; } -void LicenseHandler::handleSettings( - QDialog *parent, QCheckBox *enableTls, QCheckBox *invertConnection, QRadioButton *systemScope, - QRadioButton *userScope -) const +void LicenseHandler::handleSettings(QDialog *parent) const { - if (!m_enabled) { - qDebug("license handler disabled, skipping settings handler"); - return; - } - - const auto onTlsToggle = [this, parent, enableTls] { - qDebug("license handler, tls checkbox toggled"); - checkTlsCheckBox(parent, enableTls, true); - }; - QObject::connect(enableTls, &QCheckBox::toggled, onTlsToggle); - - const auto onInvertConnectionToggle = [this, parent, invertConnection] { - qDebug("license handler, invert connection checkbox toggled"); - checkInvertConnectionCheckBox(parent, invertConnection, true); - }; - QObject::connect(invertConnection, &QCheckBox::toggled, onInvertConnectionToggle); + Q_UNUSED(parent); - checkTlsCheckBox(parent, enableTls, false); - checkInvertConnectionCheckBox(parent, invertConnection, false); + // Settings UI injection (TLS toggle, scope radios, etc.) lives in extra/. + // To be wired when the synergy widget-injection layer lands; until then, + // license-tier clamping happens in clampFeatures() at app start and on + // license change. } void LicenseHandler::handleVersionCheck(QString &versionUrl) @@ -205,10 +180,6 @@ bool LicenseHandler::handleCoreStart() return true; } - if (m_pAppConfig == nullptr) { - qFatal("app config not set"); - } - if (m_pMainWindow == nullptr) { qFatal("main window not set"); } @@ -276,7 +247,7 @@ bool LicenseHandler::showSerialKeyDialog() return false; } - ActivationDialog dialog(m_pMainWindow, *m_pAppConfig, *this); + ActivationDialog dialog(m_pMainWindow, *this); const auto result = dialog.exec(); if (result != QDialog::Accepted) { qWarning("license serial key dialog declined"); @@ -320,43 +291,6 @@ void LicenseHandler::updateWindowTitle() const m_pMainWindow->setWindowTitle(productName); } -void LicenseHandler::checkTlsCheckBox(QDialog *parent, QCheckBox *checkBoxEnableTls, bool showDialog) const -{ - if (!m_license.isTlsAvailable() && checkBoxEnableTls->isChecked()) { - qDebug("tls not available, showing upgrade dialog"); - checkBoxEnableTls->setChecked(false); - - if (showDialog) { - UpgradeDialog dialog(parent); - dialog.showDialog( - QString("TLS Encryption"), - QString("Please upgrade to %1 to enable TLS encryption.").arg(synergy::gui::kProProductName), - synergy::gui::kUrlPersonalUpgrade - ); - } - } -} - -void LicenseHandler::checkInvertConnectionCheckBox( - QDialog *parent, QCheckBox *checkBoxInvertConnection, bool showDialog -) const -{ - if (!m_license.isInvertConnectionAvailable() && checkBoxInvertConnection->isChecked()) { - qDebug("invert connection not available, showing upgrade dialog"); - checkBoxInvertConnection->setChecked(false); - - if (showDialog) { - UpgradeDialog dialog(parent); - dialog.showDialog( - QString("Invert Connection"), - QString("Please upgrade to %1 to enable the invert connection feature.") - .arg(synergy::gui::kBusinessProductName), - synergy::gui::kUrlContact - ); - } - } -} - const synergy::license::License &LicenseHandler::license() const { return m_license; @@ -449,22 +383,18 @@ bool LicenseHandler::check() void LicenseHandler::clampFeatures() { - if (m_pAppConfig->tlsEnabled() && !m_license.isTlsAvailable()) { + if (Settings::value(Settings::Security::TlsEnabled).toBool() && !m_license.isTlsAvailable()) { qWarning("tls not available, disabling tls"); - m_pAppConfig->setTlsEnabled(false); - } - - if (m_pAppConfig->invertConnection() && !m_license.isInvertConnectionAvailable()) { - qWarning("invert connection not available, disabling invert connection"); - m_pAppConfig->setInvertConnection(false); + Settings::setValue(Settings::Security::TlsEnabled, false); } - if (m_pAppConfig->isSystemScope() && !m_license.isSettingsScopeAvailable()) { + const auto isSystemScope = (Settings::settingsFile() == Settings::SystemSettingFile); + if (isSystemScope && !m_license.isSettingsScopeAvailable()) { qFatal("settings scope not available"); } qDebug("committing default feature settings"); - m_pAppConfig->commit(); + Settings::save(); } void LicenseHandler::disable() @@ -497,13 +427,14 @@ LicenseApiClient::Data LicenseHandler::buildApiData() const const auto machineSignature = QCryptographicHash::hash(machineId, QCryptographicHash::Sha256).toHex(); const auto hostnameSignature = QCryptographicHash::hash(hostname.toUtf8(), QCryptographicHash::Sha256).toHex(); + const auto isServer = (Settings::value(Settings::Core::CoreMode).toInt() == Settings::Server); return { machineSignature, hostnameSignature, QString::fromStdString(m_license.serialKey().hexString), kVersion, QSysInfo::prettyProductName(), - m_pAppConfig->serverGroupChecked() + isServer }; } diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index eac638760..fc0a45f4b 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -23,11 +23,8 @@ #include "synergy/license/License.h" #include "synergy/license/Product.h" -class AppConfig; class QMainWindow; class QDialog; -class QCheckBox; -class QRadioButton; namespace deskflow::gui { class CoreProcess; @@ -61,12 +58,9 @@ class LicenseHandler : public QObject return instance; } - void handleMainWindow(QMainWindow *mainWindow, AppConfig *appConfig, deskflow::gui::CoreProcess *coreProcess); + void handleMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess); bool handleAppStart(); - void handleSettings( - QDialog *parent, QCheckBox *enableTls, QCheckBox *invertConnection, QRadioButton *systemScope, - QRadioButton *userScope - ) const; + void handleSettings(QDialog *parent) const; void handleVersionCheck(QString &versionUrl); bool handleCoreStart(); bool loadSettings(); @@ -84,8 +78,6 @@ class LicenseHandler : public QObject } private: - void checkTlsCheckBox(QDialog *parent, QCheckBox *checkBoxEnableTls, bool showDialog) const; - void checkInvertConnectionCheckBox(QDialog *parent, QCheckBox *checkBoxInvertConnection, bool showDialog) const; void updateWindowTitle() const; bool showSerialKeyDialog(); bool check(); @@ -104,6 +96,5 @@ class LicenseHandler : public QObject synergy::gui::license::LicenseApiClient m_apiClient; bool m_warnedAboutGrace = false; QMainWindow *m_pMainWindow = nullptr; - AppConfig *m_pAppConfig = nullptr; deskflow::gui::CoreProcess *m_pCoreProcess = nullptr; }; diff --git a/extra/src/lib/synergy/gui/license/license_utils.cpp b/extra/src/lib/synergy/gui/license/license_utils.cpp index a524d92ed..0d57b0ba9 100644 --- a/extra/src/lib/synergy/gui/license/license_utils.cpp +++ b/extra/src/lib/synergy/gui/license/license_utils.cpp @@ -17,7 +17,7 @@ #include "license_utils.h" -#include "gui/string_utils.h" +#include "synergy/gui/TestSettings.h" #include "synergy/license/parse_serial_key.h" #include @@ -31,13 +31,24 @@ const bool kEnableActivation = true; const bool kEnableActivation = false; #endif // SYNERGY_ENABLE_ACTIVATION +namespace { +// Inlined from upstream's removed gui/string_utils.h. Trivial helper kept +// local to avoid taking a new dependency just for this one call site. +bool strToTrue(const QString &str) +{ + return str.toLower() == "true" || str == "1"; +} +} // namespace + bool isActivationEnabled() { if (strToTrue(qEnvironmentVariable("SYNERGY_ENABLE_ACTIVATION"))) { return true; - } else { - return kEnableActivation; } + if (synergy::gui::TestSettings::instance().isEnabled()) { + return true; + } + return kEnableActivation; } synergy::license::SerialKey parseSerialKey(const QString &hexString) diff --git a/extra/src/lib/synergy/gui/styles.h b/extra/src/lib/synergy/gui/styles.h new file mode 100644 index 000000000..44963e5be --- /dev/null +++ b/extra/src/lib/synergy/gui/styles.h @@ -0,0 +1,34 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2024 Synergy App 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 . + */ + +#pragma once + +#include + +// Style constants used by Synergy UI. Previously lived in upstream's +// `gui/styles.h`; that header was removed during the deskflow gui refactor, +// so we keep our own copy here in the overlay. + +const auto kColorWhite = "#ffffff"; +const auto kColorPrimary = "#ff7c00"; +const auto kColorSecondary = "#4285f4"; +const auto kColorNotice = "#3b67d3"; + +const auto kStyleNoticeLabel = // + QString("padding: 3px 5px; border-radius: 3px;" + "background-color: %1; color: %2") + .arg(kColorNotice, kColorWhite); diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index ea793dcfa..5fffede66 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -19,12 +19,9 @@ #include "synergy/gui/FeatureHandler.h" #include "synergy/gui/license/LicenseHandler.h" -#include "synergy/hooks/gui_hook_config.h" // IWYU pragma: keep -#include #include #include -#include namespace deskflow::gui { class CoreProcess; @@ -32,10 +29,10 @@ class CoreProcess; namespace synergy::hooks { -inline void onMainWindow(QMainWindow *mainWindow, AppConfig *appConfig, deskflow::gui::CoreProcess *coreProcess) +inline void onMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) { - LicenseHandler::instance().handleMainWindow(mainWindow, appConfig, coreProcess); - FeatureHandler::instance().handleMainWindow(appConfig); + LicenseHandler::instance().handleMainWindow(mainWindow, coreProcess); + FeatureHandler::instance().handleMainWindow(mainWindow); } inline bool onAppStart() @@ -43,13 +40,10 @@ inline bool onAppStart() return LicenseHandler::instance().handleAppStart(); } -inline void onSettings( - QDialog *parent, QCheckBox *enableTls, QCheckBox *invertConnection, QRadioButton *systemScope, - QRadioButton *userScope -) +inline void onSettings(QDialog *parent) { - LicenseHandler::instance().handleSettings(parent, enableTls, invertConnection, systemScope, userScope); - FeatureHandler::instance().handleSettings(parent, systemScope, userScope); + LicenseHandler::instance().handleSettings(parent); + FeatureHandler::instance().handleSettings(parent); } inline void onVersionCheck(QString &versionUrl) diff --git a/extra/src/lib/synergy/hooks/gui_hook_config.h.in b/extra/src/lib/synergy/hooks/gui_hook_config.h.in deleted file mode 100644 index b1eda78c6..000000000 --- a/extra/src/lib/synergy/hooks/gui_hook_config.h.in +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 - 2025 Synergy App 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 . - */ - -#pragma once - -// clang-format off - -#cmakedefine DESKFLOW_GUI_HOOK_MAIN_WINDOW ${DESKFLOW_GUI_HOOK_MAIN_WINDOW} -#cmakedefine DESKFLOW_GUI_HOOK_APP_START ${DESKFLOW_GUI_HOOK_APP_START} -#cmakedefine DESKFLOW_GUI_HOOK_SETTINGS ${DESKFLOW_GUI_HOOK_SETTINGS} -#cmakedefine DESKFLOW_GUI_HOOK_VERSION ${DESKFLOW_GUI_HOOK_VERSION} -#cmakedefine DESKFLOW_GUI_HOOK_CORE_START ${DESKFLOW_GUI_HOOK_CORE_START} -#cmakedefine DESKFLOW_GUI_HOOK_TEST_START ${DESKFLOW_GUI_HOOK_TEST_START} - -// clang-format on diff --git a/extra/src/lib/synergy/license/License.cpp b/extra/src/lib/synergy/license/License.cpp index 9048a6df7..c51c1c10f 100644 --- a/extra/src/lib/synergy/license/License.cpp +++ b/extra/src/lib/synergy/license/License.cpp @@ -59,11 +59,6 @@ bool License::isTlsAvailable() const return m_serialKey.product.isFeatureAvailable(Product::Feature::kTls); } -bool License::isInvertConnectionAvailable() const -{ - return m_serialKey.product.isFeatureAvailable(Product::Feature::kInvertConnection); -} - bool License::isSettingsScopeAvailable() const { return m_serialKey.product.isFeatureAvailable(Product::Feature::kSettingsScope); diff --git a/extra/src/lib/synergy/license/License.h b/extra/src/lib/synergy/license/License.h index 529ba33a5..e1a34352d 100644 --- a/extra/src/lib/synergy/license/License.h +++ b/extra/src/lib/synergy/license/License.h @@ -53,7 +53,6 @@ class License } bool isTlsAvailable() const; - bool isInvertConnectionAvailable() const; bool isSettingsScopeAvailable() const; bool isValid() const { diff --git a/extra/src/lib/synergy/license/Product.cpp b/extra/src/lib/synergy/license/Product.cpp index d05ea10e0..8d9c3905b 100644 --- a/extra/src/lib/synergy/license/Product.cpp +++ b/extra/src/lib/synergy/license/Product.cpp @@ -134,24 +134,6 @@ bool Product::isTlsAvailable() const } } -bool Product::isInvertConnectionAvailable() const -{ - switch (edition()) { - using enum Edition; - - case kBusiness: - return true; - - case kBasic: - case kPro: - case kUnregistered: - return false; - - default: - throw InvalidProductEdition(); - } -} - bool Product::isSettingsScopeAvailable() const { switch (edition()) { @@ -178,9 +160,6 @@ bool Product::isFeatureAvailable(Product::Feature feature) const case kTls: return isTlsAvailable(); - case kInvertConnection: - return isInvertConnectionAvailable(); - case kSettingsScope: return isSettingsScopeAvailable(); diff --git a/extra/src/lib/synergy/license/Product.h b/extra/src/lib/synergy/license/Product.h index cddc873d7..cabf0c045 100644 --- a/extra/src/lib/synergy/license/Product.h +++ b/extra/src/lib/synergy/license/Product.h @@ -52,7 +52,6 @@ class Product enum class Feature { kTls = 0, - kInvertConnection = 1, kSettingsScope = 2, }; @@ -82,7 +81,6 @@ class Product private: bool isTlsAvailable() const; - bool isInvertConnectionAvailable() const; bool isSettingsScopeAvailable() const; Edition m_edition = Edition::kUnregistered; diff --git a/src/apps/deskflow-gui/deskflow-gui.cpp b/src/apps/deskflow-gui/deskflow-gui.cpp index 7fd5cc09e..1f5ab5feb 100644 --- a/src/apps/deskflow-gui/deskflow-gui.cpp +++ b/src/apps/deskflow-gui/deskflow-gui.cpp @@ -17,6 +17,10 @@ #include "gui/Messages.h" #include "gui/StyleUtils.h" +#ifdef SYNERGY_EXTRA_HEADER +#include "synergy/hooks/gui_hook.h" +#endif + #include #include #include @@ -152,6 +156,12 @@ int main(int argc, char *argv[]) MainWindow mainWindow; mainWindow.open(); +#ifdef SYNERGY_EXTRA_HEADER + if (!synergy::hooks::onAppStart()) { + return s_exitFailed; + } +#endif + return QApplication::exec(); } diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index ab467db76..320309ecc 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -31,6 +31,10 @@ #include "net/FingerprintDatabase.h" #include "widgets/StatusBar.h" +#ifdef SYNERGY_EXTRA_HEADER +#include "synergy/hooks/gui_hook.h" +#endif + #include #include #include @@ -157,6 +161,10 @@ MainWindow::MainWindow() applyConfig(); m_statusBar->setSecurityIcon(TlsUtility::isEnabled()); restoreWindow(); + +#ifdef SYNERGY_EXTRA_HEADER + synergy::hooks::onMainWindow(this, &m_coreProcess); +#endif } MainWindow::~MainWindow() { @@ -405,6 +413,12 @@ void MainWindow::startCore() m_actionStartCore->setVisible(false); m_actionRestartCore->setVisible(true); + +#ifdef SYNERGY_EXTRA_HEADER + if (!synergy::hooks::onCoreStart()) { + return; + } +#endif m_coreProcess.start(); } diff --git a/src/lib/gui/VersionChecker.cpp b/src/lib/gui/VersionChecker.cpp index 1dbcf6cdf..f384433a9 100644 --- a/src/lib/gui/VersionChecker.cpp +++ b/src/lib/gui/VersionChecker.cpp @@ -9,6 +9,10 @@ #include "common/Settings.h" #include "common/VersionInfo.h" +#ifdef SYNERGY_EXTRA_HEADER +#include "synergy/hooks/gui_hook.h" +#endif + #include #include #include @@ -24,7 +28,10 @@ VersionChecker::VersionChecker(QObject *parent) : QObject(parent), m_network{new void VersionChecker::checkLatest() const { - const QString url = Settings::value(Settings::Gui::UpdateCheckUrl).toString(); + QString url = Settings::value(Settings::Gui::UpdateCheckUrl).toString(); +#ifdef SYNERGY_EXTRA_HEADER + synergy::hooks::onVersionCheck(url); +#endif qDebug("checking for updates at: %s", qPrintable(url)); auto request = QNetworkRequest(url); auto userAgent = QString("%1 %2 on %3").arg(kAppName, kVersion, QSysInfo::prettyProductName()); diff --git a/src/lib/gui/dialogs/SettingsDialog.cpp b/src/lib/gui/dialogs/SettingsDialog.cpp index 45d264ad5..2b3b773ce 100644 --- a/src/lib/gui/dialogs/SettingsDialog.cpp +++ b/src/lib/gui/dialogs/SettingsDialog.cpp @@ -18,6 +18,10 @@ #include "gui/TlsUtility.h" #include "gui/core/NetworkMonitor.h" +#ifdef SYNERGY_EXTRA_HEADER +#include "synergy/hooks/gui_hook.h" +#endif + #include #include #include @@ -81,6 +85,10 @@ SettingsDialog::SettingsDialog(QWidget *parent, const ServerConfig &serverConfig setButtonBoxEnabledButtons(); initConnections(); + +#ifdef SYNERGY_EXTRA_HEADER + synergy::hooks::onSettings(this); +#endif } void SettingsDialog::changeEvent(QEvent *e) From deb0a3ad01b1a0eb10ebb3c4ac502e3964162889 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 14:52:30 +0100 Subject: [PATCH 014/144] feat: add test menu functionality to FeatureHandler --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 33 +++++++++++++++++++- extra/src/lib/synergy/gui/FeatureHandler.h | 6 ++++ extra/src/lib/synergy/hooks/gui_hook.h | 1 + 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 30fd8ae09..e697e647e 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -19,12 +19,17 @@ #include "common/Settings.h" #include "license/LicenseHandler.h" +#include "synergy/gui/TestSettings.h" #include "synergy/gui/constants.h" +#include #include #include #include #include +#include +#include +#include #include #include #include @@ -33,7 +38,33 @@ using namespace synergy::gui; void FeatureHandler::handleMainWindow(QMainWindow *mainWindow) { - Q_UNUSED(mainWindow); + m_pMainWindow = mainWindow; +} + +void FeatureHandler::handleAppStart() +{ + if (TestSettings::instance().isEnabled()) { + addTestMenu(); + } +} + +void FeatureHandler::addTestMenu() +{ + if (m_pMainWindow == nullptr) { + qWarning("cannot add test menu, main window not set"); + return; + } + + auto testMenu = new QMenu(QObject::tr("Test"), m_pMainWindow); + m_pMainWindow->menuBar()->addMenu(testMenu); + + auto fatalAction = new QAction(QObject::tr("Trigger fatal error"), m_pMainWindow); + QObject::connect(fatalAction, &QAction::triggered, [] { qFatal("test fatal error"); }); + testMenu->addAction(fatalAction); + + auto criticalAction = new QAction(QObject::tr("Trigger critical error"), m_pMainWindow); + QObject::connect(criticalAction, &QAction::triggered, [] { qCritical("test critical error"); }); + testMenu->addAction(criticalAction); } void FeatureHandler::handleSettings(QDialog *parent) const diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index e40f04fbf..7dc7fe00a 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -30,6 +30,7 @@ class FeatureHandler } void handleMainWindow(QMainWindow *mainWindow); + void handleAppStart(); void handleSettings(QDialog *parent) const; // Switches the active settings file between system and user scope and @@ -37,4 +38,9 @@ class FeatureHandler // upstream's UI; Synergy keeps the feature downstream as a license-tier // capability. Caller is responsible for license-tier gating. static void switchScope(QDialog *parent, bool toSystemScope); + +private: + void addTestMenu(); + + QMainWindow *m_pMainWindow = nullptr; }; diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index 5fffede66..d04d263ac 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -37,6 +37,7 @@ inline void onMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *co inline bool onAppStart() { + FeatureHandler::instance().handleAppStart(); return LicenseHandler::instance().handleAppStart(); } From 0eaafb6cad832bbb939cae9d3202a6e559b01005 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 7 May 2026 14:54:31 +0100 Subject: [PATCH 015/144] refactor: update ExtraSettings to use QSettings for configuration management --- extra/src/lib/synergy/gui/ExtraSettings.cpp | 52 +++++++++++++++------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/extra/src/lib/synergy/gui/ExtraSettings.cpp b/extra/src/lib/synergy/gui/ExtraSettings.cpp index 41eb6dae1..3de9b3dc2 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.cpp +++ b/extra/src/lib/synergy/gui/ExtraSettings.cpp @@ -17,44 +17,66 @@ #include "ExtraSettings.h" +#include "common/Constants.h" #include "common/Settings.h" #include +#include +#include namespace synergy::gui { -const auto kSerialKeySettingKey = QStringLiteral("synergy/serialKey"); -const auto kActivatedSettingKey = QStringLiteral("synergy/activated"); -const auto kGraceStartSettingKey = QStringLiteral("synergy/graceStartEpochSecs"); +namespace { + +// Persisted synergy-side state lives in its own sibling file rather than +// upstream's Synergy.conf, because upstream's Settings::cleanSettings() +// strips any key not in its allow-list at startup. Keeping our state out +// of that file means no upstream patch + no key-allow-list maintenance. +QString settingsFile() +{ + return QStringLiteral("%1/%2.extra.conf").arg(Settings::UserDir, kAppName); +} + +const auto kSerialKey = QStringLiteral("serialKey"); +const auto kActivated = QStringLiteral("activated"); +const auto kGraceStart = QStringLiteral("graceStartEpochSecs"); + +} // namespace void ExtraSettings::load() { - m_serialKey = Settings::value(kSerialKeySettingKey).toString(); - m_activated = Settings::value(kActivatedSettingKey).toBool(); - m_graceStartEpochSecs = Settings::value(kGraceStartSettingKey).toLongLong(); + QSettings ini(settingsFile(), QSettings::IniFormat); + m_serialKey = ini.value(kSerialKey).toString(); + m_activated = ini.value(kActivated).toBool(); + m_graceStartEpochSecs = ini.value(kGraceStart).toLongLong(); } void ExtraSettings::sync() { - if (!Settings::isWritable()) { - qCritical() << "unable to save synergy settings, file not writable:" << Settings::settingsFile(); + QSettings ini(settingsFile(), QSettings::IniFormat); + if (!ini.isWritable()) { + qCritical() << "unable to save synergy settings, file not writable:" << ini.fileName(); return; } - - Settings::setValue(kSerialKeySettingKey, m_serialKey); - Settings::setValue(kActivatedSettingKey, m_activated); - Settings::setValue(kGraceStartSettingKey, m_graceStartEpochSecs); - Settings::save(); + ini.setValue(kSerialKey, m_serialKey); + ini.setValue(kActivated, m_activated); + ini.setValue(kGraceStart, m_graceStartEpochSecs); + ini.sync(); } QString ExtraSettings::fileName() const { - return Settings::settingsFile(); + return settingsFile(); } bool ExtraSettings::isWritable() const { - return Settings::isWritable(); + QFileInfo info(settingsFile()); + if (info.exists()) { + return info.isWritable(); + } + // If the file doesn't exist yet, writability depends on the parent dir. + return QFileInfo(info.absolutePath()).isWritable(); } } // namespace synergy::gui From 304388d5379b2645988a78db9930d1a870bd3bfe Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 10:10:39 +0100 Subject: [PATCH 016/144] feat: implement settings migration and update URLs for API and download checks --- .../src/lib/synergy/gui/SettingsMigration.cpp | 323 ++++++++++++++++++ extra/src/lib/synergy/gui/SettingsMigration.h | 49 +++ extra/src/lib/synergy/gui/constants.h | 4 +- extra/src/lib/synergy/hooks/gui_hook.h | 17 + src/apps/deskflow-gui/deskflow-gui.cpp | 4 + src/lib/common/UrlConstants.h | 2 +- 6 files changed, 396 insertions(+), 3 deletions(-) create mode 100644 extra/src/lib/synergy/gui/SettingsMigration.cpp create mode 100644 extra/src/lib/synergy/gui/SettingsMigration.h diff --git a/extra/src/lib/synergy/gui/SettingsMigration.cpp b/extra/src/lib/synergy/gui/SettingsMigration.cpp new file mode 100644 index 000000000..c11f4ddaf --- /dev/null +++ b/extra/src/lib/synergy/gui/SettingsMigration.cpp @@ -0,0 +1,323 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#include "SettingsMigration.h" + +#include "common/Constants.h" +#include "common/Settings.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace synergy::gui::migration { + +namespace { + +const auto kSchemaKey = QStringLiteral("migration/schemaVersion"); +const auto kNotifiedKey = QStringLiteral("migration/notifiedFor"); +const auto kLegacySystemScopeKey = QStringLiteral("systemScope"); + +QString extraFile() +{ + return QStringLiteral("%1/%2.extra.conf").arg(Settings::UserDir, kAppName); +} + +int storedSchemaVersion() +{ + QSettings ini(extraFile(), QSettings::IniFormat); + return ini.value(kSchemaKey, 0).toInt(); +} + +void writeSchemaVersion(int version) +{ + QSettings ini(extraFile(), QSettings::IniFormat); + ini.setValue(kSchemaKey, version); + ini.sync(); +} + +int notifiedSchemaVersion() +{ + QSettings ini(extraFile(), QSettings::IniFormat); + return ini.value(kNotifiedKey, 0).toInt(); +} + +void writeNotifiedVersion(int version) +{ + QSettings ini(extraFile(), QSettings::IniFormat); + ini.setValue(kNotifiedKey, version); + ini.sync(); +} + +// Maps a legacy key + value to the new (key, value) pair, or returns +// nullopt to drop. Special cases (boolean → enum) handled inline. +std::optional> mapKey(const QString &oldKey, const QVariant &value) +{ + if (oldKey == "screenName") { + return std::make_pair(Settings::Core::ComputerName, value); + } + if (oldKey == "port") { + return std::make_pair(Settings::Core::Port, value); + } + if (oldKey == "interface") { + return std::make_pair(Settings::Core::Interface, value); + } + if (oldKey == "logLevel2") { + return std::make_pair(Settings::Log::Level, value); + } + if (oldKey == "logToFile") { + return std::make_pair(Settings::Log::ToFile, value); + } + if (oldKey == "logFilename") { + return std::make_pair(Settings::Log::File, value); + } + if (oldKey == "elevateModeEnum") { + return std::make_pair(Settings::Daemon::Elevate, value); + } + if (oldKey == "cryptoEnabled") { + return std::make_pair(Settings::Security::TlsEnabled, value); + } + if (oldKey == "autoHide") { + return std::make_pair(Settings::Gui::Autohide, value); + } + if (oldKey == "lastVersion") { + return std::make_pair(Settings::Core::LastVersion, value); + } + if (oldKey == "groupServerChecked") { + if (value.toBool()) { + return std::make_pair(Settings::Core::CoreMode, QVariant(Settings::Server)); + } + return std::nullopt; + } + if (oldKey == "groupClientChecked") { + if (value.toBool()) { + return std::make_pair(Settings::Core::CoreMode, QVariant(Settings::Client)); + } + return std::nullopt; + } + if (oldKey == "useExternalConfig") { + return std::make_pair(Settings::Server::ExternalConfig, value); + } + if (oldKey == "configFile") { + return std::make_pair(Settings::Server::ExternalConfigFile, value); + } + if (oldKey == "serverHostname") { + return std::make_pair(Settings::Client::RemoteHost, value); + } + if (oldKey == "tlsCertPath") { + return std::make_pair(Settings::Security::Certificate, value); + } + if (oldKey == "tlsKeyLength") { + return std::make_pair(Settings::Security::KeySize, value); + } + if (oldKey == "preventSleep") { + return std::make_pair(Settings::Core::PreventSleep, value); + } + if (oldKey == "languageSync") { + return std::make_pair(Settings::Client::LanguageSync, value); + } + if (oldKey == "invertScrollDirection") { + return std::make_pair(Settings::Client::InvertYScroll, value); + } + if (oldKey == "enableService") { + const auto mode = value.toBool() ? Settings::Service : Settings::Desktop; + return std::make_pair(Settings::Core::ProcessMode, QVariant(mode)); + } + if (oldKey == "closeToTray") { + return std::make_pair(Settings::Gui::CloseToTray, value); + } + if (oldKey == "showCloseReminder") { + return std::make_pair(Settings::Gui::CloseReminder, value); + } + if (oldKey == "enableUpdateCheck") { + return std::make_pair(Settings::Gui::AutoUpdateCheck, value); + } + return std::nullopt; +} + +// Sentinel keys that indicate a QSettings instance holds legacy-format +// data (Synergy 1.x, pre-Settings::* API). Detection by sentinel rather +// than file existence is necessary because on Linux the legacy NativeFormat +// path coincides with beta's IniFormat path. +bool looksLikeLegacy(const QSettings &legacy) +{ + return legacy.contains(QStringLiteral("startedBefore")) || legacy.contains(QStringLiteral("groupServerChecked")) || + legacy.contains(QStringLiteral("groupClientChecked")) || legacy.contains(kLegacySystemScopeKey); +} + +// Dumps every key/value from `legacy` into a sibling INI file. Cross-platform +// backup — works whether legacy lives in a file (Linux), a plist (macOS), +// or the registry (Windows), because we're dumping the in-memory contents, +// not file-copying. +QString backupLegacy(const QSettings &legacy, const QString &destPath) +{ + if (QFile::exists(destPath)) { + QFile::remove(destPath); + } + QSettings backup(destPath, QSettings::IniFormat); + for (const auto &key : legacy.allKeys()) { + backup.setValue(key, legacy.value(key)); + } + backup.sync(); + return destPath; +} + +// Returns true iff at least one key was migrated from `legacy` to the +// QSettings instance at `newPath`. +int migrateOneScope(QSettings &legacy, const QString &newPath) +{ + QSettings newSettings(newPath, QSettings::IniFormat); + int migrated = 0; + int dropped = 0; + for (const auto &oldKey : legacy.allKeys()) { + if (auto mapped = mapKey(oldKey, legacy.value(oldKey)); mapped.has_value()) { + newSettings.setValue(mapped->first, mapped->second); + migrated++; + } else { + dropped++; + } + } + newSettings.sync(); + qInfo("settings migration: scope %s — migrated %d keys, dropped %d obsolete", qPrintable(newPath), migrated, dropped); + return migrated; +} + +bool s_migrationRanThisLaunch = false; +QString s_lastBackupPath; + +// True iff `legacy`'s storage backend points at the same on-disk file as +// `newPath`. On Linux this is the case (NativeFormat → INI at the same +// QStandardPaths location upstream's Settings uses); on macOS/Windows +// they're separate (plist / registry vs. our explicit IniFormat path). +// When same, we MUST NOT clear() the legacy storage — that would wipe +// the new keys we just wrote. +bool sharesPathWith(const QSettings &legacy, const QString &newPath) +{ + const auto legacyCanonical = QFileInfo(legacy.fileName()).canonicalFilePath(); + const auto newCanonical = QFileInfo(newPath).canonicalFilePath(); + if (legacyCanonical.isEmpty() || newCanonical.isEmpty()) { + // canonicalFilePath returns empty if the file doesn't exist; fall back + // to absolute path comparison. + return QFileInfo(legacy.fileName()).absoluteFilePath() == QFileInfo(newPath).absoluteFilePath(); + } + return legacyCanonical == newCanonical; +} + +void maybeClearLegacy(QSettings &legacy, const QString &newPath, const char *scopeLabel) +{ + if (sharesPathWith(legacy, newPath)) { + qDebug( + "settings migration: %s legacy shares storage with new file, skipping clear (cleanSettings will handle)", + scopeLabel + ); + return; + } + if (!legacy.isWritable()) { + qWarning("settings migration: %s legacy not writable, leaving legacy keys in place", scopeLabel); + return; + } + legacy.clear(); + legacy.sync(); + qInfo("settings migration: cleared %s legacy storage at %s", scopeLabel, qPrintable(legacy.fileName())); +} + +bool runLegacyMigration() +{ + bool any = false; + + // User scope. + QSettings legacyUser(QSettings::NativeFormat, QSettings::UserScope, kAppName, kAppName); + const bool legacyHadSystemScope = legacyUser.value(kLegacySystemScopeKey, false).toBool(); + if (looksLikeLegacy(legacyUser)) { + s_lastBackupPath = backupLegacy(legacyUser, Settings::UserSettingFile + QStringLiteral(".legacy.bak")); + qInfo().noquote() << "settings migration: user-scope legacy backed up to" << s_lastBackupPath; + if (migrateOneScope(legacyUser, Settings::UserSettingFile) > 0) { + any = true; + } + maybeClearLegacy(legacyUser, Settings::UserSettingFile, "user-scope"); + } + + // System scope. + QSettings legacySystem(QSettings::NativeFormat, QSettings::SystemScope, kAppName, kAppName); + if (looksLikeLegacy(legacySystem)) { + const auto backupPath = Settings::SystemSettingFile + QStringLiteral(".legacy.bak"); + s_lastBackupPath = backupLegacy(legacySystem, backupPath); + qInfo().noquote() << "settings migration: system-scope legacy backed up to" << s_lastBackupPath; + if (migrateOneScope(legacySystem, Settings::SystemSettingFile) > 0) { + any = true; + } + maybeClearLegacy(legacySystem, Settings::SystemSettingFile, "system-scope"); + } + + // If legacy user-scope had systemScope=true, the user's preference is to + // run on system scope. Persist for the post-Settings hook to pick up. + if (legacyHadSystemScope) { + QSettings extra(extraFile(), QSettings::IniFormat); + extra.setValue(QStringLiteral("migration/preferSystemScope"), true); + extra.sync(); + } + + return any; +} + +} // namespace + +bool migrateIfNeeded() +{ + if (storedSchemaVersion() >= kCurrentSchemaVersion) { + return false; + } + + s_migrationRanThisLaunch = runLegacyMigration(); + writeSchemaVersion(kCurrentSchemaVersion); + return s_migrationRanThisLaunch; +} + +bool preferSystemScope() +{ + QSettings extra(extraFile(), QSettings::IniFormat); + return extra.value(QStringLiteral("migration/preferSystemScope"), false).toBool(); +} + +void showNoticeIfPending(QWidget *parent) +{ + if (notifiedSchemaVersion() >= kCurrentSchemaVersion) { + return; + } + if (!s_migrationRanThisLaunch) { + writeNotifiedVersion(kCurrentSchemaVersion); + return; + } + + QMessageBox::information( + parent, QObject::tr("Settings updated"), + QObject::tr("

We've migrated your settings to a new format used by this version of Synergy.

" + "

Your previous settings have been backed up to:

" + "

%1

" + "

If anything looks different, please contact us.

") + .arg(s_lastBackupPath) + ); + writeNotifiedVersion(kCurrentSchemaVersion); +} + +} // namespace synergy::gui::migration diff --git a/extra/src/lib/synergy/gui/SettingsMigration.h b/extra/src/lib/synergy/gui/SettingsMigration.h new file mode 100644 index 000000000..2c393c39f --- /dev/null +++ b/extra/src/lib/synergy/gui/SettingsMigration.h @@ -0,0 +1,49 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#pragma once + +class QWidget; + +namespace synergy::gui::migration { + +// Bumped each time a new migration is added. Stored as +// migration/schemaVersion in Synergy.extra.conf so we don't re-run. +constexpr int kCurrentSchemaVersion = 1; + +// Reads legacy-format keys (Synergy 1.x — QSettings::NativeFormat, +// user + system scope) and writes their new-format equivalents BEFORE +// upstream Settings is initialized, since Settings::cleanSettings() +// strips any key not in its allow-list. Backs up the original key set +// to .legacy.bak before any modification. Idempotent — no-op +// if migration/schemaVersion is already current. +// +// Returns true iff a migration ran this launch (caller can show a notice). +bool migrateIfNeeded(); + +// True iff the legacy user scope had `systemScope=true`. Caller can use +// this after Settings::instance() is up to call +// Settings::setSettingsFile(SystemSettingFile) and honor the user's +// scope preference across the upgrade. Persisted in Synergy.extra.conf +// as `migration/preferSystemScope` so subsequent launches keep honoring it. +bool preferSystemScope(); + +// Modal one-time notice shown after MainWindow is open. No-op if not +// pending. Marks migration/notifiedFor=schemaVersion when dismissed. +void showNoticeIfPending(QWidget *parent); + +} // namespace synergy::gui::migration diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index caccd4d1f..80c7edf02 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -28,8 +28,8 @@ namespace synergy::gui { const auto kProProductName = "Synergy 1 Pro"; const auto kBusinessProductName = "Synergy 1 Business"; -const auto kUrlApi = "https://synergyapp.io/api"; -const auto kUrlWebsite = QStringLiteral("https://%1").arg(kAppDomain); +const auto kUrlApi = "https://symless.com/synergy/api"; +const auto kUrlWebsite = QStringLiteral("https://synergyapp.io"); const auto kUrlSourceQuery = "source=gui"; const auto kLinkBuy = R"(Buy now)"; diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index d04d263ac..18552128a 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -17,7 +17,9 @@ #pragma once +#include "common/Settings.h" #include "synergy/gui/FeatureHandler.h" +#include "synergy/gui/SettingsMigration.h" #include "synergy/gui/license/LicenseHandler.h" #include @@ -29,10 +31,25 @@ class CoreProcess; namespace synergy::hooks { +// Runs before any Settings::value() call, so that legacy-format keys can be +// migrated to the new format before upstream's cleanSettings() wipes them. +inline void onPreInit() +{ + synergy::gui::migration::migrateIfNeeded(); + + // If the legacy user scope was running on system scope, switch the new + // Settings to system scope so the user's preference is honored across + // the upgrade. setSettingsFile() instantiates Settings; that's expected. + if (synergy::gui::migration::preferSystemScope()) { + Settings::setSettingsFile(Settings::SystemSettingFile); + } +} + inline void onMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) { LicenseHandler::instance().handleMainWindow(mainWindow, coreProcess); FeatureHandler::instance().handleMainWindow(mainWindow); + synergy::gui::migration::showNoticeIfPending(mainWindow); } inline bool onAppStart() diff --git a/src/apps/deskflow-gui/deskflow-gui.cpp b/src/apps/deskflow-gui/deskflow-gui.cpp index 1f5ab5feb..80ad3fe42 100644 --- a/src/apps/deskflow-gui/deskflow-gui.cpp +++ b/src/apps/deskflow-gui/deskflow-gui.cpp @@ -67,6 +67,10 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); +#ifdef SYNERGY_EXTRA_HEADER + synergy::hooks::onPreInit(); +#endif + // Ensure the I18N object is made before strings QTextStream(stdout) << "initial language: " << I18N::currentLanguage() << '\n'; diff --git a/src/lib/common/UrlConstants.h b/src/lib/common/UrlConstants.h index 1324d6ab7..536dea00d 100644 --- a/src/lib/common/UrlConstants.h +++ b/src/lib/common/UrlConstants.h @@ -20,7 +20,7 @@ const auto kUrlApp = QStringLiteral("https://%1").arg(kOrgDomain); const auto kUrlHelp = QStringLiteral("%1/help?%2").arg(kUrlApp, kUrlSourceQuery); const auto kUrlDownload = QStringLiteral("%1/download?%2").arg(kUrlApp, kUrlSourceQuery); -const auto kUrlUpdateCheck = QStringLiteral("https://api.%1/version").arg(kOrgDomain); +const auto kUrlUpdateCheck = QStringLiteral("https://api.symless.com/version").arg(kOrgDomain); #if defined(Q_OS_LINUX) const auto kUrlGnomeTrayFix = QStringLiteral("https://extensions.gnome.org/extension/615/appindicator-support/"); From 9db3ddd0bb7c6601a15056f3f3c51dec003ba2e1 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 10:16:42 +0100 Subject: [PATCH 017/144] refactor: enhance documentation and improve legacy settings migration logic --- .../src/lib/synergy/gui/SettingsMigration.cpp | 48 ++++++++----------- extra/src/lib/synergy/gui/SettingsMigration.h | 43 ++++++++++------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/extra/src/lib/synergy/gui/SettingsMigration.cpp b/extra/src/lib/synergy/gui/SettingsMigration.cpp index c11f4ddaf..00b5412d6 100644 --- a/extra/src/lib/synergy/gui/SettingsMigration.cpp +++ b/extra/src/lib/synergy/gui/SettingsMigration.cpp @@ -69,8 +69,6 @@ void writeNotifiedVersion(int version) ini.sync(); } -// Maps a legacy key + value to the new (key, value) pair, or returns -// nullopt to drop. Special cases (boolean → enum) handled inline. std::optional> mapKey(const QString &oldKey, const QVariant &value) { if (oldKey == "screenName") { @@ -155,20 +153,17 @@ std::optional> mapKey(const QString &oldKey, const return std::nullopt; } -// Sentinel keys that indicate a QSettings instance holds legacy-format -// data (Synergy 1.x, pre-Settings::* API). Detection by sentinel rather -// than file existence is necessary because on Linux the legacy NativeFormat -// path coincides with beta's IniFormat path. +// File-existence isn't a usable signal: on Linux the legacy NativeFormat +// path coincides with the new IniFormat path, so the file is always +// "present". Sentinel keys discriminate. bool looksLikeLegacy(const QSettings &legacy) { return legacy.contains(QStringLiteral("startedBefore")) || legacy.contains(QStringLiteral("groupServerChecked")) || legacy.contains(QStringLiteral("groupClientChecked")) || legacy.contains(kLegacySystemScopeKey); } -// Dumps every key/value from `legacy` into a sibling INI file. Cross-platform -// backup — works whether legacy lives in a file (Linux), a plist (macOS), -// or the registry (Windows), because we're dumping the in-memory contents, -// not file-copying. +// Dumps in-memory contents instead of file-copying so the backup format is +// uniform regardless of the legacy backend (file, plist, or registry). QString backupLegacy(const QSettings &legacy, const QString &destPath) { if (QFile::exists(destPath)) { @@ -182,8 +177,15 @@ QString backupLegacy(const QSettings &legacy, const QString &destPath) return destPath; } -// Returns true iff at least one key was migrated from `legacy` to the -// QSettings instance at `newPath`. +// Master had hardcoded behavior where beta exposes settings; align beta's +// defaults with master's runtime behavior so migrated users see no change. +void applyMasterCompatDefaults(const QString &newPath) +{ + QSettings newSettings(newPath, QSettings::IniFormat); + newSettings.setValue(Settings::Gui::AutoStartCore, true); + newSettings.sync(); +} + int migrateOneScope(QSettings &legacy, const QString &newPath) { QSettings newSettings(newPath, QSettings::IniFormat); @@ -205,19 +207,14 @@ int migrateOneScope(QSettings &legacy, const QString &newPath) bool s_migrationRanThisLaunch = false; QString s_lastBackupPath; -// True iff `legacy`'s storage backend points at the same on-disk file as -// `newPath`. On Linux this is the case (NativeFormat → INI at the same -// QStandardPaths location upstream's Settings uses); on macOS/Windows -// they're separate (plist / registry vs. our explicit IniFormat path). -// When same, we MUST NOT clear() the legacy storage — that would wipe -// the new keys we just wrote. +// On Linux, NativeFormat resolves to the same file beta's Settings uses, +// so clear() on the legacy QSettings would wipe the keys we just wrote. +// On macOS/Windows the storage backends are distinct (plist / registry). bool sharesPathWith(const QSettings &legacy, const QString &newPath) { const auto legacyCanonical = QFileInfo(legacy.fileName()).canonicalFilePath(); const auto newCanonical = QFileInfo(newPath).canonicalFilePath(); if (legacyCanonical.isEmpty() || newCanonical.isEmpty()) { - // canonicalFilePath returns empty if the file doesn't exist; fall back - // to absolute path comparison. return QFileInfo(legacy.fileName()).absoluteFilePath() == QFileInfo(newPath).absoluteFilePath(); } return legacyCanonical == newCanonical; @@ -226,10 +223,7 @@ bool sharesPathWith(const QSettings &legacy, const QString &newPath) void maybeClearLegacy(QSettings &legacy, const QString &newPath, const char *scopeLabel) { if (sharesPathWith(legacy, newPath)) { - qDebug( - "settings migration: %s legacy shares storage with new file, skipping clear (cleanSettings will handle)", - scopeLabel - ); + qDebug("settings migration: %s legacy shares storage with new file, skipping clear", scopeLabel); return; } if (!legacy.isWritable()) { @@ -245,7 +239,6 @@ bool runLegacyMigration() { bool any = false; - // User scope. QSettings legacyUser(QSettings::NativeFormat, QSettings::UserScope, kAppName, kAppName); const bool legacyHadSystemScope = legacyUser.value(kLegacySystemScopeKey, false).toBool(); if (looksLikeLegacy(legacyUser)) { @@ -253,11 +246,11 @@ bool runLegacyMigration() qInfo().noquote() << "settings migration: user-scope legacy backed up to" << s_lastBackupPath; if (migrateOneScope(legacyUser, Settings::UserSettingFile) > 0) { any = true; + applyMasterCompatDefaults(Settings::UserSettingFile); } maybeClearLegacy(legacyUser, Settings::UserSettingFile, "user-scope"); } - // System scope. QSettings legacySystem(QSettings::NativeFormat, QSettings::SystemScope, kAppName, kAppName); if (looksLikeLegacy(legacySystem)) { const auto backupPath = Settings::SystemSettingFile + QStringLiteral(".legacy.bak"); @@ -265,12 +258,11 @@ bool runLegacyMigration() qInfo().noquote() << "settings migration: system-scope legacy backed up to" << s_lastBackupPath; if (migrateOneScope(legacySystem, Settings::SystemSettingFile) > 0) { any = true; + applyMasterCompatDefaults(Settings::SystemSettingFile); } maybeClearLegacy(legacySystem, Settings::SystemSettingFile, "system-scope"); } - // If legacy user-scope had systemScope=true, the user's preference is to - // run on system scope. Persist for the post-Settings hook to pick up. if (legacyHadSystemScope) { QSettings extra(extraFile(), QSettings::IniFormat); extra.setValue(QStringLiteral("migration/preferSystemScope"), true); diff --git a/extra/src/lib/synergy/gui/SettingsMigration.h b/extra/src/lib/synergy/gui/SettingsMigration.h index 2c393c39f..5a1e07467 100644 --- a/extra/src/lib/synergy/gui/SettingsMigration.h +++ b/extra/src/lib/synergy/gui/SettingsMigration.h @@ -21,29 +21,38 @@ class QWidget; namespace synergy::gui::migration { -// Bumped each time a new migration is added. Stored as -// migration/schemaVersion in Synergy.extra.conf so we don't re-run. +/// Bumped each time a new migration is added. constexpr int kCurrentSchemaVersion = 1; -// Reads legacy-format keys (Synergy 1.x — QSettings::NativeFormat, -// user + system scope) and writes their new-format equivalents BEFORE -// upstream Settings is initialized, since Settings::cleanSettings() -// strips any key not in its allow-list. Backs up the original key set -// to .legacy.bak before any modification. Idempotent — no-op -// if migration/schemaVersion is already current. -// -// Returns true iff a migration ran this launch (caller can show a notice). +/** + * @brief Ports legacy-format settings (Synergy 1.x, both user and system + * scope, native QSettings backend) into the new Settings layout. + * + * Must run before Settings::instance() is constructed: upstream's + * cleanSettings() strips any key not in its allow-list, which would erase + * the legacy keys before this can read them. Idempotent — gated on + * migration/schemaVersion in Synergy.extra.conf. + * + * @return true if a migration was performed this launch. + */ bool migrateIfNeeded(); -// True iff the legacy user scope had `systemScope=true`. Caller can use -// this after Settings::instance() is up to call -// Settings::setSettingsFile(SystemSettingFile) and honor the user's -// scope preference across the upgrade. Persisted in Synergy.extra.conf -// as `migration/preferSystemScope` so subsequent launches keep honoring it. +/** + * @brief Whether the legacy user scope had `systemScope=true`, recorded + * during migration so subsequent launches keep honoring it. + * + * Caller should consult this after Settings::instance() is up and call + * Settings::setSettingsFile(SystemSettingFile) accordingly. + */ bool preferSystemScope(); -// Modal one-time notice shown after MainWindow is open. No-op if not -// pending. Marks migration/notifiedFor=schemaVersion when dismissed. +/** + * @brief Modal one-time notice shown after MainWindow is open. + * + * No-op unless a migration ran since the last call. Marks + * migration/notifiedFor=schemaVersion when dismissed so a new migration + * (bumped schema version) shows the popup again. + */ void showNoticeIfPending(QWidget *parent); } // namespace synergy::gui::migration From ad6df2f64f1d518dbe93fdca49ba3983e2ef34c2 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 10:18:20 +0100 Subject: [PATCH 018/144] feat: add Synergy.icns resource for macOS bundle --- .../Contents/Resources/{App.icns => Synergy.icns} | Bin src/apps/deskflow-gui/CMakeLists.txt | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename extra/deploy/mac/bundle/Contents/Resources/{App.icns => Synergy.icns} (100%) diff --git a/extra/deploy/mac/bundle/Contents/Resources/App.icns b/extra/deploy/mac/bundle/Contents/Resources/Synergy.icns similarity index 100% rename from extra/deploy/mac/bundle/Contents/Resources/App.icns rename to extra/deploy/mac/bundle/Contents/Resources/Synergy.icns diff --git a/src/apps/deskflow-gui/CMakeLists.txt b/src/apps/deskflow-gui/CMakeLists.txt index 5b67cd1b3..5bc2f1c02 100644 --- a/src/apps/deskflow-gui/CMakeLists.txt +++ b/src/apps/deskflow-gui/CMakeLists.txt @@ -32,7 +32,7 @@ elseif(BUILD_OSX_BUNDLE) @ONLY ) - set(platform_extra ../res/Deskflow.icns) + set(platform_extra ${CMAKE_SOURCE_DIR}/extra/deploy/mac/bundle/Contents/Resources/${target}.icns) set_source_files_properties(${platform_extra} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") endif() From 6e342b7ea427525a4a2f38b00b2c19be4b9f56bc Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 10:45:42 +0100 Subject: [PATCH 019/144] feat: implement settings scope management and migration logic --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 148 ++++++++++++------ extra/src/lib/synergy/gui/FeatureHandler.h | 7 +- .../src/lib/synergy/gui/SettingsMigration.cpp | 11 +- extra/src/lib/synergy/gui/SettingsMigration.h | 9 -- extra/src/lib/synergy/gui/SettingsScope.cpp | 120 ++++++++++++++ extra/src/lib/synergy/gui/SettingsScope.h | 54 +++++++ extra/src/lib/synergy/hooks/gui_hook.h | 14 +- 7 files changed, 286 insertions(+), 77 deletions(-) create mode 100644 extra/src/lib/synergy/gui/SettingsScope.cpp create mode 100644 extra/src/lib/synergy/gui/SettingsScope.h diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index e697e647e..a5b6f9d96 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -19,20 +19,27 @@ #include "common/Settings.h" #include "license/LicenseHandler.h" +#include "synergy/gui/SettingsMigration.h" +#include "synergy/gui/SettingsScope.h" #include "synergy/gui/TestSettings.h" #include "synergy/gui/constants.h" #include #include -#include #include #include +#include +#include +#include #include #include #include #include -#include +#include #include +#include +#include +#include using namespace synergy::gui; @@ -43,6 +50,11 @@ void FeatureHandler::handleMainWindow(QMainWindow *mainWindow) void FeatureHandler::handleAppStart() { + // wl-clipboard's focus-stealing behavior on common compositors makes it + // unfit for shipping; force the setting off on every launch regardless + // of how it got set. + Settings::setValue(Settings::Core::UseWlClipboard, false); + if (TestSettings::instance().isEnabled()) { addTestMenu(); } @@ -69,63 +81,103 @@ void FeatureHandler::addTestMenu() void FeatureHandler::handleSettings(QDialog *parent) const { - Q_UNUSED(parent); + if (parent == nullptr) { + return; + } + if (auto *wlClipboard = parent->findChild(QStringLiteral("widgetWlClipboard"))) { + wlClipboard->hide(); + } - // Synergy-specific settings UI (scope radios, etc.) lives in extra/. To be - // wired when the synergy widget-injection layer lands; until then, the - // scope-switching helper below is callable directly when needed. + const auto &licenseHandler = LicenseHandler::instance(); + if (licenseHandler.isEnabled() && !licenseHandler.license().isSettingsScopeAvailable()) { + return; + } + addScopeTab(parent); } -void FeatureHandler::switchScope(QDialog *parent, bool toSystemScope) +namespace { + +QString pathLabel(const QString &path) { - const auto &licenseHandler = LicenseHandler::instance(); - if (licenseHandler.isEnabled() && !licenseHandler.license().isSettingsScopeAvailable() && toSystemScope) { - qWarning("settings scope not available for this license tier"); - return; + if (QFileInfo::exists(path)) { + return QStringLiteral("%1").arg(path); } + return QStringLiteral("%1 %2") + .arg(path, QObject::tr("(not yet created)")); +} - const auto userScopeText = QStringLiteral("current user"); - const auto systemScopeText = QStringLiteral("all users"); - const auto from = toSystemScope ? userScopeText : systemScopeText; - const auto to = toSystemScope ? systemScopeText : userScopeText; - const auto result = QMessageBox::information( - parent, "Switch settings profile", - QString("Switching settings from %1 to %2 requires %3 to restart.\n\n" - "Would you like to restart the application now?") - .arg(from, to, QApplication::applicationName()), - QMessageBox::Yes | QMessageBox::Cancel - ); +} // namespace - if (result != QMessageBox::Yes) { +void FeatureHandler::addScopeTab(QDialog *parent) const +{ + auto *tabWidget = parent->findChild(); + if (tabWidget == nullptr) { + qWarning("scope tab: no QTabWidget in settings dialog, skipping"); return; } - const auto destFile = toSystemScope ? Settings::SystemSettingFile : Settings::UserSettingFile; - const auto sourceFile = toSystemScope ? Settings::UserSettingFile : Settings::SystemSettingFile; + auto *scopeTab = new QWidget(tabWidget); + auto *layout = new QVBoxLayout(scopeTab); - QSettings dest(destFile, QSettings::IniFormat); - if (dest.allKeys().isEmpty()) { - qDebug() << "destination settings empty, copying from:" << sourceFile; - QSettings source(sourceFile, QSettings::IniFormat); - for (const auto &key : source.allKeys()) { - dest.setValue(key, source.value(key)); - } - dest.sync(); - } + auto *intro = new QLabel( + QObject::tr("Choose where %1 stores its settings. Changes take effect when %1 restarts.") + .arg(QApplication::applicationName()), + scopeTab + ); + intro->setWordWrap(true); + layout->addWidget(intro); + layout->addSpacing(8); + + const auto mutedColor = scopeTab->palette().color(QPalette::Disabled, QPalette::WindowText).name(); + const auto helpStyle = QStringLiteral("color: %1;").arg(mutedColor); + const auto pathStyle = QStringLiteral("color: %1; font-size: 10pt;").arg(mutedColor); + + const auto buildOption = [&](const QString &title, const QString &help, const QString &path) { + auto *radio = new QRadioButton(title, scopeTab); + auto *helpLabel = new QLabel(help, scopeTab); + helpLabel->setWordWrap(true); + helpLabel->setIndent(22); + helpLabel->setStyleSheet(helpStyle); + auto *pathWidget = new QLabel(pathLabel(path), scopeTab); + pathWidget->setIndent(22); + pathWidget->setOpenExternalLinks(true); + pathWidget->setTextInteractionFlags(Qt::TextBrowserInteraction); + pathWidget->setWordWrap(true); + pathWidget->setStyleSheet(pathStyle); + + layout->addWidget(radio); + layout->addWidget(helpLabel); + layout->addWidget(pathWidget); + layout->addSpacing(14); + return radio; + }; + + auto *userRadio = buildOption( + QObject::tr("Current user"), + QObject::tr("Settings apply only to your user account on this computer."), + Settings::UserSettingFile + ); + auto *systemRadio = buildOption( + QObject::tr("All users"), + QObject::tr("Settings apply to every user on this computer. Requires administrator privileges."), + Settings::SystemSettingFile + ); - Settings::setSettingsFile(destFile); + const bool isSystem = (Settings::settingsFile() == Settings::SystemSettingFile); + userRadio->setChecked(!isSystem); + systemRadio->setChecked(isSystem); - // Hot-switching the active settings file mid-run touches enough state - // (signal/slot wiring, cached UI defaults) that a clean restart is the - // safest path. Inlined because upstream's `deskflow::gui::diagnostic::restart()` - // is defined in Diagnostic.cpp but not declared in the public header. - const auto program = QCoreApplication::applicationFilePath(); - auto arguments = QCoreApplication::arguments(); - if (const auto resetIndex = arguments.indexOf(QStringLiteral("--reset")); resetIndex != -1) { - arguments.remove(resetIndex); - } - qInfo("launching new process: %s", qPrintable(program)); - QProcess::startDetached(program, arguments); - qDebug("exiting current process"); - QCoreApplication::exit(); + layout->addStretch(); + + QObject::connect(systemRadio, &QRadioButton::toggled, parent, [parent, userRadio, systemRadio](bool toSystem) { + if (SettingsScope::switchTo(parent, toSystem)) { + return; + } + QSignalBlocker blockUser(userRadio); + QSignalBlocker blockSystem(systemRadio); + userRadio->setChecked(!toSystem); + systemRadio->setChecked(toSystem ? false : true); + }); + + tabWidget->addTab(scopeTab, QObject::tr("Scope")); } diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index 7dc7fe00a..fa59accf1 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -33,14 +33,9 @@ class FeatureHandler void handleAppStart(); void handleSettings(QDialog *parent) const; - // Switches the active settings file between system and user scope and - // restarts the application. The community removed system/user scope from - // upstream's UI; Synergy keeps the feature downstream as a license-tier - // capability. Caller is responsible for license-tier gating. - static void switchScope(QDialog *parent, bool toSystemScope); - private: void addTestMenu(); + void addScopeTab(QDialog *parent) const; QMainWindow *m_pMainWindow = nullptr; }; diff --git a/extra/src/lib/synergy/gui/SettingsMigration.cpp b/extra/src/lib/synergy/gui/SettingsMigration.cpp index 00b5412d6..cf3df9b77 100644 --- a/extra/src/lib/synergy/gui/SettingsMigration.cpp +++ b/extra/src/lib/synergy/gui/SettingsMigration.cpp @@ -19,6 +19,7 @@ #include "common/Constants.h" #include "common/Settings.h" +#include "synergy/gui/SettingsScope.h" #include #include @@ -264,9 +265,7 @@ bool runLegacyMigration() } if (legacyHadSystemScope) { - QSettings extra(extraFile(), QSettings::IniFormat); - extra.setValue(QStringLiteral("migration/preferSystemScope"), true); - extra.sync(); + SettingsScope::setPreferSystem(true); } return any; @@ -285,12 +284,6 @@ bool migrateIfNeeded() return s_migrationRanThisLaunch; } -bool preferSystemScope() -{ - QSettings extra(extraFile(), QSettings::IniFormat); - return extra.value(QStringLiteral("migration/preferSystemScope"), false).toBool(); -} - void showNoticeIfPending(QWidget *parent) { if (notifiedSchemaVersion() >= kCurrentSchemaVersion) { diff --git a/extra/src/lib/synergy/gui/SettingsMigration.h b/extra/src/lib/synergy/gui/SettingsMigration.h index 5a1e07467..89938a9fa 100644 --- a/extra/src/lib/synergy/gui/SettingsMigration.h +++ b/extra/src/lib/synergy/gui/SettingsMigration.h @@ -37,15 +37,6 @@ constexpr int kCurrentSchemaVersion = 1; */ bool migrateIfNeeded(); -/** - * @brief Whether the legacy user scope had `systemScope=true`, recorded - * during migration so subsequent launches keep honoring it. - * - * Caller should consult this after Settings::instance() is up and call - * Settings::setSettingsFile(SystemSettingFile) accordingly. - */ -bool preferSystemScope(); - /** * @brief Modal one-time notice shown after MainWindow is open. * diff --git a/extra/src/lib/synergy/gui/SettingsScope.cpp b/extra/src/lib/synergy/gui/SettingsScope.cpp new file mode 100644 index 000000000..edbaa18ec --- /dev/null +++ b/extra/src/lib/synergy/gui/SettingsScope.cpp @@ -0,0 +1,120 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#include "SettingsScope.h" + +#include "common/Constants.h" +#include "common/Settings.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace synergy::gui { + +namespace { + +const auto kPreferSystemKey = QStringLiteral("scope/preferSystem"); + +QString extraFile() +{ + return QStringLiteral("%1/%2.extra.conf").arg(Settings::UserDir, kAppName); +} + +} // namespace + +bool SettingsScope::preferSystem() +{ + QSettings extra(extraFile(), QSettings::IniFormat); + return extra.value(kPreferSystemKey, false).toBool(); +} + +void SettingsScope::setPreferSystem(bool prefer) +{ + QSettings extra(extraFile(), QSettings::IniFormat); + extra.setValue(kPreferSystemKey, prefer); + extra.sync(); +} + +bool SettingsScope::isSystemWritable() +{ + const QFileInfo info(Settings::SystemSettingFile); + if (info.exists()) { + return info.isWritable(); + } + QFileInfo dir(QFileInfo(Settings::SystemSettingFile).absolutePath()); + if (dir.exists()) { + return dir.isWritable(); + } + QDir cursor = dir.dir(); + while (!cursor.exists() && cursor.cdUp()) { + } + return QFileInfo(cursor.absolutePath()).isWritable(); +} + +bool SettingsScope::switchTo(QDialog *parent, bool toSystem) +{ + if (toSystem && !isSystemWritable()) { + QMessageBox::warning( + parent, QObject::tr("Cannot switch to 'All users' scope"), + QObject::tr( + "

%1 can't write to %2.

" + "

The 'All users' scope requires administrator privileges. " + "Run %1 as administrator/root to change settings here.

" + ) + .arg(QApplication::applicationName(), Settings::SystemSettingFile) + ); + return false; + } + + Settings::save(); + + const auto destFile = toSystem ? Settings::SystemSettingFile : Settings::UserSettingFile; + const auto sourceFile = toSystem ? Settings::UserSettingFile : Settings::SystemSettingFile; + + QSettings dest(destFile, QSettings::IniFormat); + if (dest.allKeys().isEmpty()) { + qDebug() << "destination settings empty, copying from:" << sourceFile; + QSettings source(sourceFile, QSettings::IniFormat); + for (const auto &key : source.allKeys()) { + dest.setValue(key, source.value(key)); + } + dest.sync(); + } + + setPreferSystem(toSystem); + + const auto choice = QMessageBox::information( + parent, QObject::tr("Restart required"), + QObject::tr( + "

Settings scope changes take effect when %1 restarts.

" + "

Quit %1 now? You'll need to launch it again from your applications menu.

" + ) + .arg(QApplication::applicationName()), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes + ); + if (choice == QMessageBox::Yes) { + QApplication::quit(); + } + return true; +} + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/SettingsScope.h b/extra/src/lib/synergy/gui/SettingsScope.h new file mode 100644 index 000000000..0882d369d --- /dev/null +++ b/extra/src/lib/synergy/gui/SettingsScope.h @@ -0,0 +1,54 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#pragma once + +class QDialog; + +namespace synergy::gui { + +class SettingsScope +{ +public: + /** + * @brief Whether the user has chosen the system-scope settings file. + * Persisted in Synergy.extra.conf so it survives restarts. + */ + static bool preferSystem(); + + /** + * @brief Records the user's scope preference. + */ + static void setPreferSystem(bool prefer); + + /** + * @brief Whether the current process can write to the system-scope settings + * directory. False on Linux without root, often false on Windows without admin. + */ + static bool isSystemWritable(); + + /** + * @brief Apply a scope switch: copy settings to the destination scope, + * persist the preference, prompt the user to restart. + * + * @return false if the switch was refused (e.g., system scope not writable + * on this user). Caller should revert any UI state on false. + */ + static bool switchTo(QDialog *parent, bool toSystem); +}; + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index 18552128a..ec71d693d 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -20,6 +20,7 @@ #include "common/Settings.h" #include "synergy/gui/FeatureHandler.h" #include "synergy/gui/SettingsMigration.h" +#include "synergy/gui/SettingsScope.h" #include "synergy/gui/license/LicenseHandler.h" #include @@ -37,11 +38,14 @@ inline void onPreInit() { synergy::gui::migration::migrateIfNeeded(); - // If the legacy user scope was running on system scope, switch the new - // Settings to system scope so the user's preference is honored across - // the upgrade. setSettingsFile() instantiates Settings; that's expected. - if (synergy::gui::migration::preferSystemScope()) { - Settings::setSettingsFile(Settings::SystemSettingFile); + // setSettingsFile() instantiates Settings; that's expected here. + if (synergy::gui::SettingsScope::preferSystem()) { + if (synergy::gui::SettingsScope::isSystemWritable()) { + Settings::setSettingsFile(Settings::SystemSettingFile); + } else { + qWarning("scope: system-scope no longer writable, falling back to user scope"); + synergy::gui::SettingsScope::setPreferSystem(false); + } } } From 2f856d01830fae365712320700f631417b217b41 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 10:55:07 +0100 Subject: [PATCH 020/144] refactor: remove unused icon files from resource configuration --- extra/src/apps/res/icons/16x16/padlock.png | Bin 450 -> 0 bytes extra/src/apps/res/icons/16x16/tray.png | Bin 905 -> 0 bytes extra/src/apps/res/icons/16x16/warning.png | Bin 693 -> 0 bytes extra/src/apps/res/icons/256x256/synergy.ico | Bin 101014 -> 0 bytes extra/src/apps/res/icons/64x64/folder.png | Bin 919 -> 0 bytes extra/src/apps/res/icons/64x64/tray-dark.png | Bin 1032 -> 0 bytes extra/src/apps/res/icons/64x64/tray-light.png | Bin 1764 -> 0 bytes extra/src/apps/res/icons/64x64/user-trash.png | Bin 3815 -> 0 bytes .../src/apps/res/icons/64x64/video-display.png | Bin 2579 -> 0 bytes extra/src/apps/res/image/placeholder.png | Bin 1605 -> 0 bytes extra/src/apps/res/image/setup-blocker.png | Bin 12202 -> 0 bytes extra/src/apps/res/image/welcome.png | Bin 11113 -> 0 bytes extra/src/apps/res/synergy.qrc | 13 +------------ 13 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 extra/src/apps/res/icons/16x16/padlock.png delete mode 100644 extra/src/apps/res/icons/16x16/tray.png delete mode 100644 extra/src/apps/res/icons/16x16/warning.png delete mode 100644 extra/src/apps/res/icons/256x256/synergy.ico delete mode 100644 extra/src/apps/res/icons/64x64/folder.png delete mode 100644 extra/src/apps/res/icons/64x64/tray-dark.png delete mode 100644 extra/src/apps/res/icons/64x64/tray-light.png delete mode 100644 extra/src/apps/res/icons/64x64/user-trash.png delete mode 100644 extra/src/apps/res/icons/64x64/video-display.png delete mode 100644 extra/src/apps/res/image/placeholder.png delete mode 100644 extra/src/apps/res/image/setup-blocker.png delete mode 100644 extra/src/apps/res/image/welcome.png diff --git a/extra/src/apps/res/icons/16x16/padlock.png b/extra/src/apps/res/icons/16x16/padlock.png deleted file mode 100644 index e313e97e96215bfc299305c9976f269fc5f3561f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}LV!<*>-+cbFU}QxzR&CZ<-E^# z8;%}5`ub$-<1H4y9!~u8_0r#;kN$qYeWZ!+#umlDzu#V2D*pQQ>ytf#$B!TX^W#HhKi{mlvrXo3J@5DDlYZVRTf27ce=wL-w)8pBE`^dHzhH<6PLd%e zM*1yKEn||myNg^Amx4Bsy7G|u~ m?A?|@T14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>9EP3g+M90oYVH%7i=;v0i_@+A>`b1 z6Wh;*Ip&^67N2z|a^itG5Cz@W9lNhPK-lfqEP)7xeQ+(13lRrNZ8~GT>6G!wdB#VV z!3AOBK&jIpwD}}R>Z)V&#dTI&&Vsc7r9cR(<*JqEu7ju|_HboQR~+ij9|+lZ#d_;G zxFC=LaX-Ya|Ns9p&z%PhNrsG)AirP+>7r9lt{kltVXXUir%6C8C#!qcmx(* ztrNNbL4^O}v(8x8Tn5A^} zv;X(!7M8tU`R=Wq>Y~dRBr>?H&gv#zz9m-l|MIql@Yu(Qz*%MxU zcRn0d+n{>;$+>k;_x@#mvb4ZMPFC8^GW6)BnI|kyb{8$Js$4o{*R}T9(f96Z&u(}B z{Oik~q>o?U-i`kM0u_BCfEpM)UHx3vIVCg! E0NHet4FCWD diff --git a/extra/src/apps/res/icons/16x16/warning.png b/extra/src/apps/res/icons/16x16/warning.png deleted file mode 100644 index 193e37733dcd9c3b4d38be7a6e77cc0a50117ba9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 693 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 bK^p|rsL&TegMu0r3ZQ-H7&#v*gUbF7M}Ogd diff --git a/extra/src/apps/res/icons/64x64/folder.png b/extra/src/apps/res/icons/64x64/folder.png deleted file mode 100644 index fb96ebdfe9f8ab1b0de979ff60afbcbee7764e3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 919 zcmV;I18Dq-P)@~0drDELIAGL9O(c600d`2O+f$vv5yP*Al(5=FmwZS1;7d*k5!9ER8bzGD`;3j=>~$Azz)a`h*YXxOwQ?y z6GMDU6FZRDo=-v~zC7@04s&KaiNYApryq{idIzu$y=~yJ(5lsa|Duf&pn?}$CqIv1 zYzt3CNhJCLpIlx*2_!1#RrV<-{kk3Pqv@VoD}UiUF4)YkE>MfG5T7sm72B(sMcEY}i#I@alDSJ&&; z&gWk{$U#*F-lOfH!zC;-Qh9+4)&F0e*3CLHAOd}CqgJaq$bbkOVKWvKA}~PfSIB?} z%!vrhi3rSz2+WBH%!vrh$y~ZI*21NZ#CNSs-x$?H5n1ti$UqK(whx0ZW^i{l&-PH! z$R^NO7T2n>xPaLOS~RTDDR{y*;?uDSer zdV$8a>84KfFt>s9v(X^ERgvNExPw6MgW);~)!}rCCBI3go5BPp)D& znbk`O%*8~3%GmR0^wDF`-KCjkSSYs7!IdhIqpurD9RAXUD(oQ8T%jtAgDW))lfd}W zwe@|8zVNOjEdo34hwz5kk3Ne72%+Fg6quMe?CL25LS2jpS6m=h97&18+HD9FTz{M6 z??VFpZOh=U6TeLohYxk3qQMmv7>_=GUQZIoD7a!>C_R2bV&WJ!2`W!ga4`=86YAa3 z0D6^cU7w`j3J3(o>s{*Y(XU+yEbv|0Ih|VXLVbh4@SgZ8?WC4^cM`*TchVIqdSLj; zCGNI}K;9E~6B^0m#A;!wcZZ~P;iH81fl}{2kkm{`A|z1i-GFetJBj-x>y^B%e zXij^z3>QT(P|aCBd~+|5$263Ji^@WgXYA&*OE(}Cfdl33arQx_;2|m7Hs0NvVA^07 tRMNrWmrs;?D`O1$7}T%a*7F{6@DD%Je2PPL64U?y002ovPDHLkV1kKHqu>Al diff --git a/extra/src/apps/res/icons/64x64/tray-dark.png b/extra/src/apps/res/icons/64x64/tray-dark.png deleted file mode 100644 index e02e133ad55828d25aa83142f77406be6abb197b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1032 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 diff --git a/extra/src/apps/res/image/setup-blocker.png b/extra/src/apps/res/image/setup-blocker.png deleted file mode 100644 index e1aa696a02fd2d583bd2ad6c481c4ecca93fae2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12202 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ⅆgDup0RLe8_yQces``f70aXlS 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 diff --git a/extra/src/apps/res/synergy.qrc b/extra/src/apps/res/synergy.qrc index 1b8d1e5e6..324f57f0b 100644 --- a/extra/src/apps/res/synergy.qrc +++ b/extra/src/apps/res/synergy.qrc @@ -1,17 +1,6 @@ - icons/16x16/tray.png - icons/64x64/video-display.png - icons/64x64/user-trash.png - icons/16x16/warning.png - icons/256x256/synergy.ico - image/logo-light.png image/logo-dark.png - icons/16x16/padlock.png - icons/64x64/tray-dark.png - icons/64x64/tray-light.png - image/welcome.png - icons/64x64/folder.png - image/placeholder.png + image/logo-light.png From 342ca06cdcec9b2148f3b17d88f6602b70992f2d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 11:01:06 +0100 Subject: [PATCH 021/144] feat: add example configuration file for Synergy test settings --- extra/Synergy.test.example.conf | 59 +++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 extra/Synergy.test.example.conf diff --git a/extra/Synergy.test.example.conf b/extra/Synergy.test.example.conf new file mode 100644 index 000000000..b8cadfa99 --- /dev/null +++ b/extra/Synergy.test.example.conf @@ -0,0 +1,59 @@ +# Synergy test settings — example/template. +# +# Copy to: +# ~/.config/Synergy/Synergy.test.conf (Linux) +# ~/Library/Synergy/Synergy.test.conf (macOS) +# %APPDATA%\Synergy\Synergy.test.conf (Windows) +# +# Sibling to Synergy.conf in the same dir. Edit this file instead of +# setting SYNERGY_TEST_* env vars — env vars are painful to thread into +# GUI launches on macOS/Windows. SYNERGY_TEST_* env vars still work and +# take precedence over this file when both are set, so CI/automation +# flows are unchanged. + +[test] + +# Master switch. When true: +# - License enforcement is on regardless of how the build was compiled. +# - The Test menu appears in the menu bar (fatal/critical-error triggers). +# - The other values in this file are honored. +# Set false (or leave the file absent) to disable test mode entirely. +# enabled=true + +# Override the URL the GUI POSTs to when activating a serial key. +# Default points at production. Use this to point at a local stack or +# staging environment during development. +# apiUrlActivate=http://localhost:4200/synergy/api/product/activate + +# Override the URL used for periodic license re-validation (the call +# the GUI makes after activation to confirm the license is still valid). +# Same default/use-case as apiUrlActivate. +# apiUrlCheck=http://localhost:4200/synergy/api/product/check + +# Pre-fill the activation dialog's serial key field. Hex-encoded payload +# matching the format the activation API expects. Test serial keys are +# distributed internally — do not hardcode customer-visible keys here. +# serialKey= + +# Time-travel testing. UNIX epoch seconds; if set, the GUI behaves as if +# the system clock were at this point in time when checking license +# expiry, grace periods, etc. Use to verify "expired", "expiring soon", +# and "in grace period" UI without waiting for real wall-clock time to +# elapse. +# startTime=1751324400 + +[features] + +# Extra debug logging from the GUI. Logs every license-check decision, +# scope-switch step, settings reload — useful when tracing a bug report. +# verbose=true + +# Skip the periodic remote license re-check. Use when developing offline +# or when the API endpoint is intentionally unavailable, so the GUI +# doesn't enter the grace-period flow. +# skipRemoteCheck=true + +# Treat expired licenses as still valid. Lets you start the core process +# with a known-expired test key without bypassing license enforcement +# entirely (useful for testing what features remain enabled vs blocked). +# allowExpiredLicenses=true From e51bb0bb70b48ed151d455b933636fef361ccbb9 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 11:12:17 +0100 Subject: [PATCH 022/144] refactor: update "Report a Bug" action text to "Get help" --- src/lib/gui/MainWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index 320309ecc..12b65aecc 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -1060,7 +1060,7 @@ void MainWindow::updateText() m_menuHelp->setTitle(tr("&Help")); m_actionClearSettings->setText(tr("Clear settings")); - m_actionReportBug->setText(tr("Report a Bug")); + m_actionReportBug->setText(tr("Get help")); m_actionMinimize->setText(tr("&Minimize to tray")); m_actionQuit->setText(tr("&Quit")); m_actionTrayQuit->setText(tr("&Quit")); From 2525d00b7524087eb448edb7eca78bf9e48088bb Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 11:14:51 +0100 Subject: [PATCH 023/144] refactor: replace "Report a Bug" text with "Get help" in multiple translation files --- translations/deskflow_es.ts | 8 ++++---- translations/deskflow_it.ts | 8 ++++---- translations/deskflow_ja.ts | 8 ++++---- translations/deskflow_ko.ts | 8 ++++---- translations/deskflow_ru.ts | 8 ++++---- translations/deskflow_zh_CN.ts | 8 ++++---- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/translations/deskflow_es.ts b/translations/deskflow_es.ts index d5eac4289..2a3fc508e 100644 --- a/translations/deskflow_es.ts +++ b/translations/deskflow_es.ts @@ -373,10 +373,6 @@ Do you want to connect to the server? Clear settings Borrar configuración
- - Report a Bug - Informar un error - &Minimize to tray &Minimizar a la bandeja @@ -465,6 +461,10 @@ Do you want to connect to the server? %1 Connection Error %1 Error de conexión + + Get help + Obtener ayuda + No IP Detected No se detectó ninguna IP diff --git a/translations/deskflow_it.ts b/translations/deskflow_it.ts index 881803de8..deaa67ee3 100644 --- a/translations/deskflow_it.ts +++ b/translations/deskflow_it.ts @@ -361,10 +361,6 @@ Vuoi connetterti al server? Clear settings Cancella impostazioni - - Report a Bug - Segnala un Bug - &Minimize to tray &Minimizza a icona @@ -453,6 +449,10 @@ Vuoi connetterti al server? %1 Connection Error Errore di connessione %1 + + Get help + Ottieni aiuto + No IP Detected Nessun IP rilevato diff --git a/translations/deskflow_ja.ts b/translations/deskflow_ja.ts index fce0b41cd..5fc3c2aab 100644 --- a/translations/deskflow_ja.ts +++ b/translations/deskflow_ja.ts @@ -421,6 +421,10 @@ Do you want to connect to the server? <p>Failed to connect to the server '%1'.</p><p>A Client with your name is already connected to the server.</p>Please ensure that you're using a unique name and that only a single instance of the client process is running.</p> <p>サーバー '%1' への接続に失敗しました。</p><p>同じ名前のクライアントがサーバーに接続済です。</p><p>名前の重複がないことと、クライアントプロセスが多重起動していない事を確認してください。</p> + + Get help + ヘルプを表示 + No IP Detected IPアドレスが見つかりません @@ -471,10 +475,6 @@ A bound IP is now invalid, you may need to restart the server. Clear settings 設定を消去 - - Report a Bug - バグレポート - &Minimize to tray トレイに最小化(&M) diff --git a/translations/deskflow_ko.ts b/translations/deskflow_ko.ts index efde4ed14..8850a3999 100644 --- a/translations/deskflow_ko.ts +++ b/translations/deskflow_ko.ts @@ -421,6 +421,10 @@ Do you want to connect to the server? <p>Failed to connect to the server '%1'.</p><p>A Client with your name is already connected to the server.</p>Please ensure that you're using a unique name and that only a single instance of the client process is running.</p> <p>서버 '%1'에 연결하지 못했습니다.</p><p>같은 이름의 클라이언트가 이미 서버에 연결되어 있습니다.</p><p>고유한 이름을 사용하고, 클라이언트 프로세스가 하나만 실행 중인지 확인하세요.</p> + + Get help + 도움 받기 + No IP Detected IP를 감지하지 못했습니다 @@ -471,10 +475,6 @@ A bound IP is now invalid, you may need to restart the server. Clear settings 설정 초기화 - - Report a Bug - 버그 신고 - &Minimize to tray 트레이로 최소화(&M) diff --git a/translations/deskflow_ru.ts b/translations/deskflow_ru.ts index 08216c8a6..c2fd20fb4 100644 --- a/translations/deskflow_ru.ts +++ b/translations/deskflow_ru.ts @@ -421,6 +421,10 @@ Do you want to connect to the server? <p>Failed to connect to the server '%1'.</p><p>A Client with your name is already connected to the server.</p>Please ensure that you're using a unique name and that only a single instance of the client process is running.</p> <p>Не удалось подключиться к серверу '%1'.</p><p>Клиент с таким именем уже подключен к серверу.</p>Убедитесь, что вы используете уникальное имя и запущен только один процесс клиента.</p> + + Get help + Получить помощь + No IP Detected IP-адрес не обнаружен @@ -471,10 +475,6 @@ A bound IP is now invalid, you may need to restart the server. Clear settings Сбросить настройки - - Report a Bug - Сообщить об ошибке - &Minimize to tray &Свернуть в трей diff --git a/translations/deskflow_zh_CN.ts b/translations/deskflow_zh_CN.ts index 7ca728833..979a23525 100644 --- a/translations/deskflow_zh_CN.ts +++ b/translations/deskflow_zh_CN.ts @@ -421,6 +421,10 @@ Do you want to connect to the server? <p>Failed to connect to the server '%1'.</p><p>A Client with your name is already connected to the server.</p>Please ensure that you're using a unique name and that only a single instance of the client process is running.</p> <p>连接到服务器“%1”失败。</p><p>一个同名的客户端已连接到服务器。</p>请确保您使用的名称唯一,且只有一个客户端进程实例在运行。</p> + + Get help + 获取帮助 + No IP Detected 未检测到 IP @@ -471,10 +475,6 @@ A bound IP is now invalid, you may need to restart the server. Clear settings 清除设置 - - Report a Bug - 报告 Bug - &Minimize to tray 最小化到托盘(&M) From 794481132071b63f07b8cf9b6473ce347586448f Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Sun, 10 May 2026 19:32:48 +0100 Subject: [PATCH 024/144] feat: add synergy icons to resource configuration --- extra/src/apps/res/synergy.qrc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extra/src/apps/res/synergy.qrc b/extra/src/apps/res/synergy.qrc index 324f57f0b..11090b359 100644 --- a/extra/src/apps/res/synergy.qrc +++ b/extra/src/apps/res/synergy.qrc @@ -2,5 +2,9 @@ image/logo-dark.png image/logo-light.png + synergy.svg + synergy.svg + synergy.svg + synergy.svg From c2f8a505a57d811bb4420c9a68fd98f29b400f72 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 11 May 2026 11:18:51 +0100 Subject: [PATCH 025/144] feat: add licensing option to test settings for activation flow --- extra/Synergy.test.example.conf | 7 ++++++- extra/src/lib/synergy/gui/TestSettings.cpp | 2 ++ extra/src/lib/synergy/gui/TestSettings.h | 11 ++++++++++- extra/src/lib/synergy/gui/license/license_utils.cpp | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/extra/Synergy.test.example.conf b/extra/Synergy.test.example.conf index b8cadfa99..0e54c71e3 100644 --- a/extra/Synergy.test.example.conf +++ b/extra/Synergy.test.example.conf @@ -14,12 +14,17 @@ [test] # Master switch. When true: -# - License enforcement is on regardless of how the build was compiled. # - The Test menu appears in the menu bar (fatal/critical-error triggers). # - The other values in this file are honored. # Set false (or leave the file absent) to disable test mode entirely. # enabled=true +# Force the license activation flow on. Useful for verifying activation +# behavior in development; left off by default so test mode (Test menu, +# URL overrides, etc.) can be on without forcing license prompts every +# launch. +# licensing=true + # Override the URL the GUI POSTs to when activating a serial key. # Default points at production. Use this to point at a local stack or # staging environment during development. diff --git a/extra/src/lib/synergy/gui/TestSettings.cpp b/extra/src/lib/synergy/gui/TestSettings.cpp index 4241387e3..813e2b450 100644 --- a/extra/src/lib/synergy/gui/TestSettings.cpp +++ b/extra/src/lib/synergy/gui/TestSettings.cpp @@ -54,6 +54,7 @@ TestSettings::TestSettings() void TestSettings::load() { m_enabled = false; + m_licensing = false; m_serialKey.clear(); m_apiUrlActivate.clear(); m_apiUrlCheck.clear(); @@ -73,6 +74,7 @@ void TestSettings::load() qDebug().noquote() << "test settings file present but test/enabled=false:" << m_fileName; return; } + m_licensing = ini.value(QStringLiteral("test/licensing"), false).toBool(); m_serialKey = ini.value(QStringLiteral("test/serialKey")).toString(); m_apiUrlActivate = ini.value(QStringLiteral("test/apiUrlActivate")).toString(); m_apiUrlCheck = ini.value(QStringLiteral("test/apiUrlCheck")).toString(); diff --git a/extra/src/lib/synergy/gui/TestSettings.h b/extra/src/lib/synergy/gui/TestSettings.h index 95150d442..c668294f5 100644 --- a/extra/src/lib/synergy/gui/TestSettings.h +++ b/extra/src/lib/synergy/gui/TestSettings.h @@ -33,13 +33,21 @@ class TestSettings public: static TestSettings &instance(); - // True iff the test config file exists and `[test]/enabled=true`. The + // True when the test config file exists and `[test]/enabled=true`. The // value is cached at construction; call reload() to re-read. bool isEnabled() const { return m_enabled; } + // True when test mode is on AND `[test]/licensing=true`. Drives the + // license activation flow when set; off by default so test mode can be + // on (Test menu, URL overrides, etc.) without forcing license prompts. + bool isLicensingEnabled() const + { + return m_enabled && m_licensing; + } + // Test overrides. Each accessor returns the env var when set, otherwise // the file value when isEnabled(), otherwise empty / 0. QString serialKey() const; @@ -80,6 +88,7 @@ class TestSettings QString m_fileName; bool m_enabled = false; + bool m_licensing = false; QString m_serialKey; QString m_apiUrlActivate; QString m_apiUrlCheck; diff --git a/extra/src/lib/synergy/gui/license/license_utils.cpp b/extra/src/lib/synergy/gui/license/license_utils.cpp index 0d57b0ba9..e14767648 100644 --- a/extra/src/lib/synergy/gui/license/license_utils.cpp +++ b/extra/src/lib/synergy/gui/license/license_utils.cpp @@ -45,7 +45,7 @@ bool isActivationEnabled() if (strToTrue(qEnvironmentVariable("SYNERGY_ENABLE_ACTIVATION"))) { return true; } - if (synergy::gui::TestSettings::instance().isEnabled()) { + if (synergy::gui::TestSettings::instance().isLicensingEnabled()) { return true; } return kEnableActivation; From 979635e12271ce5cd1709365734543c0b846a6d5 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 11 May 2026 12:03:54 +0100 Subject: [PATCH 026/144] feat: add build options for GUI and core flavor, update window title handling --- CMakeLists.txt | 6 ++- extra/CMakeLists.txt | 2 +- extra/Synergy.test.example.conf | 10 ++--- extra/cmake/Synergy.cmake | 23 ++++++++++++ extra/src/lib/synergy/CMakeLists.txt | 4 +- extra/src/lib/synergy/build_config.h | 35 ++++++++++++++++++ .../src/lib/synergy/gui/SettingsMigration.cpp | 2 +- extra/src/lib/synergy/gui/SettingsMigration.h | 2 +- extra/src/lib/synergy/gui/TestSettings.h | 2 +- extra/src/lib/synergy/gui/constants.h | 7 +--- extra/src/lib/synergy/gui/dev_mode.h | 37 +++++++++++++++++++ .../synergy/gui/license/LicenseHandler.cpp | 4 +- extra/src/lib/synergy/hooks/gui_hook.h | 7 ++++ extra/src/lib/synergy/license/License.cpp | 1 - extra/src/lib/synergy/license/Product.cpp | 3 +- src/apps/CMakeLists.txt | 4 +- src/lib/CMakeLists.txt | 4 +- src/lib/gui/MainWindow.cpp | 3 ++ 18 files changed, 133 insertions(+), 23 deletions(-) create mode 100644 extra/src/lib/synergy/build_config.h create mode 100644 extra/src/lib/synergy/gui/dev_mode.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a273c9f4..f8b3c18dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,8 +164,12 @@ endif() add_subdirectory(docs) +option(BUILD_GUI "Build GUI" ON) + # build translations before source, I18N unit tests fail if they are missing -add_subdirectory(translations) +if(BUILD_GUI) + add_subdirectory(translations) +endif() add_subdirectory(src) add_subdirectory(extra) diff --git a/extra/CMakeLists.txt b/extra/CMakeLists.txt index b7a9325b3..24207f25d 100644 --- a/extra/CMakeLists.txt +++ b/extra/CMakeLists.txt @@ -16,7 +16,7 @@ if(TARGET gui) target_compile_definitions(gui PRIVATE SYNERGY_EXTRA_HEADER) target_include_directories(gui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/lib) # Cascade synergy-gui to anything that links gui (the synergy executable, - # but also unit tests that link libgui.a — they pull in MainWindow.cpp.o + # but also unit tests that link libgui.a, which pull in MainWindow.cpp.o # which now references synergy::hooks::* symbols). Plain signature matches # upstream's gui CMakeLists. target_link_libraries(gui synergy-gui) diff --git a/extra/Synergy.test.example.conf b/extra/Synergy.test.example.conf index 0e54c71e3..88853b51a 100644 --- a/extra/Synergy.test.example.conf +++ b/extra/Synergy.test.example.conf @@ -1,4 +1,4 @@ -# Synergy test settings — example/template. +# Synergy test settings: example/template. # # Copy to: # ~/.config/Synergy/Synergy.test.conf (Linux) @@ -6,8 +6,8 @@ # %APPDATA%\Synergy\Synergy.test.conf (Windows) # # Sibling to Synergy.conf in the same dir. Edit this file instead of -# setting SYNERGY_TEST_* env vars — env vars are painful to thread into -# GUI launches on macOS/Windows. SYNERGY_TEST_* env vars still work and +# setting SYNERGY_TEST_* env vars (which are painful to thread into +# GUI launches on macOS/Windows). SYNERGY_TEST_* env vars still work and # take precedence over this file when both are set, so CI/automation # flows are unchanged. @@ -37,7 +37,7 @@ # Pre-fill the activation dialog's serial key field. Hex-encoded payload # matching the format the activation API expects. Test serial keys are -# distributed internally — do not hardcode customer-visible keys here. +# distributed internally. Do not hardcode customer-visible keys here. # serialKey= # Time-travel testing. UNIX epoch seconds; if set, the GUI behaves as if @@ -50,7 +50,7 @@ [features] # Extra debug logging from the GUI. Logs every license-check decision, -# scope-switch step, settings reload — useful when tracing a bug report. +# scope-switch step, settings reload. Useful when tracing a bug report. # verbose=true # Skip the periodic remote license re-check. Use when developing offline diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index 9f9bc83e5..1f94aa4a5 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -11,6 +11,28 @@ set(CMAKE_PROJECT_REV_FQDN "com.symless.synergy") set(CMAKE_PROJECT_DOMAIN "synergyapp.io") set(CMAKE_PROJECT_HOMEPAGE_URL "https://synergyapp.io") +# Display brand. "Synergy 1" is the default user-facing name (window title, +# About dialog). When building as the Core, flip to "Synergy Core" so the +# same codebase ships under a different product label. +# Distinct from CMAKE_PROJECT_PROPER_NAME, which stays "Synergy" to keep file paths +# (~/.config/Synergy/, Synergy.conf) and Windows globals space-free. +option(SYNERGY_CORE_FLAVOR "Build as Synergy Core" OFF) +if(SYNERGY_CORE_FLAVOR) + set(SYNERGY_DISPLAY_NAME "Synergy Core") +else() + set(SYNERGY_DISPLAY_NAME "Synergy 1") +endif() +add_compile_definitions(SYNERGY_DISPLAY_NAME="${SYNERGY_DISPLAY_NAME}") + +# Core flavor seeds headless-build defaults (no GUI, no tests, no installer). +# No `FORCE` on the cache writes: the seed only fills empty slots, so a user +# passing -DBUILD_GUI=ON alongside the flavor flag still wins. +if(SYNERGY_CORE_FLAVOR) + set(BUILD_GUI OFF CACHE BOOL "Build GUI") + set(BUILD_TESTS OFF CACHE BOOL "Build tests") + set(BUILD_INSTALLER OFF CACHE BOOL "Build installer") +endif() + # Don't run unit tests as part of the build. Devs can opt back in with # -DSKIP_BUILD_TESTS=OFF if they want post-build ctest invocation. set(SKIP_BUILD_TESTS ON CACHE BOOL "Skip build time test") @@ -59,6 +81,7 @@ elseif(SYNERGY_VERSION_SNAPSHOT) else() set(CMAKE_PROJECT_VERSION "${_base}-dev") set(CMAKE_PROJECT_VERSION_TWEAK ${_rev_count}) + add_compile_definitions(SYNERGY_VERSION_DEV) endif() unset(_base) unset(_git_describe) diff --git a/extra/src/lib/synergy/CMakeLists.txt b/extra/src/lib/synergy/CMakeLists.txt index a59c853c6..b6db05c17 100644 --- a/extra/src/lib/synergy/CMakeLists.txt +++ b/extra/src/lib/synergy/CMakeLists.txt @@ -14,4 +14,6 @@ # along with this program. If not, see . add_subdirectory(license) -add_subdirectory(gui) +if(BUILD_GUI) + add_subdirectory(gui) +endif() diff --git a/extra/src/lib/synergy/build_config.h b/extra/src/lib/synergy/build_config.h new file mode 100644 index 000000000..84492ddc2 --- /dev/null +++ b/extra/src/lib/synergy/build_config.h @@ -0,0 +1,35 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#pragma once + +// Single point of translation from cmake-defined preprocessor macros to +// typed C++ constants. Code elsewhere should consume these constants and +// never reference SYNERGY_* macros directly. Keeps the macro surface +// contained and makes the build-flavor knobs greppable. + +namespace synergy { + +constexpr auto kDisplayName = SYNERGY_DISPLAY_NAME; + +#ifdef SYNERGY_VERSION_DEV +constexpr bool kIsDevBuild = true; +#else +constexpr bool kIsDevBuild = false; +#endif + +} // namespace synergy diff --git a/extra/src/lib/synergy/gui/SettingsMigration.cpp b/extra/src/lib/synergy/gui/SettingsMigration.cpp index cf3df9b77..c73156f7e 100644 --- a/extra/src/lib/synergy/gui/SettingsMigration.cpp +++ b/extra/src/lib/synergy/gui/SettingsMigration.cpp @@ -201,7 +201,7 @@ int migrateOneScope(QSettings &legacy, const QString &newPath) } } newSettings.sync(); - qInfo("settings migration: scope %s — migrated %d keys, dropped %d obsolete", qPrintable(newPath), migrated, dropped); + qInfo("settings migration: scope %s, migrated %d keys, dropped %d obsolete", qPrintable(newPath), migrated, dropped); return migrated; } diff --git a/extra/src/lib/synergy/gui/SettingsMigration.h b/extra/src/lib/synergy/gui/SettingsMigration.h index 89938a9fa..ace999331 100644 --- a/extra/src/lib/synergy/gui/SettingsMigration.h +++ b/extra/src/lib/synergy/gui/SettingsMigration.h @@ -30,7 +30,7 @@ constexpr int kCurrentSchemaVersion = 1; * * Must run before Settings::instance() is constructed: upstream's * cleanSettings() strips any key not in its allow-list, which would erase - * the legacy keys before this can read them. Idempotent — gated on + * the legacy keys before this can read them. Idempotent, gated on * migration/schemaVersion in Synergy.extra.conf. * * @return true if a migration was performed this launch. diff --git a/extra/src/lib/synergy/gui/TestSettings.h b/extra/src/lib/synergy/gui/TestSettings.h index c668294f5..7d85745e6 100644 --- a/extra/src/lib/synergy/gui/TestSettings.h +++ b/extra/src/lib/synergy/gui/TestSettings.h @@ -55,7 +55,7 @@ class TestSettings QString apiUrlCheck() const; qint64 startTimeEpochSecs() const; - // Feature toggles read from the [features] section. File-only — no env + // Feature toggles read from the [features] section. File-only, no env // var fallback for these (no precedent). bool verbose() const { diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 80c7edf02..8f6f7109c 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -17,17 +17,12 @@ #pragma once -#include "common/Constants.h" - #include -#include +#include namespace synergy::gui { -const auto kProProductName = "Synergy 1 Pro"; -const auto kBusinessProductName = "Synergy 1 Business"; - const auto kUrlApi = "https://symless.com/synergy/api"; const auto kUrlWebsite = QStringLiteral("https://synergyapp.io"); const auto kUrlSourceQuery = "source=gui"; diff --git a/extra/src/lib/synergy/gui/dev_mode.h b/extra/src/lib/synergy/gui/dev_mode.h new file mode 100644 index 000000000..9b5953299 --- /dev/null +++ b/extra/src/lib/synergy/gui/dev_mode.h @@ -0,0 +1,37 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#pragma once + +#include "common/VersionInfo.h" +#include "synergy/build_config.h" + +#include +#include + +namespace synergy::gui { + +inline QString titleWithDevSuffix(const QString &productName) +{ + if constexpr (synergy::kIsDevBuild) { + return QStringLiteral("%1 - Developer build - v%2").arg(productName, kDisplayVersion); + } else { + return productName; + } +} + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 5eb9b6527..4023e1216 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -23,6 +23,7 @@ #include "dialogs/UpgradeDialog.h" #include "gui/core/CoreProcess.h" #include "synergy/gui/constants.h" +#include "synergy/gui/dev_mode.h" #include "synergy/gui/license/license_utils.h" #include "synergy/gui/styles.h" #include "synergy/license/Product.h" @@ -116,7 +117,6 @@ bool LicenseHandler::handleAppStart() if (!m_enabled) { qDebug("license handler disabled, skipping start handler"); - m_pMainWindow->setWindowTitle(kAppName); return true; } @@ -288,7 +288,7 @@ void LicenseHandler::updateWindowTitle() const { const auto productName = QString::fromStdString(m_license.productName()); qDebug("updating main window title: %s", qPrintable(productName)); - m_pMainWindow->setWindowTitle(productName); + m_pMainWindow->setWindowTitle(synergy::gui::titleWithDevSuffix(productName)); } const synergy::license::License &LicenseHandler::license() const diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index ec71d693d..876f74b68 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -18,9 +18,11 @@ #pragma once #include "common/Settings.h" +#include "synergy/build_config.h" #include "synergy/gui/FeatureHandler.h" #include "synergy/gui/SettingsMigration.h" #include "synergy/gui/SettingsScope.h" +#include "synergy/gui/dev_mode.h" #include "synergy/gui/license/LicenseHandler.h" #include @@ -56,6 +58,11 @@ inline void onMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *co synergy::gui::migration::showNoticeIfPending(mainWindow); } +inline void onTitleApplied(QMainWindow *mainWindow) +{ + mainWindow->setWindowTitle(synergy::gui::titleWithDevSuffix(synergy::kDisplayName)); +} + inline bool onAppStart() { FeatureHandler::instance().handleAppStart(); diff --git a/extra/src/lib/synergy/license/License.cpp b/extra/src/lib/synergy/license/License.cpp index c51c1c10f..31fdac70f 100644 --- a/extra/src/lib/synergy/license/License.cpp +++ b/extra/src/lib/synergy/license/License.cpp @@ -22,7 +22,6 @@ #include "synergy/license/parse_serial_key.h" #include -#include using namespace std::chrono; diff --git a/extra/src/lib/synergy/license/Product.cpp b/extra/src/lib/synergy/license/Product.cpp index 8d9c3905b..efbb9b8f3 100644 --- a/extra/src/lib/synergy/license/Product.cpp +++ b/extra/src/lib/synergy/license/Product.cpp @@ -18,10 +18,11 @@ #include #include "Product.h" +#include "synergy/build_config.h" using SKE = Product::SerialKeyEditionID; -const char *const kLicensedProductName = "Synergy 1"; +const char *const kLicensedProductName = synergy::kDisplayName; const std::string SKE::Pro = "pro"; const std::string SKE::Basic = "basic"; diff --git a/src/apps/CMakeLists.txt b/src/apps/CMakeLists.txt index 62e56def7..f0bb44ec0 100644 --- a/src/apps/CMakeLists.txt +++ b/src/apps/CMakeLists.txt @@ -49,4 +49,6 @@ endif() add_subdirectory(deskflow-core) add_subdirectory(deskflow-daemon) #Only used on windows -add_subdirectory(deskflow-gui) +if(BUILD_GUI) + add_subdirectory(deskflow-gui) +endif() diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index d007a8900..f114faf59 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -13,4 +13,6 @@ add_subdirectory(mt) add_subdirectory(net) add_subdirectory(platform) add_subdirectory(server) -add_subdirectory(gui) +if(BUILD_GUI) + add_subdirectory(gui) +endif() diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index 12b65aecc..74f9e9549 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -716,6 +716,9 @@ void MainWindow::applyConfig() } else { setWindowTitle(kAppName); } +#ifdef SYNERGY_EXTRA_HEADER + synergy::hooks::onTitleApplied(this); +#endif if (const auto host = Settings::value(Settings::Client::RemoteHost).toString(); !host.isEmpty()) ui->lineHostname->setText(host); From 8663a7b9b9a7733a8036a280f36b80837a900a6a Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 1 Jun 2026 23:09:09 +0100 Subject: [PATCH 027/144] Refactor versioning system to use a single source of truth - Removed hardcoded version numbers from CMakeLists.txt and replaced them with dynamic values sourced from extra/cmake/Version.cmake. - Introduced extra/cmake/SaveVersion.cmake to handle version file generation during CI. - Updated extra/cmake/Synergy.cmake to compute versioning based on the new structure, ensuring consistency between build and CI. - Added new versioning logic to manage release, snapshot, and development versions based on Git tags and commit counts. --- .github/actions/get-version/action.yml | 29 +- .github/workflows/ci-comment.yml | 2 +- .github/workflows/ci.yml | 789 +++++++++++++++++++ .github/workflows/continuous-integration.yml | 467 ----------- CMakeLists.txt | 12 +- extra/cmake/SaveVersion.cmake | 23 + extra/cmake/Synergy.cmake | 45 +- extra/cmake/Version.cmake | 52 ++ 8 files changed, 905 insertions(+), 514 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/continuous-integration.yml create mode 100644 extra/cmake/SaveVersion.cmake create mode 100644 extra/cmake/Version.cmake diff --git a/.github/actions/get-version/action.yml b/.github/actions/get-version/action.yml index 9e80a8574..d47a5babd 100644 --- a/.github/actions/get-version/action.yml +++ b/.github/actions/get-version/action.yml @@ -1,15 +1,26 @@ name: "Get version" -description: "Gets the version number from GitHub environment" +description: > + Computes the build version via extra/cmake/SaveVersion.cmake — runs the same + synergy_compute_version() the build uses, so the CI-side version (used in + package filenames, S3 paths, release titles) matches what binaries report. + +outputs: + version: + description: "Synergy version (e.g. 1.21.0, 1.21.0-snapshot+r42, 1.21.0-dev)" + value: ${{ steps.compute.outputs.version }} runs: using: "composite" - steps: - - run: | - echo "DESKFLOW_VERSION=$(git describe --long | sed 's/-/./g;s/v//g' | grep -o '^[0-9]*.[0-9]*.[0-9]*.[0-9]*')">> $GITHUB_ENV - if [[ "$GITHUB_REF" == *"tags/v"* ]]; then - echo "DESKFLOW_PACKAGE_VERSION=$(echo $GITHUB_REF | sed 's,refs/tags/v,,g')">> $GITHUB_ENV - else - echo "DESKFLOW_PACKAGE_VERSION=continuous">> $GITHUB_ENV - fi + - id: compute shell: bash + run: | + mkdir -p build + cmake \ + -DVERSION_FILE=build/VERSION \ + -DSYNERGY_VERSION_RELEASE=${SYNERGY_VERSION_RELEASE:-false} \ + -DSYNERGY_VERSION_SNAPSHOT=${SYNERGY_VERSION_SNAPSHOT:-false} \ + -P extra/cmake/SaveVersion.cmake + version=$(cat build/VERSION) + echo "Computed version: $version" + echo "version=$version" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/ci-comment.yml b/.github/workflows/ci-comment.yml index b9a02d99b..6a4d2a954 100644 --- a/.github/workflows/ci-comment.yml +++ b/.github/workflows/ci-comment.yml @@ -11,7 +11,7 @@ name: CI comment on: workflow_run: - workflows: ["Continuous Integration"] + workflows: ["CI"] types: - completed diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..9a25a1b58 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,789 @@ +# All-in-one continuous integration (CI) workflow. +# Runs on all platforms (Windows, macOS, and Linux) +# for all events (pull request, release, and schedule). + +name: CI + +on: + workflow_dispatch: + inputs: + package-type: + description: "dev (commit SHA), snapshot (nightly), release (clean semver)" + required: false + default: "dev" + type: choice + options: + - dev + - snapshot + - release + aws-s3-uri: + description: "Override AWS S3 target (e.g. s3://bucket/path)" + required: false + type: string + push: + branches: [beta] + pull_request: + release: + types: [published] + schedule: + - cron: "0 5 * * *" # 5am UTC nightly snapshot + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + GIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + PACKAGE_PREFIX: ${{ vars.SYNERGY_PACKAGE_PREFIX || 'synergy' }} + PACKAGE_PATH: ./dist + CMAKE_CONFIGURE: "cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DSKIP_BUILD_TESTS=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON" + + # Release shape, propagated to cmake (Synergy.cmake reads these as options). + # Release events + explicit "release" workflow_dispatch input → release build. + # Push to beta + schedule + explicit "snapshot" dispatch → snapshot build. + # PRs and "dev" dispatch fall through to dev (the cmake default). + SYNERGY_VERSION_RELEASE: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.package-type == 'release') }} + SYNERGY_VERSION_SNAPSHOT: ${{ github.event_name == 'push' || github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.package-type == 'snapshot') }} + +jobs: + # Quality gate to allow PR merge, used in the branch protection rules. + ci-passed: + runs-on: ubuntu-latest + needs: [build-windows, build-macos, build-linux, test-results, unix, flatpak] + if: always() + + steps: + - name: Check all steps passed + run: | + if [[ ${{needs.build-windows.result}} == 'success' \ + && ${{needs.build-macos.result}} == 'success' \ + && ${{needs.build-linux.result}} == 'success' \ + && ${{needs.test-results.result}} == 'success' \ + && ${{needs.unix.result}} == 'success' \ + && ${{needs.flatpak.result}} == 'success' ]]; then + echo "✅ CI passed" > $GITHUB_STEP_SUMMARY + else + exit 1 + fi + + # Summary of test results, combined from test result artifacts. + # Runs even if the tests fail to provide a summary of the failures. + test-results: + needs: [build-windows, build-macos, build-linux] + if: always() && (needs.build-windows.result != 'skipped' || needs.build-macos.result != 'skipped' || needs.build-linux.result != 'skipped') + + runs-on: ubuntu-slim + timeout-minutes: 5 + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Test summary + uses: ./.github/actions/test-summary + + lint-clang: + runs-on: ubuntu-slim + timeout-minutes: 5 + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Lint Checker + uses: ./.github/actions/lint-clang + + analyze-valgrind: + needs: lint-clang + if: ${{ github.event_name == 'pull_request' }} + uses: ./.github/workflows/valgrind-analysis.yml + + build-windows: + needs: lint-clang + name: ${{ matrix.target.name }} + runs-on: ${{ matrix.target.runs-on }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + target: + - name: "windows-2022-x64" + runs-on: "windows-2022" + vcpkg-triplet: x64-windows-release + arch: "amd64" + qt-version: 6.10.2 + + - name: "windows-2022-arm64" + runs-on: "windows-11-arm" + vcpkg-triplet: arm64-windows + arch: "arm64" + qt-version: 6.10.2 + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + # Effectively runs vcvarsall.bat. VC++ itself is already pre-installed on the runner. + - name: Setup VC++ environment + uses: TheMrMilchmann/setup-msvc-dev@v4 + with: + arch: ${{ matrix.target.arch }} + spectre: true + + - name: Install dependencies + id: get-deps + uses: ./.github/actions/install-dependencies + with: + qt-version: ${{ matrix.target.qt-version }} + vcpkg-triplet: ${{ matrix.target.vcpkg-triplet }} + + - name: Get version + id: get-version + uses: ./.github/actions/get-version + + - name: Update Windows Paths + shell: pwsh + run: echo "C:\Program Files\doxygen\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Configure + shell: bash + run: ${{ env.CMAKE_CONFIGURE }} -G Ninja ${{ steps.get-deps.outputs.vcpkg-cmake-config }} -DSYNERGY_VERSION_RELEASE=${{ env.SYNERGY_VERSION_RELEASE }} -DSYNERGY_VERSION_SNAPSHOT=${{ env.SYNERGY_VERSION_SNAPSHOT }} + + - name: Build + shell: bash + run: cmake --build build --config Release -j8 + + # Inner binaries (synergy-core.exe, synergy.exe, synergy-daemon.exe) must + # be signed BEFORE the MSI is built — WiX embeds them into the installer, + # and signing the MSI afterward doesn't propagate signatures to embedded + # files. Stages all non-test exes into one dir so they can be batch-signed + # with a single OTP, then restores them in-place before the package step. + - name: Stage binaries for signing + if: env.SYNERGY_VERSION_RELEASE == 'true' + shell: pwsh + run: | + $stageIn = "$env:RUNNER_TEMP\sign-stage\in" + $stageOut = "$env:RUNNER_TEMP\sign-stage\out" + New-Item -ItemType Directory -Force -Path $stageIn, $stageOut | Out-Null + $exes = Get-ChildItem build/bin/*.exe | Where-Object { $_.Name -notmatch 'test' } + if (-not $exes) { throw "no binaries found in build/bin to sign" } + foreach ($exe in $exes) { Copy-Item $exe.FullName "$stageIn\$($exe.Name)" -Force } + + - name: Sign inner binaries + if: env.SYNERGY_VERSION_RELEASE == 'true' + uses: SSLcom/esigner-codesign@develop + with: + command: batch_sign + username: ${{ secrets.SSL_USERNAME }} + password: ${{ secrets.SSL_PASSWORD }} + credential_id: ${{ secrets.SSL_CREDENTIAL_ID }} + totp_secret: ${{ secrets.SSL_TOTP_SECRET }} + dir_path: ${{ runner.temp }}\sign-stage\in + output_path: ${{ runner.temp }}\sign-stage\out + malware_block: "true" + environment_name: PROD + + - name: Restore + verify signed binaries + if: env.SYNERGY_VERSION_RELEASE == 'true' + shell: pwsh + run: | + Get-ChildItem "$env:RUNNER_TEMP\sign-stage\out\*.exe" | ForEach-Object { + $dst = "build/bin/$($_.Name)" + Copy-Item $_.FullName $dst -Force + $sig = Get-AuthenticodeSignature $dst + if ($sig.Status -ne 'Valid') { throw "signature check failed on $dst (Status: $($sig.Status))" } + Write-Host "OK: $dst signed by $($sig.SignerCertificate.Subject)" + } + + - name: Package + shell: bash + run: cmake --build build --config Release -j8 --target package + + - name: Locate MSI + if: env.SYNERGY_VERSION_RELEASE == 'true' + id: locate-msi + shell: pwsh + run: | + $msi = (Get-ChildItem build/*.msi | Select-Object -First 1).FullName + if (-not $msi) { throw "no MSI found in build/" } + "path=$msi" | Out-File $env:GITHUB_OUTPUT -Append + + - name: Sign MSI + if: env.SYNERGY_VERSION_RELEASE == 'true' + uses: SSLcom/esigner-codesign@develop + with: + command: sign + username: ${{ secrets.SSL_USERNAME }} + password: ${{ secrets.SSL_PASSWORD }} + credential_id: ${{ secrets.SSL_CREDENTIAL_ID }} + totp_secret: ${{ secrets.SSL_TOTP_SECRET }} + file_path: ${{ steps.locate-msi.outputs.path }} + override: "true" + malware_block: "true" + environment_name: PROD + + - name: Verify MSI is signed + if: env.SYNERGY_VERSION_RELEASE == 'true' + shell: pwsh + run: | + $sig = Get-AuthenticodeSignature "${{ steps.locate-msi.outputs.path }}" + if ($sig.Status -ne 'Valid') { throw "MSI not signed (Status: $($sig.Status))" } + Write-Host "OK: MSI signed by $($sig.SignerCertificate.Subject)" + + - name: Check for unexpected repo changes + shell: bash + run: | + if [[ `git status --porcelain` ]]; then + echo "Unexpected changes to the repo, Often caused by forgetting to commit the updated translation files" + exit 1 + fi + + - name: Tests + uses: ./.github/actions/run-tests + timeout-minutes: 2 + with: + job: ${{ matrix.target.name }} + + - name: Test package + uses: ./.github/actions/test-package + + - name: Upload + uses: actions/upload-artifact@v7 + with: + name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} + path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* + + - name: Notify Slack on failure + if: ${{ failure() && github.event_name != 'pull_request' }} + uses: slackapi/slack-github-action@v1 + with: + channel-id: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} + slack-message: | + + ❌ Synergy CI failed for: *${{ matrix.target.name }}* + <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run> + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} + + build-macos: + needs: lint-clang + name: ${{ matrix.target.name }} + runs-on: ${{ matrix.target.runs-on }} + timeout-minutes: ${{ matrix.target.timeout }} + strategy: + fail-fast: false + matrix: + target: + - name: "macos-arm64" + runs-on: macos-15 + timeout: 10 + qt-version: 6.10.2 + config-args: '-DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=14' + + - name: "macos-x64" + runs-on: macos-15-intel + timeout: 20 + qt-version: 6.9.3 + config-args: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET=12 -DCMAKE_OSX_SYSROOT=/Applications/Xcode_16.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk' + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Install dependencies + uses: ./.github/actions/install-dependencies + with: + qt-version: ${{ matrix.target.qt-version }} + + - name: Get version + id: get-version + uses: ./.github/actions/get-version + + - name: Configure + run: ${{ env.CMAKE_CONFIGURE }} ${{ matrix.target.config-args }} -DSYNERGY_VERSION_RELEASE=${{ env.SYNERGY_VERSION_RELEASE }} -DSYNERGY_VERSION_SNAPSHOT=${{ env.SYNERGY_VERSION_SNAPSHOT }} + + - name: Build + shell: bash + run: cmake --build build --config Release -j8 + + # Create an ephemeral keychain just for this run and import the Developer + # ID Application certificate from the base64-encoded P12 in secrets. Using + # a per-run keychain (rather than System.keychain) keeps the cert out of + # any shared state on the runner; the keychain is destroyed at job end. + # set-key-partition-list grants codesign passwordless access so the later + # sign step doesn't prompt. + - name: Set up signing keychain + if: env.SYNERGY_VERSION_RELEASE == 'true' + shell: bash + env: + APPLE_P12_CERTIFICATE: ${{ secrets.APPLE_P12_CERTIFICATE }} + APPLE_P12_PASSWORD: ${{ secrets.APPLE_P12_PASSWORD }} + run: | + keychain="$RUNNER_TEMP/sign.keychain-db" + keychain_pass=$(openssl rand -base64 32) + security create-keychain -p "$keychain_pass" "$keychain" + security set-keychain-settings -lut 3600 "$keychain" + security unlock-keychain -p "$keychain_pass" "$keychain" + # Prepend to keychain search list so codesign finds the cert without + # changing the user's default keychain. + security list-keychains -d user -s "$keychain" $(security list-keychains -d user | tr -d ' "') + cert_file="$RUNNER_TEMP/cert.p12" + echo "$APPLE_P12_CERTIFICATE" | base64 --decode > "$cert_file" + security import "$cert_file" -k "$keychain" -P "$APPLE_P12_PASSWORD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$keychain_pass" "$keychain" + rm -f "$cert_file" + echo "SIGN_KEYCHAIN=$keychain" >> "$GITHUB_ENV" + echo "SIGN_KEYCHAIN_PASS=$keychain_pass" >> "$GITHUB_ENV" + + # Sign the inner .app BEFORE the DMG is built — CPack DragNDrop embeds + # the bundle into the DMG, and signing the DMG afterward doesn't propagate + # signatures inward. `--deep --options runtime` are required for + # notarization to accept the bundle later. + - name: Sign macOS bundle + if: env.SYNERGY_VERSION_RELEASE == 'true' + shell: bash + env: + APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} + run: | + bundle=$(find build -maxdepth 4 -name "*.app" -type d | head -n1) + if [ -z "$bundle" ]; then + echo "ERROR: no .app bundle found under build/" >&2 + find build -maxdepth 4 -name "*.app" -o -name "Info.plist" 2>/dev/null + exit 1 + fi + security unlock-keychain -p "$SIGN_KEYCHAIN_PASS" "$SIGN_KEYCHAIN" + /usr/bin/codesign \ + --deep --force --timestamp --options runtime \ + --sign "$APPLE_CODESIGN_ID" "$bundle" + /usr/bin/codesign --verify --strict --verbose=2 "$bundle" + echo "Bundle signed: $bundle" + + # CPack on macOS is flaky; retry a handful of times before failing. + - name: Package + shell: bash + run: | + for i in $(seq 1 5); do + cmake --build build --config Release -j8 --target package 2>&1 && { echo "Package successful"; break; } + echo "Package attempt $i failed" + sleep 1 + done + + # Sign the DMG itself so Gatekeeper trusts it before extraction. Then + # submit to notarytool (synchronous via --wait) and staple the returned + # ticket so Gatekeeper can verify offline. + - name: Sign + notarize + staple DMG + if: env.SYNERGY_VERSION_RELEASE == 'true' + shell: bash + env: + APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} + APPLE_NOTARY_USER: ${{ secrets.APPLE_NOTARY_USER }} + APPLE_NOTARY_PASSWORD: ${{ secrets.APPLE_NOTARY_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + run: | + dmg=$(find build -maxdepth 2 -name "*.dmg" -type f | head -n1) + if [ -z "$dmg" ]; then + echo "ERROR: no DMG found under build/" >&2 + find build -maxdepth 2 -name "*.dmg" 2>/dev/null + exit 1 + fi + echo "Found DMG: $dmg" + + security unlock-keychain -p "$SIGN_KEYCHAIN_PASS" "$SIGN_KEYCHAIN" + /usr/bin/codesign --force --timestamp --sign "$APPLE_CODESIGN_ID" "$dmg" + + xcrun notarytool submit "$dmg" \ + --apple-id "$APPLE_NOTARY_USER" \ + --password "$APPLE_NOTARY_PASSWORD" \ + --team-id "$APPLE_TEAM_ID" \ + --wait + + xcrun stapler staple "$dmg" + xcrun stapler validate "$dmg" + spctl -a -t open --context context:primary-signature -v "$dmg" + echo "DMG signed + notarized + stapled: $dmg" + + - name: Check for unexpected repo changes + shell: bash + run: | + if [[ `git status --porcelain` ]]; then + echo "Unexpected changes to the repo, Often caused by forgetting to commit the updated translation files" + exit 1 + fi + + - name: Tests + uses: ./.github/actions/run-tests + timeout-minutes: 2 + with: + job: ${{ matrix.target.name }} + + - name: Test package + uses: ./.github/actions/test-package + + - name: Upload + uses: actions/upload-artifact@v7 + with: + name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} + path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* + + - name: Notify Slack on failure + if: ${{ failure() && github.event_name != 'pull_request' }} + uses: slackapi/slack-github-action@v1 + with: + channel-id: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} + slack-message: | + + ❌ Synergy CI failed for: *${{ matrix.target.name }}* + <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run> + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} + + build-linux: + needs: lint-clang + name: ${{ matrix.target.name }} + runs-on: ${{ matrix.target.runs-on }} + container: ${{ matrix.target.container }} + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + target: + - name: "debian-x86_64" + runs-on: ubuntu-latest + container: debian:stable-slim + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "debian-arm64" + runs-on: ubuntu-24.04-arm + container: debian:stable-slim + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "debian-testing-x86_64" + runs-on: ubuntu-latest + container: debian:testing-slim + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "debian-testing-arm64" + runs-on: ubuntu-24.04-arm + container: debian:testing-slim + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "fedora-43-x86_64" + runs-on: ubuntu-latest + container: fedora:43 + like: "fedora" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "fedora-43-arm64" + runs-on: ubuntu-24.04-arm + container: fedora:43 + like: "fedora" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "fedora-44-x86_64" + runs-on: ubuntu-latest + container: fedora:44 + like: "fedora" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "fedora-44-arm64" + runs-on: ubuntu-24.04-arm + container: fedora:44 + like: "fedora" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "opensuse-x86_64" + runs-on: ubuntu-latest + container: opensuse/tumbleweed:latest + like: "suse" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "opensuse-arm64" + runs-on: ubuntu-24.04-arm + container: opensuse/tumbleweed:latest + like: "suse" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "archlinux-x86_64" + runs-on: ubuntu-latest + container: archlinux:latest + like: "arch" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DEV_DOCS=ON" + + - name: "ubuntu-26.04-x86_64" + runs-on: ubuntu-latest + container: ubuntu:26.04 + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "ubuntu-26.04-arm64" + runs-on: ubuntu-24.04-arm + container: ubuntu:26.04 + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + steps: + # Containers don't ship git; everything else needs it for checkout + describe. + - name: Install Git on Container + shell: bash + run: | + if [ "${{ matrix.target.like }}" == "debian" ]; then + apt update -qqq > /dev/null && apt install -qqq git devscripts > /dev/null + elif [ "${{ matrix.target.like }}" == "fedora" ]; then + dnf install -y git + elif [ "${{ matrix.target.like }}" == "suse" ]; then + zypper refresh + zypper install -y --force-resolution git + elif [ "${{ matrix.target.like }}" == "arch" ]; then + pacman -Syu --noconfirm git + else + echo "Unknown: ${{ matrix.target.like }}" + fi + + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Install dependencies + uses: ./.github/actions/install-dependencies + with: + like: ${{ matrix.target.like }} + + - name: Get version + id: get-version + uses: ./.github/actions/get-version + + - name: Configure + run: ${{ env.CMAKE_CONFIGURE }} ${{ matrix.target.config-args }} -DSYNERGY_VERSION_RELEASE=${{ env.SYNERGY_VERSION_RELEASE }} -DSYNERGY_VERSION_SNAPSHOT=${{ env.SYNERGY_VERSION_SNAPSHOT }} + + - name: Build + shell: bash + run: cmake --build build --config Release -j8 + + - name: Package + shell: bash + run: | + if [[ "${{ matrix.target.like }}" == "arch" ]]; then + useradd -m build + sudo chown -R build build + cd build + sudo -u build makepkg -s + export OSNAME=$(cat /etc/os-release | grep ^ID= | sed 's/ID=//g') + export ARCH=$(uname -m) + mv *.pkg.tar.zst ${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-${OSNAME}-${ARCH}.pkg.tar.zst + cd .. + else + cmake --build build --config Release -j8 --target package + fi + + - name: Check for unexpected repo changes + shell: bash + run: | + if [[ `git status --porcelain` ]]; then + echo "Unexpected changes to the repo, Often caused by forgetting to commit the updated translation files" + exit 1 + fi + + - name: Tests + uses: ./.github/actions/run-tests + timeout-minutes: 2 + with: + job: ${{ matrix.target.name }} + + - name: Test package + uses: ./.github/actions/test-package + with: + like: ${{ matrix.target.like }} + + - name: Update Development Documentation + if: matrix.target.like == 'arch' && github.ref == 'refs/heads/beta' + uses: JamesIves/github-pages-deploy-action@v4.7.3 + with: + branch: gh-pages + folder: build/docs/dev/html + + - name: Upload + uses: actions/upload-artifact@v7 + with: + name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} + path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* + + - name: Notify Slack on failure + if: ${{ failure() && github.event_name != 'pull_request' }} + uses: slackapi/slack-github-action@v1 + with: + channel-id: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} + slack-message: | + + ❌ Synergy CI failed for: *${{ matrix.target.name }}* + <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run> + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} + + # Technically, "unix" is a misnomer, but we use it here to mean "Unix-like BSD-derived". + unix: + needs: lint-clang + name: unix-${{ matrix.distro.name }} + runs-on: ${{ vars.CI_UNIX_RUNNER || 'ubuntu-24.04' }} + timeout-minutes: 20 + + strategy: + fail-fast: false + + matrix: + distro: + - name: freebsd + + steps: + # Fancy checkout gets all the tags + # it also makes sure we can use git --describe correctly + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + - name: Build on FreeBSD + if: ${{ matrix.distro.name == 'freebsd' }} + uses: vmactions/freebsd-vm@v1 + with: + usesh: true + + # Workaround for unreliable pkg: Pin to a stable release. + # vmactions/freebsd-vm@v1 gets latest FreeBSD, which sometimes has a broken ports catalog. + release: "14.4" + + run: | + pkg install -y cmake ninja gmake gcc12 openssl glib \ + libX11 libXtst libxkbfile qt6-base qt6-tools gtk3 \ + googletest pkgconf libei libportal doxygen + ${{env.CMAKE_CONFIGURE}} -G Ninja + cmake --build build -j16 + flatpak: + needs: lint-clang + name: flatpak-${{matrix.flatpak.arch}} + runs-on: ${{matrix.flatpak.runs-on}} + timeout-minutes: 60 + container: + image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.10 + options: --privileged + strategy: + fail-fast: false + matrix: + flatpak: + - runs-on: ubuntu-latest + arch: x86_64 + - runs-on: ubuntu-24.04-arm + arch: aarch64 + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - run: git config --global protocol.file.allow always + + - name: Get version + id: get-version + uses: ./.github/actions/get-version + + - name: Lint appsteam + run: flatpak-builder-lint appstream deploy/linux/org.deskflow.deskflow.metainfo.xml + + - name: Lint manifest + run: flatpak-builder-lint manifest deploy/linux/flatpak/org.deskflow.deskflow.yml + + - name: Build + uses: flatpak/flatpak-github-actions/flatpak-builder@v6 + with: + bundle: ${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-linux-${{ matrix.flatpak.arch }}.flatpak + manifest-path: deploy/linux/flatpak/org.deskflow.deskflow.yml + cache-key: flatpak-builder-${{matrix.flatpak.arch}}-2.0 + arch: ${{matrix.flatpak.arch}} + upload-artifact: false + + - name: Validate build + run: flatpak-builder-lint --exceptions --user-exceptions deploy/linux/flatpak/ci-build-lint-exceptions.json repo repo + + - name: Upload + uses: actions/upload-artifact@v7 + with: + name: package-${{ env.PACKAGE_PREFIX }}-flatpak-${{matrix.flatpak.arch}} + path: ${{github.workspace}}/${{ env.PACKAGE_PREFIX }}[-_]*.flatpak + + # Upload all built packages to AWS S3 for distribution. Runs after the gate + # (ci-passed) on push/dispatch/release events; PRs are skipped (no secrets). + # Layout: s3://////... + s3-upload: + needs: ci-passed + if: ${{ always() && needs.ci-passed.result == 'success' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || github.event_name == 'release') }} + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Get version + id: get-version + uses: ./.github/actions/get-version + + - name: Download all artifacts + uses: actions/download-artifact@v8 + with: + pattern: package-* + path: dist + merge-multiple: true + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: us-east-2 + + - name: Upload to AWS S3 + shell: bash + run: | + version="${{ steps.get-version.outputs.version }}" + s3_uri="${{ inputs.aws-s3-uri || format('s3://{0}/{1}', vars.AWS_S3_BUCKET, vars.AWS_S3_UPLOAD_DIR) }}" + s3_uri="${s3_uri%/}" + + if [[ "${{ env.SYNERGY_VERSION_RELEASE }}" == "true" ]]; then + sub_dir="release" + elif [[ "${{ env.SYNERGY_VERSION_SNAPSHOT }}" == "true" ]]; then + # Organize snapshots by major.minor.patch for easier navigation. + short_version=$(echo "$version" | cut -d'-' -f1 | cut -d'+' -f1) + sub_dir="snapshot/$short_version" + else + sub_dir="dev" + fi + + url="$s3_uri/$sub_dir/$version" + echo "Uploading to AWS S3: $url" + aws s3 cp ./dist/ "$url" --recursive + + - name: Notify Slack on failure + if: failure() + uses: slackapi/slack-github-action@v1 + with: + channel-id: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} + slack-message: | + + ❌ Synergy S3 upload failed + <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run> + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml deleted file mode 100644 index a8b98d8c9..000000000 --- a/.github/workflows/continuous-integration.yml +++ /dev/null @@ -1,467 +0,0 @@ -# All-in-one continuous integration (CI) workflow. -# Runs on all platforms (Windows, macOS, and Linux) -# for all events (pull request, release, and schedule). - -name: Continuous Integration - -on: - push: - branches: [master] - tags: - - "v*" - pull_request: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - GIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} - PACKAGE_PREFIX: "deskflow" - PACKAGE_PATH: ./dist - CMAKE_CONFIGURE: "cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DSKIP_BUILD_TESTS=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON" - -jobs: - # Quality gate to allow PR merge, used in the branch protection rules. - ci-passed: - runs-on: ubuntu-latest - needs: [main-build, test-results, unix, flatpak] - if: always() - - steps: - - name: Check all steps passed - run: | - if [[ ${{needs.main-build.result}} == 'success' && ${{needs.test-results.result}} == 'success' && ${{needs.unix.result}} == 'success' && ${{needs.flatpak.result}} == 'success' ]]; then - echo "✅ CI passed" > $GITHUB_STEP_SUMMARY - else - exit 1 - fi - - # Summary of test results, combined from test result artifacts. - # Runs even if the tests fail to provide a summary of the failures. - test-results: - needs: main-build - if: always() && needs.main-build.result != 'skipped' - - runs-on: ubuntu-slim - timeout-minutes: 5 - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Test summary - uses: ./.github/actions/test-summary - - lint-reuse: - runs-on: ubuntu-latest - timeout-minutes: 5 - - steps: - - uses: actions/checkout@v6 - - - name: REUSE Compliance Check - uses: fsfe/reuse-action@v5 - - lint-clang: - needs: [lint-reuse] - runs-on: ubuntu-slim - timeout-minutes: 5 - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Lint Checker - uses: ./.github/actions/lint-clang - - analyze-valgrind: - needs: lint-clang - if: ${{ github.event_name == 'pull_request' }} - uses: ./.github/workflows/valgrind-analysis.yml - - main-build: - needs: lint-clang - name: ${{ matrix.target.name }} - runs-on: ${{ matrix.target.runs-on }} - container: ${{ matrix.target.container }} - timeout-minutes: ${{ matrix.target.timeout }} - strategy: - # Normally, we want to fail fast, but in this case we shouldn't since one target may - # fail due to transient issues unrelated to the build. - fail-fast: false - - matrix: - target: - - name: "windows-2022-x64" - runs-on: "windows-2022" - timeout: 30 - config-args: "-G Ninja" - vcpkg-triplet: x64-windows-release - arch: "amd64" - qt-version: 6.10.3 - - - name: "windows-2022-arm64" - runs-on: "windows-11-arm" - timeout: 30 - config-args: "-G Ninja" - vcpkg-triplet: arm64-windows - arch: "arm64" - qt-version: 6.11.1 - - - name: "macos-arm64" - runs-on: macos-15 - timeout: 10 - qt-version: 6.11.1 - config-args: '-DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=14' - - - name: "macos-x64" - runs-on: macos-15-intel - timeout: 20 - qt-version: 6.9.3 - config-args: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET=12 -DCMAKE_OSX_SYSROOT=/Applications/Xcode_16.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk' - - - name: "debian-x86_64" - runs-on: ubuntu-latest - container: debian:stable-slim - like: "debian" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "debian-arm64" - runs-on: ubuntu-24.04-arm - container: debian:stable-slim - like: "debian" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "debian-testing-x86_64" - runs-on: ubuntu-latest - container: debian:testing-slim - like: "debian" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "debian-testing-arm64" - runs-on: ubuntu-24.04-arm - container: debian:testing-slim - like: "debian" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "fedora-43-x86_64" - runs-on: ubuntu-latest - container: fedora:43 - like: "fedora" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "fedora-43-arm64" - runs-on: ubuntu-24.04-arm - container: fedora:43 - like: "fedora" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "fedora-44-x86_64" - runs-on: ubuntu-latest - container: fedora:44 - like: "fedora" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "fedora-44-arm64" - runs-on: ubuntu-24.04-arm - container: fedora:44 - like: "fedora" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "opensuse-x86_64" - runs-on: ubuntu-latest - container: opensuse/tumbleweed:latest - like: "suse" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "opensuse-arm64" - runs-on: ubuntu-24.04-arm - container: opensuse/tumbleweed:latest - like: "suse" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "archlinux-x86_64" - runs-on: ubuntu-latest - container: archlinux:latest - like: "arch" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DEV_DOCS=ON" - - - name: "ubuntu-26.04-x86_64" - runs-on: ubuntu-latest - container: ubuntu:26.04 - like: "debian" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - - name: "ubuntu-26.04-arm64" - runs-on: ubuntu-24.04-arm - container: ubuntu:26.04 - like: "debian" - timeout: 20 - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - - steps: - # Make sure the container has git before we do anything else - - name: Install Git on Container - if: ${{ matrix.target.container }} - shell: bash - run: | - if [ "${{matrix.target.like}}" == "debian" ]; then - apt update -qqq > /dev/null && apt install -qqq git devscripts > /dev/null - elif [ "${{matrix.target.like}}" == "fedora" ]; then - dnf install -y git - elif [ "${{matrix.target.like}}" == "suse" ]; then - zypper refresh - zypper install -y --force-resolution git - elif [ "${{matrix.target.like}}" == "arch" ]; then - pacman -Syu --noconfirm git - else - echo "Unknown: ${{matrix.target.like}}" - fi - # Fancy checkout gets all the tags - # it also makes sure we can use git --describe correctly - - name: Fancy Checkout - uses: sithlord48/fancy-checkout@v2 - - # This effectively runs `vcvarsall.bat`, etc. It's not actually installing - # VC++ as that's already pre-installed on the Windows runner. - - name: Setup VC++ environment - if: ${{ runner.os == 'Windows' }} - uses: TheMrMilchmann/setup-msvc-dev@v4 - with: - arch: ${{matrix.target.arch}} - spectre: true - - - name: Install dependencies - id: get-deps - uses: ./.github/actions/install-dependencies - with: - qt-version: ${{matrix.target.qt-version}} - vcpkg-triplet: ${{matrix.target.vcpkg-triplet}} - like: ${{ matrix.target.like }} - - - name: Get version - uses: ./.github/actions/get-version - - - name: Update Windows Paths - if: (runner.os == 'Windows') - shell: pwsh - run: echo "C:\Program Files\doxygen\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - - name: Configure - run: ${{env.CMAKE_CONFIGURE}} ${{ matrix.target.config-args }} ${{ steps.get-deps.outputs.vcpkg-cmake-config }} -DPACKAGE_VERSION_LABEL="${{env.DESKFLOW_PACKAGE_VERSION}}" - - - name: Build - shell: bash - run: | - if [[ "${{matrix.target.like}}" != "arch" ]]; then - if [ "$RUNNER_OS" != "macOS" ]; then - cmake --build build --config Release -j8 --target package - else - cmake --build build --config Release -j8 - for i in $(seq 1 5); do - cmake --build build --config Release -j8 --target package 2>&1 - if [ $? -eq 0 ]; then - echo "Package successful" - break - else - echo "Package attempt $i failed" - sleep 1 - fi - done - fi - else - cmake --build build --config Release -j8 - useradd -m build - sudo chown -R build build - cd build - sudo -u build makepkg -s - export OSNAME=$(cat /etc/os-release | grep ^ID= | sed 's/ID=//g') - export ARCH=$(uname -m) - mv *.pkg.tar.zst deskflow-${{env.DESKFLOW_PACKAGE_VERSION}}-${OSNAME}-${ARCH}.pkg.tar.zst - cd .. - fi - - - name: Check for unexpected repo changes - shell: bash - run: | - if [[ `git status --porcelain` ]]; then - echo "Unexpected changes to the repo, Often caused by forgetting to commit the updated translation files" - exit 1 - fi - - - name: Tests - uses: ./.github/actions/run-tests - timeout-minutes: 2 - with: - job: ${{ matrix.target.name }} - - - name: Test package - uses: ./.github/actions/test-package - with: - like: ${{ matrix.target.like }} - - - name: Update Development Documentation - if: matrix.target.like == 'arch' && github.ref == 'refs/heads/master' - uses: JamesIves/github-pages-deploy-action@v4.7.3 - with: - branch: gh-pages - folder: build/docs/dev/html - - - name: Upload - uses: actions/upload-artifact@v7 - with: - name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} - path: ${{github.workspace}}/build/deskflow[-_]*.* - - # Technically, "unix" is a misnomer, but we use it here to mean "Unix-like BSD-derived". - unix: - needs: lint-clang - name: unix-${{ matrix.distro.name }} - runs-on: ${{ vars.CI_UNIX_RUNNER || 'ubuntu-24.04' }} - timeout-minutes: 20 - - strategy: - fail-fast: false - - matrix: - distro: - - name: freebsd - - steps: - # Fancy checkout gets all the tags - # it also makes sure we can use git --describe correctly - - name: Fancy Checkout - uses: sithlord48/fancy-checkout@v2 - - name: Build on FreeBSD - if: ${{ matrix.distro.name == 'freebsd' }} - uses: vmactions/freebsd-vm@v1 - with: - usesh: true - - # Workaround for unreliable pkg: Pin to a stable release. - # vmactions/freebsd-vm@v1 gets latest FreeBSD, which sometimes has a broken ports catalog. - release: "14.4" - - run: | - pkg install -y cmake ninja gmake gcc12 openssl glib \ - libX11 libXtst libxkbfile qt6-base qt6-tools gtk3 \ - googletest pkgconf libei libportal doxygen - ${{env.CMAKE_CONFIGURE}} -G Ninja - cmake --build build -j16 - flatpak: - needs: lint-clang - name: flatpak-${{matrix.flatpak.arch}} - runs-on: ${{matrix.flatpak.runs-on}} - timeout-minutes: 60 - container: - image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.10 - options: --privileged - strategy: - fail-fast: false - matrix: - flatpak: - - runs-on: ubuntu-latest - arch: x86_64 - - runs-on: ubuntu-24.04-arm - arch: aarch64 - steps: - - name: Check out repository - uses: sithlord48/fancy-checkout@v2 - - - run: git config --global protocol.file.allow always - - - name: Get version - uses: ./.github/actions/get-version - - - name: Lint appsteam - run: flatpak-builder-lint appstream deploy/linux/org.deskflow.deskflow.metainfo.xml - - - name: Lint manifest - run: flatpak-builder-lint manifest deploy/linux/flatpak/org.deskflow.deskflow.yml - - - name: Build - uses: flatpak/flatpak-github-actions/flatpak-builder@v6 - with: - bundle: deskflow-${{env.DESKFLOW_PACKAGE_VERSION}}-linux-${{matrix.flatpak.arch}}.flatpak - manifest-path: deploy/linux/flatpak/org.deskflow.deskflow.yml - cache-key: flatpak-builder-${{matrix.flatpak.arch}}-2.0 - arch: ${{matrix.flatpak.arch}} - upload-artifact: false - - - name: Validate build - run: flatpak-builder-lint --exceptions --user-exceptions deploy/linux/flatpak/ci-build-lint-exceptions.json repo repo - - - name: Upload - uses: actions/upload-artifact@v7 - with: - name: package-${{ env.PACKAGE_PREFIX }}-flatpak-${{matrix.flatpak.arch}} - path: ${{github.workspace}}/deskflow[-_]*.flatpak - - release: - needs: ci-passed - if: (github.ref == 'refs/heads/master') || (contains(github.ref, '/tags/v')) - runs-on: ubuntu-latest - - steps: - - name: Fancy Checkout - uses: sithlord48/fancy-checkout@v2 - - - name: Get version - uses: ./.github/actions/get-version - - - name: Download artifacts - uses: actions/download-artifact@v8 - - - name: Generate package checksums - id: generate_notes - shell: bash - run: | - mv $GITHUB_WORKSPACE/package-*/deskflow-* . - rm -rf $GITHUB_WORKSPACE/package-* - echo "Build: ${{env.DESKFLOW_VERSION }}" > sums.txt - sha256sum deskflow-* >> sums.txt - - - name: Deploy continuous - if: (github.ref == 'refs/heads/master') && !(contains(github.ref, '/tags/v')) - uses: crowbarmaster/GH-Automatic-Releases@latest - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "continuous" - prerelease: true - title: "Continuous v${{env.DESKFLOW_VERSION}}" - files: | - deskflow-* - sums.txt - - - name: Deploy release - if: contains(github.ref, '/tags/v') - uses: crowbarmaster/GH-Automatic-Releases@latest - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false - files: | - deskflow-* - sums.txt - - - name: Update Homebrewtap - shell: bash - run: | - curl -L -X POST \ - -H "Accept: application/vnd.github+json" \ - -H "Authorization: Bearer ${{ secrets.DF_TAP_TOKEN }}" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - https://api.github.com/repos/deskflow/homebrew-tap/dispatches \ - -d '{"event_type":"update_tap"}' diff --git a/CMakeLists.txt b/CMakeLists.txt index f8b3c18dd..171c15520 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,10 +21,14 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Project version. Synergy.cmake adds the dev/snapshot/release suffix later. -set(DESKFLOW_VERSION_MAJOR 1) -set(DESKFLOW_VERSION_MINOR 21) -set(DESKFLOW_VERSION_PATCH 0) +# Project version. SYNERGY_VERSION_* constants live in extra/cmake/Version.cmake +# (single source of truth, also consumed by SaveVersion.cmake in CI); bridged to +# the upstream-named DESKFLOW_VERSION_* here so project() can keep its existing +# shape. Synergy.cmake adds the dev/snapshot/release suffix later. +include(${CMAKE_CURRENT_SOURCE_DIR}/extra/cmake/Version.cmake) +set(DESKFLOW_VERSION_MAJOR ${SYNERGY_VERSION_MAJOR}) +set(DESKFLOW_VERSION_MINOR ${SYNERGY_VERSION_MINOR}) +set(DESKFLOW_VERSION_PATCH ${SYNERGY_VERSION_PATCH}) set(DESKFLOW_VERSION_TWEAK 0) # Git short SHA, consumed by VersionInfo.h.in (kVersionGitSha). diff --git a/extra/cmake/SaveVersion.cmake b/extra/cmake/SaveVersion.cmake new file mode 100644 index 000000000..c8acf6001 --- /dev/null +++ b/extra/cmake/SaveVersion.cmake @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd +# SPDX-License-Identifier: MIT + +# Standalone driver: invoked from CI as +# cmake -DVERSION_FILE=build/VERSION \ +# -DSYNERGY_VERSION_RELEASE=true|false \ +# -DSYNERGY_VERSION_SNAPSHOT=true|false \ +# -P extra/cmake/SaveVersion.cmake +# +# Includes Version.cmake (the same one Synergy.cmake includes during the build) +# and writes the result to VERSION_FILE. Guarantees the CI-side version matches +# what the binaries report. + +include(${CMAKE_CURRENT_LIST_DIR}/Version.cmake) + +if(NOT VERSION_FILE) + message(FATAL_ERROR "VERSION_FILE not set (pass via -DVERSION_FILE=...)") +endif() + +get_filename_component(_repo_root "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) +synergy_compute_version("${_repo_root}" _version _tweak) +message(STATUS "Computed version: ${_version}") +file(WRITE "${VERSION_FILE}" "${_version}") diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index 1f94aa4a5..10ec89dab 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -48,41 +48,20 @@ set(GUI_QRC_FILE "${GUI_RES_DIR}/synergy.qrc") # target name == source basename. set(CMAKE_PROJECT_NAME synergy) -# Synergy version mode suffix. Base version (MAJOR.MINOR.PATCH) is set in -# the root CMakeLists.txt. Default mode is dev; flip with -DSYNERGY_VERSION_RELEASE=ON -# or -DSYNERGY_VERSION_SNAPSHOT=ON for CI/release builds. +# Synergy version. Base semver lives in ./VERSION (read by the root CMakeLists.txt); +# composition rules — dev/snapshot/release suffix, rev count — are shared with +# extra/cmake/SaveVersion.cmake via synergy_compute_version() so the CI-side +# version (used in package filenames, S3 paths, etc.) matches what the binaries +# report. Default mode is dev; flip with -DSYNERGY_VERSION_RELEASE=ON or +# -DSYNERGY_VERSION_SNAPSHOT=ON for CI/release builds. option(SYNERGY_VERSION_RELEASE "Release version" OFF) option(SYNERGY_VERSION_SNAPSHOT "Snapshot version" OFF) -# Compute revision count once. Used as both the +rN suffix in snapshot version -# strings and as CMAKE_PROJECT_VERSION_TWEAK (consumed by VersionInfo.h.in's -# kDisplayVersion ternary and src/apps/res/windows.rc.in's VER_VERSION 4th -# digit, which Windows update mechanisms key off for installer recognition). -set(_rev_count 0) -if(GIT_FOUND) - execute_process( - COMMAND ${GIT_EXECUTABLE} describe HEAD --tags --long --match "v[0-9]*" - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE _git_describe - ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(_git_describe MATCHES "-([0-9]+)-g") - set(_rev_count "${CMAKE_MATCH_1}") - endif() -endif() - -set(_base "${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}") -if(SYNERGY_VERSION_RELEASE) - set(CMAKE_PROJECT_VERSION "${_base}") - set(CMAKE_PROJECT_VERSION_TWEAK 0) -elseif(SYNERGY_VERSION_SNAPSHOT) - set(CMAKE_PROJECT_VERSION "${_base}-snapshot+r${_rev_count}") - set(CMAKE_PROJECT_VERSION_TWEAK ${_rev_count}) -else() - set(CMAKE_PROJECT_VERSION "${_base}-dev") - set(CMAKE_PROJECT_VERSION_TWEAK ${_rev_count}) +include(${CMAKE_CURRENT_LIST_DIR}/Version.cmake) +synergy_compute_version("${CMAKE_SOURCE_DIR}" CMAKE_PROJECT_VERSION CMAKE_PROJECT_VERSION_TWEAK) +set(CMAKE_PROJECT_VERSION_MAJOR ${SYNERGY_VERSION_MAJOR}) +set(CMAKE_PROJECT_VERSION_MINOR ${SYNERGY_VERSION_MINOR}) +set(CMAKE_PROJECT_VERSION_PATCH ${SYNERGY_VERSION_PATCH}) +if(NOT SYNERGY_VERSION_RELEASE AND NOT SYNERGY_VERSION_SNAPSHOT) add_compile_definitions(SYNERGY_VERSION_DEV) endif() -unset(_base) -unset(_git_describe) -unset(_rev_count) diff --git a/extra/cmake/Version.cmake b/extra/cmake/Version.cmake new file mode 100644 index 000000000..321ecb854 --- /dev/null +++ b/extra/cmake/Version.cmake @@ -0,0 +1,52 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd +# SPDX-License-Identifier: MIT + +# Synergy version: single source of truth, shared between the build (root +# CMakeLists.txt → Synergy.cmake) and CI (SaveVersion.cmake run via cmake -P). +# Bump these constants to release a new version. +set(SYNERGY_VERSION_MAJOR 1) +set(SYNERGY_VERSION_MINOR 21) +set(SYNERGY_VERSION_PATCH 0) + +# Composes the full version string by applying SYNERGY_VERSION_RELEASE / +# SYNERGY_VERSION_SNAPSHOT semantics on top of the SYNERGY_VERSION_MAJOR/MINOR/PATCH +# constants above, plus a git-derived revision count for snapshot/dev builds: +# +# RELEASE → "X.Y.Z" +# SNAPSHOT → "X.Y.Z-snapshot+rN" (N = commits since last v* tag) +# neither (dev) → "X.Y.Z-dev" +# +# Outputs (PARENT_SCOPE): +# OUT_VERSION the composed version string +# OUT_TWEAK rev count (used by `windows.rc.in` as the 4th MSI digit so +# each snapshot/dev build is a distinguishable Windows installer) +function(synergy_compute_version SOURCE_DIR OUT_VERSION OUT_TWEAK) + set(_rev_count 0) + find_package(Git QUIET) + if(GIT_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} describe HEAD --tags --long --match "v[0-9]*" + WORKING_DIRECTORY "${SOURCE_DIR}" + OUTPUT_VARIABLE _git_describe + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(_git_describe MATCHES "-([0-9]+)-g") + set(_rev_count "${CMAKE_MATCH_1}") + endif() + endif() + + set(_base "${SYNERGY_VERSION_MAJOR}.${SYNERGY_VERSION_MINOR}.${SYNERGY_VERSION_PATCH}") + if(SYNERGY_VERSION_RELEASE) + set(_version "${_base}") + set(_tweak 0) + elseif(SYNERGY_VERSION_SNAPSHOT) + set(_version "${_base}-snapshot+r${_rev_count}") + set(_tweak ${_rev_count}) + else() + set(_version "${_base}-dev") + set(_tweak ${_rev_count}) + endif() + + set(${OUT_VERSION} "${_version}" PARENT_SCOPE) + set(${OUT_TWEAK} "${_tweak}" PARENT_SCOPE) +endfunction() From 806c9351cb4b40e868fcd6637e09d12538015470 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 12:03:49 +0100 Subject: [PATCH 028/144] feat: ensure core uses the same settings file as GUI for consistent configuration --- src/lib/gui/core/CoreProcess.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/gui/core/CoreProcess.cpp b/src/lib/gui/core/CoreProcess.cpp index be4e5d667..3b405236e 100644 --- a/src/lib/gui/core/CoreProcess.cpp +++ b/src/lib/gui/core/CoreProcess.cpp @@ -388,6 +388,13 @@ void CoreProcess::start(std::optional processModeOption) QStringList args = {coreMode}; + // Pin the core to the same settings file the GUI is using. Without this the + // core falls through Settings::load()'s default lookup (user file before + // system file), so a user who has selected system scope in the GUI still + // gets a core running against the user-scope file. The daemon path already + // forwards the settings path via IPC below; this covers the foreground path. + args << QStringLiteral("--settings") << Settings::settingsFile(); + if (m_mode == Settings::CoreMode::Server) { const auto [hasNeededPermissions, configFilename] = persistServerConfig(); if (configFilename.isEmpty()) { From 14f2f166db58da137bf3960c7b512bc3c01995f9 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 12:03:58 +0100 Subject: [PATCH 029/144] feat: add platform selection option for CI builds to filter by target OS --- .github/workflows/ci.yml | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a25a1b58..95ae783d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,16 @@ on: description: "Override AWS S3 target (e.g. s3://bucket/path)" required: false type: string + platform: + description: "Build only one platform (blank = all)" + required: false + default: '' + type: choice + options: + - '' + - windows + - macos + - linux push: branches: [beta] pull_request: @@ -53,14 +63,20 @@ jobs: if: always() steps: - - name: Check all steps passed + # 'skipped' is treated as passing because the user/event deliberately + # filtered the platform set (workflow_dispatch with inputs.platform=windows + # skips macos+linux jobs). A failed job is still a failure. + - name: Check all jobs passed or were intentionally skipped run: | - if [[ ${{needs.build-windows.result}} == 'success' \ - && ${{needs.build-macos.result}} == 'success' \ - && ${{needs.build-linux.result}} == 'success' \ - && ${{needs.test-results.result}} == 'success' \ - && ${{needs.unix.result}} == 'success' \ - && ${{needs.flatpak.result}} == 'success' ]]; then + ok() { + [[ "$1" == "success" || "$1" == "skipped" ]] + } + if ok "${{needs.build-windows.result}}" \ + && ok "${{needs.build-macos.result}}" \ + && ok "${{needs.build-linux.result}}" \ + && ok "${{needs.test-results.result}}" \ + && ok "${{needs.unix.result}}" \ + && ok "${{needs.flatpak.result}}"; then echo "✅ CI passed" > $GITHUB_STEP_SUMMARY else exit 1 @@ -100,6 +116,7 @@ jobs: build-windows: needs: lint-clang + if: ${{ inputs.platform == '' || inputs.platform == 'windows' }} name: ${{ matrix.target.name }} runs-on: ${{ matrix.target.runs-on }} timeout-minutes: 30 @@ -270,6 +287,7 @@ jobs: build-macos: needs: lint-clang + if: ${{ inputs.platform == '' || inputs.platform == 'macos' }} name: ${{ matrix.target.name }} runs-on: ${{ matrix.target.runs-on }} timeout-minutes: ${{ matrix.target.timeout }} @@ -445,6 +463,7 @@ jobs: build-linux: needs: lint-clang + if: ${{ inputs.platform == '' || inputs.platform == 'linux' }} name: ${{ matrix.target.name }} runs-on: ${{ matrix.target.runs-on }} container: ${{ matrix.target.container }} @@ -634,6 +653,7 @@ jobs: # Technically, "unix" is a misnomer, but we use it here to mean "Unix-like BSD-derived". unix: needs: lint-clang + if: ${{ inputs.platform == '' || inputs.platform == 'linux' }} name: unix-${{ matrix.distro.name }} runs-on: ${{ vars.CI_UNIX_RUNNER || 'ubuntu-24.04' }} timeout-minutes: 20 @@ -671,6 +691,7 @@ jobs: cmake --build build -j16 flatpak: needs: lint-clang + if: ${{ inputs.platform == '' || inputs.platform == 'linux' }} name: flatpak-${{matrix.flatpak.arch}} runs-on: ${{matrix.flatpak.runs-on}} timeout-minutes: 60 From e54a98b9f7037f43d7f995dc2304f738682a556c Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 12:12:23 +0100 Subject: [PATCH 030/144] fix: update CI job dependencies and remove unused unix job --- .github/workflows/ci.yml | 46 +++------------------------------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95ae783d1..37d278f40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: # Quality gate to allow PR merge, used in the branch protection rules. ci-passed: runs-on: ubuntu-latest - needs: [build-windows, build-macos, build-linux, test-results, unix, flatpak] + needs: [build-windows, build-macos, build-linux, build-flatpak, test-results] if: always() steps: @@ -75,8 +75,7 @@ jobs: && ok "${{needs.build-macos.result}}" \ && ok "${{needs.build-linux.result}}" \ && ok "${{needs.test-results.result}}" \ - && ok "${{needs.unix.result}}" \ - && ok "${{needs.flatpak.result}}"; then + && ok "${{needs.build-flatpak.result}}"; then echo "✅ CI passed" > $GITHUB_STEP_SUMMARY else exit 1 @@ -650,46 +649,7 @@ jobs: env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} - # Technically, "unix" is a misnomer, but we use it here to mean "Unix-like BSD-derived". - unix: - needs: lint-clang - if: ${{ inputs.platform == '' || inputs.platform == 'linux' }} - name: unix-${{ matrix.distro.name }} - runs-on: ${{ vars.CI_UNIX_RUNNER || 'ubuntu-24.04' }} - timeout-minutes: 20 - - strategy: - fail-fast: false - - matrix: - distro: - - name: freebsd - - steps: - # Fancy checkout gets all the tags - # it also makes sure we can use git --describe correctly - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - fetch-tags: true - - name: Build on FreeBSD - if: ${{ matrix.distro.name == 'freebsd' }} - uses: vmactions/freebsd-vm@v1 - with: - usesh: true - - # Workaround for unreliable pkg: Pin to a stable release. - # vmactions/freebsd-vm@v1 gets latest FreeBSD, which sometimes has a broken ports catalog. - release: "14.4" - - run: | - pkg install -y cmake ninja gmake gcc12 openssl glib \ - libX11 libXtst libxkbfile qt6-base qt6-tools gtk3 \ - googletest pkgconf libei libportal doxygen - ${{env.CMAKE_CONFIGURE}} -G Ninja - cmake --build build -j16 - flatpak: + build-flatpak: needs: lint-clang if: ${{ inputs.platform == '' || inputs.platform == 'linux' }} name: flatpak-${{matrix.flatpak.arch}} From 56468d1c7eb9c0193812038a3124a9e2b34014f3 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 12:16:52 +0100 Subject: [PATCH 031/144] feat: add Synergy desktop entry and Flatpak manifest for deployment --- .github/workflows/ci.yml | 6 +- .../deploy/linux/com.symless.synergy.desktop | 13 +++ .../linux/com.symless.synergy.metainfo.xml | 47 ++++++++ .../linux/flatpak/com.symless.synergy.yml | 102 ++++++++++++++++++ 4 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 extra/deploy/linux/com.symless.synergy.desktop create mode 100644 extra/deploy/linux/com.symless.synergy.metainfo.xml create mode 100644 extra/deploy/linux/flatpak/com.symless.synergy.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37d278f40..104cef3dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -680,16 +680,16 @@ jobs: uses: ./.github/actions/get-version - name: Lint appsteam - run: flatpak-builder-lint appstream deploy/linux/org.deskflow.deskflow.metainfo.xml + run: flatpak-builder-lint appstream extra/deploy/linux/com.symless.synergy.metainfo.xml - name: Lint manifest - run: flatpak-builder-lint manifest deploy/linux/flatpak/org.deskflow.deskflow.yml + run: flatpak-builder-lint manifest extra/deploy/linux/flatpak/com.symless.synergy.yml - name: Build uses: flatpak/flatpak-github-actions/flatpak-builder@v6 with: bundle: ${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-linux-${{ matrix.flatpak.arch }}.flatpak - manifest-path: deploy/linux/flatpak/org.deskflow.deskflow.yml + manifest-path: extra/deploy/linux/flatpak/com.symless.synergy.yml cache-key: flatpak-builder-${{matrix.flatpak.arch}}-2.0 arch: ${{matrix.flatpak.arch}} upload-artifact: false diff --git a/extra/deploy/linux/com.symless.synergy.desktop b/extra/deploy/linux/com.symless.synergy.desktop new file mode 100644 index 000000000..d2546a0bb --- /dev/null +++ b/extra/deploy/linux/com.symless.synergy.desktop @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd +# SPDX-License-Identifier: GPL-2.0-only + +[Desktop Entry] +Type=Application +Version=1.0 +Name=Synergy +Comment=Share one keyboard and mouse between computers +Exec=synergy +Icon=com.symless.synergy +Terminal=false +Categories=Utility; +Keywords=keyboard;mouse;sharing;network;share; diff --git a/extra/deploy/linux/com.symless.synergy.metainfo.xml b/extra/deploy/linux/com.symless.synergy.metainfo.xml new file mode 100644 index 000000000..5582ce216 --- /dev/null +++ b/extra/deploy/linux/com.symless.synergy.metainfo.xml @@ -0,0 +1,47 @@ + + + + + com.symless.synergy + CC0-1.0 + GPL-2.0-only + Synergy + + Synergy App Ltd + + Share one keyboard and mouse between computers + +

+ Use the keyboard, mouse, or trackpad of one computer to control nearby computers, and work seamlessly between them. +

+
+ com.symless.synergy.desktop + https://synergyapp.io + https://symless.com/synergy/contact + https://symless.com/synergy/help + + com.symless.synergy.desktop + + Utility + + + Input + Sharing + KVM + + + #dddddd + #555555 + + + + + +

Synergy re-forked from Deskflow 1.26. See the release notes for the full changelog.

+
+ https://synergyapp.io/release-notes +
+
+
diff --git a/extra/deploy/linux/flatpak/com.symless.synergy.yml b/extra/deploy/linux/flatpak/com.symless.synergy.yml new file mode 100644 index 000000000..0b33524ca --- /dev/null +++ b/extra/deploy/linux/flatpak/com.symless.synergy.yml @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd +# SPDX-License-Identifier: GPL-2.0-only + +# Synergy-branded Flatpak manifest. Lives under extra/ so the build identifies +# itself to the Flatpak runtime as Synergy (com.symless.synergy), not Deskflow. +# The build modules (Jinja2, libei, libportal, gtest) are unchanged from the +# upstream manifest at deploy/linux/flatpak/org.deskflow.deskflow.yml — only +# the brand surface (app-id, command, the project module's source path) differs. +# +# Source path is relative to this file's location, so it's one ../ deeper than +# the upstream manifest's: extra/deploy/linux/flatpak → ../../../../ = repo root. + +app-id: com.symless.synergy +runtime: org.kde.Platform +runtime-version: "6.10" +sdk: org.kde.Sdk +command: synergy +finish-args: + - --share=ipc + - --socket=wayland + - --socket=fallback-x11 + - --share=network + - --device=dri + - --talk-name=org.kde.StatusNotifierWatcher +cleanup: + - /include + - "*.a" + - /bin/ei-debug-events + - /lib/cmake + - /lib/pkgconfig + - /share/pkgconfig + - /share/cmake + - /share/doc + - /share/gir-1.0 + - /lib/girepository-1.0 +modules: + - name: python3-Jinja2 + buildsystem: simple + build-commands: + - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" + --prefix=${FLATPAK_DEST} --no-build-isolation Jinja2 + sources: + - type: file + url: https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl + sha256: 85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 + x-checker-data: + type: pypi + name: Jinja2 + packagetype: bdist_wheel + - type: file + url: https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz + sha256: d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b + cleanup: + - '*' + - name: libei + buildsystem: meson + config-opts: + - -Ddocumentation=[] + - -Dtests=disabled + - -Dliboeffis=disabled + sources: + - type: git + url: https://gitlab.freedesktop.org/libinput/libei + tag: 1.5.0 + commit: 19b64535408aa47abbc8151bc2c925afd6583851 + x-checker-data: + type: git + tag-pattern: ^([\\d.]+)$ + - name: libportal + buildsystem: meson + config-opts: + - -Ddocs=false + - -Dtests=false + - -Dportal-tests=false + - -Dvapi=false + - -Dbackend-qt6=auto + sources: + - type: git + url: https://github.com/flatpak/libportal.git + tag: 0.9.1 + commit: 8f5dc8d192f6e31dafe69e35219e3b707bde71ce + - type: patch + path: ../../../../deploy/linux/flatpak/libportal-qt69.patch + - name: gtest + buildsystem: cmake-ninja + sources: + - type: git + url: https://github.com/google/googletest.git + tag: v1.17.0 + commit: 52eb8108c5bdec04579160ae17225d66034bd723 + x-checker-data: + type: git + tag-pattern: ^([\\d.]+)$ + cleanup: + - '*' + - name: synergy + buildsystem: cmake-ninja + config-opts: + - "-DCMAKE_BUILD_TYPE=Release" + sources: + - type: dir + path: ../../../../ From ba19a51bf5bcbfc47c7cc95cec2c9480b40b2e66 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 12:23:09 +0100 Subject: [PATCH 032/144] fix: update Slack notification mechanism in CI workflow for improved failure reporting --- .github/workflows/ci.yml | 76 +++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 104cef3dc..54b1b6493 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -272,17 +272,6 @@ jobs: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* - - name: Notify Slack on failure - if: ${{ failure() && github.event_name != 'pull_request' }} - uses: slackapi/slack-github-action@v1 - with: - channel-id: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} - slack-message: | - - ❌ Synergy CI failed for: *${{ matrix.target.name }}* - <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run> - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} build-macos: needs: lint-clang @@ -448,17 +437,6 @@ jobs: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* - - name: Notify Slack on failure - if: ${{ failure() && github.event_name != 'pull_request' }} - uses: slackapi/slack-github-action@v1 - with: - channel-id: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} - slack-message: | - - ❌ Synergy CI failed for: *${{ matrix.target.name }}* - <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run> - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} build-linux: needs: lint-clang @@ -637,17 +615,6 @@ jobs: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* - - name: Notify Slack on failure - if: ${{ failure() && github.event_name != 'pull_request' }} - uses: slackapi/slack-github-action@v1 - with: - channel-id: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} - slack-message: | - - ❌ Synergy CI failed for: *${{ matrix.target.name }}* - <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run> - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} build-flatpak: needs: lint-clang @@ -757,14 +724,37 @@ jobs: echo "Uploading to AWS S3: $url" aws s3 cp ./dist/ "$url" --recursive - - name: Notify Slack on failure - if: failure() - uses: slackapi/slack-github-action@v1 + report: + needs: [build-windows, build-macos, build-linux, build-flatpak, test-results, s3-upload] + if: ${{ !cancelled() && github.event_name != 'pull_request' && contains(needs.*.result, 'failure') }} + runs-on: ubuntu-latest + timeout-minutes: 2 + + steps: + - name: Build Slack message + id: message + shell: bash + run: | + link="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + { + echo "text<" + echo "❌ Synergy CI failed" + [ "${{ needs.build-windows.result }}" = "failure" ] && echo "• Windows build failed" + [ "${{ needs.build-macos.result }}" = "failure" ] && echo "• macOS build failed" + [ "${{ needs.build-linux.result }}" = "failure" ] && echo "• Linux build failed" + [ "${{ needs.build-flatpak.result }}" = "failure" ] && echo "• Flatpak build failed" + [ "${{ needs.test-results.result }}" = "failure" ] && echo "• Test summary failed" + [ "${{ needs.s3-upload.result }}" = "failure" ] && echo "• S3 upload failed" + echo "<$link|View workflow>" + echo "EOF" + } >> $GITHUB_OUTPUT + + - name: Send Slack notification + uses: slackapi/slack-github-action@v3 with: - channel-id: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} - slack-message: | - - ❌ Synergy S3 upload failed - <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|Workflow run> - env: - SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} + method: chat.postMessage + token: ${{ secrets.SLACK_TOKEN }} + payload: | + channel: ${{ secrets.SLACK_NOTIFY_CHANNEL_ID }} + text: ${{ toJSON(steps.message.outputs.text) }} From 65353fa3570940ef03621cec286e69b4245afda9 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 12:29:32 +0100 Subject: [PATCH 033/144] fix: change shell to default for CI configuration step --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54b1b6493..332da5dc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,7 +165,6 @@ jobs: run: echo "C:\Program Files\doxygen\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - name: Configure - shell: bash run: ${{ env.CMAKE_CONFIGURE }} -G Ninja ${{ steps.get-deps.outputs.vcpkg-cmake-config }} -DSYNERGY_VERSION_RELEASE=${{ env.SYNERGY_VERSION_RELEASE }} -DSYNERGY_VERSION_SNAPSHOT=${{ env.SYNERGY_VERSION_SNAPSHOT }} - name: Build From 64a77b63b780e6970a7a3132b5ebf4eb0fcaba2a Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 12:37:30 +0100 Subject: [PATCH 034/144] fix: update file paths for desktop and metainfo installations in deploy script --- deploy/linux/deploy.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy/linux/deploy.cmake b/deploy/linux/deploy.cmake index c599cced2..f3e2a8bd5 100644 --- a/deploy/linux/deploy.cmake +++ b/deploy/linux/deploy.cmake @@ -7,7 +7,7 @@ set(MY_DIR ${CMAKE_CURRENT_LIST_DIR}) # Install our desktop file install( - FILES ${MY_DIR}/${CMAKE_PROJECT_REV_FQDN}.desktop + FILES ${CMAKE_SOURCE_DIR}/extra/deploy/linux/${CMAKE_PROJECT_REV_FQDN}.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications ) @@ -22,7 +22,7 @@ install( # Install our metainfo install( - FILES ${MY_DIR}/${CMAKE_PROJECT_REV_FQDN}.metainfo.xml + FILES ${CMAKE_SOURCE_DIR}/extra/deploy/linux/${CMAKE_PROJECT_REV_FQDN}.metainfo.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo/ ) From 4b24d5a592e8818bbfcbd342d83a48d3c07af363 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 12:37:33 +0100 Subject: [PATCH 035/144] fix: replace incorrect include of bits/chrono.h with chrono in constants.h --- extra/src/lib/synergy/gui/constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 8f6f7109c..5f49a81b5 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -19,7 +19,7 @@ #include -#include +#include // IWYU pragma: keep -- clangd wrongly thinks this is unused namespace synergy::gui { From 0592c176f1ba72f169cdf0c26666849e7e207508 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 13:48:47 +0100 Subject: [PATCH 036/144] refactor: simplify CI workflow by removing test summary job and updating PR comment handling --- .github/actions/run-tests/action.yml | 40 +---------- .github/actions/test-summary/action.yml | 93 ------------------------- .github/workflows/ci-comment.yml | 17 ++--- .github/workflows/ci.yml | 22 +----- 4 files changed, 12 insertions(+), 160 deletions(-) delete mode 100644 .github/actions/test-summary/action.yml diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml index 52b234708..6a196e2d1 100644 --- a/.github/actions/run-tests/action.yml +++ b/.github/actions/run-tests/action.yml @@ -1,12 +1,7 @@ name: "Run tests" -description: "Runs both unit tests and integration tests and appends to a PR comment" +description: "Runs both unit tests and legacy tests" inputs: - job: - description: "The job name to append to the PR comment" - required: true - default: "unknown" - bin-dir: description: "The directory containing the test binaries" default: "build/bin" @@ -43,39 +38,6 @@ runs: shell: bash continue-on-error: true - - name: Get test results - id: results - run: | - pass="✅ Pass" - fail="❌ Fail" - unit_tests_outcome="${{ steps.unit-tests.outcome }}" - legacy_tests_outcome="${{ steps.legacy-tests.outcome }}" - unit_tests=$( [ "$unit_tests_outcome" = "success" ] && echo $pass || echo $fail ) - legacy_tests=$( [ "$legacy_tests_outcome" = "success" ] && echo $pass || echo $fail ) - echo "unit-tests=$unit_tests" >> $GITHUB_OUTPUT - echo "legacy-tests=$legacy_tests" >> $GITHUB_OUTPUT - shell: bash - - - name: Summary row - id: row - run: | - file="row-${{ inputs.job }}.md" - - row="" - row+="| ${{ inputs.job }} " - row+="| ${{ steps.results.outputs.unit-tests }} " - row+="| ${{ steps.results.outputs.legacy-tests }} " - echo "$row" > $file - - echo "file=$file" > $GITHUB_OUTPUT - shell: bash - - - name: Save summary row - uses: actions/upload-artifact@v7 - with: - name: test-result-${{ inputs.job }} - path: ${{ steps.row.outputs.file }} - - name: Check test outcome if: (steps.unit-tests.outcome != 'success' || steps.legacy-tests.outcome != 'success') run: | diff --git a/.github/actions/test-summary/action.yml b/.github/actions/test-summary/action.yml deleted file mode 100644 index f0890206f..000000000 --- a/.github/actions/test-summary/action.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: "Test Summary" -description: "Creates a markdown table from test results and uploads a summary file" - -inputs: - download-pattern: - description: "The pattern to download test result rows" - required: false - default: test-result-* - - upload-name: - description: "The artifact upload name for the summary" - required: false - default: summary-tests - -runs: - using: "composite" - steps: - - name: Download test result rows - uses: actions/download-artifact@v8 - with: - pattern: ${{ inputs.download-pattern }} - merge-multiple: true - path: rows - - - name: Build markdown table - id: markdown-table - run: | - # Builds a markdown table from the row artifacts. - - header=$(cat <> $GITHUB_OUTPUT - shell: bash - - - name: Set step summary - id: summary - run: | - # Sets the step summary and creates a CI summary file. - - table="${{ steps.markdown-table.outputs.markdown }}" - if [ -z "$table" ]; then - echo "No test results found" | tee $GITHUB_STEP_SUMMARY >&2 - exit 1 - fi - - echo "$table" > $GITHUB_STEP_SUMMARY - - count=$(echo "$table" | { grep -o '❌' || true; } | wc -l) - - # Keep at this indentation level for heredoc. - fail_summary=$(cat <> $GITHUB_OUTPUT - else - # For debugging; don't send success to CI summary (reduce noise). - echo > $file - echo "✅🔬 All tests passed" - fi - - shell: bash - - - name: Upload CI summary - if: steps.summary.outputs.file - uses: actions/upload-artifact@v7 - with: - name: ${{ inputs.upload-name }} - path: ${{ steps.summary.outputs.file }} - if-no-files-found: error diff --git a/.github/workflows/ci-comment.yml b/.github/workflows/ci-comment.yml index 6a4d2a954..de055f229 100644 --- a/.github/workflows/ci-comment.yml +++ b/.github/workflows/ci-comment.yml @@ -75,29 +75,30 @@ jobs: repo_url="${{ github.server_url }}/${{ github.repository }}" run_url="$repo_url/actions/runs/${{ github.event.workflow_run.id }}" + # Build the comment body in a file so newlines survive into marocchino's + # input. Inlining via the message: expression collapses multi-line values. + out="comment.md" { - echo "message<> $GITHUB_OUTPUT + } > $out + + echo "file=$out" >> $GITHUB_OUTPUT - name: Set PR comment - if: steps.get-pr-number.outputs.result && steps.summary.outputs.message + if: steps.get-pr-number.outputs.result && steps.summary.outputs.file uses: marocchino/sticky-pull-request-comment@v2 with: number: ${{ steps.get-pr-number.outputs.result }} header: ${{ github.event.workflow_run.name }} - message: ${{ steps.summary.outputs.message }} + path: ${{ steps.summary.outputs.file }} - name: Delete PR comment - if: ${{ steps.get-pr-number.outputs.result && !steps.summary.outputs.message }} + if: ${{ steps.get-pr-number.outputs.result && !steps.summary.outputs.file }} uses: marocchino/sticky-pull-request-comment@v2 with: number: ${{ steps.get-pr-number.outputs.result }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 332da5dc0..caf51fae4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: # Quality gate to allow PR merge, used in the branch protection rules. ci-passed: runs-on: ubuntu-latest - needs: [build-windows, build-macos, build-linux, build-flatpak, test-results] + needs: [build-windows, build-macos, build-linux, build-flatpak] if: always() steps: @@ -74,29 +74,12 @@ jobs: if ok "${{needs.build-windows.result}}" \ && ok "${{needs.build-macos.result}}" \ && ok "${{needs.build-linux.result}}" \ - && ok "${{needs.test-results.result}}" \ && ok "${{needs.build-flatpak.result}}"; then echo "✅ CI passed" > $GITHUB_STEP_SUMMARY else exit 1 fi - # Summary of test results, combined from test result artifacts. - # Runs even if the tests fail to provide a summary of the failures. - test-results: - needs: [build-windows, build-macos, build-linux] - if: always() && (needs.build-windows.result != 'skipped' || needs.build-macos.result != 'skipped' || needs.build-linux.result != 'skipped') - - runs-on: ubuntu-slim - timeout-minutes: 5 - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Test summary - uses: ./.github/actions/test-summary - lint-clang: runs-on: ubuntu-slim timeout-minutes: 5 @@ -724,7 +707,7 @@ jobs: aws s3 cp ./dist/ "$url" --recursive report: - needs: [build-windows, build-macos, build-linux, build-flatpak, test-results, s3-upload] + needs: [build-windows, build-macos, build-linux, build-flatpak, s3-upload] if: ${{ !cancelled() && github.event_name != 'pull_request' && contains(needs.*.result, 'failure') }} runs-on: ubuntu-latest timeout-minutes: 2 @@ -743,7 +726,6 @@ jobs: [ "${{ needs.build-macos.result }}" = "failure" ] && echo "• macOS build failed" [ "${{ needs.build-linux.result }}" = "failure" ] && echo "• Linux build failed" [ "${{ needs.build-flatpak.result }}" = "failure" ] && echo "• Flatpak build failed" - [ "${{ needs.test-results.result }}" = "failure" ] && echo "• Test summary failed" [ "${{ needs.s3-upload.result }}" = "failure" ] && echo "• S3 upload failed" echo "<$link|View workflow>" echo "EOF" From 4868de53621d28f6faeee05894ed691f5e3da158 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 16:10:25 +0100 Subject: [PATCH 037/144] fix: update CPackWiXFragment identifiers and names to use CMake variables for consistency --- deploy/windows/wix-patch.xml.in | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/deploy/windows/wix-patch.xml.in b/deploy/windows/wix-patch.xml.in index 22cf616ff..4fe496667 100644 --- a/deploy/windows/wix-patch.xml.in +++ b/deploy/windows/wix-patch.xml.in @@ -1,9 +1,9 @@ - + - - + + - - - + + + @@ -41,10 +41,10 @@ - + Date: Tue, 2 Jun 2026 16:44:09 +0100 Subject: [PATCH 038/144] refactor: update translation handling to use upstream project name for consistency --- .github/workflows/ci.yml | 6 ------ CMakeLists.txt | 4 ++++ src/lib/common/Constants.h.in | 1 + src/lib/common/I18N.cpp | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index caf51fae4..599a91a21 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -242,8 +242,6 @@ jobs: - name: Tests uses: ./.github/actions/run-tests timeout-minutes: 2 - with: - job: ${{ matrix.target.name }} - name: Test package uses: ./.github/actions/test-package @@ -407,8 +405,6 @@ jobs: - name: Tests uses: ./.github/actions/run-tests timeout-minutes: 2 - with: - job: ${{ matrix.target.name }} - name: Test package uses: ./.github/actions/test-package @@ -576,8 +572,6 @@ jobs: - name: Tests uses: ./.github/actions/run-tests timeout-minutes: 2 - with: - job: ${{ matrix.target.name }} - name: Test package uses: ./.github/actions/test-package diff --git a/CMakeLists.txt b/CMakeLists.txt index 171c15520..7105dc9fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,10 @@ set(CMAKE_PROJECT_CONTACT "${CMAKE_PROJECT_PROPER_NAME} load(QLocale(), kAppId, s_prefix, m_appTrPath)) { + if (appTranslator->load(QLocale(), kUpstreamId, s_prefix, m_appTrPath)) { m_currentTranslations.append(appTranslator); QCoreApplication::installTranslator(appTranslator); } @@ -173,7 +173,7 @@ void I18N::detectLanguages() m_translations.clear(); m_nameMap.clear(); - QStringList nameFilter = {QStringLiteral("%1_*.qm").arg(kAppId)}; + QStringList nameFilter = {QStringLiteral("%1_*.qm").arg(kUpstreamId)}; QMap appTranslations; QStringList detectedLangCodes; QDir dir(m_appTrPath); From ba459756ff4eae3c3ad4827b4c3e8b28403dc24d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 16:53:40 +0100 Subject: [PATCH 039/144] fix: update action.yml to use 'synergy' instead of 'deskflow' for package testing --- .github/actions/test-package/action.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/actions/test-package/action.yml b/.github/actions/test-package/action.yml index 574eddbb0..b81fe1792 100644 --- a/.github/actions/test-package/action.yml +++ b/.github/actions/test-package/action.yml @@ -15,14 +15,14 @@ runs: shell: bash run: | if [ "$RUNNER_OS" == "macOS" ]; then - dmg=$(ls build/deskflow-*.dmg | head -1) - echo "Y" | hdiutil attach "$dmg" -nobrowse -mountpoint /tmp/deskflow-dmg - /tmp/deskflow-dmg/Deskflow.app/Contents/MacOS/deskflow-core --version - hdiutil detach /tmp/deskflow-dmg + dmg=$(ls build/synergy-*.dmg | head -1) + echo "Y" | hdiutil attach "$dmg" -nobrowse -mountpoint /tmp/synergy-dmg + /tmp/synergy-dmg/Synergy.app/Contents/MacOS/synergy-core --version + hdiutil detach /tmp/synergy-dmg elif [ "$RUNNER_OS" == "Windows" ]; then - 7z x build/deskflow-*-portable.7z -otmp/deskflow-pkg - exe=$(find tmp/deskflow-pkg -name "deskflow-core.exe" -type f | head -1) + 7z x build/synergy-*-portable.7z -otmp/synergy-pkg + exe=$(find tmp/synergy-pkg -name "synergy-core.exe" -type f | head -1) exe_dir=$(dirname "$exe") pkg_root=$(dirname "$exe_dir") export PATH="$exe_dir:$pkg_root:$pkg_root/lib:$PATH" @@ -30,18 +30,18 @@ runs: elif [ "$RUNNER_OS" == "Linux" ]; then if [ "${{inputs.like}}" == "debian" ]; then - apt-get install -y ./build/deskflow-*.deb + apt-get install -y ./build/synergy-*.deb elif [ "${{inputs.like}}" == "fedora" ]; then - dnf install -y build/deskflow-*.rpm + dnf install -y build/synergy-*.rpm elif [ "${{inputs.like}}" == "suse" ]; then - zypper install -y --allow-unsigned-rpm build/deskflow-*.rpm + zypper install -y --allow-unsigned-rpm build/synergy-*.rpm elif [ "${{inputs.like}}" == "arch" ]; then - pacman -U --noconfirm build/deskflow-*.pkg.tar.zst + pacman -U --noconfirm build/synergy-*.pkg.tar.zst else echo "Unknown like: ${{inputs.like}}" exit 1 fi - deskflow-core --version + synergy-core --version else echo "Unknown OS: $RUNNER_OS" From 9fc648f0f132466f2fb434109893286f4cae7035 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 2 Jun 2026 23:52:14 +0100 Subject: [PATCH 040/144] fix: update help URL in metainfo.xml for consistency --- extra/deploy/linux/com.symless.synergy.metainfo.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extra/deploy/linux/com.symless.synergy.metainfo.xml b/extra/deploy/linux/com.symless.synergy.metainfo.xml index 5582ce216..01a9211a0 100644 --- a/extra/deploy/linux/com.symless.synergy.metainfo.xml +++ b/extra/deploy/linux/com.symless.synergy.metainfo.xml @@ -17,8 +17,7 @@ com.symless.synergy.desktop https://synergyapp.io - https://symless.com/synergy/contact - https://symless.com/synergy/help + https://symless.com/synergy From b29566d945f5c8ed40696eb92a9fd475fedb256e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 00:04:19 +0100 Subject: [PATCH 041/144] refactor: update version handling to use base version for consistency across project files --- CMakeLists.txt | 3 +++ deploy/linux/arch/PKGBUILD.in | 2 +- extra/cmake/Synergy.cmake | 6 +++++- extra/cmake/Version.cmake | 6 +++++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7105dc9fc..b65eb4f9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,9 @@ set(CMAKE_PROJECT_DOMAIN "deskflow.org") # by code that reads upstream-named on-disk artifacts (Qt .qm translation files). set(UPSTREAM_PROJECT_NAME ${CMAKE_PROJECT_NAME}) +# Identifier-safe form of the project version; downstream brands overwrite this. +set(CMAKE_PROJECT_VERSION_BASE "${CMAKE_PROJECT_VERSION}") + # Pull in Synergy branding overrides. include(${CMAKE_SOURCE_DIR}/extra/cmake/Synergy.cmake) diff --git a/deploy/linux/arch/PKGBUILD.in b/deploy/linux/arch/PKGBUILD.in index 67d6cc546..f7686dfba 100644 --- a/deploy/linux/arch/PKGBUILD.in +++ b/deploy/linux/arch/PKGBUILD.in @@ -4,7 +4,7 @@ _basename=@CMAKE_PROJECT_NAME@ pkgname=${_basename}-git -pkgver=@CMAKE_PROJECT_VERSION@ +pkgver=@CMAKE_PROJECT_VERSION_BASE@ pkgrel=1 pkgdesc="@CMAKE_PROJECT_DESCRIPTION@" url="@CMAKE_PROJECT_HOMEPAGE_URL@" diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index 10ec89dab..e057233e2 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -58,7 +58,11 @@ option(SYNERGY_VERSION_RELEASE "Release version" OFF) option(SYNERGY_VERSION_SNAPSHOT "Snapshot version" OFF) include(${CMAKE_CURRENT_LIST_DIR}/Version.cmake) -synergy_compute_version("${CMAKE_SOURCE_DIR}" CMAKE_PROJECT_VERSION CMAKE_PROJECT_VERSION_TWEAK) +synergy_compute_version("${CMAKE_SOURCE_DIR}" + CMAKE_PROJECT_VERSION + CMAKE_PROJECT_VERSION_TWEAK + CMAKE_PROJECT_VERSION_BASE +) set(CMAKE_PROJECT_VERSION_MAJOR ${SYNERGY_VERSION_MAJOR}) set(CMAKE_PROJECT_VERSION_MINOR ${SYNERGY_VERSION_MINOR}) set(CMAKE_PROJECT_VERSION_PATCH ${SYNERGY_VERSION_PATCH}) diff --git a/extra/cmake/Version.cmake b/extra/cmake/Version.cmake index 321ecb854..24fedca6d 100644 --- a/extra/cmake/Version.cmake +++ b/extra/cmake/Version.cmake @@ -20,7 +20,10 @@ set(SYNERGY_VERSION_PATCH 0) # OUT_VERSION the composed version string # OUT_TWEAK rev count (used by `windows.rc.in` as the 4th MSI digit so # each snapshot/dev build is a distinguishable Windows installer) -function(synergy_compute_version SOURCE_DIR OUT_VERSION OUT_TWEAK) +# OUT_BASE plain base semver (X.Y.Z) for consumers that reject prerelease +# suffixes (Arch makepkg pkgver, anything treating the version as +# a strict identifier). +function(synergy_compute_version SOURCE_DIR OUT_VERSION OUT_TWEAK OUT_BASE) set(_rev_count 0) find_package(Git QUIET) if(GIT_FOUND) @@ -49,4 +52,5 @@ function(synergy_compute_version SOURCE_DIR OUT_VERSION OUT_TWEAK) set(${OUT_VERSION} "${_version}" PARENT_SCOPE) set(${OUT_TWEAK} "${_tweak}" PARENT_SCOPE) + set(${OUT_BASE} "${_base}" PARENT_SCOPE) endfunction() From 4f896e22a9cb1d49338d8303d5e47fe01e883fc1 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 00:35:34 +0100 Subject: [PATCH 042/144] fix: update synergy_compute_version call to include base version parameter --- extra/cmake/SaveVersion.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/cmake/SaveVersion.cmake b/extra/cmake/SaveVersion.cmake index c8acf6001..7dd7278b5 100644 --- a/extra/cmake/SaveVersion.cmake +++ b/extra/cmake/SaveVersion.cmake @@ -18,6 +18,6 @@ if(NOT VERSION_FILE) endif() get_filename_component(_repo_root "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) -synergy_compute_version("${_repo_root}" _version _tweak) +synergy_compute_version("${_repo_root}" _version _tweak _base) message(STATUS "Computed version: ${_version}") file(WRITE "${VERSION_FILE}" "${_version}") From 038105ff06ec8b0337c6d81db9cef44c3c800268 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 01:10:37 +0100 Subject: [PATCH 043/144] fix: update icon installation paths and rename for synergy branding --- .github/workflows/ci.yml | 2 +- deploy/linux/deploy.cmake | 9 +++++++-- extra/deploy/linux/flatpak/ci-build-lint-exceptions.json | 6 ++++++ 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 extra/deploy/linux/flatpak/ci-build-lint-exceptions.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 599a91a21..771edacde 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -638,7 +638,7 @@ jobs: upload-artifact: false - name: Validate build - run: flatpak-builder-lint --exceptions --user-exceptions deploy/linux/flatpak/ci-build-lint-exceptions.json repo repo + run: flatpak-builder-lint --exceptions --user-exceptions extra/deploy/linux/flatpak/ci-build-lint-exceptions.json repo repo - name: Upload uses: actions/upload-artifact@v7 diff --git a/deploy/linux/deploy.cmake b/deploy/linux/deploy.cmake index f3e2a8bd5..7e31e0d54 100644 --- a/deploy/linux/deploy.cmake +++ b/deploy/linux/deploy.cmake @@ -12,11 +12,16 @@ install( ) # Install our icon -install(FILES ${MY_DIR}/org.deskflow.deskflow.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/512x512/apps/) +install( + FILES ${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.png + RENAME ${CMAKE_PROJECT_REV_FQDN}.png + DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/512x512/apps/ +) # Install our symbolic icon install( - FILES ${CMAKE_SOURCE_DIR}/src/apps/res/icons/deskflow-light/apps/64/org.deskflow.deskflow-symbolic.svg + FILES ${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.svg + RENAME ${CMAKE_PROJECT_REV_FQDN}-symbolic.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/symbolic/apps/ ) diff --git a/extra/deploy/linux/flatpak/ci-build-lint-exceptions.json b/extra/deploy/linux/flatpak/ci-build-lint-exceptions.json new file mode 100644 index 000000000..267662cae --- /dev/null +++ b/extra/deploy/linux/flatpak/ci-build-lint-exceptions.json @@ -0,0 +1,6 @@ +{ + "com.symless.synergy": [ + "metainfo-missing-screenshots", + "appstream-screenshots-not-mirrored-in-ostree" + ] +} From 10efc3e520dba79693158477d8a7002d0a4aa1ee Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 01:18:48 +0100 Subject: [PATCH 044/144] fix: improve package build error handling with retry logic --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 771edacde..b0feaa00f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -355,10 +355,15 @@ jobs: shell: bash run: | for i in $(seq 1 5); do - cmake --build build --config Release -j8 --target package 2>&1 && { echo "Package successful"; break; } + if cmake --build build --config Release -j8 --target package 2>&1; then + echo "Package successful" + exit 0 + fi echo "Package attempt $i failed" sleep 1 done + echo "ERROR: package failed after 5 attempts" >&2 + exit 1 # Sign the DMG itself so Gatekeeper trusts it before extraction. Then # submit to notarytool (synchronous via --wait) and staple the returned From adaf344152dcff834638041628bf1f446395f100 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 01:27:31 +0100 Subject: [PATCH 045/144] fix: update app position reference to use dynamic image name --- deploy/mac/generate_ds_store.applescript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/mac/generate_ds_store.applescript b/deploy/mac/generate_ds_store.applescript index 45c872e88..e9e04ab1c 100644 --- a/deploy/mac/generate_ds_store.applescript +++ b/deploy/mac/generate_ds_store.applescript @@ -39,7 +39,7 @@ on run argv set toolbar visible to false set pathbar visible to false set the bounds to { 200, 120, 800, 520 } - set position of item "Deskflow.app" to { 144, 190 } + set position of item (image_name & ".app") to { 144, 190 } set position of item "Applications" to { 455, 190 } end tell close From a28660085d2df8d22a4fdf4a50628794232b569f Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 09:26:34 +0100 Subject: [PATCH 046/144] fix: update project organization and key for synergy configuration --- sonar-project.properties | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index 82e227410..e7731c01a 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,5 +1,6 @@ -sonar.organization=deskflow -sonar.projectKey=deskflow_deskflow +sonar.projectName=synergy +sonar.organization=symless +sonar.projectKey=symless_synergy sonar.sources=src/apps,src/lib sonar.tests=src/unittests sonar.exclusions=subprojects/**,build/**,translations/** From 3d882b7a0dd587be817bbbc48db8815adecfb835 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 10:31:54 +0100 Subject: [PATCH 047/144] fix: update Qt version for Windows and macOS builds --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0feaa00f..2dee4502d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,13 +110,13 @@ jobs: runs-on: "windows-2022" vcpkg-triplet: x64-windows-release arch: "amd64" - qt-version: 6.10.2 + qt-version: 6.10.3 - name: "windows-2022-arm64" runs-on: "windows-11-arm" vcpkg-triplet: arm64-windows arch: "arm64" - qt-version: 6.10.2 + qt-version: 6.11.1 steps: - name: Checkout @@ -266,7 +266,7 @@ jobs: - name: "macos-arm64" runs-on: macos-15 timeout: 10 - qt-version: 6.10.2 + qt-version: 6.11.1 config-args: '-DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=14' - name: "macos-x64" From 2ef62fa3bc09a0faeeab041ed449dbb454af5d2e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 12:01:12 +0100 Subject: [PATCH 048/144] fix: update default network protocol to Synergy --- src/lib/common/Settings.cpp | 2 +- src/lib/gui/config/ServerConfig.h | 3 +-- src/lib/server/Server.h | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/common/Settings.cpp b/src/lib/common/Settings.cpp index 789ae76a5..2b06c1d05 100644 --- a/src/lib/common/Settings.cpp +++ b/src/lib/common/Settings.cpp @@ -208,7 +208,7 @@ QVariant Settings::defaultValue(const QString &key) return 1.0; if (key == Server::Protocol) - return QVariant::fromValue(NetworkProtocol::Barrier); + return QVariant::fromValue(NetworkProtocol::Synergy); return QVariant(); } diff --git a/src/lib/gui/config/ServerConfig.h b/src/lib/gui/config/ServerConfig.h index 5196c56b2..492ea542c 100644 --- a/src/lib/gui/config/ServerConfig.h +++ b/src/lib/gui/config/ServerConfig.h @@ -25,8 +25,7 @@ class ServerConfigDialog; namespace deskflow::gui { -// The default protocol was decided by a community vote. -const auto kDefaultProtocol = NetworkProtocol::Barrier; +const auto kDefaultProtocol = NetworkProtocol::Synergy; } // namespace deskflow::gui diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h index 424cf6028..444f8ac8e 100644 --- a/src/lib/server/Server.h +++ b/src/lib/server/Server.h @@ -419,7 +419,7 @@ class Server ClipboardInfo m_clipboards[kClipboardEnd]; // used in hello message sent to the client - NetworkProtocol m_protocol = NetworkProtocol::Barrier; + NetworkProtocol m_protocol = NetworkProtocol::Synergy; // the sequence number of enter messages uint32_t m_seqNum = 0; From f4074cfd286d4f9b7b093a1f325498ca1375ff13 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 16:00:40 +0100 Subject: [PATCH 049/144] fix: disable macOS bundle creation for headless builds --- extra/cmake/Synergy.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index e057233e2..4207815d0 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -31,6 +31,11 @@ if(SYNERGY_CORE_FLAVOR) set(BUILD_GUI OFF CACHE BOOL "Build GUI") set(BUILD_TESTS OFF CACHE BOOL "Build tests") set(BUILD_INSTALLER OFF CACHE BOOL "Build installer") + # On macOS the core normally nests into the GUI .app bundle + # ($), but with BUILD_GUI=OFF that target + # doesn't exist and cmake generation fails. Headless builds ship the core + # binary directly, so disable the bundle too. + set(BUILD_OSX_BUNDLE OFF CACHE BOOL "Build mac os bundle") endif() # Don't run unit tests as part of the build. Devs can opt back in with From 59657c3a77fe417947ca195486ecdc2ef631319e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 17:03:33 +0100 Subject: [PATCH 050/144] fix: unify macOS deployment target handling in CI and CMake --- .github/workflows/ci.yml | 6 ++++-- extra/cmake/Synergy.cmake | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dee4502d..5a2f96151 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -267,13 +267,15 @@ jobs: runs-on: macos-15 timeout: 10 qt-version: 6.11.1 - config-args: '-DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=14' + # Deployment target is set once in extra/cmake/Synergy.cmake (single + # source of truth); don't pass -DCMAKE_OSX_DEPLOYMENT_TARGET here. + config-args: '-DCMAKE_OSX_ARCHITECTURES="arm64"' - name: "macos-x64" runs-on: macos-15-intel timeout: 20 qt-version: 6.9.3 - config-args: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET=12 -DCMAKE_OSX_SYSROOT=/Applications/Xcode_16.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk' + config-args: '-DCMAKE_OSX_ARCHITECTURES="x86_64" -DCMAKE_OSX_SYSROOT=/Applications/Xcode_16.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk' steps: - name: Checkout diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index 4207815d0..e614a50f7 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -24,6 +24,16 @@ else() endif() add_compile_definitions(SYNERGY_DISPLAY_NAME="${SYNERGY_DISPLAY_NAME}") +# Single source of truth for the minimum macOS version. Synergy is long-term +# stable (unlike upstream, which tracks recent macOS), so we target the oldest +# macOS the linked Qt 6.x supports, the same value for every architecture. +# Both the build (here) and the packaged app's advertised minimum +# (apps/gui-electron/dist/package.config.js, which reads this line) derive from +# this one value, so they can't drift. CI must NOT pass -DCMAKE_OSX_DEPLOYMENT_TARGET. +if(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "12") +endif() + # Core flavor seeds headless-build defaults (no GUI, no tests, no installer). # No `FORCE` on the cache writes: the seed only fills empty slots, so a user # passing -DBUILD_GUI=ON alongside the flavor flag still wins. From 570fa1dcb59f2a364e8e6492c58f8a29a5e1acfd Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 20:26:03 +0100 Subject: [PATCH 051/144] fix: enhance CI configuration and dependency management for Wayland support --- .../actions/install-dependencies/action.yml | 7 +++- .github/workflows/ci.yml | 36 ++++++++++++++++ CMakeLists.txt | 9 ++-- src/lib/common/Settings.cpp | 6 +++ src/lib/gui/MainWindow.cpp | 6 +-- src/lib/gui/MainWindow.ui | 2 +- src/lib/gui/dialogs/AboutDialog.cpp | 2 +- src/lib/gui/dialogs/ClientConfigDialog.cpp | 15 ++++--- src/lib/gui/dialogs/ServerConfigDialog.cpp | 2 +- src/lib/gui/dialogs/ServerConfigDialog.ui | 8 ++-- src/lib/gui/dialogs/SettingsDialog.cpp | 6 +-- src/lib/gui/dialogs/SettingsDialog.ui | 6 +-- src/lib/gui/widgets/SearchWidget.cpp | 6 +-- src/lib/gui/widgets/StatusBar.cpp | 2 +- src/lib/platform/CMakeLists.txt | 42 ++++++++++++------- 15 files changed, 108 insertions(+), 47 deletions(-) diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index af9c1680e..5a2c402ab 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -38,7 +38,12 @@ runs: xorg-dev libx11-dev libxtst-dev libssl-dev \ libglib2.0-dev libxkbfile-dev qt6-base-dev qt6-tools-dev \ libgtk-3-dev libgtest-dev libgmock-dev \ - libei-dev libportal-dev help2man doxygen -y >/dev/null + help2man doxygen -y >/dev/null + + # libei/libportal are absent on older LTS (e.g. 22.04); those legs build X11-only. + if apt-cache show libei-dev >/dev/null 2>&1 && apt-cache show libportal-dev >/dev/null 2>&1; then + apt install -qqq libei-dev libportal-dev -y >/dev/null + fi elif [ ${{inputs.like}} == "fedora" ]; then dnf install -y cmake make ninja-build gcc-c++ rpm-build openssl-devel \ glib2-devel libXtst-devel libxkbfile-devel qt6-qtbase-devel qt6-qttools-devel \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a2f96151..858ce8829 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -458,6 +458,18 @@ jobs: like: "debian" config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + - name: "fedora-42-x86_64" + runs-on: ubuntu-latest + container: fedora:42 + like: "fedora" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + + - name: "fedora-42-arm64" + runs-on: ubuntu-24.04-arm + container: fedora:42 + like: "fedora" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" + - name: "fedora-43-x86_64" runs-on: ubuntu-latest container: fedora:43 @@ -500,6 +512,30 @@ jobs: like: "arch" config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DEV_DOCS=ON" + - name: "ubuntu-22.04-x86_64" + runs-on: ubuntu-latest + container: ubuntu:22.04 + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WAYLAND_SUPPORT=OFF" + + - name: "ubuntu-22.04-arm64" + runs-on: ubuntu-24.04-arm + container: ubuntu:22.04 + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WAYLAND_SUPPORT=OFF" + + - name: "ubuntu-24.04-x86_64" + runs-on: ubuntu-latest + container: ubuntu:24.04 + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WAYLAND_SUPPORT=OFF" + + - name: "ubuntu-24.04-arm64" + runs-on: ubuntu-24.04-arm + container: ubuntu:24.04 + like: "debian" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WAYLAND_SUPPORT=OFF" + - name: "ubuntu-26.04-x86_64" runs-on: ubuntu-latest container: ubuntu:26.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index b65eb4f9f..f07d5a69d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: (C) 2009 - 2012 Nick Bolton # SPDX-License-Identifier: MIT -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 3.22) # Link items by fill path cmake_policy(SET CMP0003 NEW) @@ -90,9 +90,9 @@ message(STATUS "Building ${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}") # Set lib versions set(REQUIRED_OPENSSL_VERSION 3.0) -set(REQUIRED_LIBEI_VERSION 1.3) -set(REQUIRED_LIBPORTAL_VERSION 0.9.1) -set(REQUIRED_QT_VERSION 6.7.0) +set(REQUIRED_LIBEI_VERSION 1.0) +set(REQUIRED_LIBPORTAL_VERSION 0.8.0) +set(REQUIRED_QT_VERSION 6.2.0) if (WIN32) # VSCMD_ARG_TGT_ARCH is set on CI @@ -146,6 +146,7 @@ add_definitions(-DQT_NO_KEYWORDS) #Options for Linux platform support if(UNIX AND NOT APPLE) option(BUILD_X11_SUPPORT "Build with x11 support" ON) + option(BUILD_WAYLAND_SUPPORT "Build with Wayland (libei/libportal) support" ON) elseif (APPLE) option(BUILD_OSX_BUNDLE "Build mac os bundle" ON) endif() diff --git a/src/lib/common/Settings.cpp b/src/lib/common/Settings.cpp index 2b06c1d05..1d2839110 100644 --- a/src/lib/common/Settings.cpp +++ b/src/lib/common/Settings.cpp @@ -81,9 +81,15 @@ Settings::Settings(QObject *parent) : QObject(parent) qInfo().noquote() << "initial settings file:" << m_settings->fileName(); const auto xdgStateHome = qEnvironmentVariable("XDG_STATE_HOME"); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) const auto stateBase = !xdgStateHome.isEmpty() ? xdgStateHome : QStandardPaths::standardLocations(QStandardPaths::GenericStateLocation).at(0); +#else + const auto stateBase = !xdgStateHome.isEmpty() + ? xdgStateHome + : QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); +#endif const auto stateFile = QStringLiteral("%1/%2.state").arg(stateBase, kAppName); m_stateSettings = new QSettings(stateFile, QSettings::IniFormat, this); diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index 74f9e9549..c6a32ff64 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -93,7 +93,7 @@ MainWindow::MainWindow() // Setup Actions m_actionAbout->setMenuRole(QAction::AboutRole); - m_actionAbout->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::HelpAbout)); + m_actionAbout->setIcon(QIcon::fromTheme(QStringLiteral("help-about"))); m_actionMinimize->setIcon(QIcon::fromTheme(QStringLiteral("window-minimize-pip"))); m_actionRestore->setIcon(QIcon::fromTheme(QStringLiteral("window-restore-pip"))); @@ -122,7 +122,7 @@ MainWindow::MainWindow() m_actionRestartCore->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); m_actionRestartCore->setMenuRole(QAction::NoRole); - m_actionStopCore->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::ProcessStop)); + m_actionStopCore->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); m_actionStopCore->setMenuRole(QAction::NoRole); m_actionReportBug->setIcon(QIcon::fromTheme(QStringLiteral("tools-report-bug"))); @@ -575,7 +575,7 @@ void MainWindow::updateModeControlLabels() startText = tr("Start"); stopText = tr("Stop"); startIcon = QIcon::fromTheme(QStringLiteral("system-run")); - stopIcon = QIcon::fromTheme(QIcon::ThemeIcon::ProcessStop); + stopIcon = QIcon::fromTheme(QStringLiteral("process-stop")); } else { startText = tr("Connect"); stopText = tr("Disconnect"); diff --git a/src/lib/gui/MainWindow.ui b/src/lib/gui/MainWindow.ui index 733be38ca..f85b494ae 100644 --- a/src/lib/gui/MainWindow.ui +++ b/src/lib/gui/MainWindow.ui @@ -290,7 +290,7 @@ Export server configuration - + diff --git a/src/lib/gui/dialogs/AboutDialog.cpp b/src/lib/gui/dialogs/AboutDialog.cpp index 66c03b11f..3b6779672 100644 --- a/src/lib/gui/dialogs/AboutDialog.cpp +++ b/src/lib/gui/dialogs/AboutDialog.cpp @@ -33,7 +33,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui{std::make_unique ui->lblIcon->setPixmap(QPixmap(QIcon::fromTheme(kRevFqdnName).pixmap(QSize().scaled(pixmapSize, Qt::KeepAspectRatio))) ); - ui->btnCopyVersion->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::EditCopy)); + ui->btnCopyVersion->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy"))); connect(ui->btnCopyVersion, &QPushButton::clicked, this, &AboutDialog::copyVersionText); ui->lblVersion->setText(kDisplayVersion); diff --git a/src/lib/gui/dialogs/ClientConfigDialog.cpp b/src/lib/gui/dialogs/ClientConfigDialog.cpp index 12503fb5f..642601948 100644 --- a/src/lib/gui/dialogs/ClientConfigDialog.cpp +++ b/src/lib/gui/dialogs/ClientConfigDialog.cpp @@ -52,13 +52,16 @@ void ClientConfigDialog::initConnections() const &ClientConfigDialog::resetToDefault ); - connect( - ui->cbDynamicConnectTime, &QCheckBox::checkStateChanged, this, &ClientConfigDialog::setButtonBoxEnabledButtons - ); - connect(ui->cbLanguageSync, &QCheckBox::checkStateChanged, this, &ClientConfigDialog::setButtonBoxEnabledButtons); - connect(ui->cbYScrollInvert, &QCheckBox::checkStateChanged, this, &ClientConfigDialog::setButtonBoxEnabledButtons); +#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) + const auto checkboxSignal = &QCheckBox::checkStateChanged; +#else + const auto checkboxSignal = &QCheckBox::stateChanged; +#endif + connect(ui->cbDynamicConnectTime, checkboxSignal, this, &ClientConfigDialog::setButtonBoxEnabledButtons); + connect(ui->cbLanguageSync, checkboxSignal, this, &ClientConfigDialog::setButtonBoxEnabledButtons); + connect(ui->cbYScrollInvert, checkboxSignal, this, &ClientConfigDialog::setButtonBoxEnabledButtons); connect(ui->sbYScrollScale, &QDoubleSpinBox::valueChanged, this, &ClientConfigDialog::setButtonBoxEnabledButtons); - connect(ui->cbXScrollInvert, &QCheckBox::checkStateChanged, this, &ClientConfigDialog::setButtonBoxEnabledButtons); + connect(ui->cbXScrollInvert, checkboxSignal, this, &ClientConfigDialog::setButtonBoxEnabledButtons); connect(ui->sbXScrollScale, &QDoubleSpinBox::valueChanged, this, &ClientConfigDialog::setButtonBoxEnabledButtons); } diff --git a/src/lib/gui/dialogs/ServerConfigDialog.cpp b/src/lib/gui/dialogs/ServerConfigDialog.cpp index 35b0adb64..faabb487e 100644 --- a/src/lib/gui/dialogs/ServerConfigDialog.cpp +++ b/src/lib/gui/dialogs/ServerConfigDialog.cpp @@ -67,7 +67,7 @@ ServerConfigDialog::ServerConfigDialog(QWidget *parent, ServerConfig &config) // the developer was looking at, and it's easy to accidentally save that. ui->tabWidget->setCurrentIndex(0); - ui->btnBrowseConfigFile->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::DocumentOpen)); + ui->btnBrowseConfigFile->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); ui->lineConfigFile->setText(serverConfig().configFile()); ui->rbProtocolSynergy->setChecked(serverConfig().protocol() == NetworkProtocol::Synergy); diff --git a/src/lib/gui/dialogs/ServerConfigDialog.ui b/src/lib/gui/dialogs/ServerConfigDialog.ui index e6cb3a887..599127388 100644 --- a/src/lib/gui/dialogs/ServerConfigDialog.ui +++ b/src/lib/gui/dialogs/ServerConfigDialog.ui @@ -186,7 +186,7 @@ &New - + @@ -212,7 +212,7 @@ &Remove - + @@ -255,7 +255,7 @@ Ne&w - + @@ -281,7 +281,7 @@ Re&move - + diff --git a/src/lib/gui/dialogs/SettingsDialog.cpp b/src/lib/gui/dialogs/SettingsDialog.cpp index 2b3b773ce..8a3409e06 100644 --- a/src/lib/gui/dialogs/SettingsDialog.cpp +++ b/src/lib/gui/dialogs/SettingsDialog.cpp @@ -51,7 +51,7 @@ SettingsDialog::SettingsDialog(QWidget *parent, const ServerConfig &serverConfig updateText(); ui->comboTlsKeyLength->setItemIcon(0, QIcon::fromTheme(QStringLiteral("security-medium"))); - ui->comboTlsKeyLength->setItemIcon(1, QIcon::fromTheme(QIcon::ThemeIcon::SecurityHigh)); + ui->comboTlsKeyLength->setItemIcon(1, QIcon::fromTheme(QStringLiteral("security-high"))); ui->lblTlsCertInfo->setFixedSize(28, 28); ui->rbIconMono->setIcon(QIcon::fromTheme(QStringLiteral("%1-symbolic").arg(kRevFqdnName))); @@ -369,11 +369,11 @@ void SettingsDialog::updateKeyLengthOnFile(const QString &path) } auto length = TlsUtility::getCertKeyLength(path); - auto labelIcon = QPixmap(QIcon::fromTheme(QIcon::ThemeIcon::SecurityLow).pixmap(24, 24)); + auto labelIcon = QPixmap(QIcon::fromTheme(QStringLiteral("security-low")).pixmap(24, 24)); if (length == 2048) labelIcon = QPixmap(QIcon::fromTheme(QStringLiteral("security-medium")).pixmap(24, 24)); if (length == 4096) - labelIcon = QPixmap(QIcon::fromTheme(QIcon::ThemeIcon::SecurityHigh).pixmap(24, 24)); + labelIcon = QPixmap(QIcon::fromTheme(QStringLiteral("security-high")).pixmap(24, 24)); ui->lblTlsCertInfo->setPixmap(labelIcon); ui->lblTlsCertInfo->setToolTip(QStringLiteral("Key length: %1 bits").arg(QString::number(length))); diff --git a/src/lib/gui/dialogs/SettingsDialog.ui b/src/lib/gui/dialogs/SettingsDialog.ui index ee403285b..088ce8de1 100644 --- a/src/lib/gui/dialogs/SettingsDialog.ui +++ b/src/lib/gui/dialogs/SettingsDialog.ui @@ -404,7 +404,7 @@ - + true @@ -641,7 +641,7 @@ - + true @@ -702,7 +702,7 @@ Regenerate certificate - + diff --git a/src/lib/gui/widgets/SearchWidget.cpp b/src/lib/gui/widgets/SearchWidget.cpp index bbc8e235c..24acd2c3b 100644 --- a/src/lib/gui/widgets/SearchWidget.cpp +++ b/src/lib/gui/widgets/SearchWidget.cpp @@ -27,17 +27,17 @@ SearchWidget::SearchWidget(QWidget *parent) m_btnToggle->setCheckable(true); m_btnToggle->setChecked(false); m_btnToggle->setFlat(true); - m_btnToggle->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::SystemSearch)); + m_btnToggle->setIcon(QIcon::fromTheme(QStringLiteral("system-search"))); m_btnToggle->setIconSize(iconSize); m_btnNext->setFixedSize(maxBtnSize); m_btnNext->setFlat(true); - m_btnNext->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::GoDown)); + m_btnNext->setIcon(QIcon::fromTheme(QStringLiteral("go-down"))); m_btnNext->setIconSize(iconSize); m_btnPrev->setFixedSize(maxBtnSize); m_btnPrev->setFlat(true); - m_btnPrev->setIcon(QIcon::fromTheme(QIcon::ThemeIcon::GoUp)); + m_btnPrev->setIcon(QIcon::fromTheme(QStringLiteral("go-up"))); m_btnPrev->setIconSize(iconSize); m_searchLine->setMaximumHeight(maxBtnSize.height()); diff --git a/src/lib/gui/widgets/StatusBar.cpp b/src/lib/gui/widgets/StatusBar.cpp index cd22323d5..019171bdf 100644 --- a/src/lib/gui/widgets/StatusBar.cpp +++ b/src/lib/gui/widgets/StatusBar.cpp @@ -197,7 +197,7 @@ void StatusBar::updateTimerLabel() void StatusBar::setSecurityIcon(bool encrypted) { - const auto icon = QIcon::fromTheme(encrypted ? QIcon::ThemeIcon::SecurityHigh : QIcon::ThemeIcon::SecurityLow); + const auto icon = QIcon::fromTheme(encrypted ? QStringLiteral("security-high") : QStringLiteral("security-low")); m_lblSecurityIcon->setPixmap(icon.pixmap(QSize(32, 32))); m_encrypted = encrypted; setSecurityLevel(m_securityLevel); diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index d8af35a0e..02916dec8 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -4,20 +4,24 @@ # SPDX-License-Identifier: MIT if(UNIX AND NOT APPLE) - pkg_check_modules(LIBEI REQUIRED QUIET "libei-1.0 >= ${REQUIRED_LIBEI_VERSION}") - message(STATUS "libei version: ${LIBEI_VERSION}") + if(BUILD_WAYLAND_SUPPORT) + pkg_check_modules(LIBEI REQUIRED QUIET "libei-1.0 >= ${REQUIRED_LIBEI_VERSION}") + message(STATUS "libei version: ${LIBEI_VERSION}") - pkg_check_modules(LIBPORTAL REQUIRED QUIET "libportal >= ${REQUIRED_LIBPORTAL_VERSION}") - message(STATUS "libportal version: ${LIBPORTAL_VERSION}") + pkg_check_modules(LIBPORTAL REQUIRED QUIET "libportal >= ${REQUIRED_LIBPORTAL_VERSION}") + message(STATUS "libportal version: ${LIBPORTAL_VERSION}") - # Check if we have the restore ability in the IC portal - include(CMakePushCheckState) - include(CheckSymbolExists) - cmake_push_check_state(RESET) - set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${LIBPORTAL_INCLUDE_DIRS};${GLIB2_INCLUDE_DIRS}") - set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};${LIBPORTAL_LINK_LIBRARIES};${GLIB2_LINK_LIBRARIES}") - check_symbol_exists(xdp_input_capture_session_get_restore_token "libportal/inputcapture.h" HAVE_IC_RESTORE) - cmake_pop_check_state() + # Check if we have the restore ability in the IC portal + include(CMakePushCheckState) + include(CheckSymbolExists) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${LIBPORTAL_INCLUDE_DIRS};${GLIB2_INCLUDE_DIRS}") + set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};${LIBPORTAL_LINK_LIBRARIES};${GLIB2_LINK_LIBRARIES}") + check_symbol_exists(xdp_input_capture_session_get_restore_token "libportal/inputcapture.h" HAVE_IC_RESTORE) + cmake_pop_check_state() + else() + message(STATUS "Wayland support disabled (BUILD_WAYLAND_SUPPORT=OFF): building X11-only") + endif() endif() if(WIN32) @@ -186,11 +190,17 @@ if(UNIX) if(${LIBXKBCOMMON_VERSION} VERSION_GREATER_EQUAL "1.10") target_compile_definitions(platform PRIVATE HAVE_XKB_KEYMAP_MOD_GET_MASK=1) endif() - target_compile_definitions(platform PUBLIC WINAPI_LIBEI WINAPI_LIBPORTAL) - if (HAVE_IC_RESTORE) - target_compile_definitions(platform PUBLIC HAVE_LIBPORTAL_INPUTCAPTURE_RESTORE) + if(LIBEI_FOUND) + target_compile_definitions(platform PUBLIC WINAPI_LIBEI) + target_include_directories(platform PUBLIC ${LIBEI_INCLUDE_DIRS}) + endif() + if(LIBPORTAL_FOUND) + target_compile_definitions(platform PUBLIC WINAPI_LIBPORTAL) + if (HAVE_IC_RESTORE) + target_compile_definitions(platform PUBLIC HAVE_LIBPORTAL_INPUTCAPTURE_RESTORE) + endif() + target_include_directories(platform PUBLIC ${LIBPORTAL_INCLUDE_DIRS}) endif() - target_include_directories(platform PUBLIC ${LIBEI_INCLUDE_DIRS} ${LIBPORTAL_INCLUDE_DIRS}) target_link_libraries( platform Qt6::DBus From 9563666f1144f295cfaf380b76c7487d39d96d2a Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 20:27:42 +0100 Subject: [PATCH 052/144] fix: simplify state file path assignment in Settings.cpp --- src/lib/common/Settings.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib/common/Settings.cpp b/src/lib/common/Settings.cpp index 1d2839110..dba86d356 100644 --- a/src/lib/common/Settings.cpp +++ b/src/lib/common/Settings.cpp @@ -86,9 +86,8 @@ Settings::Settings(QObject *parent) : QObject(parent) ? xdgStateHome : QStandardPaths::standardLocations(QStandardPaths::GenericStateLocation).at(0); #else - const auto stateBase = !xdgStateHome.isEmpty() - ? xdgStateHome - : QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + const auto stateBase = + !xdgStateHome.isEmpty() ? xdgStateHome : QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); #endif const auto stateFile = QStringLiteral("%1/%2.state").arg(stateBase, kAppName); From 38bec6a733e340a81a6276befb5f5fa48f9bd7f8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 20:29:36 +0100 Subject: [PATCH 053/144] fix: streamline state file path assignment in Settings.cpp --- src/lib/common/Settings.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lib/common/Settings.cpp b/src/lib/common/Settings.cpp index dba86d356..ab106bdf2 100644 --- a/src/lib/common/Settings.cpp +++ b/src/lib/common/Settings.cpp @@ -82,13 +82,11 @@ Settings::Settings(QObject *parent) : QObject(parent) const auto xdgStateHome = qEnvironmentVariable("XDG_STATE_HOME"); #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) - const auto stateBase = !xdgStateHome.isEmpty() - ? xdgStateHome - : QStandardPaths::standardLocations(QStandardPaths::GenericStateLocation).at(0); + const auto stateDir = QStandardPaths::standardLocations(QStandardPaths::GenericStateLocation).at(0); #else - const auto stateBase = - !xdgStateHome.isEmpty() ? xdgStateHome : QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); + const auto stateDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); #endif + const auto stateBase = !xdgStateHome.isEmpty() ? xdgStateHome : stateDir; const auto stateFile = QStringLiteral("%1/%2.state").arg(stateBase, kAppName); m_stateSettings = new QSettings(stateFile, QSettings::IniFormat, this); From f4684d18ac349602b144a2d2e9dfd6bc1ed4952c Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 20:33:01 +0100 Subject: [PATCH 054/144] fix: update Lint Clang action description and remove CI comment workflow --- .github/actions/lint-clang/action.yml | 36 +-------- .github/workflows/ci-comment.yml | 106 -------------------------- 2 files changed, 4 insertions(+), 138 deletions(-) delete mode 100644 .github/workflows/ci-comment.yml diff --git a/.github/actions/lint-clang/action.yml b/.github/actions/lint-clang/action.yml index 07eb9b9c9..0b83902fb 100644 --- a/.github/actions/lint-clang/action.yml +++ b/.github/actions/lint-clang/action.yml @@ -1,5 +1,5 @@ name: "Lint Clang" -description: "Checks for Clang lint errors and posts a helpful comment" +description: "Fails CI if any source file doesn't match clang-format" runs: using: "composite" @@ -43,48 +43,20 @@ runs: if-no-files-found: error - name: Set summary - id: summary if: steps.changes.outputs.diff run: | code_block="\`\`\`" - clang_version=$(clang-format --version | sed -n 's/^.*version //p') + cat <> $GITHUB_STEP_SUMMARY + ❌ \`clang-format\` \`v${clang_version}\`: changes don't match the code style. - summary=$(cat <> $GITHUB_STEP_SUMMARY - - pr_comment=$(cat <> $GITHUB_OUTPUT shell: bash - - name: Upload CI summary as artifact - if: steps.summary.outputs.file - uses: actions/upload-artifact@v7 - with: - name: summary-clang-format - path: ${{ steps.summary.outputs.file }} - if-no-files-found: error - - name: Fail if diff exists if: steps.changes.outputs.diff run: exit 1 diff --git a/.github/workflows/ci-comment.yml b/.github/workflows/ci-comment.yml deleted file mode 100644 index de055f229..000000000 --- a/.github/workflows/ci-comment.yml +++ /dev/null @@ -1,106 +0,0 @@ -# Uses `workflow_run` to securely add a comment to the PR that triggered CI. -# -# Important: For security, the `workflow_run` trigger runs the workflow from the default branch, -# so any changes to this workflow must be made in the default branch to take effect. -# -# This workflow is neccesary because PRs opened by external forks do not have write access to -# their PR, so the PR-triggered workflow can't add comments to the PR. -# If this was in the CI workflow, it'd be tidier and easier to debug but unfortunately that -# isn't possible due to the lower security context that the CI workflow runs in. -name: CI comment - -on: - workflow_run: - workflows: ["CI"] - types: - - completed - -jobs: - summary: - if: github.event.workflow_run.event == 'pull_request' - runs-on: ubuntu-slim - - steps: - # If the workflow fails before uploading summaries, no artifacts exist and this step fails. - # Allow failure so the "Delete PR comment" step still runs to clean up stale comments. - - name: Download summaries - id: download - uses: actions/download-artifact@v8 - continue-on-error: true - with: - run-id: ${{ github.event.workflow_run.id }} - pattern: summary-* - path: summaries - github-token: ${{ secrets.GITHUB_TOKEN }} - - # When the triggering workflow is running in a restricted security context, some - # data is omitted from `github.event.workflow_run` (including `pull_requests`). - # Therefore, we have to take some extra steps to find out the PR that triggered - # the original workflow. The simplest way is to iterate through our open PRs and - # find the one with the same head SHA as the triggering workflow. - - name: Get PR number - id: get-pr-number - uses: actions/github-script@v6 - with: - script: | - const head_sha = "${{ github.event.workflow_run.head_sha }}"; - console.log("Finding PR for SHA:", head_sha); - - const { data: prs } = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - }); - - const pr = prs.find(pr => pr.head.sha === head_sha); - if (pr) { - console.log("Found PR:", pr.number); - return pr.number; - } else { - core.warning("PR not found for SHA (maybe removed by force push)"); - } - - - name: Merge summaries - if: steps.get-pr-number.outputs.result - id: summary - run: | - ls -R - - files=$(find summaries -type f 2>/dev/null || true) - if [ -z "$files" ]; then - echo "No files found in dir: $dir" - exit 0 - fi - - repo_url="${{ github.server_url }}/${{ github.repository }}" - run_url="$repo_url/actions/runs/${{ github.event.workflow_run.id }}" - - # Build the comment body in a file so newlines survive into marocchino's - # input. Inlining via the message: expression collapses multi-line values. - out="comment.md" - { - echo "## CI Summary" - for file in $files; do - cat $file - done - echo - echo "[Full summary]($run_url) (scroll down)" - } > $out - - echo "file=$out" >> $GITHUB_OUTPUT - - - name: Set PR comment - if: steps.get-pr-number.outputs.result && steps.summary.outputs.file - uses: marocchino/sticky-pull-request-comment@v2 - with: - number: ${{ steps.get-pr-number.outputs.result }} - header: ${{ github.event.workflow_run.name }} - path: ${{ steps.summary.outputs.file }} - - - name: Delete PR comment - if: ${{ steps.get-pr-number.outputs.result && !steps.summary.outputs.file }} - uses: marocchino/sticky-pull-request-comment@v2 - with: - number: ${{ steps.get-pr-number.outputs.result }} - header: ${{ github.event.workflow_run.name }} - delete: true From 5b8aa8b83c95dfab0367e4e0f81943f0815caa0a Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 20:39:45 +0100 Subject: [PATCH 055/144] fix: add missing Qt6 localization tools for Linux dependency installation --- .github/actions/install-dependencies/action.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 5a2c402ab..1b7b31aea 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -34,9 +34,12 @@ runs: elif [ "$RUNNER_OS" == "Linux" ]; then if [ ${{inputs.like}} == "debian" ]; then apt update -qqq > /dev/null + # qt6-l10n-tools + qt6-tools-dev-tools provide lconvert/lupdate/lrelease, which + # qt6-tools-dev's LinguistTools cmake references but doesn't pull in on 22.04. apt install -qqq cmake build-essential ninja-build \ xorg-dev libx11-dev libxtst-dev libssl-dev \ libglib2.0-dev libxkbfile-dev qt6-base-dev qt6-tools-dev \ + qt6-l10n-tools qt6-tools-dev-tools \ libgtk-3-dev libgtest-dev libgmock-dev \ help2man doxygen -y >/dev/null From e1298bf1b4b5af7d3adb300efc02141720e24ac2 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 20:51:28 +0100 Subject: [PATCH 056/144] fix: optimize LogLevel option handling and improve string manipulation in Settings and SecureUtils --- src/lib/common/LogLevel.h | 11 ++++++++++- src/lib/common/Settings.cpp | 10 ++++++---- src/lib/net/SecureUtils.cpp | 6 +++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/lib/common/LogLevel.h b/src/lib/common/LogLevel.h index a6e790a85..3fe39fb7f 100644 --- a/src/lib/common/LogLevel.h +++ b/src/lib/common/LogLevel.h @@ -42,9 +42,18 @@ class LogLevel : private QObject return m_levelOptions.at(level); } + static qsizetype indexOfOption(const QString &level) + { + for (qsizetype i = 0; i < m_levelOptions.size(); ++i) { + if (m_levelOptions.at(i).compare(level, Qt::CaseInsensitive) == 0) + return i; + } + return -1; + } + static LogLevel::Level fromOption(const QString &level) { - const auto index = m_levelOptions.indexOf(level, 0, Qt::CaseInsensitive); + const auto index = indexOfOption(level); if (index < 0 || index >= m_levelOptions.count()) return LogLevel::Level::Info; return LogLevel::Level(index); diff --git a/src/lib/common/Settings.cpp b/src/lib/common/Settings.cpp index ab106bdf2..d5345e363 100644 --- a/src/lib/common/Settings.cpp +++ b/src/lib/common/Settings.cpp @@ -100,10 +100,12 @@ Settings::Settings(QObject *parent) : QObject(parent) void Settings::upgradeSettings() { const auto logValue = m_settings->value(Settings::Log::Level).toString(); - if (!LogLevel::logLevelOptions().contains(logValue, Qt::CaseInsensitive)) + if (LogLevel::indexOfOption(logValue) < 0) m_settings->setValue(Settings::Log::Level, defaultValue(Settings::Log::Level)); - for (const auto [oldKey, newKey] : m_upgradedMap.asKeyValueRange()) { + for (auto it = m_upgradedMap.cbegin(); it != m_upgradedMap.cend(); ++it) { + const auto &oldKey = it.key(); + const auto &newKey = it.value(); if (m_settings->contains(oldKey) && !m_settings->contains(newKey)) { m_settings->setValue(newKey, m_settings->value(oldKey)); } @@ -154,9 +156,9 @@ QString Settings::cleanComputerName(const QString &name) cleanName.replace(space, underscore); cleanName.replace(nameRegex, {}); while (cleanName.startsWith(hyphen) || cleanName.startsWith(underscore) || cleanName.startsWith(period)) - cleanName.removeFirst(); + cleanName.remove(0, 1); while (cleanName.endsWith(hyphen) || cleanName.endsWith(underscore) || cleanName.endsWith(period)) - cleanName.removeLast(); + cleanName.chop(1); if (cleanName.length() > 255) { cleanName.truncate(255); cleanName = cleanComputerName(cleanName); diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index f1833bc53..f2981a559 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -120,13 +120,13 @@ QString formatSSLFingerprintColumns(const QByteArray &fingerprint) QString formattedString; while (!hex.isEmpty()) { const auto take = std::min(kMaxColumns, hex.size()); - formattedString.append(hex.first(take)); + formattedString.append(hex.left(take)); hex.remove(0, take); if (formattedString.endsWith(QLatin1Char(':'))) - formattedString.removeLast(); + formattedString.chop(1); formattedString.append(QLatin1Char('\n')); } - formattedString.removeLast(); + formattedString.chop(1); return formattedString; } From cf04a2aa67af6e8d776df16cb446025666de0bf5 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 21:02:54 +0100 Subject: [PATCH 057/144] fix: replace QtTypes with QtGlobal in header files for consistency --- src/lib/arch/Arch.h | 4 +++- src/lib/platform/OSXClipboardBMPConverter.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/arch/Arch.h b/src/lib/arch/Arch.h index d1d75da9c..809b0a70a 100644 --- a/src/lib/arch/Arch.h +++ b/src/lib/arch/Arch.h @@ -5,7 +5,9 @@ * SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception */ -#include + +#include + // Consider whether or not to use either encapsulation (as below) // or inheritance (as it is now) for the ARCH stuff. // diff --git a/src/lib/platform/OSXClipboardBMPConverter.h b/src/lib/platform/OSXClipboardBMPConverter.h index 1f7a17594..8cfce9288 100644 --- a/src/lib/platform/OSXClipboardBMPConverter.h +++ b/src/lib/platform/OSXClipboardBMPConverter.h @@ -9,7 +9,7 @@ #include "platform/OSXClipboard.h" -#include +#include class OSXClipboardBMPConverter : public IOSXClipboardConverter { From c8fde32a1d641ad7fddc94326795e615510cb340 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 21:08:16 +0100 Subject: [PATCH 058/144] fix: add forward declaration for QTextStream in FingerprintDatabase.h --- src/lib/net/FingerprintDatabase.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/net/FingerprintDatabase.h b/src/lib/net/FingerprintDatabase.h index 57e9f9881..4fe2069fb 100644 --- a/src/lib/net/FingerprintDatabase.h +++ b/src/lib/net/FingerprintDatabase.h @@ -11,6 +11,8 @@ #include +class QTextStream; + class FingerprintDatabase { public: From 98f489133040f5e48330ec6d83199f7da99de879 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 21:21:36 +0100 Subject: [PATCH 059/144] fix: update minimum CMake version to 3.24 and required Qt version to 6.4.0; remove outdated Ubuntu 22.04 CI configurations --- .github/workflows/ci.yml | 12 ------------ CMakeLists.txt | 4 ++-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 858ce8829..f740ff8fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -512,18 +512,6 @@ jobs: like: "arch" config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DEV_DOCS=ON" - - name: "ubuntu-22.04-x86_64" - runs-on: ubuntu-latest - container: ubuntu:22.04 - like: "debian" - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WAYLAND_SUPPORT=OFF" - - - name: "ubuntu-22.04-arm64" - runs-on: ubuntu-24.04-arm - container: ubuntu:22.04 - like: "debian" - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WAYLAND_SUPPORT=OFF" - - name: "ubuntu-24.04-x86_64" runs-on: ubuntu-latest container: ubuntu:24.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index f07d5a69d..96efb9071 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # SPDX-FileCopyrightText: (C) 2009 - 2012 Nick Bolton # SPDX-License-Identifier: MIT -cmake_minimum_required(VERSION 3.22) +cmake_minimum_required(VERSION 3.24) # Link items by fill path cmake_policy(SET CMP0003 NEW) @@ -92,7 +92,7 @@ message(STATUS "Building ${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}") set(REQUIRED_OPENSSL_VERSION 3.0) set(REQUIRED_LIBEI_VERSION 1.0) set(REQUIRED_LIBPORTAL_VERSION 0.8.0) -set(REQUIRED_QT_VERSION 6.2.0) +set(REQUIRED_QT_VERSION 6.4.0) if (WIN32) # VSCMD_ARG_TGT_ARCH is set on CI From 3b7a0859bd651084c0df67aa6eb00869cfcd1c3b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 21:22:51 +0100 Subject: [PATCH 060/144] fix: convert std::string to QString for logging in longerText test --- src/unittests/deskflow/ClipboardTests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/unittests/deskflow/ClipboardTests.cpp b/src/unittests/deskflow/ClipboardTests.cpp index dc362b6a4..c2ee33b88 100644 --- a/src/unittests/deskflow/ClipboardTests.cpp +++ b/src/unittests/deskflow/ClipboardTests.cpp @@ -84,7 +84,7 @@ void ClipboardTests::longerText() // char before the last stores a bit-shifted version of the number, each // 1 more power than the last, which is done by bit-shifting [0] by 24, // [1] by 16, [2] by 8 ([3] is not bit-shifted). - qInfo() << actual; + qInfo() << QString::fromStdString(actual); QCOMPARE(actual[8], 0); // 287 >> 24 = 287 / (256^3) = 0 QCOMPARE(actual[9], 0); // 287 >> 16 = 287 / (256^2) = 0 QCOMPARE(actual[10], 1); // 287 >> 8 = 287 / (256^1) = 1(.121) From 975020e6f105dafdd26d8f7d7cd4ed7b8fc62e15 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 21:31:07 +0100 Subject: [PATCH 061/144] fix: reorder Qt header inclusion to prevent conflicts with X11 headers --- src/unittests/platform/XWindowsClipboardTests.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/unittests/platform/XWindowsClipboardTests.h b/src/unittests/platform/XWindowsClipboardTests.h index e7a698c4e..3f04b97d2 100644 --- a/src/unittests/platform/XWindowsClipboardTests.h +++ b/src/unittests/platform/XWindowsClipboardTests.h @@ -6,12 +6,14 @@ #include "base/Log.h" +// Qt headers that pull in qtextstream.h must precede X11 headers (XWindowsClipboard.h +// → Xlib.h) because X11 '#define Status int' breaks qtextstream.h's 'enum Status'. +#include + #if !WINAPI_LIBEI && !WINAPI_PORTAL #include "platform/XWindowsClipboard.h" #endif -#include - class XWindowsClipboardTests : public QObject { Q_OBJECT From 609734f072768dd40d13721c5ab6237216ca9267 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 21:41:13 +0100 Subject: [PATCH 062/144] fix: update clipboard format references to use IClipboard::Format for consistency --- src/unittests/platform/XWindowsClipboardTests.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/unittests/platform/XWindowsClipboardTests.cpp b/src/unittests/platform/XWindowsClipboardTests.cpp index 9d828c663..2a3cbdd96 100644 --- a/src/unittests/platform/XWindowsClipboardTests.cpp +++ b/src/unittests/platform/XWindowsClipboardTests.cpp @@ -61,12 +61,12 @@ void XWindowsClipboardTests::singleFormat() { auto &clipboard = getClipboard(); QVERIFY(clipboard.empty()); - clipboard.add(XWindowsClipboard::kText, m_testString); - QVERIFY(!clipboard.has(XWindowsClipboard::kText)); - QCOMPARE(clipboard.get(XWindowsClipboard::kText), m_testString); + clipboard.add(IClipboard::Format::Text, m_testString); + QVERIFY(!clipboard.has(IClipboard::Format::Text)); + QCOMPARE(clipboard.get(IClipboard::Format::Text), m_testString); - clipboard.add(XWindowsClipboard::kText, m_testString2); - QCOMPARE(clipboard.get(XWindowsClipboard::kText), m_testString2); + clipboard.add(IClipboard::Format::Text, m_testString2); + QCOMPARE(clipboard.get(IClipboard::Format::Text), m_testString2); } XWindowsClipboard &XWindowsClipboardTests::getClipboard() From 22bd3680c15ba8e80d4c95fb04e46d6cd146a082 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 22:03:16 +0100 Subject: [PATCH 063/144] fix: implement isPrivateUse function for QHostAddress compatibility across Qt versions --- src/lib/gui/core/NetworkMonitor.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/lib/gui/core/NetworkMonitor.cpp b/src/lib/gui/core/NetworkMonitor.cpp index b58ca45fb..a0815a08a 100644 --- a/src/lib/gui/core/NetworkMonitor.cpp +++ b/src/lib/gui/core/NetworkMonitor.cpp @@ -7,6 +7,7 @@ #include "NetworkMonitor.h" #include +#include #include #include #include @@ -15,6 +16,24 @@ namespace deskflow::gui { +namespace { + +// QHostAddress::isPrivateUse() arrived in Qt 6.6; on older Qt replicate its ranges +// (RFC 1918 private IPv4 + ULA fc00::/7) via isInSubnet(). +bool isPrivateUse(const QHostAddress &address) +{ +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + return address.isPrivateUse(); +#else + return address.isInSubnet(QHostAddress(QStringLiteral("10.0.0.0")), 8) || + address.isInSubnet(QHostAddress(QStringLiteral("172.16.0.0")), 12) || + address.isInSubnet(QHostAddress(QStringLiteral("192.168.0.0")), 16) || + address.isInSubnet(QHostAddress(QStringLiteral("fc00::")), 7); +#endif +} + +} // namespace + bool NetworkMonitor::isVirtualInterface(const QString &interfaceName) { // Common virtual network interface patterns @@ -96,8 +115,8 @@ QStringList NetworkMonitor::validAddresses() } std::ranges::sort(physicalIP4, [](const QHostAddress &a, const QHostAddress &b) { - if (a.isPrivateUse() != b.isPrivateUse()) - return a.isPrivateUse(); + if (isPrivateUse(a) != isPrivateUse(b)) + return isPrivateUse(a); return a.toIPv4Address() < b.toIPv4Address(); }); @@ -106,8 +125,8 @@ QStringList NetworkMonitor::validAddresses() }); std::ranges::sort(physicalIP6, [](const QHostAddress &a, const QHostAddress &b) { - if (a.isPrivateUse() != b.isPrivateUse()) - return a.isPrivateUse(); + if (isPrivateUse(a) != isPrivateUse(b)) + return isPrivateUse(a); return a.toString() < b.toString(); }); From 828adcd185415af910f63074dc0e8e3c0d58438d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 22:22:47 +0100 Subject: [PATCH 064/144] fix: handle case when X display is unavailable in XWindowsClipboardTests --- src/unittests/platform/XWindowsClipboardTests.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/unittests/platform/XWindowsClipboardTests.cpp b/src/unittests/platform/XWindowsClipboardTests.cpp index 2a3cbdd96..fcc062814 100644 --- a/src/unittests/platform/XWindowsClipboardTests.cpp +++ b/src/unittests/platform/XWindowsClipboardTests.cpp @@ -33,6 +33,8 @@ void XWindowsClipboardTests::defaultCtor() void XWindowsClipboardTests::initTestCase() { m_display = XOpenDisplay(nullptr); + if (!m_display) + QSKIP("no X display available (e.g. headless CI); X11 clipboard tests need a running X server"); int screen = DefaultScreen(m_display); Window root = XRootWindow(m_display, screen); @@ -46,6 +48,8 @@ void XWindowsClipboardTests::initTestCase() void XWindowsClipboardTests::cleanupTestCase() { + if (!m_display) + return; XDestroyWindow(m_display, m_window); XCloseDisplay(m_display); } From 4e20555466b42a0fe75403f5457c0c0b6ea5705b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 23:39:31 +0100 Subject: [PATCH 065/144] feat: add update channel option to settings dialog and implement version check URL handling --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 61 +++++++++++++++++++ extra/src/lib/synergy/gui/FeatureHandler.h | 1 + extra/src/lib/synergy/gui/UpdateChannel.cpp | 62 ++++++++++++++++++++ extra/src/lib/synergy/gui/UpdateChannel.h | 56 ++++++++++++++++++ extra/src/lib/synergy/hooks/gui_hook.h | 4 +- 5 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 extra/src/lib/synergy/gui/UpdateChannel.cpp create mode 100644 extra/src/lib/synergy/gui/UpdateChannel.h diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index a5b6f9d96..1d08dd04b 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -22,19 +22,25 @@ #include "synergy/gui/SettingsMigration.h" #include "synergy/gui/SettingsScope.h" #include "synergy/gui/TestSettings.h" +#include "synergy/gui/UpdateChannel.h" #include "synergy/gui/constants.h" #include #include +#include +#include #include #include +#include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -88,6 +94,8 @@ void FeatureHandler::handleSettings(QDialog *parent) const wlClipboard->hide(); } + addUpdateChannelOption(parent); + const auto &licenseHandler = LicenseHandler::instance(); if (licenseHandler.isEnabled() && !licenseHandler.license().isSettingsScopeAvailable()) { return; @@ -181,3 +189,56 @@ void FeatureHandler::addScopeTab(QDialog *parent) const tabWidget->addTab(scopeTab, QObject::tr("Scope")); } + +void FeatureHandler::addUpdateChannelOption(QDialog *parent) const +{ + auto *autoUpdate = parent->findChild(QStringLiteral("cbAutoUpdate")); + if (autoUpdate == nullptr) { + qWarning("update channel: no cbAutoUpdate in settings dialog, skipping"); + return; + } + + auto *page = autoUpdate->parentWidget(); + auto *pageLayout = page ? qobject_cast(page->layout()) : nullptr; + const int checkboxIndex = pageLayout ? pageLayout->indexOf(autoUpdate) : -1; + if (checkboxIndex < 0) { + qWarning("update channel: unexpected update-checkbox layout, skipping"); + return; + } + + auto *row = new QHBoxLayout(); + auto *label = new QLabel(QObject::tr("Update channel"), page); + auto *combo = new QComboBox(page); + combo->addItem(QObject::tr("Stable"), UpdateChannel::Stable); + combo->addItem(QObject::tr("Beta"), UpdateChannel::Beta); + combo->setCurrentIndex(combo->findData(UpdateChannel::current())); + label->setBuddy(combo); + row->setContentsMargins(22, 0, 0, 0); + row->addWidget(label); + row->addWidget(combo); + row->addStretch(); + pageLayout->insertLayout(checkboxIndex + 1, row); + + // The channel only affects the on-startup check, so mirror the checkbox's + // enabled state; the checkbox itself is disabled when settings aren't writable. + const auto syncEnabled = [label, combo](bool on) { + label->setEnabled(on); + combo->setEnabled(on); + }; + syncEnabled(autoUpdate->isChecked() && autoUpdate->isEnabled()); + QObject::connect(autoUpdate, &QCheckBox::toggled, combo, syncEnabled); + + // The injected combo isn't part of the dialog's isModified() check, so changing + // it would never enable Save. Enable it directly on change, and persist on the + // dialog's accept() (not on change) so Cancel still discards. + auto *buttonBox = parent->findChild(); + if (auto *saveButton = buttonBox ? buttonBox->button(QDialogButtonBox::Save) : nullptr) { + QObject::connect(combo, &QComboBox::currentIndexChanged, saveButton, [saveButton]() { + saveButton->setEnabled(true); + }); + } + + QObject::connect(parent, &QDialog::accepted, combo, [combo]() { + UpdateChannel::setCurrent(combo->currentData().toString()); + }); +} diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index fa59accf1..e8ef84e19 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -36,6 +36,7 @@ class FeatureHandler private: void addTestMenu(); void addScopeTab(QDialog *parent) const; + void addUpdateChannelOption(QDialog *parent) const; QMainWindow *m_pMainWindow = nullptr; }; diff --git a/extra/src/lib/synergy/gui/UpdateChannel.cpp b/extra/src/lib/synergy/gui/UpdateChannel.cpp new file mode 100644 index 000000000..b018d349a --- /dev/null +++ b/extra/src/lib/synergy/gui/UpdateChannel.cpp @@ -0,0 +1,62 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#include "UpdateChannel.h" + +#include "common/Constants.h" +#include "common/Settings.h" + +#include + +namespace synergy::gui { + +namespace { + +const auto kChannelKey = QStringLiteral("update/channel"); + +QString extraFile() +{ + return QStringLiteral("%1/%2.extra.conf").arg(Settings::UserDir, kAppName); +} + +} // namespace + +QString UpdateChannel::current() +{ + QSettings extra(extraFile(), QSettings::IniFormat); + const auto channel = extra.value(kChannelKey, Stable).toString(); + return channel == Beta ? Beta : Stable; +} + +void UpdateChannel::setCurrent(const QString &channel) +{ + QSettings extra(extraFile(), QSettings::IniFormat); + extra.setValue(kChannelKey, channel == Beta ? Beta : Stable); + extra.sync(); +} + +void UpdateChannel::applyToVersionCheckUrl(QString &url) +{ + const auto channel = current(); + if (channel == Stable) { + return; + } + + url += (url.contains(QLatin1Char('?')) ? QLatin1Char('&') : QLatin1Char('?')) + QStringLiteral("track=") + channel; +} + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/UpdateChannel.h b/extra/src/lib/synergy/gui/UpdateChannel.h new file mode 100644 index 000000000..061e4ee00 --- /dev/null +++ b/extra/src/lib/synergy/gui/UpdateChannel.h @@ -0,0 +1,56 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#pragma once + +#include + +namespace synergy::gui { + +/** + * @brief The update channel (stable/beta) the user receives version notifications for. + * + * Synergy-only: upstream Deskflow has no track/channel concept, so this lives in the + * overlay and persists to Synergy.extra.conf rather than upstream's allow-listed + * settings file. The choice is sent to the version server as a `track=` query param. + */ +class UpdateChannel +{ +public: + // Persisted/UI values; also the track names the version server expects. + inline static const QString Stable = QStringLiteral("stable"); + inline static const QString Beta = QStringLiteral("beta"); + + /** + * @brief The user's chosen channel; defaults to Stable when unset. + */ + static QString current(); + + /** + * @brief Records the user's channel choice. + */ + static void setCurrent(const QString &channel); + + /** + * @brief Appends `track=` to the version-check URL for non-stable + * channels (the param the version server expects, matching the 1.21 line), + * leaving stable requests byte-identical so the server needs no change. + */ + static void applyToVersionCheckUrl(QString &url); +}; + +} // namespace synergy::gui diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index 876f74b68..499fab64f 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -22,6 +22,7 @@ #include "synergy/gui/FeatureHandler.h" #include "synergy/gui/SettingsMigration.h" #include "synergy/gui/SettingsScope.h" +#include "synergy/gui/UpdateChannel.h" #include "synergy/gui/dev_mode.h" #include "synergy/gui/license/LicenseHandler.h" @@ -77,7 +78,8 @@ inline void onSettings(QDialog *parent) inline void onVersionCheck(QString &versionUrl) { - return LicenseHandler::instance().handleVersionCheck(versionUrl); + LicenseHandler::instance().handleVersionCheck(versionUrl); + synergy::gui::UpdateChannel::applyToVersionCheckUrl(versionUrl); } inline bool onCoreStart() From df7a1355936852489582f132e3517886811904e4 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 23:50:46 +0100 Subject: [PATCH 066/144] fix: downgrade REQUIRED_LIBPORTAL_VERSION from 0.8.0 to 0.7.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 96efb9071..2fbc05247 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,7 @@ message(STATUS "Building ${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}") # Set lib versions set(REQUIRED_OPENSSL_VERSION 3.0) set(REQUIRED_LIBEI_VERSION 1.0) -set(REQUIRED_LIBPORTAL_VERSION 0.8.0) +set(REQUIRED_LIBPORTAL_VERSION 0.7.0) set(REQUIRED_QT_VERSION 6.4.0) if (WIN32) From e036d6858535d64eb92d0dcd077a49231519b17f Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 3 Jun 2026 23:57:55 +0100 Subject: [PATCH 067/144] feat: add style update notice functionality to FeatureHandler and update button styling in StatusBar --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 14 ++++++++++++++ extra/src/lib/synergy/gui/FeatureHandler.h | 1 + src/lib/gui/widgets/StatusBar.cpp | 1 + 3 files changed, 16 insertions(+) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 1d08dd04b..6d89e2881 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -24,6 +24,7 @@ #include "synergy/gui/TestSettings.h" #include "synergy/gui/UpdateChannel.h" #include "synergy/gui/constants.h" +#include "synergy/gui/styles.h" #include #include @@ -52,6 +53,19 @@ using namespace synergy::gui; void FeatureHandler::handleMainWindow(QMainWindow *mainWindow) { m_pMainWindow = mainWindow; + styleUpdateNotice(mainWindow); +} + +void FeatureHandler::styleUpdateNotice(QMainWindow *mainWindow) const +{ + // Reuse the license-expiry notice pill (kStyleNoticeLabel) so the upstream + // "update available" status-bar button reads identically to it. + auto *button = mainWindow->findChild(QStringLiteral("btnUpdate")); + if (button == nullptr) { + qWarning("update notice: no btnUpdate in status bar, skipping styling"); + return; + } + button->setStyleSheet(kStyleNoticeLabel); } void FeatureHandler::handleAppStart() diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index e8ef84e19..d0756fb8f 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -35,6 +35,7 @@ class FeatureHandler private: void addTestMenu(); + void styleUpdateNotice(QMainWindow *mainWindow) const; void addScopeTab(QDialog *parent) const; void addUpdateChannelOption(QDialog *parent) const; diff --git a/src/lib/gui/widgets/StatusBar.cpp b/src/lib/gui/widgets/StatusBar.cpp index 019171bdf..12b3dc0b0 100644 --- a/src/lib/gui/widgets/StatusBar.cpp +++ b/src/lib/gui/widgets/StatusBar.cpp @@ -40,6 +40,7 @@ StatusBar::StatusBar(QWidget *parent) m_lblStatus->setText(tr("%1 is not running").arg(kAppName)); insertPermanentWidget(2, m_lblStatus, 1); + m_btnUpdate->setObjectName(QStringLiteral("btnUpdate")); m_btnUpdate->setVisible(false); m_btnUpdate->setFlat(true); m_btnUpdate->setLayoutDirection(Qt::RightToLeft); From ac136d1e59ad2835ab0c413f96c26609862cb1e4 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 4 Jun 2026 00:46:53 +0100 Subject: [PATCH 068/144] fix: update REQUIRED_LIBPORTAL_VERSION to 0.8.0 and streamline dependency checks in CMakeLists --- .../actions/install-dependencies/action.yml | 11 ++--- .github/workflows/ci.yml | 23 +++++++++- CMakeLists.txt | 3 +- src/lib/platform/CMakeLists.txt | 42 +++++++------------ 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 1b7b31aea..80051a112 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -34,19 +34,14 @@ runs: elif [ "$RUNNER_OS" == "Linux" ]; then if [ ${{inputs.like}} == "debian" ]; then apt update -qqq > /dev/null - # qt6-l10n-tools + qt6-tools-dev-tools provide lconvert/lupdate/lrelease, which - # qt6-tools-dev's LinguistTools cmake references but doesn't pull in on 22.04. + # qt6-l10n-tools + qt6-tools-dev-tools provide lconvert/lupdate/lrelease that + # qt6-tools-dev's LinguistTools cmake references but doesn't always pull in. apt install -qqq cmake build-essential ninja-build \ xorg-dev libx11-dev libxtst-dev libssl-dev \ libglib2.0-dev libxkbfile-dev qt6-base-dev qt6-tools-dev \ qt6-l10n-tools qt6-tools-dev-tools \ libgtk-3-dev libgtest-dev libgmock-dev \ - help2man doxygen -y >/dev/null - - # libei/libportal are absent on older LTS (e.g. 22.04); those legs build X11-only. - if apt-cache show libei-dev >/dev/null 2>&1 && apt-cache show libportal-dev >/dev/null 2>&1; then - apt install -qqq libei-dev libportal-dev -y >/dev/null - fi + libei-dev libportal-dev help2man doxygen -y >/dev/null elif [ ${{inputs.like}} == "fedora" ]; then dnf install -y cmake make ninja-build gcc-c++ rpm-build openssl-devel \ glib2-devel libXtst-devel libxkbfile-devel qt6-qtbase-devel qt6-qttools-devel \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f740ff8fe..50120433b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -516,13 +516,13 @@ jobs: runs-on: ubuntu-latest container: ubuntu:24.04 like: "debian" - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WAYLAND_SUPPORT=OFF" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - name: "ubuntu-24.04-arm64" runs-on: ubuntu-24.04-arm container: ubuntu:24.04 like: "debian" - config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_WAYLAND_SUPPORT=OFF" + config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr" - name: "ubuntu-26.04-x86_64" runs-on: ubuntu-latest @@ -565,6 +565,25 @@ jobs: with: like: ${{ matrix.target.like }} + # 24.04's system libportal (0.7.x) lacks the input-capture portal the code needs + # (>= 0.8.0), and that LTS's libs are frozen. Build a newer libportal and static-link + # it so the shipped binary carries no runtime dependency on a >= 0.8 system libportal + # (libei from the system is fine). The code uses no xdp_parent, so no GUI backend is + # needed, and removing the system .so makes `-lportal` resolve to the static archive. + - name: Build newer libportal (Ubuntu 24.04) + if: startsWith(matrix.target.name, 'ubuntu-24.04') + shell: bash + run: | + apt-get install -y -qq meson pkg-config + git clone --depth 1 --branch 0.9.1 https://github.com/flatpak/libportal.git /tmp/libportal + meson setup /tmp/libportal/_build /tmp/libportal --prefix=/usr --libdir=lib/$(gcc -dumpmachine) \ + --default-library=static \ + -Dbackend-gtk3=disabled -Dbackend-gtk4=disabled -Dbackend-qt5=disabled -Dbackend-qt6=disabled \ + -Ddocs=false -Dintrospection=false -Dvapi=false -Dtests=false + meson install -C /tmp/libportal/_build + rm -f /usr/lib/$(gcc -dumpmachine)/libportal.so* + ldconfig + - name: Get version id: get-version uses: ./.github/actions/get-version diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fbc05247..cb2fc6cf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,7 @@ message(STATUS "Building ${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}") # Set lib versions set(REQUIRED_OPENSSL_VERSION 3.0) set(REQUIRED_LIBEI_VERSION 1.0) -set(REQUIRED_LIBPORTAL_VERSION 0.7.0) +set(REQUIRED_LIBPORTAL_VERSION 0.8.0) set(REQUIRED_QT_VERSION 6.4.0) if (WIN32) @@ -146,7 +146,6 @@ add_definitions(-DQT_NO_KEYWORDS) #Options for Linux platform support if(UNIX AND NOT APPLE) option(BUILD_X11_SUPPORT "Build with x11 support" ON) - option(BUILD_WAYLAND_SUPPORT "Build with Wayland (libei/libportal) support" ON) elseif (APPLE) option(BUILD_OSX_BUNDLE "Build mac os bundle" ON) endif() diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt index 02916dec8..d8af35a0e 100644 --- a/src/lib/platform/CMakeLists.txt +++ b/src/lib/platform/CMakeLists.txt @@ -4,24 +4,20 @@ # SPDX-License-Identifier: MIT if(UNIX AND NOT APPLE) - if(BUILD_WAYLAND_SUPPORT) - pkg_check_modules(LIBEI REQUIRED QUIET "libei-1.0 >= ${REQUIRED_LIBEI_VERSION}") - message(STATUS "libei version: ${LIBEI_VERSION}") + pkg_check_modules(LIBEI REQUIRED QUIET "libei-1.0 >= ${REQUIRED_LIBEI_VERSION}") + message(STATUS "libei version: ${LIBEI_VERSION}") - pkg_check_modules(LIBPORTAL REQUIRED QUIET "libportal >= ${REQUIRED_LIBPORTAL_VERSION}") - message(STATUS "libportal version: ${LIBPORTAL_VERSION}") + pkg_check_modules(LIBPORTAL REQUIRED QUIET "libportal >= ${REQUIRED_LIBPORTAL_VERSION}") + message(STATUS "libportal version: ${LIBPORTAL_VERSION}") - # Check if we have the restore ability in the IC portal - include(CMakePushCheckState) - include(CheckSymbolExists) - cmake_push_check_state(RESET) - set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${LIBPORTAL_INCLUDE_DIRS};${GLIB2_INCLUDE_DIRS}") - set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};${LIBPORTAL_LINK_LIBRARIES};${GLIB2_LINK_LIBRARIES}") - check_symbol_exists(xdp_input_capture_session_get_restore_token "libportal/inputcapture.h" HAVE_IC_RESTORE) - cmake_pop_check_state() - else() - message(STATUS "Wayland support disabled (BUILD_WAYLAND_SUPPORT=OFF): building X11-only") - endif() + # Check if we have the restore ability in the IC portal + include(CMakePushCheckState) + include(CheckSymbolExists) + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${LIBPORTAL_INCLUDE_DIRS};${GLIB2_INCLUDE_DIRS}") + set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES};${LIBPORTAL_LINK_LIBRARIES};${GLIB2_LINK_LIBRARIES}") + check_symbol_exists(xdp_input_capture_session_get_restore_token "libportal/inputcapture.h" HAVE_IC_RESTORE) + cmake_pop_check_state() endif() if(WIN32) @@ -190,17 +186,11 @@ if(UNIX) if(${LIBXKBCOMMON_VERSION} VERSION_GREATER_EQUAL "1.10") target_compile_definitions(platform PRIVATE HAVE_XKB_KEYMAP_MOD_GET_MASK=1) endif() - if(LIBEI_FOUND) - target_compile_definitions(platform PUBLIC WINAPI_LIBEI) - target_include_directories(platform PUBLIC ${LIBEI_INCLUDE_DIRS}) - endif() - if(LIBPORTAL_FOUND) - target_compile_definitions(platform PUBLIC WINAPI_LIBPORTAL) - if (HAVE_IC_RESTORE) - target_compile_definitions(platform PUBLIC HAVE_LIBPORTAL_INPUTCAPTURE_RESTORE) - endif() - target_include_directories(platform PUBLIC ${LIBPORTAL_INCLUDE_DIRS}) + target_compile_definitions(platform PUBLIC WINAPI_LIBEI WINAPI_LIBPORTAL) + if (HAVE_IC_RESTORE) + target_compile_definitions(platform PUBLIC HAVE_LIBPORTAL_INPUTCAPTURE_RESTORE) endif() + target_include_directories(platform PUBLIC ${LIBEI_INCLUDE_DIRS} ${LIBPORTAL_INCLUDE_DIRS}) target_link_libraries( platform Qt6::DBus From 817d9010eaf16bc66654816bf794ecabf00a920b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 4 Jun 2026 21:41:26 +0100 Subject: [PATCH 069/144] feat: enhance launch configurations and add server/client tasks for synergy-core --- .vscode/launch.json | 64 ++++++++++++++++++++++++++++++--------------- .vscode/tasks.json | 60 ++++++++++++++++++++++++++++-------------- 2 files changed, 83 insertions(+), 41 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 8e0d516ce..a03a33af0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,8 +6,10 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", + "runInTerminal": true, "program": "${workspaceFolder}/build/bin/synergy", "preLaunchTask": "build-silent", + "initCommands": ["settings set target.preload-symbols false"], "osx": { "program": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy" } @@ -17,6 +19,7 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", + "runInTerminal": true, "program": "${workspaceFolder}/build/bin/unittests", "args": ["${input:gtest-args}"], "preLaunchTask": "build-silent" @@ -26,15 +29,43 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", + "runInTerminal": true, "program": "${workspaceFolder}/build/bin/integtests", "args": ["${input:gtest-args}"], "preLaunchTask": "build-silent" }, + { + "name": "unix - server", + "type": "lldb-dap", + "cwd": "${workspaceRoot}", + "request": "launch", + "runInTerminal": true, + "program": "${workspaceFolder}/build/bin/synergy-core", + "args": ["server"], + "preLaunchTask": "build-silent", + "osx": { + "program": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy-core" + } + }, + { + "name": "unix - client", + "type": "lldb-dap", + "cwd": "${workspaceRoot}", + "request": "launch", + "runInTerminal": true, + "program": "${workspaceFolder}/build/bin/synergy-core", + "args": ["client"], + "preLaunchTask": "build-silent", + "osx": { + "program": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy-core" + } + }, { "name": "unix - daemon", "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", + "runInTerminal": true, "program": "${workspaceFolder}/build/bin/synergy-daemon", "args": ["-f"], "preLaunchTask": "build-silent" @@ -54,15 +85,6 @@ "internalConsoleOptions": "openOnSessionStart", "preLaunchTask": "build-kill-gui" }, - { - "name": "windows - version", - "type": "cppvsdbg", - "cwd": "${workspaceRoot}", - "request": "launch", - "program": "${workspaceFolder}/build/bin-copy/synergy-core", - "args": ["--version"], - "internalConsoleOptions": "openOnSessionStart" - }, { "name": "windows - unittests", "type": "cppvsdbg", @@ -82,44 +104,44 @@ "internalConsoleOptions": "openOnSessionStart" }, { - "name": "windows - active-desktop", + "name": "windows - server", "type": "cppvsdbg", "cwd": "${workspaceRoot}", "request": "launch", "program": "${workspaceFolder}/build/bin-copy/synergy-core", - "args": ["--active-desktop"], + "args": ["server"], "internalConsoleOptions": "openOnSessionStart", "preLaunchTask": "build-kill-core" }, { - "name": "windows - daemon foreground", + "name": "windows - client", "type": "cppvsdbg", "cwd": "${workspaceRoot}", "request": "launch", - "program": "${workspaceFolder}/build/bin-copy/synergy-daemon", - "args": ["-f"], + "program": "${workspaceFolder}/build/bin-copy/synergy-core", + "args": ["client"], "internalConsoleOptions": "openOnSessionStart", "preLaunchTask": "build-kill-core" }, { - "name": "windows - daemon install", + "name": "windows - active-desktop", "type": "cppvsdbg", "cwd": "${workspaceRoot}", "request": "launch", - "program": "${workspaceFolder}/build/bin-copy/synergy-daemon", - "args": ["--install"], + "program": "${workspaceFolder}/build/bin-copy/synergy-core", + "args": ["--active-desktop"], "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": "build" + "preLaunchTask": "build-kill-core" }, { - "name": "windows - daemon uninstall", + "name": "windows - daemon", "type": "cppvsdbg", "cwd": "${workspaceRoot}", "request": "launch", "program": "${workspaceFolder}/build/bin-copy/synergy-daemon", - "args": ["--uninstall"], + "args": ["-f"], "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": "build" + "preLaunchTask": "build-kill-core" }, { "name": "windows - attach", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f21a2ff01..7bb778ada 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -11,7 +11,7 @@ "group": { "kind": "build", "isDefault": true - }, + } }, { "label": "build-cmake", @@ -23,7 +23,7 @@ "problemMatcher": { "base": "$gcc", "fileLocation": ["absolute"] - }, + } }, { "label": "build-kill-core", @@ -41,7 +41,7 @@ "dependsOn": ["build"], "group": "build", "presentation": { - "reveal": "silent", + "reveal": "silent" } }, { @@ -87,32 +87,32 @@ { "label": "kill-all", "type": "shell", - "dependsOn": ["kill-gui", "kill-daemon", "kill-core"], + "dependsOn": ["kill-gui", "kill-daemon", "kill-core"] }, { "label": "kill-gui", "type": "shell", - "command": "killall -9 synergy || true", + "command": "killall -v -9 synergy || true", "windows": { "command": "taskkill /F /IM synergy.exe; $true" - }, + } }, { "label": "kill-daemon", "type": "shell", - "command": "killall -9 synergy-daemon || true", + "command": "killall -v -9 synergy-daemon || true", "windows": { "command": "taskkill /F /IM synergy-daemon.exe; $true" }, - "dependsOn": ["daemon-stop"], + "dependsOn": ["daemon-stop"] }, { "label": "kill-core", "type": "shell", - "command": "killall -9 synergy-core || true", + "command": "killall -v -9 synergy-core || true", "windows": { "command": "taskkill /F /IM synergy-core.exe; $true" - }, + } }, { "label": "gui", @@ -123,6 +123,20 @@ "windows": { "dependsOn": ["build-kill-gui"], "command": "${workspaceFolder}/build/bin-copy/synergy.exe" + }, + "osx": { + "command": "${workspaceFolder}/build/bin/synergy.app/Contents/MacOS/synergy" + } + }, + { + "label": "daemon", + "type": "process", + "command": "echo noop > /dev/null", + "problemMatcher": [], + "dependsOn": ["daemon-stop", "build"], + "windows": { + "command": "${workspaceFolder}/build/bin-copy/synergy-daemon.exe", + "args": ["-f"] } }, { @@ -130,24 +144,24 @@ "type": "shell", "command": "echo noop > /dev/null", "windows": { - "command": "Stop-Service -Name 'Synergy' -Force; while ((Get-Service -Name 'Synergy').Status -ne 'Stopped') { Start-Sleep -Seconds 1 }", - }, + "command": "Stop-Service -Name 'Synergy' -Force; while ((Get-Service -Name 'Synergy').Status -ne 'Stopped') { Start-Sleep -Seconds 1 }" + } }, { "label": "daemon-start", "type": "shell", "command": "echo noop > /dev/null", "windows": { - "command": "Start-Service -Name 'Synergy'; while ((Get-Service -Name 'Synergy').Status -ne 'Running') { Start-Sleep -Seconds 1 }", - }, + "command": "Start-Service -Name 'Synergy'; while ((Get-Service -Name 'Synergy').Status -ne 'Running') { Start-Sleep -Seconds 1 }" + } }, { "label": "daemon-restart", "type": "shell", "command": "echo noop > /dev/null", "windows": { - "command": "Restart-Service -Name 'Synergy'", - }, + "command": "Restart-Service -Name 'Synergy'" + } }, { "label": "daemon-deploy", @@ -160,7 +174,7 @@ "type": "shell", "command": "echo noop > /dev/null", "windows": { - "command": "${workspaceFolder}/build/bin-copy/synergy-daemon.exe --install" + "command": "sc.exe create Synergy binPath= \"${workspaceFolder}\\build\\bin-copy\\synergy-daemon.exe\" start= auto DisplayName= Synergy" } }, { @@ -168,14 +182,20 @@ "type": "shell", "command": "echo noop > /dev/null", "windows": { - "command": "${workspaceFolder}/build/bin-copy/synergy-daemon.exe --uninstall" + "command": "sc.exe delete Synergy" } }, { "label": "daemon-reinstall", "type": "shell", - "dependsOn": ["build-kill-core", "daemon-stop", "daemon-uninstall", "daemon-install"], - "dependsOrder": "sequence", + "dependsOn": [ + "build-kill-core", + "daemon-stop", + "daemon-uninstall", + "daemon-install", + "daemon-start" + ], + "dependsOrder": "sequence" } ] } From 701d7ba738483da252b3e7d8723af9dcaddbb412 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 12:08:50 +0100 Subject: [PATCH 070/144] feat: update versioning scheme to include stage in version string --- extra/cmake/Version.cmake | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/extra/cmake/Version.cmake b/extra/cmake/Version.cmake index 24fedca6d..31534a2da 100644 --- a/extra/cmake/Version.cmake +++ b/extra/cmake/Version.cmake @@ -7,14 +7,15 @@ set(SYNERGY_VERSION_MAJOR 1) set(SYNERGY_VERSION_MINOR 21) set(SYNERGY_VERSION_PATCH 0) +set(SYNERGY_VERSION_STAGE "beta") # Composes the full version string by applying SYNERGY_VERSION_RELEASE / # SYNERGY_VERSION_SNAPSHOT semantics on top of the SYNERGY_VERSION_MAJOR/MINOR/PATCH # constants above, plus a git-derived revision count for snapshot/dev builds: # -# RELEASE → "X.Y.Z" -# SNAPSHOT → "X.Y.Z-snapshot+rN" (N = commits since last v* tag) -# neither (dev) → "X.Y.Z-dev" +# RELEASE → "X.Y.Z[-STAGE]" (STAGE = SYNERGY_VERSION_STAGE, e.g. beta1) +# SNAPSHOT → "X.Y.Z[-STAGE]-snapshot+rN" (N = commits since last v* tag) +# neither (dev) → "X.Y.Z[-STAGE]-dev" # # Outputs (PARENT_SCOPE): # OUT_VERSION the composed version string @@ -39,14 +40,18 @@ function(synergy_compute_version SOURCE_DIR OUT_VERSION OUT_TWEAK OUT_BASE) endif() set(_base "${SYNERGY_VERSION_MAJOR}.${SYNERGY_VERSION_MINOR}.${SYNERGY_VERSION_PATCH}") + set(_stage "") + if(SYNERGY_VERSION_STAGE) + set(_stage "-${SYNERGY_VERSION_STAGE}") + endif() if(SYNERGY_VERSION_RELEASE) - set(_version "${_base}") + set(_version "${_base}${_stage}") set(_tweak 0) elseif(SYNERGY_VERSION_SNAPSHOT) - set(_version "${_base}-snapshot+r${_rev_count}") + set(_version "${_base}${_stage}-snapshot+r${_rev_count}") set(_tweak ${_rev_count}) else() - set(_version "${_base}-dev") + set(_version "${_base}${_stage}-dev") set(_tweak ${_rev_count}) endif() From f74bfe15ba0a4334f73d4d0ebda0cfa99f6cabde Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 12:52:18 +0100 Subject: [PATCH 071/144] feat: implement About dialog functionality in FeatureHandler and LicenseHandler --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 26 ++++++++ extra/src/lib/synergy/gui/FeatureHandler.h | 1 + .../synergy/gui/license/LicenseHandler.cpp | 63 +++++++++++++++++++ .../lib/synergy/gui/license/LicenseHandler.h | 1 + extra/src/lib/synergy/hooks/gui_hook.h | 6 ++ extra/src/lib/synergy/license/SerialKey.h | 3 + .../lib/synergy/license/parse_serial_key.cpp | 9 +++ src/lib/common/Constants.h.in | 1 + src/lib/gui/dialogs/AboutDialog.cpp | 8 +++ 9 files changed, 118 insertions(+) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 6d89e2881..27ba1a239 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -117,6 +117,32 @@ void FeatureHandler::handleSettings(QDialog *parent) const addScopeTab(parent); } +void FeatureHandler::handleAbout(QDialog *parent) const +{ + if (parent == nullptr) { + return; + } + auto *description = parent->findChild(QStringLiteral("lblDescription")); + if (description == nullptr) { + return; + } + description->setWordWrap(true); + auto font = description->font(); + font.setPointSizeF(font.pointSizeF() * 0.85); + description->setFont(font); + auto palette = description->palette(); + auto color = palette.color(QPalette::WindowText); + color.setAlphaF(0.6); + palette.setColor(QPalette::WindowText, color); + description->setPalette(palette); + description->setText(QObject::tr( + "Synergy was originally created by Chris Schoeneman and today builds on Deskflow, " + "the open-source upstream project maintained by Nick Bolton and Chris Rizzitello. " + "Thanks to the Deskflow developers, all our contributors, and the wider open-source " + "community." + )); +} + namespace { QString pathLabel(const QString &path) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index d0756fb8f..80edf4b02 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -32,6 +32,7 @@ class FeatureHandler void handleMainWindow(QMainWindow *mainWindow); void handleAppStart(); void handleSettings(QDialog *parent) const; + void handleAbout(QDialog *parent) const; private: void addTestMenu(); diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 4023e1216..8e5c5b505 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -34,12 +34,17 @@ #include #include #include +#include #include +#include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -151,6 +156,64 @@ void LicenseHandler::handleSettings(QDialog *parent) const // license change. } +void LicenseHandler::handleAbout(QDialog *parent) const +{ + if (!m_enabled || parent == nullptr) { + return; + } + + if (!license().serialKey().isValid) { + return; + } + + auto *mainLayout = qobject_cast(parent->layout()); + auto *anchor = parent->findChild(QStringLiteral("frameLogo")); + const int index = (mainLayout && anchor) ? mainLayout->indexOf(anchor) : -1; + if (index < 0) { + qWarning("about: no frameLogo anchor, skipping license info"); + return; + } + + auto *section = new QVBoxLayout(); + section->setContentsMargins(0, 8, 0, 8); + + auto *registrantLabel = new QLabel(parent); + auto *keyField = new QLineEdit(parent); + keyField->setReadOnly(true); + auto *changeButton = new QPushButton(QObject::tr("Change"), parent); + + section->addWidget(registrantLabel); + auto *keyRow = new QHBoxLayout(); + keyRow->addWidget(keyField, 1); + keyRow->addWidget(changeButton); + section->addLayout(keyRow); + + // The serial key can change while the About dialog is open (via the Change + // button), so refresh the widgets from the current license each time rather + // than building them once with whatever key was active at construction. + const auto refresh = [registrantLabel, keyField] { + const auto &k = LicenseHandler::instance().license().serialKey(); + const auto name = QString::fromStdString(k.name); + const auto company = QString::fromStdString(k.company); + QString registrant = name; + if (!company.isEmpty()) { + registrant = name.isEmpty() ? company : QStringLiteral("%1, %2").arg(name, company); + } + registrantLabel->setText(QObject::tr("Registered to %1").arg(registrant)); + registrantLabel->setVisible(!registrant.isEmpty()); + keyField->setText(QString::fromStdString(k.toString())); + keyField->setCursorPosition(0); + }; + refresh(); + + QObject::connect(changeButton, &QPushButton::clicked, changeButton, [refresh] { + LicenseHandler::instance().showSerialKeyDialog(); + refresh(); + }); + + mainLayout->insertLayout(index + 1, section); +} + void LicenseHandler::handleVersionCheck(QString &versionUrl) { if (!m_enabled) { diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index fc0a45f4b..94c7f30b6 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -61,6 +61,7 @@ class LicenseHandler : public QObject void handleMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess); bool handleAppStart(); void handleSettings(QDialog *parent) const; + void handleAbout(QDialog *parent) const; void handleVersionCheck(QString &versionUrl); bool handleCoreStart(); bool loadSettings(); diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index 499fab64f..ae22f2dfe 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -76,6 +76,12 @@ inline void onSettings(QDialog *parent) FeatureHandler::instance().handleSettings(parent); } +inline void onAbout(QDialog *parent) +{ + FeatureHandler::instance().handleAbout(parent); + LicenseHandler::instance().handleAbout(parent); +} + inline void onVersionCheck(QString &versionUrl) { LicenseHandler::instance().handleVersionCheck(versionUrl); diff --git a/extra/src/lib/synergy/license/SerialKey.h b/extra/src/lib/synergy/license/SerialKey.h index b930fd24c..56620ad61 100644 --- a/extra/src/lib/synergy/license/SerialKey.h +++ b/extra/src/lib/synergy/license/SerialKey.h @@ -58,6 +58,9 @@ struct SerialKey std::optional warnTime = std::nullopt; std::optional expireTime = std::nullopt; bool isOffline = false; + std::string name = ""; + std::string email = ""; + std::string company = ""; private: explicit SerialKey(Product::Edition edition) : product(edition) diff --git a/extra/src/lib/synergy/license/parse_serial_key.cpp b/extra/src/lib/synergy/license/parse_serial_key.cpp index 759f5ffe3..dde4e901d 100644 --- a/extra/src/lib/synergy/license/parse_serial_key.cpp +++ b/extra/src/lib/synergy/license/parse_serial_key.cpp @@ -96,6 +96,9 @@ SerialKey parseV1(const std::string &hexString, const Parts &parts) // e.g.: {v1;basic;name;seats;email;company;1398297600;1398384000} SerialKey serialKey(hexString); serialKey.product = Product(parts.at(1)); + serialKey.name = parts.at(2); + serialKey.email = parts.at(4); + serialKey.company = parts.at(5); serialKey.warnTime = parseDate(parts.at(6)); serialKey.expireTime = parseDate(parts.at(7)); serialKey.isValid = true; @@ -111,6 +114,9 @@ SerialKey parseV2(const std::string &hexString, const Parts &parts) SerialKey serialKey(hexString); serialKey.type = SerialKeyType(parts.at(1)); serialKey.product = Product(parts.at(2)); + serialKey.name = parts.at(3); + serialKey.email = parts.at(5); + serialKey.company = parts.at(6); serialKey.warnTime = parseDate(parts.at(7)); serialKey.expireTime = parseDate(parts.at(8)); serialKey.isValid = true; @@ -127,6 +133,9 @@ SerialKey parseV3(const std::string &hexString, const Parts &parts) serialKey.isOffline = (parts.at(1) == "offline"); serialKey.type = SerialKeyType(parts.at(2)); serialKey.product = Product(parts.at(3)); + serialKey.name = parts.at(4); + serialKey.email = parts.at(6); + serialKey.company = parts.at(7); serialKey.warnTime = parseDate(parts.at(8)); serialKey.expireTime = parseDate(parts.at(9)); serialKey.isValid = true; diff --git a/src/lib/common/Constants.h.in b/src/lib/common/Constants.h.in index 4bf449406..1f587794f 100644 --- a/src/lib/common/Constants.h.in +++ b/src/lib/common/Constants.h.in @@ -18,6 +18,7 @@ const auto kRevFqdnName = "@CMAKE_PROJECT_REV_FQDN@"; const auto kCopyright = // "Copyright @CMAKE_PROJECT_COPYRIGHT@\n" + "Copyright (C) 2024-2026 Deskflow Developers\n" "Copyright (C) 2009-2012 Nick Bolton\n" "Copyright (C) 2002-2009 Chris Schoeneman"; diff --git a/src/lib/gui/dialogs/AboutDialog.cpp b/src/lib/gui/dialogs/AboutDialog.cpp index 3b6779672..74a884ee6 100644 --- a/src/lib/gui/dialogs/AboutDialog.cpp +++ b/src/lib/gui/dialogs/AboutDialog.cpp @@ -14,6 +14,10 @@ #include "common/VersionInfo.h" #include "gui/StyleUtils.h" +#ifdef SYNERGY_EXTRA_HEADER +#include "synergy/hooks/gui_hook.h" +#endif + #include AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui{std::make_unique()} @@ -52,6 +56,10 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui{std::make_unique ui->btnOk->setDefault(true); connect(ui->btnOk, &QPushButton::clicked, this, &AboutDialog::close); + +#ifdef SYNERGY_EXTRA_HEADER + synergy::hooks::onAbout(this); +#endif } void AboutDialog::copyVersionText() const From 38ad1e729518aeea47cf900fb8656b443bcdd246 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 13:08:06 +0100 Subject: [PATCH 072/144] feat: add GPL and EULA links to About dialog in FeatureHandler --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 13 +++++++++++++ extra/src/lib/synergy/gui/constants.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 27ba1a239..8f2550e1d 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -141,6 +141,19 @@ void FeatureHandler::handleAbout(QDialog *parent) const "Thanks to the Deskflow developers, all our contributors, and the wider open-source " "community." )); + + if (auto *linkGpl = parent->findChild(QStringLiteral("linkGpl"))) { + const auto gpl = QString(kLink).arg(kUrlGpl, kColorSecondary, QObject::tr("License: GNU GPL Version 2")); + const auto eula = QString(kLink).arg(kUrlEula, kColorSecondary, QObject::tr("End User License Agreement")); + linkGpl->setText(QStringLiteral("%1  |  %2").arg(gpl, eula)); + } + + if (auto *copyright = parent->findChild(QStringLiteral("lblCopyright"))) { + copyright->setText( + copyright->text() + QStringLiteral("\n") + + QObject::tr("The Synergy logo is a trademark of Synergy App Ltd.") + ); + } } namespace { diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 5f49a81b5..0284eed5a 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -27,6 +27,10 @@ const auto kUrlApi = "https://symless.com/synergy/api"; const auto kUrlWebsite = QStringLiteral("https://synergyapp.io"); const auto kUrlSourceQuery = "source=gui"; +const auto kUrlGpl = QStringLiteral("https://www.gnu.org/licenses/old-licenses/gpl-2.0.html"); +const auto kUrlEula = QString("%1/eula").arg(kUrlWebsite); + +const auto kLink = R"(%3)"; const auto kLinkBuy = R"(Buy now)"; const auto kLinkRenew = R"(Renew now)"; const auto kLinkDownload = R"(Download now)"; From 2c869656ff38e519c083a5a0db53818cf49ae265 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 13:36:23 +0100 Subject: [PATCH 073/144] feat: enhance About dialog with tagline, attribution, and license links --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 49 +++++++++++++++++++ extra/src/lib/synergy/gui/FeatureHandler.h | 5 ++ .../synergy/gui/license/LicenseHandler.cpp | 2 +- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 8f2550e1d..2f273a310 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -117,15 +118,48 @@ void FeatureHandler::handleSettings(QDialog *parent) const addScopeTab(parent); } +// The helpers below reach into upstream's AboutDialog.ui by object name and grid +// coordinate. Those references are upstream's, not ours, so they look like magic +// strings/numbers: the logo, version and license links share "gridLayout", where +// row 1 is the logo, row 3 the version and row 6 a trailing spacer. + void FeatureHandler::handleAbout(QDialog *parent) const { if (parent == nullptr) { return; } + addTagline(parent); + setAttribution(parent); + tightenVersionRow(parent); + addLicenseLinks(parent); + addTrademark(parent); +} + +void FeatureHandler::addTagline(QDialog *parent) const +{ + auto *grid = parent->findChild(QStringLiteral("gridLayout")); + if (grid == nullptr) { + return; + } + // Row 2 is empty, so the tagline sits under the logo; the margin is its gap to the version. + auto *tagline = new QLabel(QObject::tr("Unite every computer on your desk"), parent); + tagline->setContentsMargins(0, 0, 0, 16); + grid->addWidget(tagline, 2, 1); + + // Collapse the trailing spacer to tighten the gap to the section below. + if (auto *item = grid->itemAtPosition(6, 1); item != nullptr && item->spacerItem() != nullptr) { + item->spacerItem()->changeSize(0, 0); + grid->invalidate(); + } +} + +void FeatureHandler::setAttribution(QDialog *parent) const +{ auto *description = parent->findChild(QStringLiteral("lblDescription")); if (description == nullptr) { return; } + // Dim via alpha on the theme foreground (not a fixed grey) so it adapts to light/dark. description->setWordWrap(true); auto font = description->font(); font.setPointSizeF(font.pointSizeF() * 0.85); @@ -141,13 +175,28 @@ void FeatureHandler::handleAbout(QDialog *parent) const "Thanks to the Deskflow developers, all our contributors, and the wider open-source " "community." )); +} +void FeatureHandler::tightenVersionRow(QDialog *parent) const +{ + // The copy button is taller than the version text and padded the centred row; cap it. + if (auto *copyButton = parent->findChild(QStringLiteral("btnCopyVersion"))) { + copyButton->setMaximumHeight(copyButton->fontMetrics().height() + 4); + } +} + +void FeatureHandler::addLicenseLinks(QDialog *parent) const +{ + // Upstream shows only the GPL link; add the EULA alongside it. if (auto *linkGpl = parent->findChild(QStringLiteral("linkGpl"))) { const auto gpl = QString(kLink).arg(kUrlGpl, kColorSecondary, QObject::tr("License: GNU GPL Version 2")); const auto eula = QString(kLink).arg(kUrlEula, kColorSecondary, QObject::tr("End User License Agreement")); linkGpl->setText(QStringLiteral("%1  |  %2").arg(gpl, eula)); } +} +void FeatureHandler::addTrademark(QDialog *parent) const +{ if (auto *copyright = parent->findChild(QStringLiteral("lblCopyright"))) { copyright->setText( copyright->text() + QStringLiteral("\n") + diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index 80edf4b02..48e901b67 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -39,6 +39,11 @@ class FeatureHandler void styleUpdateNotice(QMainWindow *mainWindow) const; void addScopeTab(QDialog *parent) const; void addUpdateChannelOption(QDialog *parent) const; + void addTagline(QDialog *parent) const; + void setAttribution(QDialog *parent) const; + void tightenVersionRow(QDialog *parent) const; + void addLicenseLinks(QDialog *parent) const; + void addTrademark(QDialog *parent) const; QMainWindow *m_pMainWindow = nullptr; }; diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 8e5c5b505..8dfeb800f 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -175,7 +175,7 @@ void LicenseHandler::handleAbout(QDialog *parent) const } auto *section = new QVBoxLayout(); - section->setContentsMargins(0, 8, 0, 8); + section->setContentsMargins(0, 0, 0, 8); auto *registrantLabel = new QLabel(parent); auto *keyField = new QLineEdit(parent); From 08e0c6d521e95521afb6865d6a393c6d5f1f26b7 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 13:38:30 +0100 Subject: [PATCH 074/144] refactor: change pathLabel function to static and remove unnecessary namespace --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 2f273a310..491ec687e 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -205,9 +205,7 @@ void FeatureHandler::addTrademark(QDialog *parent) const } } -namespace { - -QString pathLabel(const QString &path) +static QString pathLabel(const QString &path) { if (QFileInfo::exists(path)) { return QStringLiteral("%1").arg(path); @@ -216,8 +214,6 @@ QString pathLabel(const QString &path) .arg(path, QObject::tr("(not yet created)")); } -} // namespace - void FeatureHandler::addScopeTab(QDialog *parent) const { auto *tabWidget = parent->findChild(); From a2558287ec4f18428fafbf4820d7ce88f2074369 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 13:46:55 +0100 Subject: [PATCH 075/144] fix: remove unnecessary argument from kUrlUpdateCheck definition --- src/lib/common/UrlConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/common/UrlConstants.h b/src/lib/common/UrlConstants.h index 536dea00d..986c23ffb 100644 --- a/src/lib/common/UrlConstants.h +++ b/src/lib/common/UrlConstants.h @@ -20,7 +20,7 @@ const auto kUrlApp = QStringLiteral("https://%1").arg(kOrgDomain); const auto kUrlHelp = QStringLiteral("%1/help?%2").arg(kUrlApp, kUrlSourceQuery); const auto kUrlDownload = QStringLiteral("%1/download?%2").arg(kUrlApp, kUrlSourceQuery); -const auto kUrlUpdateCheck = QStringLiteral("https://api.symless.com/version").arg(kOrgDomain); +const auto kUrlUpdateCheck = QStringLiteral("https://api.symless.com/version"); #if defined(Q_OS_LINUX) const auto kUrlGnomeTrayFix = QStringLiteral("https://extensions.gnome.org/extension/615/appindicator-support/"); From 76234f9c343e7c1b561627bbd59efbd978d4299a Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 13:56:37 +0100 Subject: [PATCH 076/144] feat: update window title handling to include version stage and show/hide version option --- extra/src/lib/synergy/gui/dev_mode.h | 39 +++++++++++++++++-- .../synergy/gui/license/LicenseHandler.cpp | 3 +- extra/src/lib/synergy/hooks/gui_hook.h | 3 +- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/extra/src/lib/synergy/gui/dev_mode.h b/extra/src/lib/synergy/gui/dev_mode.h index 9b5953299..6a2531b38 100644 --- a/extra/src/lib/synergy/gui/dev_mode.h +++ b/extra/src/lib/synergy/gui/dev_mode.h @@ -21,17 +21,48 @@ #include "synergy/build_config.h" #include +#include #include namespace synergy::gui { -inline QString titleWithDevSuffix(const QString &productName) +// Extracts the release stage (e.g. "beta") from the composed version string. +// kVersion is "X.Y.Z[-STAGE][-dev|-snapshot+rN]", so the stage is the first +// token after the base semver that isn't a build-type marker. +inline QString versionStage() { + const QStringList parts = QString::fromLatin1(kVersion).split('-'); + if (parts.size() < 2) { + return {}; + } + const QString &candidate = parts.at(1); + if (candidate == QStringLiteral("dev") || candidate.startsWith(QStringLiteral("snapshot"))) { + return {}; + } + return candidate; +} + +// Composes the main-window title. The stage / dev-build marker is always part +// of the name so a pre-release binary can't masquerade as stable; showVersion +// (the upstream "Include version in the window title" setting) gates only the +// trailing version-number suffix. +inline QString windowTitle(const QString &productName, bool showVersion) +{ + const QString stage = versionStage(); + const QString prettyStage = stage.isEmpty() ? QString() : stage.left(1).toUpper() + stage.mid(1); + + QString name = productName; if constexpr (synergy::kIsDevBuild) { - return QStringLiteral("%1 - Developer build - v%2").arg(productName, kDisplayVersion); - } else { - return productName; + name = prettyStage.isEmpty() ? QStringLiteral("%1 (Development Build)").arg(productName) + : QStringLiteral("%1 (%2 Development Build)").arg(productName, prettyStage); + } else if (!prettyStage.isEmpty()) { + name = QStringLiteral("%1 %2").arg(productName, prettyStage); + } + + if (showVersion) { + return QStringLiteral("%1 - %2").arg(name, kDisplayVersion); } + return name; } } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 8dfeb800f..c011b1ae9 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -351,7 +351,8 @@ void LicenseHandler::updateWindowTitle() const { const auto productName = QString::fromStdString(m_license.productName()); qDebug("updating main window title: %s", qPrintable(productName)); - m_pMainWindow->setWindowTitle(synergy::gui::titleWithDevSuffix(productName)); + const bool showVersion = Settings::value(Settings::Gui::ShowVersionInTitle).toBool(); + m_pMainWindow->setWindowTitle(synergy::gui::windowTitle(productName, showVersion)); } const synergy::license::License &LicenseHandler::license() const diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index ae22f2dfe..b9bb1db64 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -61,7 +61,8 @@ inline void onMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *co inline void onTitleApplied(QMainWindow *mainWindow) { - mainWindow->setWindowTitle(synergy::gui::titleWithDevSuffix(synergy::kDisplayName)); + const bool showVersion = Settings::value(Settings::Gui::ShowVersionInTitle).toBool(); + mainWindow->setWindowTitle(synergy::gui::windowTitle(synergy::kDisplayName, showVersion)); } inline bool onAppStart() From 8abef9070ff135bb1a992c931b9935acb73b35ca Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 14:26:38 +0100 Subject: [PATCH 077/144] feat: bundle MSVC C++ runtime as merge module in installer and remove custom DLL checks --- deploy/windows/deploy.cmake | 47 +++++++++++++-------- deploy/windows/wix-custom.cpp | 72 --------------------------------- deploy/windows/wix-custom.h.in | 15 ------- deploy/windows/wix-patch.xml.in | 36 ++++------------- 4 files changed, 37 insertions(+), 133 deletions(-) delete mode 100644 deploy/windows/wix-custom.cpp delete mode 100644 deploy/windows/wix-custom.h.in diff --git a/deploy/windows/deploy.cmake b/deploy/windows/deploy.cmake index 47ed908f2..9e784107a 100644 --- a/deploy/windows/deploy.cmake +++ b/deploy/windows/deploy.cmake @@ -47,26 +47,39 @@ list(APPEND CPACK_WIX_EXTENSIONS "WixToolset.Util.wixext" "WixToolset.Firewall.w # Make sure to also put the xmlns for the ext into the wix block on generated files list(APPEND CPACK_WIX_CUSTOM_XMLNS "util=http://wixtoolset.org/schemas/v4/wxs/util" "firewall=http://wixtoolset.org/schemas/v4/wxs/firewall") -# The patch has to know the full path of our msm file +# Bundle the MSVC C++ runtime (CRT) as a merge module inside the MSI so that end +# users don't have to download and install the Visual C++ redistributable +# themselves. The merge module ships with the Visual Studio build environment. +# Docs: https://learn.microsoft.com/en-us/cpp/windows/redistributing-components-by-using-merge-modules +set(REDIST_MERGE_MODULE_DIR "$ENV{VCINSTALLDIR}Redist/MSVC") +file(GLOB REDIST_MERGE_MODULE_PATHS + "${REDIST_MERGE_MODULE_DIR}/*/MergeModules/Microsoft_VC*_CRT_${BUILD_ARCHITECTURE}.msm") +if (REDIST_MERGE_MODULE_PATHS) + # Multiple toolset versions may be installed; pick the newest. + list(SORT REDIST_MERGE_MODULE_PATHS) + list(GET REDIST_MERGE_MODULE_PATHS -1 REDIST_MERGE_MODULE_PATH) + message(STATUS "MSVC CRT merge module: ${REDIST_MERGE_MODULE_PATH}") + # XML snippets injected into wix-patch.xml.in to merge the CRT into the MSI. + set(WIX_REDIST_MERGE + "") + set(WIX_REDIST_MERGE_REF "") +elseif(SYNERGY_VERSION_RELEASE OR SYNERGY_VERSION_SNAPSHOT) + # Shipping build: a missing CRT module means a broken installer for end users. + message(WARNING + "MSVC CRT merge module not found under ${REDIST_MERGE_MODULE_DIR}; " + "the installer will NOT bundle the Visual C++ runtime") +else() + # Dev builds routinely lack the redist merge module; not bundling it is expected. + message(STATUS + "MSVC CRT merge module not found; installer will not bundle the Visual C++ " + "runtime (expected for dev builds)") +endif() + +# The patch needs the full path of the CRT merge module (see above). configure_file( ${MY_DIR}/wix-patch.xml.in ${CMAKE_CURRENT_BINARY_DIR}/wix-patch.xml @ONLY ) -# This patch set ups filewall rules, the service and msm module +# This patch sets up firewall rules, the service and the CRT merge module. set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_BINARY_DIR}/wix-patch.xml") - -# Creates a DLL that can be used by our MSI for custom actions. -configure_file( - ${MY_DIR}/wix-custom.h.in - ${CMAKE_CURRENT_BINARY_DIR}/wix-custom.h @ONLY -) -add_library( - wix-custom SHARED - ${MY_DIR}/wix-custom.cpp -) -target_include_directories(wix-custom PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) -set_target_properties(wix-custom PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" -) -target_link_libraries(wix-custom PRIVATE Msi) diff --git a/deploy/windows/wix-custom.cpp b/deploy/windows/wix-custom.cpp deleted file mode 100644 index 0d58bac75..000000000 --- a/deploy/windows/wix-custom.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2025 Symless Ltd. - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#include "wix-custom.h" - -#define WIN32_LEAN_AND_MEAN -#include - -// Include after Windows.h -#include - -#include -#include - -namespace { -// Warning: DLL will crash with error code 1603 if we exceed this. -const auto kLogLineMax = 1024; - -// Prefixes log messages with the app name so they're easier to find/filter. -const std::string kLogPrefix = std::string(kAppId) + " installer: "; - -// Note: Resized to log line max when used. -static std::string s_logMessageBuffer; // NOSONAR - Must be mutable. -} // namespace - -// This log output can be viewed by using the DebugView program. -#define MS_LOG_DEBUG(message, ...) \ - s_logMessageBuffer.resize(kLogLineMax); \ - sprintf(s_logMessageBuffer.data(), message, __VA_ARGS__); \ - OutputDebugStringA((kLogPrefix + s_logMessageBuffer + "\n").c_str()) - -extern "C" __declspec(dllexport) UINT __stdcall CheckVCRedist(MSIHANDLE hInstall) -{ - const auto kKeyName = TEXT(kRegKey); - const auto kValueName = TEXT("Minor"); - const auto kProperty = "VC_REDIST_VERSION_OK"; - - MS_LOG_DEBUG("checking for msvc redist v%d.%d", kWindowsRuntimeMajor, kWindowsRuntimeMinor); - - HKEY hKey; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, kKeyName, 0, KEY_READ, &hKey) != ERROR_SUCCESS) { - MS_LOG_DEBUG("msvc redist registry key not found"); - return ERROR_FUNCTION_FAILED; - } - - MS_LOG_DEBUG("msvc redist registry key found, querying minor version"); - - DWORD minorVersion = 0; - DWORD size = sizeof(DWORD); - RegQueryValueEx(hKey, kValueName, nullptr, nullptr, (LPBYTE)&minorVersion, &size); - RegCloseKey(hKey); - - MS_LOG_DEBUG("msvc redist minor version: %lu", minorVersion); - - if (minorVersion < kWindowsRuntimeMinor) { - MS_LOG_DEBUG("msvc redist minor version %lu too low, expected >= %d", minorVersion, kWindowsRuntimeMinor); - // Returning success allows the installer will show a friendly error message. - return ERROR_SUCCESS; - } - - MS_LOG_DEBUG("msvc redist version ok, setting: %s", kProperty); - if (MsiSetProperty(hInstall, kProperty, "ok") != ERROR_SUCCESS) { - MS_LOG_DEBUG("failed to set property: %s", kProperty); - return ERROR_FUNCTION_FAILED; - } - - MS_LOG_DEBUG("msvc redist version check successful"); - return ERROR_SUCCESS; -} diff --git a/deploy/windows/wix-custom.h.in b/deploy/windows/wix-custom.h.in deleted file mode 100644 index 2032be38f..000000000 --- a/deploy/windows/wix-custom.h.in +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2025 Symless Ltd. - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#pragma once - -const auto kAppId = "@CMAKE_PROJECT_NAME@"; -const auto kRegKey = "SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\@BUILD_ARCHITECTURE@"; - -// clang-format off -const auto kWindowsRuntimeMajor = @REQUIRED_MSVC_RUNTIME_MAJOR@; -const auto kWindowsRuntimeMinor = @REQUIRED_MSVC_RUNTIME_MINOR@; -// clang-format on diff --git a/deploy/windows/wix-patch.xml.in b/deploy/windows/wix-patch.xml.in index 4fe496667..b81507024 100644 --- a/deploy/windows/wix-patch.xml.in +++ b/deploy/windows/wix-patch.xml.in @@ -26,16 +26,10 @@ - - - - - + + @WIX_REDIST_MERGE@ - - - + - - - - + + @WIX_REDIST_MERGE_REF@ From f48d772d7c940272065cd52cdded237c23728f69 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 14:36:10 +0100 Subject: [PATCH 078/144] feat: add synergy-dark and synergy-light theme files to resource configuration --- extra/src/apps/res/synergy-dark.theme | 12 ++++++++++++ extra/src/apps/res/synergy-light.theme | 12 ++++++++++++ extra/src/apps/res/synergy.qrc | 2 ++ 3 files changed, 26 insertions(+) create mode 100644 extra/src/apps/res/synergy-dark.theme create mode 100644 extra/src/apps/res/synergy-light.theme diff --git a/extra/src/apps/res/synergy-dark.theme b/extra/src/apps/res/synergy-dark.theme new file mode 100644 index 000000000..534eaab75 --- /dev/null +++ b/extra/src/apps/res/synergy-dark.theme @@ -0,0 +1,12 @@ +[Icon Theme] +Name=synergy-dark +Inherits=deskflow-dark + +Directories=apps/64 + +[apps/64] +Size=64 +Context=Applications +Type=Scalable +MinSize=22 +MaxSize=256 diff --git a/extra/src/apps/res/synergy-light.theme b/extra/src/apps/res/synergy-light.theme new file mode 100644 index 000000000..061a89bf8 --- /dev/null +++ b/extra/src/apps/res/synergy-light.theme @@ -0,0 +1,12 @@ +[Icon Theme] +Name=synergy-light +Inherits=deskflow-light + +Directories=apps/64 + +[apps/64] +Size=64 +Context=Applications +Type=Scalable +MinSize=22 +MaxSize=256 diff --git a/extra/src/apps/res/synergy.qrc b/extra/src/apps/res/synergy.qrc index 11090b359..a904ed61c 100644 --- a/extra/src/apps/res/synergy.qrc +++ b/extra/src/apps/res/synergy.qrc @@ -2,6 +2,8 @@ image/logo-dark.png image/logo-light.png + synergy-dark.theme + synergy-light.theme synergy.svg synergy.svg synergy.svg From 56848bb85a5fec2f696b8a4828e5dc922057e94c Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 14:37:37 +0100 Subject: [PATCH 079/144] fix: remove redundant runInTerminal property from launch configurations --- .vscode/launch.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index a03a33af0..db58957e2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,6 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", - "runInTerminal": true, "program": "${workspaceFolder}/build/bin/synergy", "preLaunchTask": "build-silent", "initCommands": ["settings set target.preload-symbols false"], @@ -19,7 +18,6 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", - "runInTerminal": true, "program": "${workspaceFolder}/build/bin/unittests", "args": ["${input:gtest-args}"], "preLaunchTask": "build-silent" @@ -29,7 +27,6 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", - "runInTerminal": true, "program": "${workspaceFolder}/build/bin/integtests", "args": ["${input:gtest-args}"], "preLaunchTask": "build-silent" @@ -39,7 +36,6 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", - "runInTerminal": true, "program": "${workspaceFolder}/build/bin/synergy-core", "args": ["server"], "preLaunchTask": "build-silent", @@ -52,7 +48,6 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", - "runInTerminal": true, "program": "${workspaceFolder}/build/bin/synergy-core", "args": ["client"], "preLaunchTask": "build-silent", @@ -65,7 +60,6 @@ "type": "lldb-dap", "cwd": "${workspaceRoot}", "request": "launch", - "runInTerminal": true, "program": "${workspaceFolder}/build/bin/synergy-daemon", "args": ["-f"], "preLaunchTask": "build-silent" From 732851dc6742e78ac96114ef3c181337897c81b5 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 16:57:56 +0100 Subject: [PATCH 080/144] feat: add support for crisp system-tray icons using SVG resources --- extra/src/apps/res/synergy.svg | 36 ++++++++++++++++---------- extra/src/lib/synergy/hooks/gui_hook.h | 25 ++++++++++++++++++ src/lib/gui/MainWindow.cpp | 4 +++ 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/extra/src/apps/res/synergy.svg b/extra/src/apps/res/synergy.svg index 6e8edd76e..dbbe9bc49 100644 --- a/extra/src/apps/res/synergy.svg +++ b/extra/src/apps/res/synergy.svg @@ -1,15 +1,23 @@ - - - - - - - - - - - - - - + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index b9bb1db64..a8ccb9955 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -27,7 +27,9 @@ #include "synergy/gui/license/LicenseHandler.h" #include +#include #include +#include namespace deskflow::gui { class CoreProcess; @@ -99,4 +101,27 @@ inline void onTestStart() LicenseHandler::instance().disable(); } +/** + * @brief Build a crisp system-tray icon from a Qt-resource SVG. + * + * Synergy shows its colored brand logo in the tray, rendered straight from a + * resource SVG rather than a themed monochrome icon that GNOME would size + * itself. A fresh SVG-backed QIcon reports no available sizes, so Qt's + * StatusNotifierItem backend only hands GNOME 22px and 64px renderings; GNOME + * upscales the nearest one to its panel slot and the icon looks blurry. + * Pre-rendering the common tray sizes gives GNOME a near-exact match, so it + * barely scales the bitmap and the icon stays sharp. + * + * @param resourcePath Qt resource path of the SVG to render. + * @return A multi-size icon suitable for QSystemTrayIcon::setIcon. + */ +inline QIcon trayIcon(const QString &resourcePath) +{ + const QIcon source(resourcePath); + QIcon icon; + for (const int size : {16, 22, 24, 32, 48, 64}) + icon.addPixmap(source.pixmap(QSize(size, size))); + return icon; +} + } // namespace synergy::hooks diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index c6a32ff64..8b3e37c16 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -755,7 +755,11 @@ void MainWindow::setTrayIcon() if (deskflow::platform::isMac()) m_trayIcon->setIcon(QIcon::fromTheme(themeIcon)); else +#ifdef SYNERGY_EXTRA_HEADER + m_trayIcon->setIcon(synergy::hooks::trayIcon(fallbackPath.arg(kAppId, QStringLiteral("dark"), themeIcon))); +#else m_trayIcon->setIcon(QIcon(fallbackPath.arg(kAppId, QStringLiteral("dark"), themeIcon))); +#endif return; } From 33dfa856c6d6b66c8834714ad2f4ed4b8ad29098 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 18:14:50 +0100 Subject: [PATCH 081/144] feat: add symbolic SVG icons for dark and light themes --- extra/src/apps/res/synergy-symbolic-dark.svg | 11 +++++++++++ extra/src/apps/res/synergy-symbolic-light.svg | 11 +++++++++++ extra/src/apps/res/synergy.qrc | 4 ++-- 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 extra/src/apps/res/synergy-symbolic-dark.svg create mode 100644 extra/src/apps/res/synergy-symbolic-light.svg diff --git a/extra/src/apps/res/synergy-symbolic-dark.svg b/extra/src/apps/res/synergy-symbolic-dark.svg new file mode 100644 index 000000000..11f2cec4c --- /dev/null +++ b/extra/src/apps/res/synergy-symbolic-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/extra/src/apps/res/synergy-symbolic-light.svg b/extra/src/apps/res/synergy-symbolic-light.svg new file mode 100644 index 000000000..585ddb8d5 --- /dev/null +++ b/extra/src/apps/res/synergy-symbolic-light.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/extra/src/apps/res/synergy.qrc b/extra/src/apps/res/synergy.qrc index a904ed61c..b2bbcf042 100644 --- a/extra/src/apps/res/synergy.qrc +++ b/extra/src/apps/res/synergy.qrc @@ -5,8 +5,8 @@ synergy-dark.theme synergy-light.theme synergy.svg - synergy.svg + synergy-symbolic-dark.svg synergy.svg - synergy.svg + synergy-symbolic-light.svg From aa5d6415a9e7288c40363b91c515dc80834a4eb0 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 18:34:02 +0100 Subject: [PATCH 082/144] feat: update tray icon handling to support synergy extra header --- deploy/linux/deploy.cmake | 2 +- extra/src/apps/res/synergy-symbolic-dark.svg | 15 +++++---------- extra/src/apps/res/synergy-symbolic-light.svg | 15 +++++---------- src/lib/gui/MainWindow.cpp | 7 ++++++- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/deploy/linux/deploy.cmake b/deploy/linux/deploy.cmake index 7e31e0d54..1d64cb917 100644 --- a/deploy/linux/deploy.cmake +++ b/deploy/linux/deploy.cmake @@ -20,7 +20,7 @@ install( # Install our symbolic icon install( - FILES ${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy.svg + FILES ${CMAKE_SOURCE_DIR}/extra/src/apps/res/synergy-symbolic-light.svg RENAME ${CMAKE_PROJECT_REV_FQDN}-symbolic.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/symbolic/apps/ ) diff --git a/extra/src/apps/res/synergy-symbolic-dark.svg b/extra/src/apps/res/synergy-symbolic-dark.svg index 11f2cec4c..5991401e4 100644 --- a/extra/src/apps/res/synergy-symbolic-dark.svg +++ b/extra/src/apps/res/synergy-symbolic-dark.svg @@ -1,11 +1,6 @@ - - - - - - - - - - + + + + + diff --git a/extra/src/apps/res/synergy-symbolic-light.svg b/extra/src/apps/res/synergy-symbolic-light.svg index 585ddb8d5..237b279e7 100644 --- a/extra/src/apps/res/synergy-symbolic-light.svg +++ b/extra/src/apps/res/synergy-symbolic-light.svg @@ -1,11 +1,6 @@ - - - - - - - - - - + + + + + diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index 8b3e37c16..9f1fd78e1 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -776,7 +776,12 @@ void MainWindow::setTrayIcon() return; } - auto icon = QIcon::fromTheme(themeIcon, QIcon(fallbackPath.arg(kAppId, iconMode(), themeIcon))); + auto icon = +#ifdef SYNERGY_EXTRA_HEADER + synergy::hooks::trayIcon(fallbackPath.arg(kAppId, iconMode(), themeIcon)); +#else + QIcon::fromTheme(themeIcon, QIcon(fallbackPath.arg(kAppId, iconMode(), themeIcon))); +#endif icon.setIsMask(true); m_trayIcon->setIcon(icon); } From 1f432e4488a675697b26b30fec4163d91a497009 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 18:43:07 +0100 Subject: [PATCH 083/144] feat: update SVG viewBox dimensions for dark and light theme icons --- extra/src/apps/res/synergy-symbolic-dark.svg | 2 +- extra/src/apps/res/synergy-symbolic-light.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/src/apps/res/synergy-symbolic-dark.svg b/extra/src/apps/res/synergy-symbolic-dark.svg index 5991401e4..800ad15b6 100644 --- a/extra/src/apps/res/synergy-symbolic-dark.svg +++ b/extra/src/apps/res/synergy-symbolic-dark.svg @@ -1,4 +1,4 @@ - + diff --git a/extra/src/apps/res/synergy-symbolic-light.svg b/extra/src/apps/res/synergy-symbolic-light.svg index 237b279e7..a8fae6fec 100644 --- a/extra/src/apps/res/synergy-symbolic-light.svg +++ b/extra/src/apps/res/synergy-symbolic-light.svg @@ -1,4 +1,4 @@ - + From a2bb61d2e723647a2b838b05ca8989027dde6339 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 5 Jun 2026 20:30:50 +0100 Subject: [PATCH 084/144] feat: update SVG dimensions for consistency in dark and light theme icons --- extra/src/apps/res/synergy-symbolic-dark.svg | 2 +- extra/src/apps/res/synergy-symbolic-light.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/src/apps/res/synergy-symbolic-dark.svg b/extra/src/apps/res/synergy-symbolic-dark.svg index 800ad15b6..10909340e 100644 --- a/extra/src/apps/res/synergy-symbolic-dark.svg +++ b/extra/src/apps/res/synergy-symbolic-dark.svg @@ -1,4 +1,4 @@ - + diff --git a/extra/src/apps/res/synergy-symbolic-light.svg b/extra/src/apps/res/synergy-symbolic-light.svg index a8fae6fec..c08f6a86e 100644 --- a/extra/src/apps/res/synergy-symbolic-light.svg +++ b/extra/src/apps/res/synergy-symbolic-light.svg @@ -1,4 +1,4 @@ - + From 198caafd61c2b5643a0027fe427f21ebf4185995 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 8 Jun 2026 13:20:17 +0100 Subject: [PATCH 085/144] fix: standardize default values and improve concurrency handling in CI workflow --- .github/workflows/ci.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50120433b..5438cfd63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,10 +23,10 @@ on: platform: description: "Build only one platform (blank = all)" required: false - default: '' + default: "" type: choice options: - - '' + - "" - windows - macos - linux @@ -38,9 +38,10 @@ on: schedule: - cron: "0 5 * * *" # 5am UTC nightly snapshot +# Cancel superseded PR builds; pushes to branches/tags always run to completion. concurrency: group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: GIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} @@ -252,7 +253,6 @@ jobs: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* - build-macos: needs: lint-clang if: ${{ inputs.platform == '' || inputs.platform == 'macos' }} @@ -422,7 +422,6 @@ jobs: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* - build-linux: needs: lint-clang if: ${{ inputs.platform == '' || inputs.platform == 'linux' }} @@ -641,7 +640,6 @@ jobs: name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* - build-flatpak: needs: lint-clang if: ${{ inputs.platform == '' || inputs.platform == 'linux' }} From b6d697631aaa373bb6745ab43df776f9064ba3bd Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 10 Jun 2026 18:51:48 +0100 Subject: [PATCH 086/144] refactor: streamline test settings configuration and remove environment variable dependencies --- .gitignore | 1 + extra/Synergy.test.example.conf | 15 +++--- extra/src/lib/synergy/gui/TestSettings.cpp | 48 +++++++------------ extra/src/lib/synergy/gui/TestSettings.h | 35 +++++++++----- .../lib/synergy/gui/license/license_utils.cpp | 17 +------ 5 files changed, 52 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 685035b71..a7da0a026 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ Brewfile.lock.json # typical developer-created files deskflow-config.toml +/Synergy.test.conf .vscode .DS_Store *.code-workspace diff --git a/extra/Synergy.test.example.conf b/extra/Synergy.test.example.conf index 88853b51a..503491427 100644 --- a/extra/Synergy.test.example.conf +++ b/extra/Synergy.test.example.conf @@ -1,15 +1,18 @@ # Synergy test settings: example/template. # -# Copy to: +# Copy to the repo root as `Synergy.test.conf` (git-ignored). The app +# walks up from the binary's directory and loads the first +# Synergy.test.conf it finds, so a repo-root copy covers any build dir +# inside the tree and survives wipes of the app settings dir. +# +# When nothing is found above the binary (e.g. installed builds), it +# falls back to the location sibling to Synergy.conf: # ~/.config/Synergy/Synergy.test.conf (Linux) # ~/Library/Synergy/Synergy.test.conf (macOS) # %APPDATA%\Synergy\Synergy.test.conf (Windows) # -# Sibling to Synergy.conf in the same dir. Edit this file instead of -# setting SYNERGY_TEST_* env vars (which are painful to thread into -# GUI launches on macOS/Windows). SYNERGY_TEST_* env vars still work and -# take precedence over this file when both are set, so CI/automation -# flows are unchanged. +# This file is the only source of test settings; SYNERGY_TEST_* env vars +# are no longer read. [test] diff --git a/extra/src/lib/synergy/gui/TestSettings.cpp b/extra/src/lib/synergy/gui/TestSettings.cpp index 813e2b450..4b238aacd 100644 --- a/extra/src/lib/synergy/gui/TestSettings.cpp +++ b/extra/src/lib/synergy/gui/TestSettings.cpp @@ -20,7 +20,9 @@ #include "common/Constants.h" #include "common/Settings.h" +#include #include +#include #include #include @@ -28,13 +30,23 @@ namespace synergy::gui { namespace { -QString envOrFile(const char *envName, const QString &fileValue, bool fileEnabled) +QString resolveFileName() { - const auto env = qEnvironmentVariable(envName); - if (!env.isEmpty()) { - return env; + const auto name = QStringLiteral("%1.test.conf").arg(kAppName); + const auto fallback = QStringLiteral("%1/%2").arg(Settings::UserDir, name); + + // applicationDirPath() warns if called before the app object exists + if (QCoreApplication::instance() == nullptr) { + return fallback; } - return fileEnabled ? fileValue : QString(); + + QDir dir(QCoreApplication::applicationDirPath()); + do { + if (dir.exists(name)) { + return dir.filePath(name); + } + } while (dir.cdUp()); + return fallback; } } // namespace @@ -46,13 +58,13 @@ TestSettings &TestSettings::instance() } TestSettings::TestSettings() - : m_fileName(QStringLiteral("%1/%2.test.conf").arg(Settings::UserDir, kAppName)) { load(); } void TestSettings::load() { + m_fileName = resolveFileName(); m_enabled = false; m_licensing = false; m_serialKey.clear(); @@ -92,28 +104,4 @@ void TestSettings::reload() load(); } -QString TestSettings::serialKey() const -{ - return envOrFile("SYNERGY_TEST_SERIAL_KEY", m_serialKey, m_enabled); -} - -QString TestSettings::apiUrlActivate() const -{ - return envOrFile("SYNERGY_TEST_API_URL_ACTIVATE", m_apiUrlActivate, m_enabled); -} - -QString TestSettings::apiUrlCheck() const -{ - return envOrFile("SYNERGY_TEST_API_URL_CHECK", m_apiUrlCheck, m_enabled); -} - -qint64 TestSettings::startTimeEpochSecs() const -{ - const auto env = qEnvironmentVariable("SYNERGY_TEST_START_TIME"); - if (!env.isEmpty()) { - return env.toLongLong(); - } - return m_enabled ? m_startTimeEpochSecs : 0; -} - } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/TestSettings.h b/extra/src/lib/synergy/gui/TestSettings.h index 7d85745e6..c14b68b65 100644 --- a/extra/src/lib/synergy/gui/TestSettings.h +++ b/extra/src/lib/synergy/gui/TestSettings.h @@ -21,13 +21,14 @@ namespace synergy::gui { -// QA/test overrides for Synergy. Lives at `${UserDir}/${kAppName}.test.conf`, +// QA/test overrides for Synergy. The config file is `${kAppName}.test.conf`, +// found by walking up from the application binary's directory: a git-ignored +// copy at the repo root covers any build dir inside the tree and survives +// wipes of the app settings dir. When nothing is found above the binary +// (e.g. installed builds), falls back to `${UserDir}/${kAppName}.test.conf`, // sibling to the main Synergy.conf settings file. Sourcing values from a file // (rather than env vars) sidesteps the pain of getting env vars into GUI // launches on macOS / Windows / VS Code F5. -// -// All accessors fall back to the corresponding SYNERGY_TEST_* env var when -// the env var is set, so existing CI/automation flows are unchanged. class TestSettings { public: @@ -48,15 +49,25 @@ class TestSettings return m_enabled && m_licensing; } - // Test overrides. Each accessor returns the env var when set, otherwise - // the file value when isEnabled(), otherwise empty / 0. - QString serialKey() const; - QString apiUrlActivate() const; - QString apiUrlCheck() const; - qint64 startTimeEpochSecs() const; + // Test overrides; empty / 0 unless isEnabled(). + QString serialKey() const + { + return m_serialKey; + } + QString apiUrlActivate() const + { + return m_apiUrlActivate; + } + QString apiUrlCheck() const + { + return m_apiUrlCheck; + } + qint64 startTimeEpochSecs() const + { + return m_startTimeEpochSecs; + } - // Feature toggles read from the [features] section. File-only, no env - // var fallback for these (no precedent). + // Feature toggles read from the [features] section. bool verbose() const { return m_verbose; diff --git a/extra/src/lib/synergy/gui/license/license_utils.cpp b/extra/src/lib/synergy/gui/license/license_utils.cpp index e14767648..2d5ece151 100644 --- a/extra/src/lib/synergy/gui/license/license_utils.cpp +++ b/extra/src/lib/synergy/gui/license/license_utils.cpp @@ -31,24 +31,9 @@ const bool kEnableActivation = true; const bool kEnableActivation = false; #endif // SYNERGY_ENABLE_ACTIVATION -namespace { -// Inlined from upstream's removed gui/string_utils.h. Trivial helper kept -// local to avoid taking a new dependency just for this one call site. -bool strToTrue(const QString &str) -{ - return str.toLower() == "true" || str == "1"; -} -} // namespace - bool isActivationEnabled() { - if (strToTrue(qEnvironmentVariable("SYNERGY_ENABLE_ACTIVATION"))) { - return true; - } - if (synergy::gui::TestSettings::instance().isLicensingEnabled()) { - return true; - } - return kEnableActivation; + return synergy::gui::TestSettings::instance().isLicensingEnabled() || kEnableActivation; } synergy::license::SerialKey parseSerialKey(const QString &hexString) From 74d27d52f5d3d18f2f52e628eef683d8be67fb3d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 10 Jun 2026 18:51:54 +0100 Subject: [PATCH 087/144] feat: enhance license handling with activation unreachable signal and improved grace period management --- .../synergy/gui/license/LicenseApiClient.cpp | 23 ++++++++++---- .../synergy/gui/license/LicenseApiClient.h | 2 +- .../synergy/gui/license/LicenseHandler.cpp | 30 ++++++++++++++----- .../lib/synergy/gui/license/LicenseHandler.h | 2 +- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index 2f723846a..6ae018829 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -89,9 +89,19 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) } }; + // A transport failure is not a license verdict; activation tolerates it and retries + // later, while the check path owns the grace period. + const auto emitUnreachable = [this, kind](const QString &message) { + if (kind == RequestKind::kActivate) { + Q_EMIT activationUnreachable(); + } else { + Q_EMIT checkFailed(message); + } + }; + if (!reply) { qWarning("no license api reply"); - emitFailed("License request failed, empty network reply."); + emitUnreachable("License request failed, empty network reply."); return; } @@ -101,7 +111,7 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) const auto kLimit = 200; const auto responseSliced = response.length() > kLimit ? response.sliced(0, kLimit) + "..." : response; qWarning().noquote() << "license api error:" << reply->error() << reply->errorString() << responseSliced; - emitFailed("License request failed, there was a network error."); + emitUnreachable("License request failed, there was a network error."); reply->deleteLater(); return; } @@ -110,7 +120,7 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) const auto jsonDoc = QJsonDocument::fromJson(response); if (response.isNull()) { qWarning("empty license api response"); - emitFailed("License request failed, the server sent an empty response."); + emitUnreachable("License request failed, the server sent an empty response."); reply->deleteLater(); return; } @@ -132,10 +142,11 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) qWarning("license api message was empty"); } - if (status == "disabled") { - Q_EMIT licenseDisabled(message.isEmpty() ? QStringLiteral("License has been disabled.") : message); - } else if (!message.isEmpty()) { + // An explicit disable gets the grace window like any other failure, not an abrupt cutoff. + if (!message.isEmpty()) { emitFailed(message); + } else if (status == "disabled") { + emitFailed("License has been disabled."); } else { emitFailed("License request failed, unknown error."); } diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h index 126e63514..2bd43fe46 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -53,9 +53,9 @@ class LicenseApiClient : public QObject Q_SIGNALS: void activationFailed(const QString &message); void activationSucceeded(); + void activationUnreachable(); void checkFailed(const QString &message); void checkSucceeded(); - void licenseDisabled(const QString &message); private Q_SLOTS: void handleResponse(QNetworkReply *reply); diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index c011b1ae9..0af16cdec 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -91,9 +91,21 @@ LicenseHandler::LicenseHandler() m_pCoreProcess->start(); }); + connect(&m_apiClient, &LicenseApiClient::activationUnreachable, this, [this] { + if (isGracePeriodExpired()) { + qWarning("license activation server unreachable and grace period expired, not starting core"); + return; + } + + qWarning("license activation server unreachable, continuing without activation"); + + if (m_pCoreProcess != nullptr && !m_pCoreProcess->isStarted()) { + m_pCoreProcess->start(); + } + }); + connect(&m_apiClient, &LicenseApiClient::checkSucceeded, this, &LicenseHandler::handleRemoteCheckSucceeded); connect(&m_apiClient, &LicenseApiClient::checkFailed, this, &LicenseHandler::handleRemoteCheckFailed); - connect(&m_apiClient, &LicenseApiClient::licenseDisabled, this, &LicenseHandler::disableLicenseRemotely); } void LicenseHandler::handleMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) @@ -504,8 +516,8 @@ LicenseApiClient::Data LicenseHandler::buildApiData() const void LicenseHandler::runRemoteCheck() { - if (!m_settings.activated() || !m_license.isValid() || m_license.serialKey().isOffline) { - qDebug("license not activated or offline, skipping remote check"); + if (!m_license.isValid() || m_license.serialKey().isOffline) { + qDebug("license invalid or offline, skipping remote check"); return; } @@ -544,13 +556,15 @@ void LicenseHandler::handleRemoteCheckFailed(const QString &message) } if (isGracePeriodExpired()) { - disableLicenseRemotely(message); + disableLicenseAfterGrace(message); return; } if (!m_warnedAboutGrace && m_pMainWindow != nullptr) { m_warnedAboutGrace = true; - const auto graceDays = static_cast(kLicenseGracePeriod.count()); + const auto now = QDateTime::currentSecsSinceEpoch(); + const auto elapsed = seconds{now - m_settings.graceStartEpochSecs()}; + const auto daysRemaining = ceil(kLicenseGracePeriod - elapsed).count(); QMessageBox::warning( m_pMainWindow, "License check failed", tr("

We could not verify your license:

" @@ -560,14 +574,14 @@ void LicenseHandler::handleRemoteCheckFailed(const QString &message) "

") .arg(message.toHtmlEscaped()) .arg(productName()) - .arg(graceDays) + .arg(daysRemaining) .arg(kUrlContact) .arg(kColorSecondary) ); } } -void LicenseHandler::disableLicenseRemotely(const QString &reason) +void LicenseHandler::disableLicenseAfterGrace(const QString &reason) { qWarning().noquote() << "license grace period expired, disabling:" << reason; @@ -578,8 +592,8 @@ void LicenseHandler::disableLicenseRemotely(const QString &reason) // Keep the serial key + in-memory license so the next activation attempt can succeed // automatically if the server re-enables the license (e.g. after the customer pays). + // Keep the grace clock too, so a restart stays disabled instead of granting a fresh grace. m_settings.setActivated(false); - m_settings.setGraceStartEpochSecs(0); m_settings.sync(); m_warnedAboutGrace = false; diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index 94c7f30b6..c44ed180b 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -87,7 +87,7 @@ class LicenseHandler : public QObject void handleRemoteCheckFailed(const QString &message); bool isInGracePeriod() const; bool isGracePeriodExpired() const; - void disableLicenseRemotely(const QString &reason); + void disableLicenseAfterGrace(const QString &reason); synergy::gui::license::LicenseApiClient::Data buildApiData() const; bool m_enabled = true; From b60a035799642f49a05cc339ccc37672a954ec71 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 10 Jun 2026 18:59:25 +0100 Subject: [PATCH 088/144] fix: improve core process start handling with clearer warnings for unset modes --- src/lib/gui/MainWindow.cpp | 4 ++++ src/lib/gui/core/CoreProcess.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index 9f1fd78e1..635f66e65 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -663,6 +663,10 @@ void MainWindow::open() } if (Settings::value(Settings::Gui::AutoStartCore).toBool()) { + if (m_coreProcess.mode() == CoreMode::None) { + qWarning() << "skipping core auto start, mode is not set"; + return; + } if (ui->rbModeClient->isChecked() && ui->lineHostname->text().isEmpty()) return; startCore(); diff --git a/src/lib/gui/core/CoreProcess.cpp b/src/lib/gui/core/CoreProcess.cpp index 3b405236e..de1b5c2e5 100644 --- a/src/lib/gui/core/CoreProcess.cpp +++ b/src/lib/gui/core/CoreProcess.cpp @@ -353,7 +353,7 @@ void CoreProcess::start(std::optional processModeOption) } if (m_mode == Settings::CoreMode::None) { - qFatal("set core mode before starting"); + qCritical("core mode is not set, skipping core start"); return; } From ba04159cbbb2fdddea44b5f6ddec75e015101569 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 10 Jun 2026 23:36:21 +0100 Subject: [PATCH 089/144] feat: Implement offline activation feature - Added support for offline activation in the license management system. - Introduced `OfflineActivationDialog` for user interaction during offline activation. - Created `OfflineActivation` class to handle challenge-response logic for offline activation. - Updated `LicenseHandler` to manage offline activation responses and challenges. - Enhanced `TestSettings` and `ExtraSettings` to store machine ID and offline activation response. - Added unit tests for offline activation functionality, ensuring compatibility with vendor tools. - Updated CMake configuration to include necessary libraries for offline activation. - Modified existing tests to accommodate changes in the license handling logic. --- .github/actions/run-tests/action.yml | 9 +- extra/Synergy.test.example.conf | 9 + extra/src/CMakeLists.txt | 4 + extra/src/lib/synergy/gui/ExtraSettings.cpp | 3 + extra/src/lib/synergy/gui/ExtraSettings.h | 10 + .../synergy/gui/OfflineActivationDialog.cpp | 100 +++++++ .../lib/synergy/gui/OfflineActivationDialog.h | 45 +++ extra/src/lib/synergy/gui/TestSettings.cpp | 2 + extra/src/lib/synergy/gui/TestSettings.h | 5 + extra/src/lib/synergy/gui/constants.h | 1 + .../synergy/gui/license/LicenseHandler.cpp | 112 ++++++- .../lib/synergy/gui/license/LicenseHandler.h | 9 + extra/src/lib/synergy/license/CMakeLists.txt | 4 +- .../lib/synergy/license/OfflineActivation.cpp | 277 ++++++++++++++++++ .../lib/synergy/license/OfflineActivation.h | 69 +++++ .../lib/synergy/license/parse_serial_key.cpp | 3 +- extra/src/unittests/CMakeLists.txt | 44 ++- .../license/OfflineActivationTests.cpp | 141 +++++++++ extra/src/unittests/license/ProductTests.cpp | 2 +- src/lib/gui/MainWindow.cpp | 7 +- 20 files changed, 833 insertions(+), 23 deletions(-) create mode 100644 extra/src/lib/synergy/gui/OfflineActivationDialog.cpp create mode 100644 extra/src/lib/synergy/gui/OfflineActivationDialog.h create mode 100644 extra/src/lib/synergy/license/OfflineActivation.cpp create mode 100644 extra/src/lib/synergy/license/OfflineActivation.h create mode 100644 extra/src/unittests/license/OfflineActivationTests.cpp diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml index 6a196e2d1..e92828213 100644 --- a/.github/actions/run-tests/action.yml +++ b/.github/actions/run-tests/action.yml @@ -15,11 +15,12 @@ runs: env: QT_QPA_PLATFORM: offscreen run: | - ctest --test-dir "build/src/unittests" --output-on-failure - result=$? + ctest --test-dir "build/src/unittests" --output-on-failure || result=1 + ctest --test-dir "build/extra/src/unittests" --output-on-failure || result=1 - if [ $result -ne 0 ]; then - echo "Unit tests failed with code: $result" >> $GITHUB_STEP_SUMMARY + if [ "${result:-0}" -ne 0 ]; then + echo "Unit tests failed" >> $GITHUB_STEP_SUMMARY + exit 1 fi shell: bash continue-on-error: true diff --git a/extra/Synergy.test.example.conf b/extra/Synergy.test.example.conf index 503491427..609b2fd31 100644 --- a/extra/Synergy.test.example.conf +++ b/extra/Synergy.test.example.conf @@ -43,6 +43,15 @@ # distributed internally. Do not hardcode customer-visible keys here. # serialKey= +# Override the machine identity used for offline activation. Offline +# activation codes are bound to this machine's hardware id, so on a real +# box the activation code never changes for a given serial key. Set this +# to any value to impersonate a different machine: change it (or the +# stored response) to confirm one machine's response code does not +# activate another, i.e. that an offline serial cannot be shared. Affects +# only offline activation, not the online activation machine signature. +# machineId=test-machine-1 + # Time-travel testing. UNIX epoch seconds; if set, the GUI behaves as if # the system clock were at this point in time when checking license # expiry, grace periods, etc. Use to verify "expired", "expiring soon", diff --git a/extra/src/CMakeLists.txt b/extra/src/CMakeLists.txt index 9c2b296e9..1cde58922 100644 --- a/extra/src/CMakeLists.txt +++ b/extra/src/CMakeLists.txt @@ -2,3 +2,7 @@ # SPDX-License-Identifier: MIT add_subdirectory(lib) + +if(BUILD_TESTS) + add_subdirectory(unittests) +endif() diff --git a/extra/src/lib/synergy/gui/ExtraSettings.cpp b/extra/src/lib/synergy/gui/ExtraSettings.cpp index 3de9b3dc2..8369834e9 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.cpp +++ b/extra/src/lib/synergy/gui/ExtraSettings.cpp @@ -40,6 +40,7 @@ QString settingsFile() const auto kSerialKey = QStringLiteral("serialKey"); const auto kActivated = QStringLiteral("activated"); const auto kGraceStart = QStringLiteral("graceStartEpochSecs"); +const auto kOfflineActivationResponse = QStringLiteral("offlineActivationResponse"); } // namespace @@ -49,6 +50,7 @@ void ExtraSettings::load() m_serialKey = ini.value(kSerialKey).toString(); m_activated = ini.value(kActivated).toBool(); m_graceStartEpochSecs = ini.value(kGraceStart).toLongLong(); + m_offlineActivationResponse = ini.value(kOfflineActivationResponse).toString(); } void ExtraSettings::sync() @@ -61,6 +63,7 @@ void ExtraSettings::sync() ini.setValue(kSerialKey, m_serialKey); ini.setValue(kActivated, m_activated); ini.setValue(kGraceStart, m_graceStartEpochSecs); + ini.setValue(kOfflineActivationResponse, m_offlineActivationResponse); ini.sync(); } diff --git a/extra/src/lib/synergy/gui/ExtraSettings.h b/extra/src/lib/synergy/gui/ExtraSettings.h index ce737cc76..c840ed1bd 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.h +++ b/extra/src/lib/synergy/gui/ExtraSettings.h @@ -58,6 +58,15 @@ class ExtraSettings m_graceStartEpochSecs = epochSecs; } + QString offlineActivationResponse() const + { + return m_offlineActivationResponse; + } + void setOfflineActivationResponse(const QString &response) + { + m_offlineActivationResponse = response; + } + QString fileName() const; bool isWritable() const; @@ -65,6 +74,7 @@ class ExtraSettings QString m_serialKey; bool m_activated = false; qint64 m_graceStartEpochSecs = 0; + QString m_offlineActivationResponse; }; } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/OfflineActivationDialog.cpp b/extra/src/lib/synergy/gui/OfflineActivationDialog.cpp new file mode 100644 index 000000000..c61204451 --- /dev/null +++ b/extra/src/lib/synergy/gui/OfflineActivationDialog.cpp @@ -0,0 +1,100 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#include "OfflineActivationDialog.h" + +#include "synergy/gui/constants.h" +#include "synergy/gui/license/LicenseHandler.h" +#include "synergy/gui/styles.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace synergy::gui; + +OfflineActivationDialog::OfflineActivationDialog(QWidget *parent, LicenseHandler &licenseHandler) + : QDialog(parent), + m_licenseHandler(licenseHandler) +{ + setWindowTitle(tr("Offline activation")); + setMinimumWidth(440); + + const auto layout = new QVBoxLayout(this); + + const auto infoLabel = new QLabel( + tr(R"(On a device with internet access, enter the activation code at )" + R"(your account page to get a response code.)") + .arg(kUrlAccount) + .arg(kColorSecondary), + this + ); + infoLabel->setWordWrap(true); + infoLabel->setOpenExternalLinks(true); + layout->addWidget(infoLabel); + + const auto form = new QFormLayout(); + + const auto challengeEdit = new QLineEdit(m_licenseHandler.offlineActivationChallenge(), this); + challengeEdit->setReadOnly(true); + challengeEdit->setCursorPosition(0); + form->addRow(tr("Activation code"), challengeEdit); + + m_pResponseEdit = new QLineEdit(this); + form->addRow(tr("Response code"), m_pResponseEdit); + + layout->addLayout(form); + + const auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + buttons->button(QDialogButtonBox::Ok)->setText(tr("Activate")); + connect(buttons, &QDialogButtonBox::accepted, this, &OfflineActivationDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &OfflineActivationDialog::reject); + layout->addWidget(buttons); + + m_pResponseEdit->setFocus(); +} + +void OfflineActivationDialog::accept() +{ + const auto response = m_pResponseEdit->text(); + if (response.trimmed().isEmpty()) { + QMessageBox::information(this, tr("Offline activation"), tr("Please enter the response code.")); + return; + } + + if (!m_licenseHandler.applyOfflineActivationResponse(response)) { + QMessageBox::warning( + this, tr("Offline activation"), + tr("

That response code is not valid for this computer.

" + "

Please check that you typed the whole response code correctly, and that " + "the activation code was generated on this computer. " + R"(If the problem persists, please contact us.

)") + .arg(kUrlContact) + .arg(kColorSecondary) + ); + return; + } + + QMessageBox::information( + this, tr("Offline activation"), tr("Thanks, your license is now activated on this computer.") + ); + QDialog::accept(); +} diff --git a/extra/src/lib/synergy/gui/OfflineActivationDialog.h b/extra/src/lib/synergy/gui/OfflineActivationDialog.h new file mode 100644 index 000000000..ba4a147ff --- /dev/null +++ b/extra/src/lib/synergy/gui/OfflineActivationDialog.h @@ -0,0 +1,45 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#pragma once + +#include + +class LicenseHandler; +class QLineEdit; + +/** + * @brief Walks the customer through offline challenge-response activation. + * + * Shows the machine-bound challenge code the customer exchanges for a response + * code on their account page, and accepts that response code. Accepted only once + * the response verifies, after which it is persisted by the license handler. + */ +class OfflineActivationDialog : public QDialog +{ + Q_OBJECT + +public: + OfflineActivationDialog(QWidget *parent, LicenseHandler &licenseHandler); + +public Q_SLOTS: + void accept() override; + +private: + LicenseHandler &m_licenseHandler; + QLineEdit *m_pResponseEdit = nullptr; +}; diff --git a/extra/src/lib/synergy/gui/TestSettings.cpp b/extra/src/lib/synergy/gui/TestSettings.cpp index 4b238aacd..d7fb9bbd9 100644 --- a/extra/src/lib/synergy/gui/TestSettings.cpp +++ b/extra/src/lib/synergy/gui/TestSettings.cpp @@ -70,6 +70,7 @@ void TestSettings::load() m_serialKey.clear(); m_apiUrlActivate.clear(); m_apiUrlCheck.clear(); + m_machineId.clear(); m_startTimeEpochSecs = 0; m_verbose = false; m_skipRemoteCheck = false; @@ -90,6 +91,7 @@ void TestSettings::load() m_serialKey = ini.value(QStringLiteral("test/serialKey")).toString(); m_apiUrlActivate = ini.value(QStringLiteral("test/apiUrlActivate")).toString(); m_apiUrlCheck = ini.value(QStringLiteral("test/apiUrlCheck")).toString(); + m_machineId = ini.value(QStringLiteral("test/machineId")).toString(); m_startTimeEpochSecs = ini.value(QStringLiteral("test/startTime"), 0).toLongLong(); m_verbose = ini.value(QStringLiteral("features/verbose"), false).toBool(); diff --git a/extra/src/lib/synergy/gui/TestSettings.h b/extra/src/lib/synergy/gui/TestSettings.h index c14b68b65..d74483413 100644 --- a/extra/src/lib/synergy/gui/TestSettings.h +++ b/extra/src/lib/synergy/gui/TestSettings.h @@ -62,6 +62,10 @@ class TestSettings { return m_apiUrlCheck; } + QString machineId() const + { + return m_machineId; + } qint64 startTimeEpochSecs() const { return m_startTimeEpochSecs; @@ -103,6 +107,7 @@ class TestSettings QString m_serialKey; QString m_apiUrlActivate; QString m_apiUrlCheck; + QString m_machineId; qint64 m_startTimeEpochSecs = 0; bool m_verbose = false; bool m_skipRemoteCheck = false; diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 0284eed5a..3e138fe0e 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -37,6 +37,7 @@ const auto kLinkDownload = R"(Download now)"; const auto kUrlPersonalUpgrade = QString("%1/purchase/upgrade?%2").arg(kUrlWebsite, kUrlSourceQuery); const auto kUrlContact = QString("%1/contact?%2").arg(kUrlWebsite, kUrlSourceQuery); +const auto kUrlAccount = QString("%1/account?%2").arg(kUrlWebsite, kUrlSourceQuery); const auto kUrlApiLicenseActivate = QString("%1/product/activate").arg(kUrlApi); const auto kUrlApiLicenseCheck = QString("%1/product/check").arg(kUrlApi); diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 0af16cdec..3b04bce1b 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -18,14 +18,16 @@ #include "LicenseHandler.h" #include "ActivationDialog.h" +#include "OfflineActivationDialog.h" #include "common/Settings.h" #include "common/VersionInfo.h" -#include "dialogs/UpgradeDialog.h" #include "gui/core/CoreProcess.h" +#include "synergy/gui/TestSettings.h" #include "synergy/gui/constants.h" #include "synergy/gui/dev_mode.h" #include "synergy/gui/license/license_utils.h" #include "synergy/gui/styles.h" +#include "synergy/license/OfflineActivation.h" #include "synergy/license/Product.h" #include @@ -54,6 +56,16 @@ using namespace std::chrono; using namespace synergy::gui::license; using namespace synergy::gui; using namespace deskflow::gui; + +namespace { + +std::string offlineMachineId() +{ + const auto testId = TestSettings::instance().machineId(); + return testId.isEmpty() ? QSysInfo::machineUniqueId().toStdString() : testId.toStdString(); +} + +} // namespace using License = synergy::license::License; LicenseHandler::LicenseHandler() @@ -87,8 +99,10 @@ LicenseHandler::LicenseHandler() qFatal("core process not set"); } - qDebug("resuming core process after activation"); - m_pCoreProcess->start(); + if (m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { + qDebug("resuming core process after activation"); + m_pCoreProcess->start(); + } }); connect(&m_apiClient, &LicenseApiClient::activationUnreachable, this, [this] { @@ -99,7 +113,7 @@ LicenseHandler::LicenseHandler() qWarning("license activation server unreachable, continuing without activation"); - if (m_pCoreProcess != nullptr && !m_pCoreProcess->isStarted()) { + if (m_pCoreProcess != nullptr && m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { m_pCoreProcess->start(); } }); @@ -263,14 +277,18 @@ bool LicenseHandler::handleCoreStart() qFatal("core process not set"); } - if (m_settings.activated()) { + if (m_settings.activated() && !m_license.serialKey().isOffline) { qDebug("license is activated, starting core"); return true; } if (m_license.serialKey().isOffline) { - qDebug("offline serial key, starting core"); - return true; + if (isOfflineActivated()) { + qDebug("offline activation verified, starting core"); + return true; + } + qInfo("offline serial key not activated, showing offline activation dialog"); + return showOfflineActivationDialog(); } if (!m_license.isValid()) { @@ -334,6 +352,7 @@ bool LicenseHandler::showSerialKeyDialog() qDebug("serial key changed, updating settings"); m_settings.setActivated(false); m_settings.setGraceStartEpochSecs(0); + m_settings.setOfflineActivationResponse({}); m_warnedAboutGrace = false; m_settings.sync(); } @@ -342,9 +361,23 @@ bool LicenseHandler::showSerialKeyDialog() updateWindowTitle(); clampFeatures(); + bool offlineActivationDeclined = false; + if (m_license.serialKey().isOffline && !isOfflineActivated()) { + qInfo("serial key requires offline activation"); + offlineActivationDeclined = !showOfflineActivationDialog(); + } + if (dialog.serialKeyChanged() && m_pCoreProcess->isStarted()) { - qDebug("restarting core on serial key change"); - m_pCoreProcess->restart(); + if (offlineActivationDeclined) { + qDebug("stopping core, offline activation declined"); + m_pCoreProcess->stop(); + } else { + qDebug("restarting core on serial key change"); + m_pCoreProcess->restart(); + } + } else if (m_license.serialKey().isOffline && isOfflineActivated() && !m_pCoreProcess->isStarted()) { + qDebug("starting core after offline activation"); + m_pCoreProcess->start(); } // If the user accepted the dialog while not activated (e.g. recovering from a @@ -359,6 +392,67 @@ bool LicenseHandler::showSerialKeyDialog() return true; } +bool LicenseHandler::showOfflineActivationDialog() +{ + if (!m_settings.isWritable()) { + QMessageBox::warning( + m_pMainWindow, "Write access required", + tr("

The settings file is not writable:

" + "

%1

" + "

Please check the file permissions and try again.

") + .arg(m_settings.fileName()) + ); + return false; + } + + if (offlineActivationChallenge().isEmpty()) { + QMessageBox::critical( + m_pMainWindow, tr("Offline activation"), + tr("

A unique ID for this computer could not be determined, " + "so an activation code cannot be generated.

" + R"(

Please contact us for help.

)") + .arg(kUrlContact) + .arg(kColorSecondary) + ); + return false; + } + + OfflineActivationDialog dialog(m_pMainWindow, *this); + return dialog.exec() == QDialog::Accepted; +} + +QString LicenseHandler::offlineActivationChallenge() const +{ + const auto machineId = offlineMachineId(); + const auto challenge = synergy::license::buildOfflineChallenge(machineId, m_license.serialKey().hexString); + return QString::fromStdString(synergy::license::formatOfflineCode(challenge, 4)); +} + +bool LicenseHandler::isOfflineActivated() const +{ + const auto response = m_settings.offlineActivationResponse(); + if (response.isEmpty()) { + return false; + } + const auto machineId = offlineMachineId(); + return synergy::license::verifyOfflineResponse(machineId, m_license.serialKey().hexString, response.toStdString()); +} + +bool LicenseHandler::applyOfflineActivationResponse(const QString &responseCode) +{ + const auto response = responseCode.simplified(); + const auto machineId = offlineMachineId(); + if (!synergy::license::verifyOfflineResponse(machineId, m_license.serialKey().hexString, response.toStdString())) { + qWarning("offline activation response not valid for this machine"); + return false; + } + + qInfo("offline activation response verified"); + m_settings.setOfflineActivationResponse(response); + m_settings.sync(); + return true; +} + void LicenseHandler::updateWindowTitle() const { const auto productName = QString::fromStdString(m_license.productName()); diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index c44ed180b..76c1c3373 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -73,6 +73,13 @@ class LicenseHandler : public QObject void clampFeatures(); void disable(); + /// @brief Challenge code for offline activation, formatted for display. + QString offlineActivationChallenge() const; + + /// @brief Verifies an offline activation response code and persists it if valid. + /// @return False if the response does not verify for this machine and serial key. + bool applyOfflineActivationResponse(const QString &responseCode); + bool isEnabled() const { return m_enabled; @@ -81,6 +88,8 @@ class LicenseHandler : public QObject private: void updateWindowTitle() const; bool showSerialKeyDialog(); + bool showOfflineActivationDialog(); + bool isOfflineActivated() const; bool check(); void runRemoteCheck(); void handleRemoteCheckSucceeded(); diff --git a/extra/src/lib/synergy/license/CMakeLists.txt b/extra/src/lib/synergy/license/CMakeLists.txt index cc8f71243..8bc210e5b 100644 --- a/extra/src/lib/synergy/license/CMakeLists.txt +++ b/extra/src/lib/synergy/license/CMakeLists.txt @@ -20,6 +20,8 @@ if(ADD_HEADERS_TO_SOURCES) list(APPEND sources ${headers}) endif() +find_package(OpenSSL ${REQUIRED_OPENSSL_VERSION} REQUIRED COMPONENTS Crypto) + add_library(license STATIC ${sources}) -target_link_libraries(license arch base) +target_link_libraries(license arch base OpenSSL::Crypto) diff --git a/extra/src/lib/synergy/license/OfflineActivation.cpp b/extra/src/lib/synergy/license/OfflineActivation.cpp new file mode 100644 index 000000000..8724db535 --- /dev/null +++ b/extra/src/lib/synergy/license/OfflineActivation.cpp @@ -0,0 +1,277 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#include "OfflineActivation.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace synergy::license { + +namespace { + +// The wire format here must stay in lockstep with the vendor signer in the web-muskoka repo +// (website lib/server/product-license/offline-activation-protocol.ts). +constexpr auto kDomain = std::string_view{"Synergy1OfflineActivationV1"}; +constexpr size_t kSecretLength = 32; + +// No vowels, so codes can never spell words; no I, L, or O, so nothing is mistaken +// for 1 or 0 (and typed O/I/L are forgiven by mapping them to digits). +constexpr auto kCodeAlphabet = std::string_view{"0123456789BCDFGHJKMNPQRSTVWXYZ"}; + +constexpr std::uint64_t kCodeAlphabetSize = 30; +constexpr size_t kCodeLength = 12; +constexpr size_t kFingerprintDigits = 2; +constexpr size_t kMachineDigits = 10; + +constexpr std::uint64_t codeSpace(size_t digits) +{ + std::uint64_t value = 1; + for (size_t i = 0; i < digits; ++i) { + value *= kCodeAlphabetSize; + } + return value; +} + +constexpr auto kFingerprintRange = codeSpace(kFingerprintDigits); +constexpr auto kMachineRange = codeSpace(kMachineDigits); +constexpr auto kResponseRange = codeSpace(kCodeLength); + +using Bytes = std::vector; + +std::string encodeBase30(std::uint64_t value, size_t digits) +{ + std::string out(digits, '0'); + for (size_t i = digits; i > 0; --i) { + out[i - 1] = kCodeAlphabet[value % kCodeAlphabetSize]; + value /= kCodeAlphabetSize; + } + return out; +} + +std::uint64_t bytesToUint(const Bytes &bytes, size_t count) +{ + std::uint64_t value = 0; + for (size_t i = 0; i < count; ++i) { + value = (value << 8) | bytes[i]; + } + return value; +} + +constexpr auto kAsciiWhitespace = std::string_view{" \t\n\r\f\v"}; + +char asciiUpper(char c) +{ + return (c >= 'a' && c <= 'z') ? static_cast(c - ('a' - 'A')) : c; +} + +char forgiveConfusable(char c) +{ + if (c == 'O') { + return '0'; + } + if (c == 'I' || c == 'L') { + return '1'; + } + return c; +} + +std::optional canonicalizeCode(const std::string &code) +{ + std::string out; + for (const auto c : code) { + if (c == '-' || kAsciiWhitespace.find(c) != std::string_view::npos) { + continue; + } + const auto upper = forgiveConfusable(asciiUpper(c)); + if (kCodeAlphabet.find(upper) == std::string_view::npos) { + return std::nullopt; + } + out.push_back(upper); + } + return out; +} + +std::optional hexDecode(const std::string &hex) +{ + if (hex.length() % 2 != 0) { + return std::nullopt; + } + const auto nibble = [](char c) -> int { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return -1; + }; + Bytes out; + out.reserve(hex.length() / 2); + for (size_t i = 0; i < hex.length(); i += 2) { + const auto high = nibble(hex[i]); + const auto low = nibble(hex[i + 1]); + if (high < 0 || low < 0) { + return std::nullopt; + } + out.push_back(static_cast((high << 4) | low)); + } + return out; +} + +Bytes sha256(const Bytes &data) +{ + Bytes digest(EVP_MAX_MD_SIZE); + unsigned int length = 0; + if (EVP_Digest(data.data(), data.size(), digest.data(), &length, EVP_sha256(), nullptr) != 1) { + return {}; + } + digest.resize(length); + return digest; +} + +void append(Bytes &bytes, std::string_view text) +{ + bytes.insert(bytes.end(), text.begin(), text.end()); +} + +std::string canonicalizeSerial(const std::string &serialHex) +{ + const auto first = serialHex.find_first_not_of(kAsciiWhitespace); + if (first == std::string::npos) { + return {}; + } + const auto last = serialHex.find_last_not_of(kAsciiWhitespace); + auto serial = serialHex.substr(first, last - first + 1); + std::transform(serial.begin(), serial.end(), serial.begin(), [](char c) { + return (c >= 'A' && c <= 'Z') ? static_cast(c + ('a' - 'A')) : c; + }); + return serial; +} + +Bytes hmacSha256(const Bytes &key, const Bytes &data) +{ + Bytes digest(EVP_MAX_MD_SIZE); + unsigned int length = 0; + const auto result = + HMAC(EVP_sha256(), key.data(), static_cast(key.size()), data.data(), data.size(), digest.data(), &length); + if (result == nullptr) { + return {}; + } + digest.resize(length); + return digest; +} + +} // namespace + +std::string buildOfflineChallenge(const std::string &machineId, const std::string &serialHex) +{ + if (machineId.empty()) { + return {}; + } + + const auto serial = canonicalizeSerial(serialHex); + + Bytes serialBytes; + append(serialBytes, serial); + const auto serialDigest = sha256(serialBytes); + + Bytes message; + append(message, kDomain); + message.push_back(0); + append(message, machineId); + message.push_back(0); + append(message, serial); + const auto machineDigest = sha256(message); + + if (serialDigest.size() < 4 || machineDigest.size() < 8) { + return {}; + } + + const auto fingerprint = bytesToUint(serialDigest, 4) % kFingerprintRange; + const auto machineCode = bytesToUint(machineDigest, 8) % kMachineRange; + return encodeBase30(fingerprint, kFingerprintDigits) + encodeBase30(machineCode, kMachineDigits); +} + +bool verifyOfflineResponse(const std::string &machineId, const std::string &serialHex, const std::string &responseCode) +{ + return verifyOfflineResponse(machineId, serialHex, responseCode, kOfflineActivationSecretHex); +} + +bool verifyOfflineResponse( + const std::string &machineId, const std::string &serialHex, const std::string &responseCode, + const std::string &secretHex +) +{ + if (machineId.empty()) { + return false; + } + + const auto secret = hexDecode(secretHex); + if (!secret.has_value() || secret->size() != kSecretLength) { + return false; + } + + const auto canonicalResponse = canonicalizeCode(responseCode); + if (!canonicalResponse.has_value() || canonicalResponse->length() != kCodeLength) { + return false; + } + + const auto challenge = buildOfflineChallenge(machineId, serialHex); + if (challenge.empty()) { + return false; + } + + Bytes message; + append(message, kDomain); + message.push_back(':'); + append(message, challenge); + const auto mac = hmacSha256(*secret, message); + if (mac.size() < 8) { + return false; + } + + const auto expected = encodeBase30(bytesToUint(mac, 8) % kResponseRange, kCodeLength); + return CRYPTO_memcmp(expected.data(), canonicalResponse->data(), kCodeLength) == 0; +} + +std::string formatOfflineCode(const std::string &code, int groupSize) +{ + if (groupSize <= 0) { + return code; + } + std::string out; + for (size_t i = 0; i < code.length(); ++i) { + if (i > 0 && i % static_cast(groupSize) == 0) { + out.push_back('-'); + } + out.push_back(code[i]); + } + return out; +} + +} // namespace synergy::license diff --git a/extra/src/lib/synergy/license/OfflineActivation.h b/extra/src/lib/synergy/license/OfflineActivation.h new file mode 100644 index 000000000..03695181f --- /dev/null +++ b/extra/src/lib/synergy/license/OfflineActivation.h @@ -0,0 +1,69 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#pragma once + +#include + +namespace synergy::license { + +/** + * Challenge-response activation for air-gapped machines. The machine shows a + * short challenge code derived from its hardware id and serial key, the customer + * exchanges it for a response code on their account page (web-muskoka repo), and + * the machine verifies the response with no network access. Both codes are + * hand-typed, so the response is a short truncated MAC rather than a signature; + * extracting the embedded secret defeats it, which is an accepted trade-off + * (deterrence, not copy protection). + */ + +/// @brief Shared secret the response codes are verified against. +inline constexpr auto kOfflineActivationSecretHex = + "8acfc45b6524b47ae91e68610c7d2463c28b7e5f80a04d3fd589c87917848534"; + +/** + * @brief Builds the challenge code the customer enters on their account page. + * @param machineId A stable hardware identifier for this machine. + * @param serialHex The license serial key as a hex string. + * @return Canonical 12-character challenge code (use @ref formatOfflineCode for display). + */ +std::string buildOfflineChallenge(const std::string &machineId, const std::string &serialHex); + +/** + * @brief Verifies a response code against the embedded shared secret. + * @param machineId Must match the machine the challenge was built on. + * @param serialHex Must match the serial key the challenge was built with. + * @param responseCode The response code from the account page; separators and letter case are ignored. + * @return True only if the response is valid for this machine and serial. + */ +bool verifyOfflineResponse(const std::string &machineId, const std::string &serialHex, const std::string &responseCode); + +/** + * @brief Same as the three-argument overload, with an explicit secret for tests. + * @param secretHex Shared secret, 64 hex characters. + */ +bool verifyOfflineResponse( + const std::string &machineId, const std::string &serialHex, const std::string &responseCode, + const std::string &secretHex +); + +/** + * @brief Inserts dash separators for display, e.g. "AAAABBBB" with group size 4 becomes "AAAA-BBBB". + */ +std::string formatOfflineCode(const std::string &code, int groupSize); + +} // namespace synergy::license diff --git a/extra/src/lib/synergy/license/parse_serial_key.cpp b/extra/src/lib/synergy/license/parse_serial_key.cpp index dde4e901d..e043c8dec 100644 --- a/extra/src/lib/synergy/license/parse_serial_key.cpp +++ b/extra/src/lib/synergy/license/parse_serial_key.cpp @@ -130,7 +130,8 @@ SerialKey parseV3(const std::string &hexString, const Parts &parts) } // e.g.: {v3;offline;trial;basic;name;seats;email;company;1398297600;1398384000} SerialKey serialKey(hexString); - serialKey.isOffline = (parts.at(1) == "offline"); + const auto &mode = parts.at(1); + serialKey.isOffline = (mode == "offline"); serialKey.type = SerialKeyType(parts.at(2)); serialKey.product = Product(parts.at(3)); serialKey.name = parts.at(4); diff --git a/extra/src/unittests/CMakeLists.txt b/extra/src/unittests/CMakeLists.txt index dba1a08e4..a96e9a5a6 100644 --- a/extra/src/unittests/CMakeLists.txt +++ b/extra/src/unittests/CMakeLists.txt @@ -13,7 +13,43 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -config_test() -set(target ${UNIT_TESTS_BIN}) -add_executable(${target} ${sources}) -target_link_libraries(${target} ${test_libs}) +enable_testing() +find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Test) + +# legacytests provisions googletest, but when that is satisfied by find_package +# the imported targets are scoped to its own directory, so resolve them here too. +if(NOT TARGET GTest::gtest_main) + find_package(GTest REQUIRED) +endif() + +create_test( + NAME LicenseTests + DEPENDS license + LIBS GTest::gtest_main + SOURCE license/LicenseTests.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +create_test( + NAME ProductTests + DEPENDS license + LIBS GTest::gtest_main + SOURCE license/ProductTests.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +create_test( + NAME SerialKeyTypeTests + DEPENDS license + LIBS GTest::gtest_main + SOURCE license/SerialKeyTypeTests.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +create_test( + NAME OfflineActivationTests + DEPENDS license + LIBS GTest::gtest_main + SOURCE license/OfflineActivationTests.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) diff --git a/extra/src/unittests/license/OfflineActivationTests.cpp b/extra/src/unittests/license/OfflineActivationTests.cpp new file mode 100644 index 000000000..f7c2fc57b --- /dev/null +++ b/extra/src/unittests/license/OfflineActivationTests.cpp @@ -0,0 +1,141 @@ +/* + * Synergy -- mouse and keyboard sharing utility + * Copyright (C) 2026 Synergy App 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 . + */ + +#include "synergy/license/OfflineActivation.h" +#include "synergy/license/parse_serial_key.h" + +#include + +#include + +using namespace synergy::license; + +// Vectors produced by the vendor signer (web-muskoka offline-activation-protocol.spec.ts, +// fixed secret 0707...07), so these tests prove the client and the server signer implement +// the same wire format. +const auto kVectorSecret = "0707070707070707070707070707070707070707070707070707070707070707"; +const auto kVectorMachineId = "test-machine-id-0001"; +const auto kVectorSerial = "deadbeef00112233"; +const auto kVectorChallenge = "CS6WZ1BVJJSN"; +const auto kVectorResponse = "HG5CR1761BVQ"; + +TEST(OfflineActivationTests, buildOfflineChallenge_vectorInputs_matchesVendorTool) +{ + EXPECT_EQ(buildOfflineChallenge(kVectorMachineId, kVectorSerial), kVectorChallenge); +} + +TEST(OfflineActivationTests, buildOfflineChallenge_serialCaseAndPaddingDiffer_sameChallenge) +{ + EXPECT_EQ(buildOfflineChallenge(kVectorMachineId, " DEADBEEF00112233 "), kVectorChallenge); +} + +TEST(OfflineActivationTests, buildOfflineChallenge_differentMachine_differentChallenge) +{ + EXPECT_NE(buildOfflineChallenge("test-machine-id-0002", kVectorSerial), kVectorChallenge); +} + +TEST(OfflineActivationTests, buildOfflineChallenge_differentSerial_differentChallenge) +{ + EXPECT_NE(buildOfflineChallenge(kVectorMachineId, "deadbeef00112234"), kVectorChallenge); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_vendorSignedResponse_verifies) +{ + EXPECT_TRUE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, kVectorResponse, kVectorSecret)); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_groupedLowercaseResponse_verifies) +{ + EXPECT_TRUE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "hg5c-r176-1bvq", kVectorSecret)); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_confusableLettersTyped_verifies) +{ + EXPECT_TRUE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "HG5CRI76LBVQ", kVectorSecret)); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_tamperedResponse_fails) +{ + auto tampered = std::string(kVectorResponse); + tampered[0] = (tampered[0] == 'C') ? 'D' : 'C'; + + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, tampered, kVectorSecret)); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_differentMachine_fails) +{ + EXPECT_FALSE(verifyOfflineResponse("test-machine-id-0002", kVectorSerial, kVectorResponse, kVectorSecret)); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_differentSerial_fails) +{ + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, "deadbeef00112234", kVectorResponse, kVectorSecret)); +} + +TEST(OfflineActivationTests, buildOfflineChallenge_emptyMachineId_returnsEmpty) +{ + EXPECT_EQ(buildOfflineChallenge("", kVectorSerial), ""); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_emptyMachineId_fails) +{ + EXPECT_FALSE(verifyOfflineResponse("", kVectorSerial, kVectorResponse, kVectorSecret)); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_malformedResponse_fails) +{ + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "", kVectorSecret)); + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "not a code @#!", kVectorSecret)); + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "HG5C", kVectorSecret)); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_malformedSecret_fails) +{ + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, kVectorResponse, "")); + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, kVectorResponse, "abc123")); +} + +TEST(OfflineActivationTests, verifyOfflineResponse_embeddedProductionSecret_rejectsTestVector) +{ + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, kVectorResponse)); +} + +// Fixed-secret vector asserted identically in the vendor signer's +// offline-activation-protocol.spec.ts (web-muskoka), so this pins both +// implementations to one shared vector. +TEST(OfflineActivationTests, verifyOfflineResponse_sharedVendorVector_verifies) +{ + EXPECT_EQ(buildOfflineChallenge("test machine id", "TEST SERIAL KEY"), "7DQSYXM59MV5"); + EXPECT_TRUE(verifyOfflineResponse("test machine id", "TEST SERIAL KEY", "T6R4-TGRC-WDQ9", kVectorSecret)); +} + +TEST(OfflineActivationTests, formatOfflineCode_groupsOfFour_insertsDashes) +{ + EXPECT_EQ(formatOfflineCode("AAAABBBBCC", 4), "AAAA-BBBB-CC"); + EXPECT_EQ(formatOfflineCode("AAAA", 4), "AAAA"); + EXPECT_EQ(formatOfflineCode("", 4), ""); +} + +TEST(OfflineActivationTests, parseSerialKey_offlineMode_isOffline) +{ + // {v3;offline;;business;Bob;1;bob@example.com;Acme;;} + const auto serialKey = parseSerialKey("7b76333b6f66666c696e653b3b627573696e6573733b426f623b313b626f6240657" + "8616d706c652e636f6d3b41636d653b3b7d"); + + EXPECT_TRUE(serialKey.isValid); + EXPECT_TRUE(serialKey.isOffline); +} diff --git a/extra/src/unittests/license/ProductTests.cpp b/extra/src/unittests/license/ProductTests.cpp index 63a8e89c2..81f27d370 100644 --- a/extra/src/unittests/license/ProductTests.cpp +++ b/extra/src/unittests/license/ProductTests.cpp @@ -52,7 +52,7 @@ TEST(ProductTests, setEdition_invalidType_throws) { Product product; - EXPECT_THROW(product.setEdition("test"), Product::InvalidType); + EXPECT_THROW(product.setEdition("test"), Product::InvalidProductEdition); } TEST(ProductTests, setEdition_pro_isValid) diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index 635f66e65..f7c2fef24 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -411,14 +411,15 @@ void MainWindow::startCore() m_serverStartSuggestedIP = m_serverStartIPs.isEmpty() ? "" : m_serverStartIPs.first(); } - m_actionStartCore->setVisible(false); - m_actionRestartCore->setVisible(true); - #ifdef SYNERGY_EXTRA_HEADER if (!synergy::hooks::onCoreStart()) { return; } #endif + + m_actionStartCore->setVisible(false); + m_actionRestartCore->setVisible(true); + m_coreProcess.start(); } From e91ead4e57a2c0da4cdad8acec378a0fd4af8595 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 01:31:39 +0100 Subject: [PATCH 090/144] feat: implement destructor for MSWindowsWatchdog and improve thread stopping logic --- src/lib/platform/MSWindowsWatchdog.cpp | 26 +++++++++++++++----------- src/lib/platform/MSWindowsWatchdog.h | 7 ++++--- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index a9f8be337..892e8ef20 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -72,6 +72,11 @@ MSWindowsWatchdog::MSWindowsWatchdog(bool foreground, FileLogOutputter &fileLogO initOutputReadPipe(); } +MSWindowsWatchdog::~MSWindowsWatchdog() +{ + stop(); +} + void MSWindowsWatchdog::startAsync() { m_mainThread = std::make_unique(new TMethodJob(this, &MSWindowsWatchdog::mainLoop, nullptr)); @@ -81,21 +86,20 @@ void MSWindowsWatchdog::startAsync() void MSWindowsWatchdog::stop() { - const auto kThreadWaitSeconds = 5; + static constexpr double kThreadWaitSeconds = 5; m_running = false; - if (!m_mainThread->wait(kThreadWaitSeconds)) { - LOG_WARN("could not stop main thread"); - } - - if (!m_outputThread->wait(kThreadWaitSeconds)) { - LOG_WARN("could not stop output thread"); - } + const auto waitForThread = [](const std::unique_ptr &thread, const char *name) { + if (thread != nullptr && !thread->wait(kThreadWaitSeconds)) { + LOG_WARN("%s thread is slow to stop, waiting", name); + thread->wait(); + } + }; - if (!m_sasThread->wait(kThreadWaitSeconds)) { - LOG_WARN("could not stop sas thread"); - } + waitForThread(m_mainThread, "main"); + waitForThread(m_outputThread, "output"); + waitForThread(m_sasThread, "sas"); } HANDLE diff --git a/src/lib/platform/MSWindowsWatchdog.h b/src/lib/platform/MSWindowsWatchdog.h index b61715429..73282796e 100644 --- a/src/lib/platform/MSWindowsWatchdog.h +++ b/src/lib/platform/MSWindowsWatchdog.h @@ -13,6 +13,7 @@ #define WIN32_LEAN_AND_MEAN #include +#include #include #include #include @@ -38,7 +39,7 @@ class MSWindowsWatchdog public: explicit MSWindowsWatchdog(bool foreground, FileLogOutputter &fileLogOutputter); - ~MSWindowsWatchdog() = default; + ~MSWindowsWatchdog(); /** * @brief Start threads for main loop and and output loop. @@ -51,7 +52,7 @@ class MSWindowsWatchdog void setProcessConfig(const std::string_view &command, bool elevate); /** - * @brief Stop the main loop and output loop threads. + * @brief Stop the watchdog threads, blocking until they have all exited. */ void stop(); @@ -129,7 +130,7 @@ class MSWindowsWatchdog static void shutdownExistingProcesses(); private: - bool m_running = true; + std::atomic m_running = true; std::unique_ptr m_mainThread; std::unique_ptr m_outputThread; std::unique_ptr m_sasThread; From 87e6f5893a6ee256001d96faa7b702ecde32e3c6 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 01:44:48 +0100 Subject: [PATCH 091/144] fix: simplify thread stopping logic in MSWindowsWatchdog::stop --- src/lib/platform/MSWindowsWatchdog.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index 892e8ef20..cb9a5003f 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -14,7 +14,6 @@ #include "common/Constants.h" #include "common/LogLevel.h" #include "deskflow/App.h" -#include "mt/Thread.h" #include "platform/MSWindowsHandle.h" #include @@ -90,16 +89,18 @@ void MSWindowsWatchdog::stop() m_running = false; - const auto waitForThread = [](const std::unique_ptr &thread, const char *name) { - if (thread != nullptr && !thread->wait(kThreadWaitSeconds)) { - LOG_WARN("%s thread is slow to stop, waiting", name); - thread->wait(); - } - }; - - waitForThread(m_mainThread, "main"); - waitForThread(m_outputThread, "output"); - waitForThread(m_sasThread, "sas"); + if (m_mainThread != nullptr && !m_mainThread->wait(kThreadWaitSeconds)) { + LOG_WARN("main thread is slow to stop, waiting"); + m_mainThread->wait(); + } + if (m_outputThread != nullptr && !m_outputThread->wait(kThreadWaitSeconds)) { + LOG_WARN("output thread is slow to stop, waiting"); + m_outputThread->wait(); + } + if (m_sasThread != nullptr && !m_sasThread->wait(kThreadWaitSeconds)) { + LOG_WARN("sas thread is slow to stop, waiting"); + m_sasThread->wait(); + } } HANDLE From 49c2b1730417e3d307d05a46cb3e75600907f8e6 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 09:39:14 +0100 Subject: [PATCH 092/144] feat: add seat count handling to SerialKey and update related parsing logic --- .../lib/synergy/gui/license/LicenseHandler.cpp | 7 ++++++- extra/src/lib/synergy/license/SerialKey.h | 2 +- .../lib/synergy/license/parse_serial_key.cpp | 17 +++++++++++++++++ .../license/OfflineActivationTests.cpp | 10 ++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 3b04bce1b..b4779cb4b 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -220,7 +220,12 @@ void LicenseHandler::handleAbout(QDialog *parent) const const auto refresh = [registrantLabel, keyField] { const auto &k = LicenseHandler::instance().license().serialKey(); const auto name = QString::fromStdString(k.name); - const auto company = QString::fromStdString(k.company); + auto company = QString::fromStdString(k.company); + if (!company.isEmpty()) { + const auto seats = + k.seats == 1 ? QObject::tr("1 seat") : QObject::tr("%1 seats").arg(k.seats); + company = QStringLiteral("%1 (%2)").arg(company, seats); + } QString registrant = name; if (!company.isEmpty()) { registrant = name.isEmpty() ? company : QStringLiteral("%1, %2").arg(name, company); diff --git a/extra/src/lib/synergy/license/SerialKey.h b/extra/src/lib/synergy/license/SerialKey.h index 56620ad61..9fbc06218 100644 --- a/extra/src/lib/synergy/license/SerialKey.h +++ b/extra/src/lib/synergy/license/SerialKey.h @@ -21,7 +21,6 @@ #include "SerialKeyType.h" #include -#include #include #include @@ -58,6 +57,7 @@ struct SerialKey std::optional warnTime = std::nullopt; std::optional expireTime = std::nullopt; bool isOffline = false; + int seats = 1; std::string name = ""; std::string email = ""; std::string company = ""; diff --git a/extra/src/lib/synergy/license/parse_serial_key.cpp b/extra/src/lib/synergy/license/parse_serial_key.cpp index e043c8dec..f3ab37195 100644 --- a/extra/src/lib/synergy/license/parse_serial_key.cpp +++ b/extra/src/lib/synergy/license/parse_serial_key.cpp @@ -40,6 +40,20 @@ std::string trim(const std::string &s) const auto last = s.find_last_not_of(" \t\n\r\f\v"); return s.substr(first, last - first + 1); } + +int parseSeats(const std::string &value) +{ + const auto clean = trim(value); + if (clean.empty()) { + return 1; + } + try { + const auto seats = std::stoi(clean); + return seats > 0 ? seats : 1; + } catch (const std::exception &) { + return 1; + } +} } // namespace namespace synergy::license { @@ -97,6 +111,7 @@ SerialKey parseV1(const std::string &hexString, const Parts &parts) SerialKey serialKey(hexString); serialKey.product = Product(parts.at(1)); serialKey.name = parts.at(2); + serialKey.seats = parseSeats(parts.at(3)); serialKey.email = parts.at(4); serialKey.company = parts.at(5); serialKey.warnTime = parseDate(parts.at(6)); @@ -115,6 +130,7 @@ SerialKey parseV2(const std::string &hexString, const Parts &parts) serialKey.type = SerialKeyType(parts.at(1)); serialKey.product = Product(parts.at(2)); serialKey.name = parts.at(3); + serialKey.seats = parseSeats(parts.at(4)); serialKey.email = parts.at(5); serialKey.company = parts.at(6); serialKey.warnTime = parseDate(parts.at(7)); @@ -135,6 +151,7 @@ SerialKey parseV3(const std::string &hexString, const Parts &parts) serialKey.type = SerialKeyType(parts.at(2)); serialKey.product = Product(parts.at(3)); serialKey.name = parts.at(4); + serialKey.seats = parseSeats(parts.at(5)); serialKey.email = parts.at(6); serialKey.company = parts.at(7); serialKey.warnTime = parseDate(parts.at(8)); diff --git a/extra/src/unittests/license/OfflineActivationTests.cpp b/extra/src/unittests/license/OfflineActivationTests.cpp index f7c2fc57b..834dd7ccc 100644 --- a/extra/src/unittests/license/OfflineActivationTests.cpp +++ b/extra/src/unittests/license/OfflineActivationTests.cpp @@ -138,4 +138,14 @@ TEST(OfflineActivationTests, parseSerialKey_offlineMode_isOffline) EXPECT_TRUE(serialKey.isValid); EXPECT_TRUE(serialKey.isOffline); + EXPECT_EQ(serialKey.seats, 1); +} + +TEST(OfflineActivationTests, parseSerialKey_multiSeatKey_parsesSeatCount) +{ + // {v3;offline;;business;Bob;2;bob@example.com;Acme;;} + const auto serialKey = parseSerialKey("7b76333b6f66666c696e653b3b627573696e6573733b426f623b323b626f6240657" + "8616d706c652e636f6d3b41636d653b3b7d"); + + EXPECT_EQ(serialKey.seats, 2); } From e0c3e403360128c6f0a292a2d4dc39122a67999b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 10:57:25 +0100 Subject: [PATCH 093/144] feat: add handling for deactivated license state and related signals --- .../synergy/gui/license/LicenseApiClient.cpp | 9 ++++ .../synergy/gui/license/LicenseApiClient.h | 1 + .../synergy/gui/license/LicenseHandler.cpp | 44 ++++++++++++++++++- .../lib/synergy/gui/license/LicenseHandler.h | 1 + 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index 6ae018829..b48b25653 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -155,6 +155,15 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) return; } + // The deactivated flag rides on a success status; the license is valid but another + // computer has taken over this machine's server activation. + if (kind == RequestKind::kCheck && json["deactivated"].toBool()) { + qWarning("license api check found this machine deactivated"); + Q_EMIT checkDeactivated(json["message"].toString()); + reply->deleteLater(); + return; + } + qInfo().noquote() << "license api request successful"; emitSucceeded(); reply->deleteLater(); diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h index 2bd43fe46..135b84d19 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -56,6 +56,7 @@ class LicenseApiClient : public QObject void activationUnreachable(); void checkFailed(const QString &message); void checkSucceeded(); + void checkDeactivated(const QString &message); private Q_SLOTS: void handleResponse(QNetworkReply *reply); diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index b4779cb4b..aa9a067fe 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -120,6 +120,7 @@ LicenseHandler::LicenseHandler() connect(&m_apiClient, &LicenseApiClient::checkSucceeded, this, &LicenseHandler::handleRemoteCheckSucceeded); connect(&m_apiClient, &LicenseApiClient::checkFailed, this, &LicenseHandler::handleRemoteCheckFailed); + connect(&m_apiClient, &LicenseApiClient::checkDeactivated, this, &LicenseHandler::handleRemoteCheckDeactivated); } void LicenseHandler::handleMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) @@ -283,7 +284,8 @@ bool LicenseHandler::handleCoreStart() } if (m_settings.activated() && !m_license.serialKey().isOffline) { - qDebug("license is activated, starting core"); + qDebug("license is activated, starting core and checking remotely"); + runRemoteCheck(); return true; } @@ -680,6 +682,46 @@ void LicenseHandler::handleRemoteCheckFailed(const QString &message) } } +void LicenseHandler::handleRemoteCheckDeactivated(const QString &message) +{ + qWarning().noquote() << "license check found this machine deactivated:" << message; + + // The license itself is valid and the server was reachable, so the grace clock resets. + m_settings.setGraceStartEpochSecs(0); + m_warnedAboutGrace = false; + + // Only server activations are ever deactivated. If this machine has since become a + // client, reactivating as a client restores it without bothering the customer. + const auto coreMode = Settings::value(Settings::Core::CoreMode).toInt(); + if (coreMode == Settings::Client) { + qInfo("machine is now a client, reactivating"); + m_settings.sync(); + m_apiClient.activate(buildApiData()); + return; + } + + m_settings.setActivated(false); + m_settings.sync(); + + Settings::setValue(Settings::Core::CoreMode, Settings::CoreMode::None); + Settings::save(); + + if (m_pCoreProcess != nullptr && m_pCoreProcess->isStarted()) { + qDebug("stopping core process after deactivation"); + m_pCoreProcess->stop(); + } + + if (m_pMainWindow != nullptr) { + QMessageBox::warning( + m_pMainWindow, "Server changed", + tr("

Another computer has been activated as the server for your license.

" + "

To keep using this computer as the server, choose the server option and " + "start %1 again.

") + .arg(productName()) + ); + } +} + void LicenseHandler::disableLicenseAfterGrace(const QString &reason) { qWarning().noquote() << "license grace period expired, disabling:" << reason; diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index 76c1c3373..828a1b987 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -94,6 +94,7 @@ class LicenseHandler : public QObject void runRemoteCheck(); void handleRemoteCheckSucceeded(); void handleRemoteCheckFailed(const QString &message); + void handleRemoteCheckDeactivated(const QString &message); bool isInGracePeriod() const; bool isGracePeriodExpired() const; void disableLicenseAfterGrace(const QString &reason); From dc7e87a5ab9d98a6d22ed3bc20e97dfddd23b014 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 11:41:23 +0100 Subject: [PATCH 094/144] feat: enhance LicenseApiClient and LicenseHandler for server takeover handling --- .../synergy/gui/license/LicenseApiClient.cpp | 31 ++++---- .../synergy/gui/license/LicenseApiClient.h | 10 ++- .../synergy/gui/license/LicenseHandler.cpp | 74 +++++++++++-------- .../lib/synergy/gui/license/LicenseHandler.h | 2 +- 4 files changed, 66 insertions(+), 51 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index b48b25653..eda7ac0f3 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -45,9 +45,9 @@ LicenseApiClient::LicenseApiClient() connect(&m_manager, &QNetworkAccessManager::finished, this, &LicenseApiClient::handleResponse); } -void LicenseApiClient::activate(Data data) +void LicenseApiClient::activate(Data data, bool takeover) { - post(RequestKind::kActivate, QUrl(activateUrl()), data); + post(RequestKind::kActivate, QUrl(activateUrl()), data, takeover); } void LicenseApiClient::check(Data data) @@ -55,7 +55,7 @@ void LicenseApiClient::check(Data data) post(RequestKind::kCheck, QUrl(checkUrl()), data); } -void LicenseApiClient::post(RequestKind kind, const QUrl &url, const Data &data) +void LicenseApiClient::post(RequestKind kind, const QUrl &url, const Data &data, std::optional takeover) { m_isBusy = true; m_pendingKind = kind; @@ -65,7 +65,7 @@ void LicenseApiClient::post(RequestKind kind, const QUrl &url, const Data &data) auto request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - m_manager.post(request, getRequestData(data)); + m_manager.post(request, getRequestData(data, takeover)); } void LicenseApiClient::handleResponse(QNetworkReply *reply) @@ -130,6 +130,15 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) const auto status = json["status"].toString(); const auto message = json["message"].toString(); + // The license is valid but another computer holds the server activation; the caller + // prompts the customer before taking it over, so this is not a failure. + if (kind == RequestKind::kActivate && status == "deactivated") { + qWarning("license api found this machine deactivated"); + Q_EMIT activationDeactivated(message); + reply->deleteLater(); + return; + } + if (!status.isEmpty()) { qWarning().noquote() << "license api status:" << status; } else { @@ -155,21 +164,12 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) return; } - // The deactivated flag rides on a success status; the license is valid but another - // computer has taken over this machine's server activation. - if (kind == RequestKind::kCheck && json["deactivated"].toBool()) { - qWarning("license api check found this machine deactivated"); - Q_EMIT checkDeactivated(json["message"].toString()); - reply->deleteLater(); - return; - } - qInfo().noquote() << "license api request successful"; emitSucceeded(); reply->deleteLater(); } -QByteArray LicenseApiClient::getRequestData(const Data &data) const +QByteArray LicenseApiClient::getRequestData(const Data &data, std::optional takeover) const { if (data.machineSignature.isEmpty()) { qFatal("cannot create license request, no machine id"); @@ -198,6 +198,9 @@ QByteArray LicenseApiClient::getRequestData(const Data &data) const requestData["appVersion"] = data.appVersion; requestData["osName"] = data.osName; requestData["isServer"] = data.isServer; + if (takeover.has_value()) { + requestData["takeover"] = takeover.value(); + } return QJsonDocument(requestData).toJson(); } diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h index 135b84d19..3c4e43725 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -21,6 +21,8 @@ #include #include +#include + class QNetworkReply; namespace synergy::gui::license { @@ -42,7 +44,7 @@ class LicenseApiClient : public QObject explicit LicenseApiClient(); - void activate(Data data); + void activate(Data data, bool takeover = false); void check(Data data); bool isBusy() @@ -54,9 +56,9 @@ class LicenseApiClient : public QObject void activationFailed(const QString &message); void activationSucceeded(); void activationUnreachable(); + void activationDeactivated(const QString &message); void checkFailed(const QString &message); void checkSucceeded(); - void checkDeactivated(const QString &message); private Q_SLOTS: void handleResponse(QNetworkReply *reply); @@ -68,8 +70,8 @@ private Q_SLOTS: kCheck }; - void post(RequestKind kind, const QUrl &url, const Data &data); - QByteArray getRequestData(const Data &data) const; + void post(RequestKind kind, const QUrl &url, const Data &data, std::optional takeover = std::nullopt); + QByteArray getRequestData(const Data &data, std::optional takeover) const; QNetworkAccessManager m_manager; bool m_isBusy = false; diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index aa9a067fe..a9546c54b 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -118,9 +118,11 @@ LicenseHandler::LicenseHandler() } }); + connect( + &m_apiClient, &LicenseApiClient::activationDeactivated, this, &LicenseHandler::handleActivationDeactivated + ); connect(&m_apiClient, &LicenseApiClient::checkSucceeded, this, &LicenseHandler::handleRemoteCheckSucceeded); connect(&m_apiClient, &LicenseApiClient::checkFailed, this, &LicenseHandler::handleRemoteCheckFailed); - connect(&m_apiClient, &LicenseApiClient::checkDeactivated, this, &LicenseHandler::handleRemoteCheckDeactivated); } void LicenseHandler::handleMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) @@ -283,18 +285,29 @@ bool LicenseHandler::handleCoreStart() qFatal("core process not set"); } + // Re-activate on every core start: the role is only reliably known here, the server slot + // follows whichever computer starts as the server, and clients verify the license this way. + // The core starts optimistically; a deactivated verdict stops it and prompts the customer. if (m_settings.activated() && !m_license.serialKey().isOffline) { - qDebug("license is activated, starting core and checking remotely"); - runRemoteCheck(); + qDebug("license is activated, refreshing activation on core start"); + m_apiClient.activate(buildApiData()); return true; } if (m_license.serialKey().isOffline) { + // Offline activation is required only to run as the server. Clients are free and + // unlimited, like online clients, so an air-gapped setup only spends activations + // on the machines that ever host the server. + if (Settings::value(Settings::Core::CoreMode).toInt() != Settings::Server) { + qDebug("offline license in client mode, starting core without activation"); + return true; + } + if (isOfflineActivated()) { qDebug("offline activation verified, starting core"); return true; } - qInfo("offline serial key not activated, showing offline activation dialog"); + qInfo("offline serial key not activated as server, showing offline activation dialog"); return showOfflineActivationDialog(); } @@ -368,9 +381,12 @@ bool LicenseHandler::showSerialKeyDialog() updateWindowTitle(); clampFeatures(); + // Offline activation is only needed to run as the server; clients are free and unlimited. + const auto serverMode = Settings::value(Settings::Core::CoreMode).toInt() == Settings::Server; + bool offlineActivationDeclined = false; - if (m_license.serialKey().isOffline && !isOfflineActivated()) { - qInfo("serial key requires offline activation"); + if (m_license.serialKey().isOffline && serverMode && !isOfflineActivated()) { + qInfo("serial key requires offline activation for server mode"); offlineActivationDeclined = !showOfflineActivationDialog(); } @@ -382,8 +398,8 @@ bool LicenseHandler::showSerialKeyDialog() qDebug("restarting core on serial key change"); m_pCoreProcess->restart(); } - } else if (m_license.serialKey().isOffline && isOfflineActivated() && !m_pCoreProcess->isStarted()) { - qDebug("starting core after offline activation"); + } else if (m_license.serialKey().isOffline && (isOfflineActivated() || !serverMode) && !m_pCoreProcess->isStarted()) { + qDebug("starting core after offline serial key accepted"); m_pCoreProcess->start(); } @@ -682,44 +698,38 @@ void LicenseHandler::handleRemoteCheckFailed(const QString &message) } } -void LicenseHandler::handleRemoteCheckDeactivated(const QString &message) +void LicenseHandler::handleActivationDeactivated(const QString &message) { - qWarning().noquote() << "license check found this machine deactivated:" << message; + qWarning().noquote() << "server activation held by another computer:" << message; // The license itself is valid and the server was reachable, so the grace clock resets. m_settings.setGraceStartEpochSecs(0); + m_settings.sync(); m_warnedAboutGrace = false; - // Only server activations are ever deactivated. If this machine has since become a - // client, reactivating as a client restores it without bothering the customer. - const auto coreMode = Settings::value(Settings::Core::CoreMode).toInt(); - if (coreMode == Settings::Client) { - qInfo("machine is now a client, reactivating"); - m_settings.sync(); - m_apiClient.activate(buildApiData()); + if (m_pCoreProcess != nullptr && m_pCoreProcess->isStarted()) { + qDebug("stopping core process while the server takeover is unconfirmed"); + m_pCoreProcess->stop(); + } + + const auto reply = QMessageBox::question( + m_pMainWindow, "Server changed", + tr("

Another computer has been activated as the server for your license.

" + "

Do you want to use this computer as the server instead? " + "The other computer will stop working as the server.

") + ); + if (reply == QMessageBox::Yes) { + qInfo("server takeover confirmed, reactivating"); + m_apiClient.activate(buildApiData(), true); return; } + qInfo("server takeover declined, clearing core mode"); m_settings.setActivated(false); m_settings.sync(); Settings::setValue(Settings::Core::CoreMode, Settings::CoreMode::None); Settings::save(); - - if (m_pCoreProcess != nullptr && m_pCoreProcess->isStarted()) { - qDebug("stopping core process after deactivation"); - m_pCoreProcess->stop(); - } - - if (m_pMainWindow != nullptr) { - QMessageBox::warning( - m_pMainWindow, "Server changed", - tr("

Another computer has been activated as the server for your license.

" - "

To keep using this computer as the server, choose the server option and " - "start %1 again.

") - .arg(productName()) - ); - } } void LicenseHandler::disableLicenseAfterGrace(const QString &reason) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index 828a1b987..8b456afe3 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -94,7 +94,7 @@ class LicenseHandler : public QObject void runRemoteCheck(); void handleRemoteCheckSucceeded(); void handleRemoteCheckFailed(const QString &message); - void handleRemoteCheckDeactivated(const QString &message); + void handleActivationDeactivated(const QString &message); bool isInGracePeriod() const; bool isGracePeriodExpired() const; void disableLicenseAfterGrace(const QString &reason); From 49c149f82cd0a2c2465ca2bda36e5b1539f50909 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 12:46:12 +0100 Subject: [PATCH 095/144] feat: add handling for deactivated license state and introduce checkDeactivated signal --- .../synergy/gui/license/LicenseApiClient.cpp | 11 +-- .../synergy/gui/license/LicenseApiClient.h | 1 + .../synergy/gui/license/LicenseHandler.cpp | 69 ++++++++++++++----- .../lib/synergy/gui/license/LicenseHandler.h | 1 + 4 files changed, 59 insertions(+), 23 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index eda7ac0f3..faed11bd2 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -130,11 +130,14 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) const auto status = json["status"].toString(); const auto message = json["message"].toString(); - // The license is valid but another computer holds the server activation; the caller - // prompts the customer before taking it over, so this is not a failure. - if (kind == RequestKind::kActivate && status == "deactivated") { + // Not a failure: the license is valid, another computer just holds the server activation. + if (status == "deactivated") { qWarning("license api found this machine deactivated"); - Q_EMIT activationDeactivated(message); + if (kind == RequestKind::kActivate) { + Q_EMIT activationDeactivated(message); + } else { + Q_EMIT checkDeactivated(message); + } reply->deleteLater(); return; } diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h index 3c4e43725..5773d8ab1 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -59,6 +59,7 @@ class LicenseApiClient : public QObject void activationDeactivated(const QString &message); void checkFailed(const QString &message); void checkSucceeded(); + void checkDeactivated(const QString &message); private Q_SLOTS: void handleResponse(QNetworkReply *reply); diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index a9546c54b..7afaca714 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -46,9 +46,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -118,11 +118,10 @@ LicenseHandler::LicenseHandler() } }); - connect( - &m_apiClient, &LicenseApiClient::activationDeactivated, this, &LicenseHandler::handleActivationDeactivated - ); + connect(&m_apiClient, &LicenseApiClient::activationDeactivated, this, &LicenseHandler::handleActivationDeactivated); connect(&m_apiClient, &LicenseApiClient::checkSucceeded, this, &LicenseHandler::handleRemoteCheckSucceeded); connect(&m_apiClient, &LicenseApiClient::checkFailed, this, &LicenseHandler::handleRemoteCheckFailed); + connect(&m_apiClient, &LicenseApiClient::checkDeactivated, this, &LicenseHandler::handleCheckDeactivated); } void LicenseHandler::handleMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) @@ -225,8 +224,7 @@ void LicenseHandler::handleAbout(QDialog *parent) const const auto name = QString::fromStdString(k.name); auto company = QString::fromStdString(k.company); if (!company.isEmpty()) { - const auto seats = - k.seats == 1 ? QObject::tr("1 seat") : QObject::tr("%1 seats").arg(k.seats); + const auto seats = k.seats == 1 ? QObject::tr("1 seat") : QObject::tr("%1 seats").arg(k.seats); company = QStringLiteral("%1 (%2)").arg(company, seats); } QString registrant = name; @@ -285,9 +283,7 @@ bool LicenseHandler::handleCoreStart() qFatal("core process not set"); } - // Re-activate on every core start: the role is only reliably known here, the server slot - // follows whichever computer starts as the server, and clients verify the license this way. - // The core starts optimistically; a deactivated verdict stops it and prompts the customer. + // The role is only reliably known at core start; start optimistically, a deactivated verdict stops the core. if (m_settings.activated() && !m_license.serialKey().isOffline) { qDebug("license is activated, refreshing activation on core start"); m_apiClient.activate(buildApiData()); @@ -295,9 +291,6 @@ bool LicenseHandler::handleCoreStart() } if (m_license.serialKey().isOffline) { - // Offline activation is required only to run as the server. Clients are free and - // unlimited, like online clients, so an air-gapped setup only spends activations - // on the machines that ever host the server. if (Settings::value(Settings::Core::CoreMode).toInt() != Settings::Server) { qDebug("offline license in client mode, starting core without activation"); return true; @@ -381,7 +374,6 @@ bool LicenseHandler::showSerialKeyDialog() updateWindowTitle(); clampFeatures(); - // Offline activation is only needed to run as the server; clients are free and unlimited. const auto serverMode = Settings::value(Settings::Core::CoreMode).toInt() == Settings::Server; bool offlineActivationDeclined = false; @@ -620,14 +612,17 @@ LicenseApiClient::Data LicenseHandler::buildApiData() const const auto machineSignature = QCryptographicHash::hash(machineId, QCryptographicHash::Sha256).toHex(); const auto hostnameSignature = QCryptographicHash::hash(hostname.toUtf8(), QCryptographicHash::Sha256).toHex(); - const auto isServer = (Settings::value(Settings::Core::CoreMode).toInt() == Settings::Server); + // The saved mode setting lags the UI at core start, so prefer the live core process mode. + const auto coreMode = m_pCoreProcess != nullptr + ? m_pCoreProcess->mode() + : static_cast(Settings::value(Settings::Core::CoreMode).toInt()); return { machineSignature, hostnameSignature, QString::fromStdString(m_license.serialKey().hexString), kVersion, QSysInfo::prettyProductName(), - isServer + coreMode == Settings::Server }; } @@ -712,11 +707,16 @@ void LicenseHandler::handleActivationDeactivated(const QString &message) m_pCoreProcess->stop(); } + // The license allows one server at a time, but we never block the customer standing at this + // machine; they are almost always the rightful user (switching desks, replacing a machine). + // Asking first keeps use of one license fair and deliberate, and the other computer is + // notified rather than cut off. Switching the old server to client mode releases the server + // slot, so the normal switching flow never sees this question. const auto reply = QMessageBox::question( - m_pMainWindow, "Server changed", - tr("

Another computer has been activated as the server for your license.

" - "

Do you want to use this computer as the server instead? " - "The other computer will stop working as the server.

") + m_pMainWindow, "Server in use", + tr("

Another computer is currently the server for your license. " + "Your license allows one computer to act as the server at a time.

" + "

Do you want to make this computer the server?

") ); if (reply == QMessageBox::Yes) { qInfo("server takeover confirmed, reactivating"); @@ -732,6 +732,37 @@ void LicenseHandler::handleActivationDeactivated(const QString &message) Settings::save(); } +void LicenseHandler::handleCheckDeactivated(const QString &message) +{ + qWarning().noquote() << "server activation taken over by another computer:" << message; + + // The license itself is valid and the server was reachable, so the grace clock resets. + m_settings.setGraceStartEpochSecs(0); + m_settings.sync(); + m_warnedAboutGrace = false; + + if (m_pCoreProcess == nullptr || !m_pCoreProcess->isStarted() || m_pCoreProcess->mode() != Settings::Server) { + qDebug("not running as server, ignoring deactivated check"); + return; + } + + qInfo("stopping core, another computer took over as server"); + m_pCoreProcess->stop(); + + // A notice rather than a question: starting the server again is the answer, and that flow + // asks before taking the slot back. Nothing restarts the core here; reclaiming the slot + // must stay a human decision, or two machines could silently ping-pong the activation. + if (m_pMainWindow != nullptr) { + QMessageBox::information( + m_pMainWindow, "Server changed", + tr("

Another computer is now the server for your license, so sharing from " + "this computer has stopped. Your license allows one computer to act as " + "the server at a time.

" + "

To make this computer the server again, press Start.

") + ); + } +} + void LicenseHandler::disableLicenseAfterGrace(const QString &reason) { qWarning().noquote() << "license grace period expired, disabling:" << reason; diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index 8b456afe3..af88efc2a 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -95,6 +95,7 @@ class LicenseHandler : public QObject void handleRemoteCheckSucceeded(); void handleRemoteCheckFailed(const QString &message); void handleActivationDeactivated(const QString &message); + void handleCheckDeactivated(const QString &message); bool isInGracePeriod() const; bool isGracePeriodExpired() const; void disableLicenseAfterGrace(const QString &reason); From 30ef107aca6afe3e412211cf1546121cb33ece51 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 14:12:47 +0100 Subject: [PATCH 096/144] feat: implement server takeover handling and introduce askServerQuestion method --- .../synergy/gui/license/LicenseApiClient.cpp | 2 - .../synergy/gui/license/LicenseApiClient.h | 1 - .../synergy/gui/license/LicenseHandler.cpp | 57 +++++++++++-------- .../lib/synergy/gui/license/LicenseHandler.h | 2 + 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index faed11bd2..cefc0d1c2 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -23,8 +23,6 @@ #include #include #include -#include -#include namespace synergy::gui::license { diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h index 5773d8ab1..2e8dcba3f 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -19,7 +19,6 @@ #include #include -#include #include diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 7afaca714..52493189d 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -286,6 +286,7 @@ bool LicenseHandler::handleCoreStart() // The role is only reliably known at core start; start optimistically, a deactivated verdict stops the core. if (m_settings.activated() && !m_license.serialKey().isOffline) { qDebug("license is activated, refreshing activation on core start"); + m_coreStartActivation = true; m_apiClient.activate(buildApiData()); return true; } @@ -310,6 +311,7 @@ bool LicenseHandler::handleCoreStart() } qInfo("activating license"); + m_coreStartActivation = true; m_apiClient.activate(buildApiData()); return false; @@ -400,6 +402,7 @@ bool LicenseHandler::showSerialKeyDialog() // whether the serial key changed. if (!m_settings.activated() && m_license.isValid() && !m_license.serialKey().isOffline && !m_apiClient.isBusy()) { qInfo("retrying activation after dialog accept"); + m_coreStartActivation = false; m_apiClient.activate(buildApiData()); } @@ -702,29 +705,46 @@ void LicenseHandler::handleActivationDeactivated(const QString &message) m_settings.sync(); m_warnedAboutGrace = false; + // The role is only reliably known at core start; an activation sent from anywhere else + // (e.g. serial key entry) must not ask or stop anything, the next core start will. + if (!m_coreStartActivation) { + qInfo("not a core start activation, leaving the server question for the next core start"); + return; + } + if (m_pCoreProcess != nullptr && m_pCoreProcess->isStarted()) { - qDebug("stopping core process while the server takeover is unconfirmed"); + qDebug("stopping core process while the server question is unanswered"); m_pCoreProcess->stop(); } - // The license allows one server at a time, but we never block the customer standing at this + askServerQuestion(); +} + +void LicenseHandler::askServerQuestion() +{ + // The license allows one server per seat, but we never block the customer standing at this // machine; they are almost always the rightful user (switching desks, replacing a machine). // Asking first keeps use of one license fair and deliberate, and the other computer is - // notified rather than cut off. Switching the old server to client mode releases the server - // slot, so the normal switching flow never sees this question. - const auto reply = QMessageBox::question( - m_pMainWindow, "Server in use", - tr("

Another computer is currently the server for your license. " - "Your license allows one computer to act as the server at a time.

" - "

Do you want to make this computer the server?

") - ); + // asked the same question rather than cut off. Switching the old server to client mode + // releases the server slot, so the normal switching flow never sees this question. + QString question; + if (m_license.serialKey().seats > 1) { + question = + tr("

All of the server activations for your team's license are currently in use.

" + "

Do you want to reassign a server activation to this computer?

"); + } else { + question = + tr("

Another computer is currently the server for your license.

" + "

Do you want to reassign the server activation to this computer?

"); + } + const auto reply = QMessageBox::question(m_pMainWindow, "Server in use", question); if (reply == QMessageBox::Yes) { - qInfo("server takeover confirmed, reactivating"); + qInfo("server question accepted, reactivating"); m_apiClient.activate(buildApiData(), true); return; } - qInfo("server takeover declined, clearing core mode"); + qInfo("server question declined, clearing core mode"); m_settings.setActivated(false); m_settings.sync(); @@ -749,18 +769,7 @@ void LicenseHandler::handleCheckDeactivated(const QString &message) qInfo("stopping core, another computer took over as server"); m_pCoreProcess->stop(); - // A notice rather than a question: starting the server again is the answer, and that flow - // asks before taking the slot back. Nothing restarts the core here; reclaiming the slot - // must stay a human decision, or two machines could silently ping-pong the activation. - if (m_pMainWindow != nullptr) { - QMessageBox::information( - m_pMainWindow, "Server changed", - tr("

Another computer is now the server for your license, so sharing from " - "this computer has stopped. Your license allows one computer to act as " - "the server at a time.

" - "

To make this computer the server again, press Start.

") - ); - } + askServerQuestion(); } void LicenseHandler::disableLicenseAfterGrace(const QString &reason) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index af88efc2a..bdef5231c 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -96,6 +96,7 @@ class LicenseHandler : public QObject void handleRemoteCheckFailed(const QString &message); void handleActivationDeactivated(const QString &message); void handleCheckDeactivated(const QString &message); + void askServerQuestion(); bool isInGracePeriod() const; bool isGracePeriodExpired() const; void disableLicenseAfterGrace(const QString &reason); @@ -107,6 +108,7 @@ class LicenseHandler : public QObject synergy::gui::ExtraSettings m_settings; synergy::gui::license::LicenseApiClient m_apiClient; bool m_warnedAboutGrace = false; + bool m_coreStartActivation = false; QMainWindow *m_pMainWindow = nullptr; deskflow::gui::CoreProcess *m_pCoreProcess = nullptr; }; From 33ba2cb149bc9de32ea5acfa375bc0db8a45579b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 14:24:59 +0100 Subject: [PATCH 097/144] feat: add QTimer include to ActivationDialog for improved timing functionality --- extra/src/lib/synergy/gui/ActivationDialog.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/extra/src/lib/synergy/gui/ActivationDialog.cpp b/extra/src/lib/synergy/gui/ActivationDialog.cpp index f1e8f2dc0..85a884028 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/ActivationDialog.cpp @@ -32,6 +32,7 @@ #include #include #include +#include using namespace deskflow::gui; using namespace synergy::gui; From 0d0cb4e966c78b138d1fedc8d7715cb2eb8bcaae Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 14:45:52 +0100 Subject: [PATCH 098/144] feat: refine core process activation logic based on activation type --- .../src/lib/synergy/gui/license/LicenseHandler.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 52493189d..4cddcd794 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -99,7 +99,9 @@ LicenseHandler::LicenseHandler() qFatal("core process not set"); } - if (m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { + // Only an activation that a core start is waiting on may start the core; an activation + // from serial key entry must not, the customer has not chosen to start anything yet. + if (m_coreStartActivation && m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { qDebug("resuming core process after activation"); m_pCoreProcess->start(); } @@ -113,7 +115,8 @@ LicenseHandler::LicenseHandler() qWarning("license activation server unreachable, continuing without activation"); - if (m_pCoreProcess != nullptr && m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { + if (m_coreStartActivation && m_pCoreProcess != nullptr && + m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { m_pCoreProcess->start(); } }); @@ -392,9 +395,6 @@ bool LicenseHandler::showSerialKeyDialog() qDebug("restarting core on serial key change"); m_pCoreProcess->restart(); } - } else if (m_license.serialKey().isOffline && (isOfflineActivated() || !serverMode) && !m_pCoreProcess->isStarted()) { - qDebug("starting core after offline serial key accepted"); - m_pCoreProcess->start(); } // If the user accepted the dialog while not activated (e.g. recovering from a @@ -740,6 +740,9 @@ void LicenseHandler::askServerQuestion() const auto reply = QMessageBox::question(m_pMainWindow, "Server in use", question); if (reply == QMessageBox::Yes) { qInfo("server question accepted, reactivating"); + + // The customer just chose to be the server, so success may resume the stopped core. + m_coreStartActivation = true; m_apiClient.activate(buildApiData(), true); return; } From c8285953d488b598dd114de2b1089391a673b457 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 14:59:34 +0100 Subject: [PATCH 099/144] feat: improve core process mode handling for offline licenses and simplify serial key dialog logic --- .../synergy/gui/license/LicenseHandler.cpp | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 4cddcd794..1c5fefab2 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -295,7 +295,8 @@ bool LicenseHandler::handleCoreStart() } if (m_license.serialKey().isOffline) { - if (Settings::value(Settings::Core::CoreMode).toInt() != Settings::Server) { + // The saved mode setting lags the UI at core start, so prefer the live core process mode. + if (m_pCoreProcess->mode() != Settings::Server) { qDebug("offline license in client mode, starting core without activation"); return true; } @@ -379,24 +380,8 @@ bool LicenseHandler::showSerialKeyDialog() updateWindowTitle(); clampFeatures(); - const auto serverMode = Settings::value(Settings::Core::CoreMode).toInt() == Settings::Server; - - bool offlineActivationDeclined = false; - if (m_license.serialKey().isOffline && serverMode && !isOfflineActivated()) { - qInfo("serial key requires offline activation for server mode"); - offlineActivationDeclined = !showOfflineActivationDialog(); - } - - if (dialog.serialKeyChanged() && m_pCoreProcess->isStarted()) { - if (offlineActivationDeclined) { - qDebug("stopping core, offline activation declined"); - m_pCoreProcess->stop(); - } else { - qDebug("restarting core on serial key change"); - m_pCoreProcess->restart(); - } - } - + // Key entry never touches the core process; everything a new key implies (activation, the + // offline challenge, feature clamps) is applied at the next core start. // If the user accepted the dialog while not activated (e.g. recovering from a // remote disable), retry activation so something visible happens regardless of // whether the serial key changed. From b01be49d818b83ddc84fa98d1bab8804e3c2aeea Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 15:55:06 +0100 Subject: [PATCH 100/144] feat: simplify contact link styling in activation dialogs and improve dark theme support --- .../src/lib/synergy/gui/ActivationDialog.cpp | 11 +++---- .../synergy/gui/OfflineActivationDialog.cpp | 9 ++---- .../synergy/gui/license/LicenseHandler.cpp | 32 +++++++++---------- extra/src/lib/synergy/hooks/gui_hook.h | 11 +++++++ 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/extra/src/lib/synergy/gui/ActivationDialog.cpp b/extra/src/lib/synergy/gui/ActivationDialog.cpp index 85a884028..d45ec48a8 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/ActivationDialog.cpp @@ -167,10 +167,9 @@ void ActivationDialog::showResultDialog(LicenseHandler::SetSerialKeyResult resul this, problemTitle, QString( "Invalid serial key. " - R"(Please contact us for help.)" + R"(Please contact us for help.)" ) .arg(kUrlContact) - .arg(kColorSecondary) ); break; @@ -179,10 +178,9 @@ void ActivationDialog::showResultDialog(LicenseHandler::SetSerialKeyResult resul this, problemTitle, QString( "Sorry, that serial key has expired. " - R"(Please renew your license.)" + R"(Please renew your license.)" ) .arg(kUrlContact) - .arg(kColorSecondary) ); break; @@ -221,12 +219,11 @@ void ActivationDialog::showErrorDialog(const QString &message) { QString fullMessage = QString( "

There was a problem with your serial key.

" - R"(

Please contact us )" + R"(

Please contact us )" "and provide the following information:

" - "%3" + "%2" ) .arg(kUrlContact) - .arg(kColorSecondary) .arg(message); QMessageBox::warning(this, problemTitle, fullMessage); } diff --git a/extra/src/lib/synergy/gui/OfflineActivationDialog.cpp b/extra/src/lib/synergy/gui/OfflineActivationDialog.cpp index c61204451..e44c0d95f 100644 --- a/extra/src/lib/synergy/gui/OfflineActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/OfflineActivationDialog.cpp @@ -19,7 +19,6 @@ #include "synergy/gui/constants.h" #include "synergy/gui/license/LicenseHandler.h" -#include "synergy/gui/styles.h" #include #include @@ -42,9 +41,8 @@ OfflineActivationDialog::OfflineActivationDialog(QWidget *parent, LicenseHandler const auto infoLabel = new QLabel( tr(R"(On a device with internet access, enter the activation code at )" - R"(your account page to get a response code.)") - .arg(kUrlAccount) - .arg(kColorSecondary), + R"(your account page to get a response code.)") + .arg(kUrlAccount), this ); infoLabel->setWordWrap(true); @@ -86,9 +84,8 @@ void OfflineActivationDialog::accept() tr("

That response code is not valid for this computer.

" "

Please check that you typed the whole response code correctly, and that " "the activation code was generated on this computer. " - R"(If the problem persists, please contact us.

)") + R"(If the problem persists, please contact us.

)") .arg(kUrlContact) - .arg(kColorSecondary) ); return; } diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 1c5fefab2..ca2a6741a 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -75,12 +75,11 @@ LicenseHandler::LicenseHandler() connect(&m_apiClient, &LicenseApiClient::activationFailed, this, [this](const QString &message) { QString fullMessage = QString( "

There was a problem activating your license.

" - "%3" - R"(

Please contact us )" + "%2" + R"(

Please contact us )" "if there is anything we can do to help.

" ) .arg(kUrlContact) - .arg(kColorSecondary) .arg(message); QMessageBox::warning(m_pMainWindow, "Activation failed", fullMessage); @@ -413,9 +412,8 @@ bool LicenseHandler::showOfflineActivationDialog() m_pMainWindow, tr("Offline activation"), tr("

A unique ID for this computer could not be determined, " "so an activation code cannot be generated.

" - R"(

Please contact us for help.

)") + R"(

Please contact us for help.

)") .arg(kUrlContact) - .arg(kColorSecondary) ); return false; } @@ -670,13 +668,12 @@ void LicenseHandler::handleRemoteCheckFailed(const QString &message) tr("

We could not verify your license:

" "

%1

" "

%2 will keep working for %3 days. " - R"(If the problem persists, please contact us.)" + R"(If the problem persists, please contact us.)" "

") .arg(message.toHtmlEscaped()) .arg(productName()) .arg(daysRemaining) .arg(kUrlContact) - .arg(kColorSecondary) ); } } @@ -714,15 +711,19 @@ void LicenseHandler::askServerQuestion() // releases the server slot, so the normal switching flow never sees this question. QString question; if (m_license.serialKey().seats > 1) { - question = - tr("

All of the server activations for your team's license are currently in use.

" - "

Do you want to reassign a server activation to this computer?

"); + question = tr("

All of the server activations for your team's license are currently in use.

" + "

Need to add more seats to your team's license? " + R"(Contact us.

)" + "

Do you want to reassign a server activation to this computer?

") + .arg(kUrlContact); } else { - question = - tr("

Another computer is currently the server for your license.

" - "

Do you want to reassign the server activation to this computer?

"); + question = tr("

Another computer is currently the server for your license.

" + "

Need more than one server running at the same time? " + R"(Contact us.

)" + "

Do you want to reassign the server activation to this computer?

") + .arg(kUrlContact); } - const auto reply = QMessageBox::question(m_pMainWindow, "Server in use", question); + const auto reply = QMessageBox::question(m_pMainWindow, "License limit reached", question); if (reply == QMessageBox::Yes) { qInfo("server question accepted, reactivating"); @@ -781,11 +782,10 @@ void LicenseHandler::disableLicenseAfterGrace(const QString &reason) m_pMainWindow, "License disabled", tr("

Your license has been disabled and could not be verified within the grace period:

" "

%1

" - R"(

Please contact us to restore access. )" + R"(

Please contact us to restore access. )" "Once your license is reinstated, the app will resume automatically.

") .arg(reason.toHtmlEscaped()) .arg(kUrlContact) - .arg(kColorSecondary) ); } } diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index a8ccb9955..a49a58344 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -26,9 +26,14 @@ #include "synergy/gui/dev_mode.h" #include "synergy/gui/license/LicenseHandler.h" +#include "synergy/gui/styles.h" + +#include #include +#include #include #include +#include #include namespace deskflow::gui { @@ -56,6 +61,12 @@ inline void onPreInit() inline void onMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) { + // Qt's default link color is unreadable on the dark theme; setting the palette link role + // once colors every anchor, so dialog copy never needs inline link styles. + auto palette = QGuiApplication::palette(); + palette.setColor(QPalette::Link, QColor(kColorSecondary)); + QGuiApplication::setPalette(palette); + LicenseHandler::instance().handleMainWindow(mainWindow, coreProcess); FeatureHandler::instance().handleMainWindow(mainWindow); synergy::gui::migration::showNoticeIfPending(mainWindow); From d7158a533f8eedadd37129488bb4cbc82a807711 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 16:23:30 +0100 Subject: [PATCH 101/144] feat: implement transfer timeout for LicenseApiClient and improve core start handling in LicenseHandler --- .../synergy/gui/license/LicenseApiClient.cpp | 4 ++ .../synergy/gui/license/LicenseHandler.cpp | 41 +++++++++++-------- .../lib/synergy/gui/license/LicenseHandler.h | 1 + 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index cefc0d1c2..b8cc3000b 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -40,6 +40,10 @@ QString checkUrl() LicenseApiClient::LicenseApiClient() { + // Without a timeout, a request that connects but never completes leaves the client busy + // forever, and busy gates core start. + m_manager.setTransferTimeout(); + connect(&m_manager, &QNetworkAccessManager::finished, this, &LicenseApiClient::handleResponse); } diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index ca2a6741a..628a28335 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -26,7 +26,6 @@ #include "synergy/gui/constants.h" #include "synergy/gui/dev_mode.h" #include "synergy/gui/license/license_utils.h" -#include "synergy/gui/styles.h" #include "synergy/license/OfflineActivation.h" #include "synergy/license/Product.h" @@ -59,7 +58,7 @@ using namespace deskflow::gui; namespace { -std::string offlineMachineId() +std::string licenseMachineId() { const auto testId = TestSettings::instance().machineId(); return testId.isEmpty() ? QSysInfo::machineUniqueId().toStdString() : testId.toStdString(); @@ -265,12 +264,15 @@ void LicenseHandler::handleVersionCheck(QString &versionUrl) bool LicenseHandler::handleCoreStart() { - // HACK: For some reason, the core start trigger gets called twice when clicking the 'start' button. - // If the activator is called twice in quick succession, the core is started twice. - if (m_apiClient.isBusy()) { - qDebug("activator is busy, skipping core start handler"); + // HACK: For some reason, the core start trigger gets called twice when clicking the 'start' + // button. Absorb the duplicate by time rather than by network state, or an unanswered + // license request blocks core starts entirely. + const auto now = QDateTime::currentMSecsSinceEpoch(); + if (now - m_lastCoreStartMs < 500) { + qDebug("duplicate core start trigger, skipping"); return false; } + m_lastCoreStartMs = now; if (!m_enabled) { qDebug("license handler disabled, skipping core start handler"); @@ -287,6 +289,10 @@ bool LicenseHandler::handleCoreStart() // The role is only reliably known at core start; start optimistically, a deactivated verdict stops the core. if (m_settings.activated() && !m_license.serialKey().isOffline) { + if (m_apiClient.isBusy()) { + qInfo("license api busy, starting core without refreshing activation"); + return true; + } qDebug("license is activated, refreshing activation on core start"); m_coreStartActivation = true; m_apiClient.activate(buildApiData()); @@ -313,6 +319,11 @@ bool LicenseHandler::handleCoreStart() return false; } + if (m_apiClient.isBusy()) { + qWarning("license api busy, cannot activate for core start, try again shortly"); + return false; + } + qInfo("activating license"); m_coreStartActivation = true; m_apiClient.activate(buildApiData()); @@ -424,7 +435,7 @@ bool LicenseHandler::showOfflineActivationDialog() QString LicenseHandler::offlineActivationChallenge() const { - const auto machineId = offlineMachineId(); + const auto machineId = licenseMachineId(); const auto challenge = synergy::license::buildOfflineChallenge(machineId, m_license.serialKey().hexString); return QString::fromStdString(synergy::license::formatOfflineCode(challenge, 4)); } @@ -435,14 +446,14 @@ bool LicenseHandler::isOfflineActivated() const if (response.isEmpty()) { return false; } - const auto machineId = offlineMachineId(); + const auto machineId = licenseMachineId(); return synergy::license::verifyOfflineResponse(machineId, m_license.serialKey().hexString, response.toStdString()); } bool LicenseHandler::applyOfflineActivationResponse(const QString &responseCode) { const auto response = responseCode.simplified(); - const auto machineId = offlineMachineId(); + const auto machineId = licenseMachineId(); if (!synergy::license::verifyOfflineResponse(machineId, m_license.serialKey().hexString, response.toStdString())) { qWarning("offline activation response not valid for this machine"); return false; @@ -591,7 +602,7 @@ bool LicenseHandler::isGracePeriodExpired() const LicenseApiClient::Data LicenseHandler::buildApiData() const { - const auto machineId = QSysInfo::machineUniqueId(); + const auto machineId = QByteArray::fromStdString(licenseMachineId()); const auto hostname = QHostInfo::localHostName(); // Anonymise so a leak doesn't reveal which customer machine the data belongs to. @@ -733,12 +744,10 @@ void LicenseHandler::askServerQuestion() return; } - qInfo("server question declined, clearing core mode"); - m_settings.setActivated(false); - m_settings.sync(); - - Settings::setValue(Settings::Core::CoreMode, Settings::CoreMode::None); - Settings::save(); + // Decline changes nothing: the core is already stopped, and writing the mode setting here + // would desync it from the mode radios and the core process, which only the main window owns. + // Starting again simply asks again. + qInfo("server question declined, leaving core stopped"); } void LicenseHandler::handleCheckDeactivated(const QString &message) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index bdef5231c..1eee37ace 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -109,6 +109,7 @@ class LicenseHandler : public QObject synergy::gui::license::LicenseApiClient m_apiClient; bool m_warnedAboutGrace = false; bool m_coreStartActivation = false; + qint64 m_lastCoreStartMs = 0; QMainWindow *m_pMainWindow = nullptr; deskflow::gui::CoreProcess *m_pCoreProcess = nullptr; }; From ed53959c3d9d688ba33234d3a54de1e94c50aa68 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 16:58:54 +0100 Subject: [PATCH 102/144] feat: enhance license activation error handling and improve user messaging in dialogs --- .../synergy/gui/license/LicenseHandler.cpp | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 628a28335..75e53a4bf 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -72,18 +72,17 @@ LicenseHandler::LicenseHandler() m_enabled = synergy::gui::license::isActivationEnabled(); connect(&m_apiClient, &LicenseApiClient::activationFailed, this, [this](const QString &message) { - QString fullMessage = QString( - "

There was a problem activating your license.

" - "%2" - R"(

Please contact us )" - "if there is anything we can do to help.

" - ) - .arg(kUrlContact) - .arg(message); - QMessageBox::warning(m_pMainWindow, "Activation failed", fullMessage); - - qWarning("activation failed, showing serial key dialog"); - return showSerialKeyDialog(); + // A rejected activation is an unverified license, the same as a failed check: the grace + // period starts (or continues), the customer sees how many days remain, and the grace + // expiry disables the license. + handleRemoteCheckFailed(message); + + // Mid key entry, reopen the dialog so the customer can correct the key; a failure during + // a core start refresh is served by the grace warning alone. + if (!m_coreStartActivation) { + qWarning("activation failed at key entry, showing serial key dialog"); + showSerialKeyDialog(); + } }); connect(&m_apiClient, &LicenseApiClient::activationSucceeded, this, [this] { @@ -723,14 +722,14 @@ void LicenseHandler::askServerQuestion() QString question; if (m_license.serialKey().seats > 1) { question = tr("

All of the server activations for your team's license are currently in use.

" - "

Need to add more seats to your team's license? " - R"(Contact us.

)" + "

If you need to add more seats to your team's license, please " + R"(contact us today.

)" "

Do you want to reassign a server activation to this computer?

") .arg(kUrlContact); } else { question = tr("

Another computer is currently the server for your license.

" - "

Need more than one server running at the same time? " - R"(Contact us.

)" + "

If you need more than one server running at the same time, please " + R"(contact us today.

)" "

Do you want to reassign the server activation to this computer?

") .arg(kUrlContact); } From 652e142760c3b44b18778a49d6cfcac16485ef49 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 17:09:44 +0100 Subject: [PATCH 103/144] feat: update license handling logic and improve method naming for clarity --- .../synergy/gui/license/LicenseHandler.cpp | 85 +++++++++---------- .../lib/synergy/gui/license/LicenseHandler.h | 7 +- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 75e53a4bf..f131c5e35 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy App Ltd + * Copyright (C) 2015 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -64,6 +64,11 @@ std::string licenseMachineId() return testId.isEmpty() ? QSysInfo::machineUniqueId().toStdString() : testId.toStdString(); } +QByteArray anonymousSignature(const QByteArray &value) +{ + return QCryptographicHash::hash(value, QCryptographicHash::Sha256).toHex(); +} + } // namespace using License = synergy::license::License; @@ -72,13 +77,8 @@ LicenseHandler::LicenseHandler() m_enabled = synergy::gui::license::isActivationEnabled(); connect(&m_apiClient, &LicenseApiClient::activationFailed, this, [this](const QString &message) { - // A rejected activation is an unverified license, the same as a failed check: the grace - // period starts (or continues), the customer sees how many days remain, and the grace - // expiry disables the license. - handleRemoteCheckFailed(message); + handleLicenseUnverified(message); - // Mid key entry, reopen the dialog so the customer can correct the key; a failure during - // a core start refresh is served by the grace warning alone. if (!m_coreStartActivation) { qWarning("activation failed at key entry, showing serial key dialog"); showSerialKeyDialog(); @@ -88,16 +88,13 @@ LicenseHandler::LicenseHandler() connect(&m_apiClient, &LicenseApiClient::activationSucceeded, this, [this] { qDebug("license activation succeeded, saving settings"); m_settings.setActivated(true); - m_settings.setGraceStartEpochSecs(0); - m_settings.sync(); - m_warnedAboutGrace = false; + resetGracePeriod(); if (m_pCoreProcess == nullptr) { qFatal("core process not set"); } - // Only an activation that a core start is waiting on may start the core; an activation - // from serial key entry must not, the customer has not chosen to start anything yet. + // Key entry activations must never start the core; the customer chose nothing yet. if (m_coreStartActivation && m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { qDebug("resuming core process after activation"); m_pCoreProcess->start(); @@ -119,8 +116,8 @@ LicenseHandler::LicenseHandler() }); connect(&m_apiClient, &LicenseApiClient::activationDeactivated, this, &LicenseHandler::handleActivationDeactivated); - connect(&m_apiClient, &LicenseApiClient::checkSucceeded, this, &LicenseHandler::handleRemoteCheckSucceeded); - connect(&m_apiClient, &LicenseApiClient::checkFailed, this, &LicenseHandler::handleRemoteCheckFailed); + connect(&m_apiClient, &LicenseApiClient::checkSucceeded, this, &LicenseHandler::handleLicenseVerified); + connect(&m_apiClient, &LicenseApiClient::checkFailed, this, &LicenseHandler::handleLicenseUnverified); connect(&m_apiClient, &LicenseApiClient::checkDeactivated, this, &LicenseHandler::handleCheckDeactivated); } @@ -299,8 +296,7 @@ bool LicenseHandler::handleCoreStart() } if (m_license.serialKey().isOffline) { - // The saved mode setting lags the UI at core start, so prefer the live core process mode. - if (m_pCoreProcess->mode() != Settings::Server) { + if (liveCoreMode() != Settings::Server) { qDebug("offline license in client mode, starting core without activation"); return true; } @@ -599,26 +595,34 @@ bool LicenseHandler::isGracePeriodExpired() const return elapsed >= duration_cast(kLicenseGracePeriod); } -LicenseApiClient::Data LicenseHandler::buildApiData() const +void LicenseHandler::resetGracePeriod() +{ + m_settings.setGraceStartEpochSecs(0); + m_settings.sync(); + m_warnedAboutGrace = false; +} + +Settings::CoreMode LicenseHandler::liveCoreMode() const { - const auto machineId = QByteArray::fromStdString(licenseMachineId()); - const auto hostname = QHostInfo::localHostName(); + // The saved mode setting lags the UI at core start. + if (m_pCoreProcess != nullptr) { + return m_pCoreProcess->mode(); + } + return static_cast(Settings::value(Settings::Core::CoreMode).toInt()); +} - // Anonymise so a leak doesn't reveal which customer machine the data belongs to. - const auto machineSignature = QCryptographicHash::hash(machineId, QCryptographicHash::Sha256).toHex(); - const auto hostnameSignature = QCryptographicHash::hash(hostname.toUtf8(), QCryptographicHash::Sha256).toHex(); +LicenseApiClient::Data LicenseHandler::buildApiData() const +{ + const auto machineSignature = anonymousSignature(QByteArray::fromStdString(licenseMachineId())); + const auto hostnameSignature = anonymousSignature(QHostInfo::localHostName().toUtf8()); - // The saved mode setting lags the UI at core start, so prefer the live core process mode. - const auto coreMode = m_pCoreProcess != nullptr - ? m_pCoreProcess->mode() - : static_cast(Settings::value(Settings::Core::CoreMode).toInt()); return { machineSignature, hostnameSignature, QString::fromStdString(m_license.serialKey().hexString), kVersion, QSysInfo::prettyProductName(), - coreMode == Settings::Server + liveCoreMode() == Settings::Server }; } @@ -638,14 +642,12 @@ void LicenseHandler::runRemoteCheck() m_apiClient.check(buildApiData()); } -void LicenseHandler::handleRemoteCheckSucceeded() +void LicenseHandler::handleLicenseVerified() { qInfo("remote license check succeeded"); const bool wasInGrace = isInGracePeriod(); - m_settings.setGraceStartEpochSecs(0); - m_settings.sync(); - m_warnedAboutGrace = false; + resetGracePeriod(); if (wasInGrace && m_pMainWindow != nullptr) { QMessageBox::information( @@ -654,9 +656,9 @@ void LicenseHandler::handleRemoteCheckSucceeded() } } -void LicenseHandler::handleRemoteCheckFailed(const QString &message) +void LicenseHandler::handleLicenseUnverified(const QString &message) { - qWarning().noquote() << "remote license check failed:" << message; + qWarning().noquote() << "license could not be verified:" << message; if (!isInGracePeriod()) { m_settings.setGraceStartEpochSecs(QDateTime::currentSecsSinceEpoch()); @@ -692,10 +694,7 @@ void LicenseHandler::handleActivationDeactivated(const QString &message) { qWarning().noquote() << "server activation held by another computer:" << message; - // The license itself is valid and the server was reachable, so the grace clock resets. - m_settings.setGraceStartEpochSecs(0); - m_settings.sync(); - m_warnedAboutGrace = false; + resetGracePeriod(); // The role is only reliably known at core start; an activation sent from anywhere else // (e.g. serial key entry) must not ask or stop anything, the next core start will. @@ -743,9 +742,8 @@ void LicenseHandler::askServerQuestion() return; } - // Decline changes nothing: the core is already stopped, and writing the mode setting here - // would desync it from the mode radios and the core process, which only the main window owns. - // Starting again simply asks again. + // Decline changes nothing; writing the mode setting here would desync it from the main + // window's mode controls and the core process, which only the main window owns. qInfo("server question declined, leaving core stopped"); } @@ -753,12 +751,9 @@ void LicenseHandler::handleCheckDeactivated(const QString &message) { qWarning().noquote() << "server activation taken over by another computer:" << message; - // The license itself is valid and the server was reachable, so the grace clock resets. - m_settings.setGraceStartEpochSecs(0); - m_settings.sync(); - m_warnedAboutGrace = false; + resetGracePeriod(); - if (m_pCoreProcess == nullptr || !m_pCoreProcess->isStarted() || m_pCoreProcess->mode() != Settings::Server) { + if (m_pCoreProcess == nullptr || !m_pCoreProcess->isStarted() || liveCoreMode() != Settings::Server) { qDebug("not running as server, ignoring deactivated check"); return; } diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index 1eee37ace..d5e273d7a 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -17,6 +17,7 @@ #pragma once +#include "common/Settings.h" #include "synergy/gui/AppTime.h" #include "synergy/gui/ExtraSettings.h" #include "synergy/gui/license/LicenseApiClient.h" @@ -92,13 +93,15 @@ class LicenseHandler : public QObject bool isOfflineActivated() const; bool check(); void runRemoteCheck(); - void handleRemoteCheckSucceeded(); - void handleRemoteCheckFailed(const QString &message); + void handleLicenseVerified(); + void handleLicenseUnverified(const QString &message); void handleActivationDeactivated(const QString &message); void handleCheckDeactivated(const QString &message); void askServerQuestion(); bool isInGracePeriod() const; bool isGracePeriodExpired() const; + void resetGracePeriod(); + Settings::CoreMode liveCoreMode() const; void disableLicenseAfterGrace(const QString &reason); synergy::gui::license::LicenseApiClient::Data buildApiData() const; From 9e981b024b67b2539f46347ad886f04e0774ad45 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 17:11:22 +0100 Subject: [PATCH 104/144] feat: update copyright years to 2026 across multiple files --- extra/src/integtests/CMakeLists.txt | 2 +- extra/src/lib/synergy/CMakeLists.txt | 2 +- extra/src/lib/synergy/gui/ActivationDialog.cpp | 2 +- extra/src/lib/synergy/gui/ActivationDialog.h | 2 +- extra/src/lib/synergy/gui/AppTime.cpp | 2 +- extra/src/lib/synergy/gui/AppTime.h | 2 +- extra/src/lib/synergy/gui/CMakeLists.txt | 2 +- extra/src/lib/synergy/gui/CancelActivationDialog.cpp | 2 +- extra/src/lib/synergy/gui/CancelActivationDialog.h | 2 +- extra/src/lib/synergy/gui/ExtraSettings.cpp | 2 +- extra/src/lib/synergy/gui/ExtraSettings.h | 2 +- extra/src/lib/synergy/gui/FeatureHandler.cpp | 2 +- extra/src/lib/synergy/gui/FeatureHandler.h | 2 +- extra/src/lib/synergy/gui/constants.h | 2 +- extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp | 2 +- extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h | 2 +- extra/src/lib/synergy/gui/license/LicenseApiClient.cpp | 2 +- extra/src/lib/synergy/gui/license/LicenseApiClient.h | 2 +- extra/src/lib/synergy/gui/license/LicenseHandler.h | 2 +- extra/src/lib/synergy/gui/license/license_notices.cpp | 2 +- extra/src/lib/synergy/gui/license/license_notices.h | 2 +- extra/src/lib/synergy/gui/license/license_utils.cpp | 2 +- extra/src/lib/synergy/gui/license/license_utils.h | 2 +- extra/src/lib/synergy/gui/styles.h | 2 +- extra/src/lib/synergy/hooks/gui_hook.h | 2 +- extra/src/lib/synergy/license/CMakeLists.txt | 2 +- extra/src/lib/synergy/license/License.cpp | 2 +- extra/src/lib/synergy/license/License.h | 2 +- extra/src/lib/synergy/license/Product.cpp | 2 +- extra/src/lib/synergy/license/Product.h | 2 +- extra/src/lib/synergy/license/SerialKey.h | 2 +- extra/src/lib/synergy/license/SerialKeyType.cpp | 2 +- extra/src/lib/synergy/license/SerialKeyType.h | 2 +- extra/src/lib/synergy/license/parse_serial_key.cpp | 2 +- extra/src/lib/synergy/license/parse_serial_key.h | 2 +- extra/src/unittests/CMakeLists.txt | 2 +- extra/src/unittests/gui/license/LicenseHandlerTests.cpp | 2 +- extra/src/unittests/gui/license/license_notices_tests.cpp | 2 +- extra/src/unittests/license/LicenseTests.cpp | 2 +- extra/src/unittests/license/ProductTests.cpp | 2 +- extra/src/unittests/license/SerialKeyTypeTests.cpp | 2 +- 41 files changed, 41 insertions(+), 41 deletions(-) diff --git a/extra/src/integtests/CMakeLists.txt b/extra/src/integtests/CMakeLists.txt index dcb66dfbf..be5c31e32 100644 --- a/extra/src/integtests/CMakeLists.txt +++ b/extra/src/integtests/CMakeLists.txt @@ -1,5 +1,5 @@ # Deskflow -- mouse and keyboard sharing utility -# Copyright (C) 2024 Synergy App Ltd +# Copyright (C) 2024 - 2026 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/CMakeLists.txt b/extra/src/lib/synergy/CMakeLists.txt index b6db05c17..ed29bec0f 100644 --- a/extra/src/lib/synergy/CMakeLists.txt +++ b/extra/src/lib/synergy/CMakeLists.txt @@ -1,5 +1,5 @@ # Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2024 Synergy App Ltd +# Copyright (C) 2024 - 2026 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/ActivationDialog.cpp b/extra/src/lib/synergy/gui/ActivationDialog.cpp index d45ec48a8..0e3a5375e 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/ActivationDialog.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/ActivationDialog.h b/extra/src/lib/synergy/gui/ActivationDialog.h index 2e1389dae..b54203cf7 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.h +++ b/extra/src/lib/synergy/gui/ActivationDialog.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/AppTime.cpp b/extra/src/lib/synergy/gui/AppTime.cpp index 7f27594ff..e15c2c16c 100644 --- a/extra/src/lib/synergy/gui/AppTime.cpp +++ b/extra/src/lib/synergy/gui/AppTime.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/AppTime.h b/extra/src/lib/synergy/gui/AppTime.h index 97aeac4a7..3bb009624 100644 --- a/extra/src/lib/synergy/gui/AppTime.h +++ b/extra/src/lib/synergy/gui/AppTime.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/CMakeLists.txt b/extra/src/lib/synergy/gui/CMakeLists.txt index 9b6518254..cdfeca2a6 100644 --- a/extra/src/lib/synergy/gui/CMakeLists.txt +++ b/extra/src/lib/synergy/gui/CMakeLists.txt @@ -1,5 +1,5 @@ # Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2024 Synergy App Ltd +# Copyright (C) 2024 - 2026 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.cpp b/extra/src/lib/synergy/gui/CancelActivationDialog.cpp index 21e217c9f..3155b14cd 100644 --- a/extra/src/lib/synergy/gui/CancelActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.h b/extra/src/lib/synergy/gui/CancelActivationDialog.h index 05362214a..8484de2d1 100644 --- a/extra/src/lib/synergy/gui/CancelActivationDialog.h +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.h @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/ExtraSettings.cpp b/extra/src/lib/synergy/gui/ExtraSettings.cpp index 8369834e9..de7955568 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.cpp +++ b/extra/src/lib/synergy/gui/ExtraSettings.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/ExtraSettings.h b/extra/src/lib/synergy/gui/ExtraSettings.h index c840ed1bd..c4c4517e6 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.h +++ b/extra/src/lib/synergy/gui/ExtraSettings.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 491ec687e..c257cb1aa 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2025 Synergy App Ltd + * Copyright (C) 2025 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index 48e901b67..fc6c4642e 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2025 Synergy App Ltd + * Copyright (C) 2025 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 3e138fe0e..1e4d96761 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp index dad7fbd14..3fedfbde6 100644 --- a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp +++ b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2022 Synergy App Ltd + * Copyright (C) 2022 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h index 77d6ba7e6..da650ad81 100644 --- a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h +++ b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2022 Synergy App Ltd + * Copyright (C) 2022 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index b8cc3000b..577d61921 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022-2026 Synergy Ltd. + * Copyright (C) 2022 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h index 2e8dcba3f..71a6ceaa5 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -1,6 +1,6 @@ /* * synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022-2026 Synergy Ltd. + * Copyright (C) 2022 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index d5e273d7a..9dd9c88c0 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy App Ltd + * Copyright (C) 2015 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/license_notices.cpp b/extra/src/lib/synergy/gui/license/license_notices.cpp index 635ee8701..ce7ac3fa2 100644 --- a/extra/src/lib/synergy/gui/license/license_notices.cpp +++ b/extra/src/lib/synergy/gui/license/license_notices.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/license_notices.h b/extra/src/lib/synergy/gui/license/license_notices.h index 72c0f98a7..a5a7e7423 100644 --- a/extra/src/lib/synergy/gui/license/license_notices.h +++ b/extra/src/lib/synergy/gui/license/license_notices.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/license_utils.cpp b/extra/src/lib/synergy/gui/license/license_utils.cpp index 2d5ece151..41a01ce92 100644 --- a/extra/src/lib/synergy/gui/license/license_utils.cpp +++ b/extra/src/lib/synergy/gui/license/license_utils.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/license/license_utils.h b/extra/src/lib/synergy/gui/license/license_utils.h index 448724a1c..ac4e16d58 100644 --- a/extra/src/lib/synergy/gui/license/license_utils.h +++ b/extra/src/lib/synergy/gui/license/license_utils.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/gui/styles.h b/extra/src/lib/synergy/gui/styles.h index 44963e5be..00d4e9ade 100644 --- a/extra/src/lib/synergy/gui/styles.h +++ b/extra/src/lib/synergy/gui/styles.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/hooks/gui_hook.h b/extra/src/lib/synergy/hooks/gui_hook.h index a49a58344..f7668df81 100644 --- a/extra/src/lib/synergy/hooks/gui_hook.h +++ b/extra/src/lib/synergy/hooks/gui_hook.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 - 2025 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/CMakeLists.txt b/extra/src/lib/synergy/license/CMakeLists.txt index 8bc210e5b..3ff4af3b4 100644 --- a/extra/src/lib/synergy/license/CMakeLists.txt +++ b/extra/src/lib/synergy/license/CMakeLists.txt @@ -1,5 +1,5 @@ # Synergy -- mouse and keyboard sharing utility -# Copyright (C) 2024 Synergy App Ltd +# Copyright (C) 2024 - 2026 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/License.cpp b/extra/src/lib/synergy/license/License.cpp index 31fdac70f..834d57487 100644 --- a/extra/src/lib/synergy/license/License.cpp +++ b/extra/src/lib/synergy/license/License.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/License.h b/extra/src/lib/synergy/license/License.h index e1a34352d..473c85fc7 100644 --- a/extra/src/lib/synergy/license/License.h +++ b/extra/src/lib/synergy/license/License.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/Product.cpp b/extra/src/lib/synergy/license/Product.cpp index efbb9b8f3..316471b69 100644 --- a/extra/src/lib/synergy/license/Product.cpp +++ b/extra/src/lib/synergy/license/Product.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/Product.h b/extra/src/lib/synergy/license/Product.h index cabf0c045..a8a9d552b 100644 --- a/extra/src/lib/synergy/license/Product.h +++ b/extra/src/lib/synergy/license/Product.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/SerialKey.h b/extra/src/lib/synergy/license/SerialKey.h index 9fbc06218..bf9de5719 100644 --- a/extra/src/lib/synergy/license/SerialKey.h +++ b/extra/src/lib/synergy/license/SerialKey.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2022 Synergy App Ltd + * Copyright (C) 2022 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/SerialKeyType.cpp b/extra/src/lib/synergy/license/SerialKeyType.cpp index e819b5adb..9d7331596 100644 --- a/extra/src/lib/synergy/license/SerialKeyType.cpp +++ b/extra/src/lib/synergy/license/SerialKeyType.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2015 Synergy App Ltd + * Copyright (C) 2015 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/SerialKeyType.h b/extra/src/lib/synergy/license/SerialKeyType.h index 78b538b24..8927217ca 100644 --- a/extra/src/lib/synergy/license/SerialKeyType.h +++ b/extra/src/lib/synergy/license/SerialKeyType.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/parse_serial_key.cpp b/extra/src/lib/synergy/license/parse_serial_key.cpp index f3ab37195..ac763f8d2 100644 --- a/extra/src/lib/synergy/license/parse_serial_key.cpp +++ b/extra/src/lib/synergy/license/parse_serial_key.cpp @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/lib/synergy/license/parse_serial_key.h b/extra/src/lib/synergy/license/parse_serial_key.h index 971df756f..bbddd46de 100644 --- a/extra/src/lib/synergy/license/parse_serial_key.h +++ b/extra/src/lib/synergy/license/parse_serial_key.h @@ -1,6 +1,6 @@ /* * Synergy -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/CMakeLists.txt b/extra/src/unittests/CMakeLists.txt index a96e9a5a6..e8e872979 100644 --- a/extra/src/unittests/CMakeLists.txt +++ b/extra/src/unittests/CMakeLists.txt @@ -1,5 +1,5 @@ # Deskflow -- mouse and keyboard sharing utility -# Copyright (C) 2024 Synergy App Ltd +# Copyright (C) 2024 - 2026 Synergy App Ltd # # This package is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/gui/license/LicenseHandlerTests.cpp b/extra/src/unittests/gui/license/LicenseHandlerTests.cpp index 9f4853880..202efe96e 100644 --- a/extra/src/unittests/gui/license/LicenseHandlerTests.cpp +++ b/extra/src/unittests/gui/license/LicenseHandlerTests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/gui/license/license_notices_tests.cpp b/extra/src/unittests/gui/license/license_notices_tests.cpp index 92d8a480e..14043ed16 100644 --- a/extra/src/unittests/gui/license/license_notices_tests.cpp +++ b/extra/src/unittests/gui/license/license_notices_tests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Synergy App Ltd + * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/license/LicenseTests.cpp b/extra/src/unittests/license/LicenseTests.cpp index f5a523aea..c12f5a9f1 100644 --- a/extra/src/unittests/license/LicenseTests.cpp +++ b/extra/src/unittests/license/LicenseTests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/license/ProductTests.cpp b/extra/src/unittests/license/ProductTests.cpp index 81f27d370..e53583750 100644 --- a/extra/src/unittests/license/ProductTests.cpp +++ b/extra/src/unittests/license/ProductTests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/extra/src/unittests/license/SerialKeyTypeTests.cpp b/extra/src/unittests/license/SerialKeyTypeTests.cpp index db90b8492..ba9e7de9b 100644 --- a/extra/src/unittests/license/SerialKeyTypeTests.cpp +++ b/extra/src/unittests/license/SerialKeyTypeTests.cpp @@ -1,6 +1,6 @@ /* * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Synergy App Ltd + * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License From a2f3d7ae05b4302fb4331f96a27e2680d1896dce Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 17:34:59 +0100 Subject: [PATCH 105/144] feat: update URL parameters for tracking in activation dialogs and constants --- extra/src/lib/synergy/gui/ActivationDialog.ui | 2 +- extra/src/lib/synergy/gui/CancelActivationDialog.ui | 2 +- extra/src/lib/synergy/gui/constants.h | 2 +- extra/src/lib/synergy/gui/license/LicenseHandler.cpp | 6 ++++-- src/lib/common/UrlConstants.h | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/extra/src/lib/synergy/gui/ActivationDialog.ui b/extra/src/lib/synergy/gui/ActivationDialog.ui index 909b9ab7b..4074f59ee 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.ui +++ b/extra/src/lib/synergy/gui/ActivationDialog.ui @@ -29,7 +29,7 @@ - <p>Your serial key is on your <a href="https://synergyapp.io/account?source=gui" style="color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://synergyapp.io/contact?source=gui" style="color:#4285f4;">Contact us</span></a></p> + <p>Your serial key is on your <a href="https://synergyapp.io/account?utm_source=gui-s1" style="color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://synergyapp.io/contact?utm_source=gui-s1" style="color:#4285f4;">Contact us</span></a></p> true diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.ui b/extra/src/lib/synergy/gui/CancelActivationDialog.ui index 7640407ea..15cc6878f 100644 --- a/extra/src/lib/synergy/gui/CancelActivationDialog.ui +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.ui @@ -17,7 +17,7 @@ - <html><head/><body><p>You'll need to purchase a license to use Synergy.</p><p><a href="https://synergyapp.io/contact?source=gui"><span style=" text-decoration: underline; color:#007af4;">Contact us</span></a></p><p>The application will now exit.</p></body></html> + <html><head/><body><p>You'll need to purchase a license to use Synergy.</p><p><a href="https://synergyapp.io/contact?utm_source=gui-s1"><span style=" text-decoration: underline; color:#007af4;">Contact us</span></a></p><p>The application will now exit.</p></body></html> true diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 1e4d96761..40a2fbab1 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -25,7 +25,7 @@ namespace synergy::gui { const auto kUrlApi = "https://symless.com/synergy/api"; const auto kUrlWebsite = QStringLiteral("https://synergyapp.io"); -const auto kUrlSourceQuery = "source=gui"; +const auto kUrlSourceQuery = "utm_source=gui-s1"; const auto kUrlGpl = QStringLiteral("https://www.gnu.org/licenses/old-licenses/gpl-2.0.html"); const auto kUrlEula = QString("%1/eula").arg(kUrlWebsite); diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index f131c5e35..8d981f978 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -723,13 +723,15 @@ void LicenseHandler::askServerQuestion() question = tr("

All of the server activations for your team's license are currently in use.

" "

If you need to add more seats to your team's license, please " R"(contact us today.

)" - "

Do you want to reassign a server activation to this computer?

") + "

Do you want to reassign a server activation to this computer?

" + "

The activation will be taken away from another computer on this license.

") .arg(kUrlContact); } else { question = tr("

Another computer is currently the server for your license.

" "

If you need more than one server running at the same time, please " R"(contact us today.

)" - "

Do you want to reassign the server activation to this computer?

") + "

Do you want to reassign the server activation to this computer?

" + "

The activation will be taken away from the other computer.

") .arg(kUrlContact); } const auto reply = QMessageBox::question(m_pMainWindow, "License limit reached", question); diff --git a/src/lib/common/UrlConstants.h b/src/lib/common/UrlConstants.h index 986c23ffb..7dc6d011c 100644 --- a/src/lib/common/UrlConstants.h +++ b/src/lib/common/UrlConstants.h @@ -15,7 +15,7 @@ // notation (rdn), e.g. org.deskflow const auto kOrgDomain = QString::fromUtf8(kAppDomain); -const auto kUrlSourceQuery = QStringLiteral("source=gui"); +const auto kUrlSourceQuery = QStringLiteral("utm_source=gui-s1"); const auto kUrlApp = QStringLiteral("https://%1").arg(kOrgDomain); const auto kUrlHelp = QStringLiteral("%1/help?%2").arg(kUrlApp, kUrlSourceQuery); const auto kUrlDownload = QStringLiteral("%1/download?%2").arg(kUrlApp, kUrlSourceQuery); From 0238bbe6cd9477b88da34ff4b9480b5473054f84 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 11 Jun 2026 20:43:25 +0100 Subject: [PATCH 106/144] feat: update license test assertions to reflect new product naming conventions --- extra/src/unittests/license/LicenseTests.cpp | 6 +++--- extra/src/unittests/license/ProductTests.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extra/src/unittests/license/LicenseTests.cpp b/extra/src/unittests/license/LicenseTests.cpp index c12f5a9f1..c4948712a 100644 --- a/extra/src/unittests/license/LicenseTests.cpp +++ b/extra/src/unittests/license/LicenseTests.cpp @@ -139,7 +139,7 @@ TEST_F(LicenseTests, daysLeft_validExactlyOneDayV2TrialBasicSerial_returnOne) "F6D70616E79206E616D653B303B38363430307D"); setNow(license, 0); - EXPECT_EQ(1, license.secondsLeft().count()); + EXPECT_EQ(1, license.daysLeft().count()); } TEST_F(LicenseTests, daysLeft_validWithinOneDayV2TrialBasicSerial_returnOne) @@ -149,7 +149,7 @@ TEST_F(LicenseTests, daysLeft_validWithinOneDayV2TrialBasicSerial_returnOne) "F6D70616E79206E616D653B303B38363430307D"); setNow(license, 0); - EXPECT_EQ(1, license.secondsLeft().count()); + EXPECT_EQ(1, license.daysLeft().count()); } TEST_F(LicenseTests, daysLeft_expiredV2TrialBasicSerial_returnZero) @@ -159,7 +159,7 @@ TEST_F(LicenseTests, daysLeft_expiredV2TrialBasicSerial_returnZero) "F6D70616E79206E616D653B303B38363430307D"); setNow(license, 86401); - EXPECT_EQ(0, license.secondsLeft().count()); + EXPECT_EQ(0, license.daysLeft().count()); } // Subscription license tests diff --git a/extra/src/unittests/license/ProductTests.cpp b/extra/src/unittests/license/ProductTests.cpp index e53583750..6d38d142e 100644 --- a/extra/src/unittests/license/ProductTests.cpp +++ b/extra/src/unittests/license/ProductTests.cpp @@ -63,7 +63,7 @@ TEST(ProductTests, setEdition_pro_isValid) EXPECT_EQ(kPro, product.edition()); EXPECT_EQ(Product::SerialKeyEditionID::Pro, product.serialKeyId()); - EXPECT_EQ("Deskflow Pro", product.name()); + EXPECT_EQ("Synergy 1 Pro", product.name()); EXPECT_TRUE(product.isValid()); } @@ -75,7 +75,7 @@ TEST(ProductTests, setEdition_basic_isValid) EXPECT_EQ(kBasic, product.edition()); EXPECT_EQ(Product::SerialKeyEditionID::Basic, product.serialKeyId()); - EXPECT_EQ("Deskflow Basic", product.name()); + EXPECT_EQ("Synergy 1 Basic", product.name()); } TEST(ProductTests, setEdition_business_isValid) @@ -86,5 +86,5 @@ TEST(ProductTests, setEdition_business_isValid) EXPECT_EQ(kBusiness, product.edition()); EXPECT_EQ(Product::SerialKeyEditionID::Business, product.serialKeyId()); - EXPECT_EQ("Deskflow Business", product.name()); + EXPECT_EQ("Synergy 1 Business", product.name()); } From b5b9df4c886c444a5fecf4f3327174364e9722cb Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 12 Jun 2026 09:31:06 +0100 Subject: [PATCH 107/144] feat: update copyright information to reflect current ownership and years --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 28 +++++++++----------- src/lib/common/Constants.h.in | 1 - 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index c257cb1aa..3fee0cecd 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -37,11 +37,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -169,12 +169,13 @@ void FeatureHandler::setAttribution(QDialog *parent) const color.setAlphaF(0.6); palette.setColor(QPalette::WindowText, color); description->setPalette(palette); - description->setText(QObject::tr( - "Synergy was originally created by Chris Schoeneman and today builds on Deskflow, " - "the open-source upstream project maintained by Nick Bolton and Chris Rizzitello. " - "Thanks to the Deskflow developers, all our contributors, and the wider open-source " - "community." - )); + description->setText( + QObject::tr( + "Synergy was originally created by Chris Schoeneman. Thanks to our contributors " + "and the open source projects Synergy builds on, including Deskflow, Qt, OpenSSL, " + "and many others." + ) + ); } void FeatureHandler::tightenVersionRow(QDialog *parent) const @@ -189,9 +190,9 @@ void FeatureHandler::addLicenseLinks(QDialog *parent) const { // Upstream shows only the GPL link; add the EULA alongside it. if (auto *linkGpl = parent->findChild(QStringLiteral("linkGpl"))) { - const auto gpl = QString(kLink).arg(kUrlGpl, kColorSecondary, QObject::tr("License: GNU GPL Version 2")); const auto eula = QString(kLink).arg(kUrlEula, kColorSecondary, QObject::tr("End User License Agreement")); - linkGpl->setText(QStringLiteral("%1  |  %2").arg(gpl, eula)); + const auto gpl = QString(kLink).arg(kUrlGpl, kColorSecondary, QObject::tr("License: GNU GPL Version 2")); + linkGpl->setText(QStringLiteral("%1  |  %2").arg(eula, gpl)); } } @@ -199,8 +200,7 @@ void FeatureHandler::addTrademark(QDialog *parent) const { if (auto *copyright = parent->findChild(QStringLiteral("lblCopyright"))) { copyright->setText( - copyright->text() + QStringLiteral("\n") + - QObject::tr("The Synergy logo is a trademark of Synergy App Ltd.") + copyright->text() + QStringLiteral("\n") + QObject::tr("The Synergy logo is a trademark of Synergy App Ltd.") ); } } @@ -210,8 +210,7 @@ static QString pathLabel(const QString &path) if (QFileInfo::exists(path)) { return QStringLiteral("%1").arg(path); } - return QStringLiteral("%1 %2") - .arg(path, QObject::tr("(not yet created)")); + return QStringLiteral("%1 %2").arg(path, QObject::tr("(not yet created)")); } void FeatureHandler::addScopeTab(QDialog *parent) const @@ -259,8 +258,7 @@ void FeatureHandler::addScopeTab(QDialog *parent) const }; auto *userRadio = buildOption( - QObject::tr("Current user"), - QObject::tr("Settings apply only to your user account on this computer."), + QObject::tr("Current user"), QObject::tr("Settings apply only to your user account on this computer."), Settings::UserSettingFile ); auto *systemRadio = buildOption( diff --git a/src/lib/common/Constants.h.in b/src/lib/common/Constants.h.in index 1f587794f..4bf449406 100644 --- a/src/lib/common/Constants.h.in +++ b/src/lib/common/Constants.h.in @@ -18,7 +18,6 @@ const auto kRevFqdnName = "@CMAKE_PROJECT_REV_FQDN@"; const auto kCopyright = // "Copyright @CMAKE_PROJECT_COPYRIGHT@\n" - "Copyright (C) 2024-2026 Deskflow Developers\n" "Copyright (C) 2009-2012 Nick Bolton\n" "Copyright (C) 2002-2009 Chris Schoeneman"; From a89481744bfb50c90fd299a85276dda537bd8dc8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 12 Jun 2026 09:31:59 +0100 Subject: [PATCH 108/144] feat: update trademark notice for consistency in FeatureHandler --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 3fee0cecd..38b752ad1 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -200,7 +200,7 @@ void FeatureHandler::addTrademark(QDialog *parent) const { if (auto *copyright = parent->findChild(QStringLiteral("lblCopyright"))) { copyright->setText( - copyright->text() + QStringLiteral("\n") + QObject::tr("The Synergy logo is a trademark of Synergy App Ltd.") + copyright->text() + QStringLiteral("\n") + QObject::tr("The Synergy logo is a trademark of Synergy App Ltd") ); } } From fdd4779dece9e80eb95b733b22be28d3673cc69f Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 12 Jun 2026 14:16:25 +0100 Subject: [PATCH 109/144] feat: enhance ActivationDialog UI and improve license handling for personal licenses --- .../src/lib/synergy/gui/ActivationDialog.cpp | 2 ++ extra/src/lib/synergy/gui/ActivationDialog.ui | 20 +++++-------------- .../synergy/gui/license/LicenseHandler.cpp | 19 ++++++++++++++++++ 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/extra/src/lib/synergy/gui/ActivationDialog.cpp b/extra/src/lib/synergy/gui/ActivationDialog.cpp index 0e3a5375e..ab157feff 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/ActivationDialog.cpp @@ -28,6 +28,7 @@ #include "ui_ActivationDialog.h" #include +#include #include #include #include @@ -49,6 +50,7 @@ ActivationDialog::ActivationDialog(QWidget *parent, LicenseHandler &licenseHandl m_ui->setupUi(this); m_ui->m_pLabelNotice->setStyleSheet(kStyleNoticeLabel); + m_ui->m_pTextEditSerialKey->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); refreshSerialKey(); } diff --git a/extra/src/lib/synergy/gui/ActivationDialog.ui b/extra/src/lib/synergy/gui/ActivationDialog.ui index 4074f59ee..2a2f443f3 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.ui +++ b/extra/src/lib/synergy/gui/ActivationDialog.ui @@ -7,7 +7,7 @@ 0 0 541 - 241 + 330
@@ -29,7 +29,10 @@ - <p>Your serial key is on your <a href="https://synergyapp.io/account?utm_source=gui-s1" style="color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://synergyapp.io/contact?utm_source=gui-s1" style="color:#4285f4;">Contact us</span></a></p> + <p>Your serial key is on your <a href="https://synergyapp.io/account?utm_source=gui-s1" style="color:#4285f4;">account</a> page.<br/>Don't have a license? <a href="https://synergyapp.io/contact?utm_source=gui-s1" style="color:#4285f4;">Contact us</a> or <a href="https://synergyapp.io/purchase?utm_source=gui-s1" style="color:#4285f4;">purchase</a> today.</p><p>Using Synergy on an offline or air-gapped network?<br/>Offline serial keys are <a href="https://synergyapp.io/contact?utm_source=gui-s1" style="color:#4285f4;">available on request</a>.</p> + + + true true @@ -38,22 +41,9 @@ - - true - true - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> -p, li { white-space: pre-wrap; } -hr { height: 1px; border-width: 0; } -li.unchecked::marker { content: "\2610"; } -li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style="-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;"><br /></p></body></html> - false diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 8d981f978..083605feb 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -285,6 +285,12 @@ bool LicenseHandler::handleCoreStart() // The role is only reliably known at core start; start optimistically, a deactivated verdict stops the core. if (m_settings.activated() && !m_license.serialKey().isOffline) { + // A personal license activates once and stays activated; refreshing here would be the + // kind of post-activation phone-home that breaks offline networks. + if (m_license.productEdition() != Product::Edition::kBusiness) { + qDebug("license is activated, starting core for personal license"); + return true; + } if (m_apiClient.isBusy()) { qInfo("license api busy, starting core without refreshing activation"); return true; @@ -633,6 +639,19 @@ void LicenseHandler::runRemoteCheck() return; } + // Personal licenses activate once and are never re-validated, so they keep working on + // offline and restricted networks. Only business licenses are re-checked. + if (m_license.productEdition() != Product::Edition::kBusiness) { + if (isInGracePeriod()) { + // Nothing else clears grace once personal checks stop, and a stale grace flag + // suppresses the renew nag forever. + qInfo("clearing stale grace period for personal license"); + resetGracePeriod(); + } + qDebug("personal license, skipping remote check"); + return; + } + if (m_apiClient.isBusy()) { qDebug("license activator busy, skipping remote check"); return; From 2b7cb3f9ba61ba74a80fa45675ab708db7d59aa0 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 12 Jun 2026 16:06:02 +0100 Subject: [PATCH 110/144] feat: update SSL signing credentials and disable malware block for signing process --- .github/workflows/ci.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5438cfd63..96f9d5360 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,15 +176,18 @@ jobs: uses: SSLcom/esigner-codesign@develop with: command: batch_sign - username: ${{ secrets.SSL_USERNAME }} - password: ${{ secrets.SSL_PASSWORD }} - credential_id: ${{ secrets.SSL_CREDENTIAL_ID }} - totp_secret: ${{ secrets.SSL_TOTP_SECRET }} + username: ${{ secrets.WINDOWS_SSL_USERNAME }} + password: ${{ secrets.WINDOWS_SSL_PASSWORD }} + credential_id: ${{ secrets.WINDOWS_SSL_CREDENTIAL_ID }} + totp_secret: ${{ secrets.WINDOWS_SSL_TOTP_SECRET }} dir_path: ${{ runner.temp }}\sign-stage\in output_path: ${{ runner.temp }}\sign-stage\out - malware_block: "true" environment_name: PROD + # The pre-sign malware scan draws on a separate, much smaller quota than signing and + # exhausts it across a full release; signing produces a valid signature without it. + malware_block: "false" + - name: Restore + verify signed binaries if: env.SYNERGY_VERSION_RELEASE == 'true' shell: pwsh @@ -215,13 +218,15 @@ jobs: uses: SSLcom/esigner-codesign@develop with: command: sign - username: ${{ secrets.SSL_USERNAME }} - password: ${{ secrets.SSL_PASSWORD }} - credential_id: ${{ secrets.SSL_CREDENTIAL_ID }} - totp_secret: ${{ secrets.SSL_TOTP_SECRET }} + username: ${{ secrets.WINDOWS_SSL_USERNAME }} + password: ${{ secrets.WINDOWS_SSL_PASSWORD }} + credential_id: ${{ secrets.WINDOWS_SSL_CREDENTIAL_ID }} + totp_secret: ${{ secrets.WINDOWS_SSL_TOTP_SECRET }} file_path: ${{ steps.locate-msi.outputs.path }} override: "true" - malware_block: "true" + # The pre-sign malware scan draws on a separate, much smaller quota than signing and + # exhausts it across a full payload; signing produces a valid signature without it. + malware_block: "false" environment_name: PROD - name: Verify MSI is signed From 7e500f516253925e6c63438578bfc972091fdbfe Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 12 Jun 2026 16:58:46 +0100 Subject: [PATCH 111/144] feat: add build date display to About dialog in FeatureHandler --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 19 +++++++++++++++++++ extra/src/lib/synergy/gui/FeatureHandler.h | 1 + 2 files changed, 20 insertions(+) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 38b752ad1..98a6726a9 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -131,6 +133,7 @@ void FeatureHandler::handleAbout(QDialog *parent) const addTagline(parent); setAttribution(parent); tightenVersionRow(parent); + addBuildDate(parent); addLicenseLinks(parent); addTrademark(parent); } @@ -186,6 +189,22 @@ void FeatureHandler::tightenVersionRow(QDialog *parent) const } } +void FeatureHandler::addBuildDate(QDialog *parent) const +{ + auto *version = parent->findChild(QStringLiteral("lblVersion")); + if (version == nullptr) { + return; + } + + // Compile date of this file; close enough to the build date since release + // builds always compile from scratch in CI. + const auto date = QLocale::c().toDate(QStringLiteral(__DATE__).simplified(), QStringLiteral("MMM d yyyy")); + if (!date.isValid()) { + return; + } + version->setText(QStringLiteral("%1, built %2").arg(version->text(), date.toString(Qt::ISODate))); +} + void FeatureHandler::addLicenseLinks(QDialog *parent) const { // Upstream shows only the GPL link; add the EULA alongside it. diff --git a/extra/src/lib/synergy/gui/FeatureHandler.h b/extra/src/lib/synergy/gui/FeatureHandler.h index fc6c4642e..3c6059d53 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.h +++ b/extra/src/lib/synergy/gui/FeatureHandler.h @@ -42,6 +42,7 @@ class FeatureHandler void addTagline(QDialog *parent) const; void setAttribution(QDialog *parent) const; void tightenVersionRow(QDialog *parent) const; + void addBuildDate(QDialog *parent) const; void addLicenseLinks(QDialog *parent) const; void addTrademark(QDialog *parent) const; From 9599ea699e2637e53823628eccf6d12aca83628d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 15 Jun 2026 18:19:02 +0100 Subject: [PATCH 112/144] feat: refactor license handling and add remote check functionality --- extra/src/lib/synergy/gui/ExtraSettings.cpp | 33 +++++- extra/src/lib/synergy/gui/ExtraSettings.h | 10 ++ extra/src/lib/synergy/gui/constants.h | 1 + .../synergy/gui/license/LicenseApiClient.cpp | 86 ++++++++++---- .../synergy/gui/license/LicenseApiClient.h | 47 ++++++-- .../synergy/gui/license/LicenseHandler.cpp | 111 ++++++++++++++---- .../lib/synergy/gui/license/LicenseHandler.h | 8 +- src/lib/gui/MainWindow.cpp | 6 + 8 files changed, 238 insertions(+), 64 deletions(-) diff --git a/extra/src/lib/synergy/gui/ExtraSettings.cpp b/extra/src/lib/synergy/gui/ExtraSettings.cpp index de7955568..6c9977478 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.cpp +++ b/extra/src/lib/synergy/gui/ExtraSettings.cpp @@ -37,20 +37,33 @@ QString settingsFile() return QStringLiteral("%1/%2.extra.conf").arg(Settings::UserDir, kAppName); } +const auto kLicenseGroup = QStringLiteral("license"); + const auto kSerialKey = QStringLiteral("serialKey"); const auto kActivated = QStringLiteral("activated"); +const auto kHoldsServerActivation = QStringLiteral("holdsServerActivation"); const auto kGraceStart = QStringLiteral("graceStartEpochSecs"); const auto kOfflineActivationResponse = QStringLiteral("offlineActivationResponse"); +const auto kLegacyActivationState = QStringLiteral("activationState"); } // namespace void ExtraSettings::load() { QSettings ini(settingsFile(), QSettings::IniFormat); - m_serialKey = ini.value(kSerialKey).toString(); - m_activated = ini.value(kActivated).toBool(); - m_graceStartEpochSecs = ini.value(kGraceStart).toLongLong(); - m_offlineActivationResponse = ini.value(kOfflineActivationResponse).toString(); + + // License state moved from the top level into the license group; fall back to the old flat key + // so existing installs keep their serial key until the next sync rewrites it under the group. + const auto value = [&ini](const QString &key) { + const auto grouped = QStringLiteral("%1/%2").arg(kLicenseGroup, key); + return ini.contains(grouped) ? ini.value(grouped) : ini.value(key); + }; + + m_serialKey = value(kSerialKey).toString(); + m_activated = value(kActivated).toBool(); + m_holdsServerActivation = value(kHoldsServerActivation).toBool(); + m_graceStartEpochSecs = value(kGraceStart).toLongLong(); + m_offlineActivationResponse = value(kOfflineActivationResponse).toString(); } void ExtraSettings::sync() @@ -60,10 +73,22 @@ void ExtraSettings::sync() qCritical() << "unable to save synergy settings, file not writable:" << ini.fileName(); return; } + + // Drop the old top-level keys (license state now lives under the license group) and the dead + // activationState left over from the enum. + for (const auto &key : + {kSerialKey, kActivated, kHoldsServerActivation, kGraceStart, kOfflineActivationResponse, + kLegacyActivationState}) { + ini.remove(key); + } + + ini.beginGroup(kLicenseGroup); ini.setValue(kSerialKey, m_serialKey); ini.setValue(kActivated, m_activated); + ini.setValue(kHoldsServerActivation, m_holdsServerActivation); ini.setValue(kGraceStart, m_graceStartEpochSecs); ini.setValue(kOfflineActivationResponse, m_offlineActivationResponse); + ini.endGroup(); ini.sync(); } diff --git a/extra/src/lib/synergy/gui/ExtraSettings.h b/extra/src/lib/synergy/gui/ExtraSettings.h index c4c4517e6..b7079fef2 100644 --- a/extra/src/lib/synergy/gui/ExtraSettings.h +++ b/extra/src/lib/synergy/gui/ExtraSettings.h @@ -49,6 +49,15 @@ class ExtraSettings m_activated = activated; } + bool holdsServerActivation() const + { + return m_holdsServerActivation; + } + void setHoldsServerActivation(bool holdsServerActivation) + { + m_holdsServerActivation = holdsServerActivation; + } + qint64 graceStartEpochSecs() const { return m_graceStartEpochSecs; @@ -73,6 +82,7 @@ class ExtraSettings private: QString m_serialKey; bool m_activated = false; + bool m_holdsServerActivation = false; qint64 m_graceStartEpochSecs = 0; QString m_offlineActivationResponse; }; diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 40a2fbab1..0445fc163 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -43,5 +43,6 @@ const auto kUrlApiLicenseActivate = QString("%1/product/activate").arg(kUrlApi); const auto kUrlApiLicenseCheck = QString("%1/product/check").arg(kUrlApi); constexpr auto kLicenseGracePeriod = std::chrono::days{14}; +constexpr auto kRemoteCheckInterval = std::chrono::hours{24}; } // namespace synergy::gui diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index 577d61921..bc58584ed 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -26,6 +26,10 @@ namespace synergy::gui::license { +constexpr auto kPropRequestKind = "requestKind"; +constexpr auto kPropActivationIntent = "activationIntent"; +constexpr auto kPropCheckIntent = "checkIntent"; + QString activateUrl() { const auto testUrl = TestSettings::instance().apiUrlActivate(); @@ -47,66 +51,85 @@ LicenseApiClient::LicenseApiClient() connect(&m_manager, &QNetworkAccessManager::finished, this, &LicenseApiClient::handleResponse); } -void LicenseApiClient::activate(Data data, bool takeover) +void LicenseApiClient::activate(Data data, ActivationIntent intent, bool takeover) { - post(RequestKind::kActivate, QUrl(activateUrl()), data, takeover); + post(RequestKind::kActivate, QUrl(activateUrl()), data, intent, CheckIntent::kPoll, takeover); } -void LicenseApiClient::check(Data data) +void LicenseApiClient::check(Data data, CheckIntent intent) { - post(RequestKind::kCheck, QUrl(checkUrl()), data); + post(RequestKind::kCheck, QUrl(checkUrl()), data, ActivationIntent::kCoreStart, intent); } -void LicenseApiClient::post(RequestKind kind, const QUrl &url, const Data &data, std::optional takeover) +void LicenseApiClient::validate(Data data) +{ + post(RequestKind::kValidate, QUrl(checkUrl()), data); +} + +void LicenseApiClient::post( + RequestKind kind, const QUrl &url, const Data &data, ActivationIntent intent, CheckIntent checkIntent, + std::optional takeover +) { m_isBusy = true; - m_pendingKind = kind; qDebug().noquote() << "license api request:" << url.toString(); auto request = QNetworkRequest(url); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - m_manager.post(request, getRequestData(data, takeover)); + auto *reply = m_manager.post(request, getRequestData(data, takeover)); + reply->setProperty(kPropRequestKind, static_cast(kind)); + reply->setProperty(kPropActivationIntent, static_cast(intent)); + reply->setProperty(kPropCheckIntent, static_cast(checkIntent)); } void LicenseApiClient::handleResponse(QNetworkReply *reply) { m_isBusy = false; - const auto kind = m_pendingKind; - const auto emitFailed = [this, kind](const QString &message) { + if (!reply) { + qWarning("no license api reply"); + return; + } + + const auto kind = static_cast(reply->property(kPropRequestKind).toInt()); + const auto intent = static_cast(reply->property(kPropActivationIntent).toInt()); + const auto checkIntent = static_cast(reply->property(kPropCheckIntent).toInt()); + + const auto emitFailed = [this, kind, intent](const QString &message) { if (kind == RequestKind::kActivate) { - Q_EMIT activationFailed(message); + Q_EMIT activationFailed(intent, message); + } else if (kind == RequestKind::kValidate) { + Q_EMIT validateFailed(message); } else { Q_EMIT checkFailed(message); } }; - const auto emitSucceeded = [this, kind] { + const auto emitSucceeded = [this, kind, intent] { if (kind == RequestKind::kActivate) { - Q_EMIT activationSucceeded(); + Q_EMIT activationSucceeded(intent); + } else if (kind == RequestKind::kValidate) { + Q_EMIT validateSucceeded(); } else { Q_EMIT checkSucceeded(); } }; // A transport failure is not a license verdict; activation tolerates it and retries - // later, while the check path owns the grace period. - const auto emitUnreachable = [this, kind](const QString &message) { + // later, while the check path owns the grace period. Key entry can't validate offline, + // so it proceeds and lets the runtime check catch a bad key later. + const auto emitUnreachable = [this, kind, intent](const QString &message) { if (kind == RequestKind::kActivate) { - Q_EMIT activationUnreachable(); + Q_EMIT activationUnreachable(intent); + } else if (kind == RequestKind::kValidate) { + Q_EMIT validateSucceeded(); } else { Q_EMIT checkFailed(message); } }; - if (!reply) { - qWarning("no license api reply"); - emitUnreachable("License request failed, empty network reply."); - return; - } - const auto response = reply->readAll(); if (reply->error() != QNetworkReply::NoError) { @@ -136,14 +159,31 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) if (status == "deactivated") { qWarning("license api found this machine deactivated"); if (kind == RequestKind::kActivate) { - Q_EMIT activationDeactivated(message); + Q_EMIT activationDeactivated(intent, message); + } else if (kind == RequestKind::kValidate) { + Q_EMIT validateDeactivated(message); } else { - Q_EMIT checkDeactivated(message); + Q_EMIT checkDeactivated(checkIntent, message); } reply->deleteLater(); return; } + // The license is valid but this machine has no activation row. At key entry that is + // expected (the row appears once the role is known); at runtime the app must (re)claim it. + if (status == "notActivated") { + if (kind == RequestKind::kValidate) { + Q_EMIT validateSucceeded(); + reply->deleteLater(); + return; + } + if (kind == RequestKind::kCheck) { + Q_EMIT checkNotActivated(); + reply->deleteLater(); + return; + } + } + if (!status.isEmpty()) { qWarning().noquote() << "license api status:" << status; } else { diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.h b/extra/src/lib/synergy/gui/license/LicenseApiClient.h index 71a6ceaa5..4d0fdd645 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.h +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.h @@ -41,10 +41,30 @@ class LicenseApiClient : public QObject bool isServer; }; + /// @brief Why an activation was requested, which decides whether its result starts the core. + enum class ActivationIntent + { + kKeyEntry, + kCoreStart + }; + + /// @brief Why a check was requested. A core start asks the takeover question on a deactivated + /// verdict regardless of how far the process has got; the recurring poll only acts on a server + /// that is actually running. + enum class CheckIntent + { + kPoll, + kCoreStart + }; + explicit LicenseApiClient(); - void activate(Data data, bool takeover = false); - void check(Data data); + void activate(Data data, ActivationIntent intent, bool takeover = false); + void check(Data data, CheckIntent intent = CheckIntent::kPoll); + + // Validates a serial key without activating, for business key entry where the role is not yet + // known. Hits the check endpoint but stays out of the grace-period machinery the poll owns. + void validate(Data data); bool isBusy() { @@ -52,13 +72,17 @@ class LicenseApiClient : public QObject } Q_SIGNALS: - void activationFailed(const QString &message); - void activationSucceeded(); - void activationUnreachable(); - void activationDeactivated(const QString &message); + void activationFailed(ActivationIntent intent, const QString &message); + void activationSucceeded(ActivationIntent intent); + void activationUnreachable(ActivationIntent intent); + void activationDeactivated(ActivationIntent intent, const QString &message); void checkFailed(const QString &message); void checkSucceeded(); - void checkDeactivated(const QString &message); + void checkDeactivated(CheckIntent intent, const QString &message); + void checkNotActivated(); + void validateFailed(const QString &message); + void validateSucceeded(); + void validateDeactivated(const QString &message); private Q_SLOTS: void handleResponse(QNetworkReply *reply); @@ -67,15 +91,18 @@ private Q_SLOTS: enum class RequestKind { kActivate, - kCheck + kCheck, + kValidate }; - void post(RequestKind kind, const QUrl &url, const Data &data, std::optional takeover = std::nullopt); + void post( + RequestKind kind, const QUrl &url, const Data &data, ActivationIntent intent = ActivationIntent::kCoreStart, + CheckIntent checkIntent = CheckIntent::kPoll, std::optional takeover = std::nullopt + ); QByteArray getRequestData(const Data &data, std::optional takeover) const; QNetworkAccessManager m_manager; bool m_isBusy = false; - RequestKind m_pendingKind = RequestKind::kActivate; }; } // namespace synergy::gui::license diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 083605feb..621f0cb8e 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -56,6 +56,9 @@ using namespace synergy::gui::license; using namespace synergy::gui; using namespace deskflow::gui; +using ActivationIntent = LicenseApiClient::ActivationIntent; +using CheckIntent = LicenseApiClient::CheckIntent; + namespace { std::string licenseMachineId() @@ -76,18 +79,21 @@ LicenseHandler::LicenseHandler() { m_enabled = synergy::gui::license::isActivationEnabled(); - connect(&m_apiClient, &LicenseApiClient::activationFailed, this, [this](const QString &message) { + connect(&m_apiClient, &LicenseApiClient::activationFailed, this, [this](ActivationIntent intent, const QString &message) { handleLicenseUnverified(message); - if (!m_coreStartActivation) { + if (intent == ActivationIntent::kKeyEntry) { qWarning("activation failed at key entry, showing serial key dialog"); showSerialKeyDialog(); } }); - connect(&m_apiClient, &LicenseApiClient::activationSucceeded, this, [this] { + connect(&m_apiClient, &LicenseApiClient::activationSucceeded, this, [this](ActivationIntent intent) { qDebug("license activation succeeded, saving settings"); + const bool holdsServerActivation = + m_license.productEdition() == Product::Edition::kBusiness && liveCoreMode() == Settings::Server; m_settings.setActivated(true); + m_settings.setHoldsServerActivation(holdsServerActivation); resetGracePeriod(); if (m_pCoreProcess == nullptr) { @@ -95,13 +101,14 @@ LicenseHandler::LicenseHandler() } // Key entry activations must never start the core; the customer chose nothing yet. - if (m_coreStartActivation && m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { + if (intent == ActivationIntent::kCoreStart && + m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { qDebug("resuming core process after activation"); m_pCoreProcess->start(); } }); - connect(&m_apiClient, &LicenseApiClient::activationUnreachable, this, [this] { + connect(&m_apiClient, &LicenseApiClient::activationUnreachable, this, [this](ActivationIntent intent) { if (isGracePeriodExpired()) { qWarning("license activation server unreachable and grace period expired, not starting core"); return; @@ -109,7 +116,7 @@ LicenseHandler::LicenseHandler() qWarning("license activation server unreachable, continuing without activation"); - if (m_coreStartActivation && m_pCoreProcess != nullptr && + if (intent == ActivationIntent::kCoreStart && m_pCoreProcess != nullptr && m_pCoreProcess->processState() == deskflow::core::ProcessState::Stopped) { m_pCoreProcess->start(); } @@ -119,6 +126,29 @@ LicenseHandler::LicenseHandler() connect(&m_apiClient, &LicenseApiClient::checkSucceeded, this, &LicenseHandler::handleLicenseVerified); connect(&m_apiClient, &LicenseApiClient::checkFailed, this, &LicenseHandler::handleLicenseUnverified); connect(&m_apiClient, &LicenseApiClient::checkDeactivated, this, &LicenseHandler::handleCheckDeactivated); + + connect(&m_apiClient, &LicenseApiClient::checkNotActivated, this, [this] { + // The server has no record of this machine, so a read-only check cannot heal it; claim now. + qInfo("license check found no activation for this machine, activating"); + m_apiClient.activate(buildApiData(), ActivationIntent::kCoreStart); + }); + + connect(&m_apiClient, &LicenseApiClient::validateSucceeded, this, [] { + qInfo("business license key validated"); + }); + + connect(&m_apiClient, &LicenseApiClient::validateFailed, this, [this](const QString &message) { + qWarning().noquote() << "business license key validation failed:" << message; + if (m_pMainWindow != nullptr) { + QMessageBox::warning(m_pMainWindow, "License check failed", message); + } + showSerialKeyDialog(); + }); + + connect(&m_apiClient, &LicenseApiClient::validateDeactivated, this, [](const QString &message) { + // The role is unknown at key entry; the next core start resolves any server contention. + qInfo().noquote() << "validation found another server, deferring to core start:" << message; + }); } void LicenseHandler::handleMainWindow(QMainWindow *mainWindow, deskflow::gui::CoreProcess *coreProcess) @@ -168,6 +198,12 @@ bool LicenseHandler::handleAppStart() updateWindowTitle(); clampFeatures(); runRemoteCheck(); + + // A long-running session must still honor a disabled (e.g. unpaid) license within a day, so + // re-check on a cadence; runRemoteCheck self-skips personal and offline licenses. + m_remoteCheckTimer.setInterval(kRemoteCheckInterval); + connect(&m_remoteCheckTimer, &QTimer::timeout, this, &LicenseHandler::runRemoteCheck); + m_remoteCheckTimer.start(); return true; } @@ -295,9 +331,21 @@ bool LicenseHandler::handleCoreStart() qInfo("license api busy, starting core without refreshing activation"); return true; } - qDebug("license is activated, refreshing activation on core start"); - m_coreStartActivation = true; - m_apiClient.activate(buildApiData()); + + // A routine restart must not re-claim the slot, or the takeover ledger would inflate and a + // displaced server would silently reclaim instead of asking. So a machine that already holds + // its slot (and a plain client) only checks; a machine taking on the server role claims it. + const bool isServer = liveCoreMode() == Settings::Server; + if (isServer && !m_settings.holdsServerActivation()) { + qDebug("claiming server slot on core start"); + m_apiClient.activate(buildApiData(), ActivationIntent::kCoreStart); + } else if (!isServer && m_settings.holdsServerActivation()) { + qDebug("releasing server slot as client on core start"); + m_apiClient.activate(buildApiData(), ActivationIntent::kCoreStart); + } else { + qDebug("license is activated, checking on core start"); + m_apiClient.check(buildApiData(), CheckIntent::kCoreStart); + } return true; } @@ -326,8 +374,7 @@ bool LicenseHandler::handleCoreStart() } qInfo("activating license"); - m_coreStartActivation = true; - m_apiClient.activate(buildApiData()); + m_apiClient.activate(buildApiData(), ActivationIntent::kCoreStart); return false; } @@ -381,6 +428,7 @@ bool LicenseHandler::showSerialKeyDialog() // Reset activation so new serial key can be activated. qDebug("serial key changed, updating settings"); m_settings.setActivated(false); + m_settings.setHoldsServerActivation(false); m_settings.setGraceStartEpochSecs(0); m_settings.setOfflineActivationResponse({}); m_warnedAboutGrace = false; @@ -394,12 +442,19 @@ bool LicenseHandler::showSerialKeyDialog() // Key entry never touches the core process; everything a new key implies (activation, the // offline challenge, feature clamps) is applied at the next core start. // If the user accepted the dialog while not activated (e.g. recovering from a - // remote disable), retry activation so something visible happens regardless of - // whether the serial key changed. + // remote disable), reach out so something visible happens regardless of whether the + // serial key changed. if (!m_settings.activated() && m_license.isValid() && !m_license.serialKey().isOffline && !m_apiClient.isBusy()) { - qInfo("retrying activation after dialog accept"); - m_coreStartActivation = false; - m_apiClient.activate(buildApiData()); + // The role is unknown at key entry, so business only validates the key here; its first + // activation happens at core start once the role is known. Personal has no role, so it + // activates immediately. + if (m_license.productEdition() == Product::Edition::kBusiness) { + qInfo("validating business license after dialog accept"); + m_apiClient.validate(buildApiData()); + } else { + qInfo("retrying activation after dialog accept"); + m_apiClient.activate(buildApiData(), ActivationIntent::kKeyEntry); + } } qDebug("license serial key dialog accepted"); @@ -709,15 +764,16 @@ void LicenseHandler::handleLicenseUnverified(const QString &message) } } -void LicenseHandler::handleActivationDeactivated(const QString &message) +void LicenseHandler::handleActivationDeactivated(ActivationIntent intent, const QString &message) { qWarning().noquote() << "server activation held by another computer:" << message; + m_settings.setHoldsServerActivation(false); resetGracePeriod(); // The role is only reliably known at core start; an activation sent from anywhere else // (e.g. serial key entry) must not ask or stop anything, the next core start will. - if (!m_coreStartActivation) { + if (intent != ActivationIntent::kCoreStart) { qInfo("not a core start activation, leaving the server question for the next core start"); return; } @@ -758,8 +814,7 @@ void LicenseHandler::askServerQuestion() qInfo("server question accepted, reactivating"); // The customer just chose to be the server, so success may resume the stopped core. - m_coreStartActivation = true; - m_apiClient.activate(buildApiData(), true); + m_apiClient.activate(buildApiData(), ActivationIntent::kCoreStart, true); return; } @@ -768,19 +823,26 @@ void LicenseHandler::askServerQuestion() qInfo("server question declined, leaving core stopped"); } -void LicenseHandler::handleCheckDeactivated(const QString &message) +void LicenseHandler::handleCheckDeactivated(CheckIntent intent, const QString &message) { qWarning().noquote() << "server activation taken over by another computer:" << message; + m_settings.setHoldsServerActivation(false); resetGracePeriod(); - if (m_pCoreProcess == nullptr || !m_pCoreProcess->isStarted() || liveCoreMode() != Settings::Server) { + // The recurring poll only acts on a server that is actually running; a core start is becoming + // the server right now, so it asks regardless of how far the process has got. + const bool runningAsServer = + m_pCoreProcess != nullptr && m_pCoreProcess->isStarted() && liveCoreMode() == Settings::Server; + if (intent == CheckIntent::kPoll && !runningAsServer) { qDebug("not running as server, ignoring deactivated check"); return; } - qInfo("stopping core, another computer took over as server"); - m_pCoreProcess->stop(); + if (m_pCoreProcess != nullptr && m_pCoreProcess->isStarted()) { + qInfo("stopping core, another computer took over as server"); + m_pCoreProcess->stop(); + } askServerQuestion(); } @@ -798,6 +860,7 @@ void LicenseHandler::disableLicenseAfterGrace(const QString &reason) // automatically if the server re-enables the license (e.g. after the customer pays). // Keep the grace clock too, so a restart stays disabled instead of granting a fresh grace. m_settings.setActivated(false); + m_settings.setHoldsServerActivation(false); m_settings.sync(); m_warnedAboutGrace = false; diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.h b/extra/src/lib/synergy/gui/license/LicenseHandler.h index 9dd9c88c0..9258901d4 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.h +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.h @@ -24,6 +24,8 @@ #include "synergy/license/License.h" #include "synergy/license/Product.h" +#include + class QMainWindow; class QDialog; @@ -95,8 +97,8 @@ class LicenseHandler : public QObject void runRemoteCheck(); void handleLicenseVerified(); void handleLicenseUnverified(const QString &message); - void handleActivationDeactivated(const QString &message); - void handleCheckDeactivated(const QString &message); + void handleActivationDeactivated(synergy::gui::license::LicenseApiClient::ActivationIntent intent, const QString &message); + void handleCheckDeactivated(synergy::gui::license::LicenseApiClient::CheckIntent intent, const QString &message); void askServerQuestion(); bool isInGracePeriod() const; bool isGracePeriodExpired() const; @@ -111,8 +113,8 @@ class LicenseHandler : public QObject synergy::gui::ExtraSettings m_settings; synergy::gui::license::LicenseApiClient m_apiClient; bool m_warnedAboutGrace = false; - bool m_coreStartActivation = false; qint64 m_lastCoreStartMs = 0; + QTimer m_remoteCheckTimer; QMainWindow *m_pMainWindow = nullptr; deskflow::gui::CoreProcess *m_pCoreProcess = nullptr; }; diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp index f7c2fef24..c598589d8 100644 --- a/src/lib/gui/MainWindow.cpp +++ b/src/lib/gui/MainWindow.cpp @@ -498,6 +498,12 @@ void MainWindow::openSettings() void MainWindow::resetCore() { +#ifdef SYNERGY_EXTRA_HEADER + if (!synergy::hooks::onCoreStart()) { + return; + } +#endif + m_coreProcess.restart(); } From 519f768173eabcd368be45cb5fe4d56e881ded23 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Mon, 15 Jun 2026 18:35:39 +0100 Subject: [PATCH 113/144] feat: improve code readability in LicenseHandler by formatting connection signals --- .../synergy/gui/license/LicenseHandler.cpp | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 621f0cb8e..25e126877 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -79,14 +79,17 @@ LicenseHandler::LicenseHandler() { m_enabled = synergy::gui::license::isActivationEnabled(); - connect(&m_apiClient, &LicenseApiClient::activationFailed, this, [this](ActivationIntent intent, const QString &message) { - handleLicenseUnverified(message); - - if (intent == ActivationIntent::kKeyEntry) { - qWarning("activation failed at key entry, showing serial key dialog"); - showSerialKeyDialog(); - } - }); + connect( + &m_apiClient, &LicenseApiClient::activationFailed, this, + [this](ActivationIntent intent, const QString &message) { + handleLicenseUnverified(message); + + if (intent == ActivationIntent::kKeyEntry) { + qWarning("activation failed at key entry, showing serial key dialog"); + showSerialKeyDialog(); + } + } + ); connect(&m_apiClient, &LicenseApiClient::activationSucceeded, this, [this](ActivationIntent intent) { qDebug("license activation succeeded, saving settings"); @@ -133,9 +136,7 @@ LicenseHandler::LicenseHandler() m_apiClient.activate(buildApiData(), ActivationIntent::kCoreStart); }); - connect(&m_apiClient, &LicenseApiClient::validateSucceeded, this, [] { - qInfo("business license key validated"); - }); + connect(&m_apiClient, &LicenseApiClient::validateSucceeded, this, [] { qInfo("business license key validated"); }); connect(&m_apiClient, &LicenseApiClient::validateFailed, this, [this](const QString &message) { qWarning().noquote() << "business license key validation failed:" << message; @@ -336,11 +337,12 @@ bool LicenseHandler::handleCoreStart() // displaced server would silently reclaim instead of asking. So a machine that already holds // its slot (and a plain client) only checks; a machine taking on the server role claims it. const bool isServer = liveCoreMode() == Settings::Server; - if (isServer && !m_settings.holdsServerActivation()) { - qDebug("claiming server slot on core start"); - m_apiClient.activate(buildApiData(), ActivationIntent::kCoreStart); - } else if (!isServer && m_settings.holdsServerActivation()) { - qDebug("releasing server slot as client on core start"); + if (isServer != m_settings.holdsServerActivation()) { + if (isServer) { + qDebug("core server, claiming server activation slot on core start"); + } else { + qDebug("core client, releasing server activation slot on core start"); + } m_apiClient.activate(buildApiData(), ActivationIntent::kCoreStart); } else { qDebug("license is activated, checking on core start"); From d22477cd195c9c21c6a81fa4cd68ab80efec2ccd Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 16 Jun 2026 12:26:11 +0100 Subject: [PATCH 114/144] feat: clarify server activation deactivation messages in LicenseHandler --- extra/src/lib/synergy/gui/license/LicenseHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index 25e126877..d35025f52 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -801,14 +801,14 @@ void LicenseHandler::askServerQuestion() "

If you need to add more seats to your team's license, please " R"(contact us today.

)" "

Do you want to reassign a server activation to this computer?

" - "

The activation will be taken away from another computer on this license.

") + "

This will deactivate the other computer and interrupt an existing setup.

") .arg(kUrlContact); } else { question = tr("

Another computer is currently the server for your license.

" "

If you need more than one server running at the same time, please " R"(contact us today.

)" "

Do you want to reassign the server activation to this computer?

" - "

The activation will be taken away from the other computer.

") + "

This will deactivate the other computer and interrupt an existing setup.

") .arg(kUrlContact); } const auto reply = QMessageBox::question(m_pMainWindow, "License limit reached", question); From 7419a95be8d6dd85ba9bf6c771ca54f875e134a3 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 16 Jun 2026 14:52:19 +0100 Subject: [PATCH 115/144] feat: enhance warning message for running instance in deskflow core --- src/apps/deskflow-core/deskflow-core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/deskflow-core/deskflow-core.cpp b/src/apps/deskflow-core/deskflow-core.cpp index 9d6368718..52df86ef4 100644 --- a/src/apps/deskflow-core/deskflow-core.cpp +++ b/src/apps/deskflow-core/deskflow-core.cpp @@ -116,7 +116,7 @@ int main(int argc, char **argv) sharedMemory.detach(); if (!sharedMemory.create(1) && parser.singleInstanceOnly()) { - LOG_WARN("an instance of deskflow core is already running"); + LOG_WARN("an instance of %s is already running", kCoreBinName); return s_exitDuplicate; } From 4aae9d83eb0013ae716420f35abe3e07286344b4 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Tue, 16 Jun 2026 21:43:09 +0100 Subject: [PATCH 116/144] feat: update offline activation constants and test vectors for consistency --- .../src/lib/synergy/license/OfflineActivation.cpp | 4 ++-- extra/src/lib/synergy/license/OfflineActivation.h | 5 ++--- .../unittests/license/OfflineActivationTests.cpp | 14 +++++++------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/extra/src/lib/synergy/license/OfflineActivation.cpp b/extra/src/lib/synergy/license/OfflineActivation.cpp index 8724db535..3483fc3e6 100644 --- a/extra/src/lib/synergy/license/OfflineActivation.cpp +++ b/extra/src/lib/synergy/license/OfflineActivation.cpp @@ -33,7 +33,7 @@ namespace { // The wire format here must stay in lockstep with the vendor signer in the web-muskoka repo // (website lib/server/product-license/offline-activation-protocol.ts). -constexpr auto kDomain = std::string_view{"Synergy1OfflineActivationV1"}; +constexpr auto kDomain = std::string_view{"synergy-offline-activation-v1"}; constexpr size_t kSecretLength = 32; // No vowels, so codes can never spell words; no I, L, or O, so nothing is mistaken @@ -219,7 +219,7 @@ std::string buildOfflineChallenge(const std::string &machineId, const std::strin bool verifyOfflineResponse(const std::string &machineId, const std::string &serialHex, const std::string &responseCode) { - return verifyOfflineResponse(machineId, serialHex, responseCode, kOfflineActivationSecretHex); + return verifyOfflineResponse(machineId, serialHex, responseCode, kOfflineActivationHex); } bool verifyOfflineResponse( diff --git a/extra/src/lib/synergy/license/OfflineActivation.h b/extra/src/lib/synergy/license/OfflineActivation.h index 03695181f..3b115d8e4 100644 --- a/extra/src/lib/synergy/license/OfflineActivation.h +++ b/extra/src/lib/synergy/license/OfflineActivation.h @@ -31,9 +31,8 @@ namespace synergy::license { * (deterrence, not copy protection). */ -/// @brief Shared secret the response codes are verified against. -inline constexpr auto kOfflineActivationSecretHex = - "8acfc45b6524b47ae91e68610c7d2463c28b7e5f80a04d3fd589c87917848534"; +/// @brief Shared hex the response codes are verified against (deliberately not secret) +inline constexpr auto kOfflineActivationHex = "8acfc45b6524b47ae91e68610c7d2463c28b7e5f80a04d3fd589c87917848534"; /** * @brief Builds the challenge code the customer enters on their account page. diff --git a/extra/src/unittests/license/OfflineActivationTests.cpp b/extra/src/unittests/license/OfflineActivationTests.cpp index 834dd7ccc..9398fb87c 100644 --- a/extra/src/unittests/license/OfflineActivationTests.cpp +++ b/extra/src/unittests/license/OfflineActivationTests.cpp @@ -30,8 +30,8 @@ using namespace synergy::license; const auto kVectorSecret = "0707070707070707070707070707070707070707070707070707070707070707"; const auto kVectorMachineId = "test-machine-id-0001"; const auto kVectorSerial = "deadbeef00112233"; -const auto kVectorChallenge = "CS6WZ1BVJJSN"; -const auto kVectorResponse = "HG5CR1761BVQ"; +const auto kVectorChallenge = "CS34563YJPF9"; +const auto kVectorResponse = "FNY0QQP3MM39"; TEST(OfflineActivationTests, buildOfflineChallenge_vectorInputs_matchesVendorTool) { @@ -60,12 +60,12 @@ TEST(OfflineActivationTests, verifyOfflineResponse_vendorSignedResponse_verifies TEST(OfflineActivationTests, verifyOfflineResponse_groupedLowercaseResponse_verifies) { - EXPECT_TRUE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "hg5c-r176-1bvq", kVectorSecret)); + EXPECT_TRUE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "fny0-qqp3-mm39", kVectorSecret)); } TEST(OfflineActivationTests, verifyOfflineResponse_confusableLettersTyped_verifies) { - EXPECT_TRUE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "HG5CRI76LBVQ", kVectorSecret)); + EXPECT_TRUE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "FNYOQQP3MM39", kVectorSecret)); } TEST(OfflineActivationTests, verifyOfflineResponse_tamperedResponse_fails) @@ -100,7 +100,7 @@ TEST(OfflineActivationTests, verifyOfflineResponse_malformedResponse_fails) { EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "", kVectorSecret)); EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "not a code @#!", kVectorSecret)); - EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "HG5C", kVectorSecret)); + EXPECT_FALSE(verifyOfflineResponse(kVectorMachineId, kVectorSerial, "FNY0", kVectorSecret)); } TEST(OfflineActivationTests, verifyOfflineResponse_malformedSecret_fails) @@ -119,8 +119,8 @@ TEST(OfflineActivationTests, verifyOfflineResponse_embeddedProductionSecret_reje // implementations to one shared vector. TEST(OfflineActivationTests, verifyOfflineResponse_sharedVendorVector_verifies) { - EXPECT_EQ(buildOfflineChallenge("test machine id", "TEST SERIAL KEY"), "7DQSYXM59MV5"); - EXPECT_TRUE(verifyOfflineResponse("test machine id", "TEST SERIAL KEY", "T6R4-TGRC-WDQ9", kVectorSecret)); + EXPECT_EQ(buildOfflineChallenge("test machine id", "TEST SERIAL KEY"), "7D6NNQP2VMRH"); + EXPECT_TRUE(verifyOfflineResponse("test machine id", "TEST SERIAL KEY", "PHSD-R6RS-YSSX", kVectorSecret)); } TEST(OfflineActivationTests, formatOfflineCode_groupsOfFour_insertsDashes) From 4dfe0c435e2c675d7e3ac8d50ea2e79a8af0fa7f Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 13:36:41 +0100 Subject: [PATCH 117/144] feat: refactor license API URL handling to use a base URL for activation and validation --- extra/Synergy.test.example.conf | 14 +++++--------- extra/src/lib/synergy/gui/TestSettings.cpp | 6 ++---- extra/src/lib/synergy/gui/TestSettings.h | 11 +++-------- extra/src/lib/synergy/gui/constants.h | 3 --- .../lib/synergy/gui/license/LicenseApiClient.cpp | 12 ++++++++---- 5 files changed, 18 insertions(+), 28 deletions(-) diff --git a/extra/Synergy.test.example.conf b/extra/Synergy.test.example.conf index 609b2fd31..bd161667b 100644 --- a/extra/Synergy.test.example.conf +++ b/extra/Synergy.test.example.conf @@ -28,15 +28,11 @@ # launch. # licensing=true -# Override the URL the GUI POSTs to when activating a serial key. -# Default points at production. Use this to point at a local stack or -# staging environment during development. -# apiUrlActivate=http://localhost:4200/synergy/api/product/activate - -# Override the URL used for periodic license re-validation (the call -# the GUI makes after activation to confirm the license is still valid). -# Same default/use-case as apiUrlActivate. -# apiUrlCheck=http://localhost:4200/synergy/api/product/check +# Override the base URL for license API calls. The app appends the +# endpoint paths (/product/activate, /product/check) itself, so set this +# to the API root only. Default points at production. Use this to point +# at a local stack or staging environment during development. +# apiUrlBase=http://localhost:4200/synergy/api # Pre-fill the activation dialog's serial key field. Hex-encoded payload # matching the format the activation API expects. Test serial keys are diff --git a/extra/src/lib/synergy/gui/TestSettings.cpp b/extra/src/lib/synergy/gui/TestSettings.cpp index d7fb9bbd9..f684ad7f3 100644 --- a/extra/src/lib/synergy/gui/TestSettings.cpp +++ b/extra/src/lib/synergy/gui/TestSettings.cpp @@ -68,8 +68,7 @@ void TestSettings::load() m_enabled = false; m_licensing = false; m_serialKey.clear(); - m_apiUrlActivate.clear(); - m_apiUrlCheck.clear(); + m_apiUrlBase.clear(); m_machineId.clear(); m_startTimeEpochSecs = 0; m_verbose = false; @@ -89,8 +88,7 @@ void TestSettings::load() } m_licensing = ini.value(QStringLiteral("test/licensing"), false).toBool(); m_serialKey = ini.value(QStringLiteral("test/serialKey")).toString(); - m_apiUrlActivate = ini.value(QStringLiteral("test/apiUrlActivate")).toString(); - m_apiUrlCheck = ini.value(QStringLiteral("test/apiUrlCheck")).toString(); + m_apiUrlBase = ini.value(QStringLiteral("test/apiUrlBase")).toString(); m_machineId = ini.value(QStringLiteral("test/machineId")).toString(); m_startTimeEpochSecs = ini.value(QStringLiteral("test/startTime"), 0).toLongLong(); diff --git a/extra/src/lib/synergy/gui/TestSettings.h b/extra/src/lib/synergy/gui/TestSettings.h index d74483413..1f945a1da 100644 --- a/extra/src/lib/synergy/gui/TestSettings.h +++ b/extra/src/lib/synergy/gui/TestSettings.h @@ -54,13 +54,9 @@ class TestSettings { return m_serialKey; } - QString apiUrlActivate() const + QString apiUrlBase() const { - return m_apiUrlActivate; - } - QString apiUrlCheck() const - { - return m_apiUrlCheck; + return m_apiUrlBase; } QString machineId() const { @@ -105,8 +101,7 @@ class TestSettings bool m_enabled = false; bool m_licensing = false; QString m_serialKey; - QString m_apiUrlActivate; - QString m_apiUrlCheck; + QString m_apiUrlBase; QString m_machineId; qint64 m_startTimeEpochSecs = 0; bool m_verbose = false; diff --git a/extra/src/lib/synergy/gui/constants.h b/extra/src/lib/synergy/gui/constants.h index 0445fc163..3cc865169 100644 --- a/extra/src/lib/synergy/gui/constants.h +++ b/extra/src/lib/synergy/gui/constants.h @@ -39,9 +39,6 @@ const auto kUrlPersonalUpgrade = QString("%1/purchase/upgrade?%2").arg(kUrlWebsi const auto kUrlContact = QString("%1/contact?%2").arg(kUrlWebsite, kUrlSourceQuery); const auto kUrlAccount = QString("%1/account?%2").arg(kUrlWebsite, kUrlSourceQuery); -const auto kUrlApiLicenseActivate = QString("%1/product/activate").arg(kUrlApi); -const auto kUrlApiLicenseCheck = QString("%1/product/check").arg(kUrlApi); - constexpr auto kLicenseGracePeriod = std::chrono::days{14}; constexpr auto kRemoteCheckInterval = std::chrono::hours{24}; diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index bc58584ed..502c9a02c 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -30,16 +30,20 @@ constexpr auto kPropRequestKind = "requestKind"; constexpr auto kPropActivationIntent = "activationIntent"; constexpr auto kPropCheckIntent = "checkIntent"; +QString apiBaseUrl() +{ + const auto testBase = TestSettings::instance().apiUrlBase(); + return testBase.isEmpty() ? QString::fromUtf8(kUrlApi) : testBase; +} + QString activateUrl() { - const auto testUrl = TestSettings::instance().apiUrlActivate(); - return testUrl.isEmpty() ? kUrlApiLicenseActivate : testUrl; + return QStringLiteral("%1/product/activate").arg(apiBaseUrl()); } QString checkUrl() { - const auto testUrl = TestSettings::instance().apiUrlCheck(); - return testUrl.isEmpty() ? kUrlApiLicenseCheck : testUrl; + return QStringLiteral("%1/product/check").arg(apiBaseUrl()); } LicenseApiClient::LicenseApiClient() From cc1305b7e4e338b8107908c80e2dedb6410fb4a6 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 14:14:09 +0100 Subject: [PATCH 118/144] feat: enhance versioning display logic for better traceability in builds --- extra/cmake/Synergy.cmake | 17 +++++++++++++++++ src/lib/common/VersionInfo.h.in | 4 +--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index e614a50f7..c80cead7d 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -81,6 +81,23 @@ synergy_compute_version("${CMAKE_SOURCE_DIR}" set(CMAKE_PROJECT_VERSION_MAJOR ${SYNERGY_VERSION_MAJOR}) set(CMAKE_PROJECT_VERSION_MINOR ${SYNERGY_VERSION_MINOR}) set(CMAKE_PROJECT_VERSION_PATCH ${SYNERGY_VERSION_PATCH}) + +# Human-facing version: release stays clean; dev/snapshot append the short sha +# for traceability. Composed here (not the C++ template) because only the build +# knows the stage. Consumed by VersionInfo.h.in (kDisplayVersion). +if(SYNERGY_VERSION_RELEASE OR NOT GIT_SHA_SHORT) + set(CMAKE_PROJECT_VERSION_DISPLAY "${CMAKE_PROJECT_VERSION}") +else() + set(CMAKE_PROJECT_VERSION_DISPLAY "${CMAKE_PROJECT_VERSION} (${GIT_SHA_SHORT})") +endif() + if(NOT SYNERGY_VERSION_RELEASE AND NOT SYNERGY_VERSION_SNAPSHOT) add_compile_definitions(SYNERGY_VERSION_DEV) endif() + +# Compile activation in for distributable builds only; dev builds opt in at +# runtime via Synergy.test.conf (licensing=true) so local iteration isn't gated +# on a serial key. +if(SYNERGY_VERSION_RELEASE OR SYNERGY_VERSION_SNAPSHOT) + add_compile_definitions(SYNERGY_ENABLE_ACTIVATION) +endif() diff --git a/src/lib/common/VersionInfo.h.in b/src/lib/common/VersionInfo.h.in index f380ac9fb..5ab83af87 100644 --- a/src/lib/common/VersionInfo.h.in +++ b/src/lib/common/VersionInfo.h.in @@ -9,6 +9,4 @@ const auto kVersion = "@CMAKE_PROJECT_VERSION@"; const auto kVersionGitSha = "@GIT_SHA_SHORT@"; -const auto kDisplayVersion = @CMAKE_PROJECT_VERSION_TWEAK@ - ? "@CMAKE_PROJECT_VERSION@ (@GIT_SHA_SHORT@)" - : "@CMAKE_PROJECT_VERSION_MAJOR@.@CMAKE_PROJECT_VERSION_MINOR@.@CMAKE_PROJECT_VERSION_PATCH@"; +const auto kDisplayVersion = "@CMAKE_PROJECT_VERSION_DISPLAY@"; From 4e76f50c74da5beab0148cca18b925d8f5f27e96 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 14:47:17 +0100 Subject: [PATCH 119/144] feat: add MSVC runtime requirement handling to ensure bundled CRT is used --- CMakeLists.txt | 3 +++ extra/cmake/MsvcRuntime.cmake | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 extra/cmake/MsvcRuntime.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index cb2fc6cf5..8dd6215b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,9 @@ if (MSVC) endif() endif() +# Synergy overlay: anchor the runtime requirement to the bundled CRT merge module. +include(${CMAKE_SOURCE_DIR}/extra/cmake/MsvcRuntime.cmake) + # Control debug item visibility # When not set logging is forced to DEBUG and show code locations # Also exposes a test menu diff --git a/extra/cmake/MsvcRuntime.cmake b/extra/cmake/MsvcRuntime.cmake new file mode 100644 index 000000000..3b5460a99 --- /dev/null +++ b/extra/cmake/MsvcRuntime.cmake @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd +# SPDX-License-Identifier: MIT + +# Synergy bundles the MSVC C++ runtime as a merge module inside the MSI +# (deploy/windows/deploy.cmake) instead of making the user install the +# redistributable. The runtime guard (ArchMiscWindows::guardRuntimeVersion) must +# therefore require the version we actually ship, not the build machine's +# installed redist that upstream's registry query returns. The two drift: CI +# runners get a newer redist than the toolset's merge module, so the guard ends +# up rejecting our own bundled runtime and the service fails to start. +# +# Override the upstream registry-derived minor with the version of the CRT merge +# module we bundle, taken from the redist directory name +# (.../Redist/MSVC//MergeModules/...). Builds without a merge module +# (dev) keep upstream's registry value as the fallback. +# +# This globs the same path deploy/windows/deploy.cmake bundles from; keep the two +# in sync until a future change discovers the module once and shares the path. +if(MSVC) + file(GLOB _crt_msms + "$ENV{VCINSTALLDIR}Redist/MSVC/*/MergeModules/Microsoft_VC*_CRT_${BUILD_ARCHITECTURE}.msm") + list(SORT _crt_msms) + if(_crt_msms) + list(GET _crt_msms -1 _crt_msm) + string(REGEX MATCH "Redist/MSVC/[0-9]+\\.([0-9]+)\\." _ "${_crt_msm}") + set(REQUIRED_MSVC_RUNTIME_MINOR "${CMAKE_MATCH_1}") + message(STATUS "MSVC runtime (bundled CRT merge module): ${REQUIRED_MSVC_RUNTIME_MAJOR}.${REQUIRED_MSVC_RUNTIME_MINOR}") + endif() +endif() From ffce523eeac36a639dbb46a6101b668f6ef52d25 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 15:27:33 +0100 Subject: [PATCH 120/144] refactor: improve comments and streamline MSVC runtime version handling --- extra/cmake/MsvcRuntime.cmake | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/extra/cmake/MsvcRuntime.cmake b/extra/cmake/MsvcRuntime.cmake index 3b5460a99..ee05856c9 100644 --- a/extra/cmake/MsvcRuntime.cmake +++ b/extra/cmake/MsvcRuntime.cmake @@ -5,25 +5,23 @@ # (deploy/windows/deploy.cmake) instead of making the user install the # redistributable. The runtime guard (ArchMiscWindows::guardRuntimeVersion) must # therefore require the version we actually ship, not the build machine's -# installed redist that upstream's registry query returns. The two drift: CI -# runners get a newer redist than the toolset's merge module, so the guard ends -# up rejecting our own bundled runtime and the service fails to start. +# installed redist that upstream's registry query returns: CI runners carry a +# newer redist than the bundled module, so the guard rejects our own runtime and +# the service fails to start. # -# Override the upstream registry-derived minor with the version of the CRT merge -# module we bundle, taken from the redist directory name -# (.../Redist/MSVC//MergeModules/...). Builds without a merge module -# (dev) keep upstream's registry value as the fallback. +# The bundled module ships the runtime of the compiler's own toolset, but its +# path carries only the toolset major (.../Redist/MSVC/v143/MergeModules/...), so +# the runtime version isn't in it. Take the minor from the compiler version +# instead (cl 19.x maps to CRT 14.x, with matching minors); that floor is always +# <= the runtime we bundle, so it can never reject the runtime we ship. Builds +# with no bundled module (dev) keep upstream's registry value. # -# This globs the same path deploy/windows/deploy.cmake bundles from; keep the two -# in sync until a future change discovers the module once and shares the path. +# The glob mirrors deploy/windows/deploy.cmake's bundling condition; keep in sync. if(MSVC) file(GLOB _crt_msms "$ENV{VCINSTALLDIR}Redist/MSVC/*/MergeModules/Microsoft_VC*_CRT_${BUILD_ARCHITECTURE}.msm") - list(SORT _crt_msms) - if(_crt_msms) - list(GET _crt_msms -1 _crt_msm) - string(REGEX MATCH "Redist/MSVC/[0-9]+\\.([0-9]+)\\." _ "${_crt_msm}") + if(_crt_msms AND CMAKE_CXX_COMPILER_VERSION MATCHES "^[0-9]+\\.([0-9]+)") set(REQUIRED_MSVC_RUNTIME_MINOR "${CMAKE_MATCH_1}") - message(STATUS "MSVC runtime (bundled CRT merge module): ${REQUIRED_MSVC_RUNTIME_MAJOR}.${REQUIRED_MSVC_RUNTIME_MINOR}") + message(STATUS "MSVC runtime (compiler toolset): ${REQUIRED_MSVC_RUNTIME_MAJOR}.${REQUIRED_MSVC_RUNTIME_MINOR}") endif() endif() From 2074ccb53a61398a85b21f4d0fe8fe9e4ab4803c Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 15:27:58 +0100 Subject: [PATCH 121/144] feat: update CPack configuration for improved project name handling and add Wix UI assets --- deploy/windows/deploy.cmake | 6 +++--- deploy/windows/pre-cpack.cmake.in | 8 ++++---- extra/deploy/windows/wix-banner.png | Bin 0 -> 3408 bytes extra/deploy/windows/wix-dialog.png | Bin 0 -> 41237 bytes 4 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 extra/deploy/windows/wix-banner.png create mode 100644 extra/deploy/windows/wix-dialog.png diff --git a/deploy/windows/deploy.cmake b/deploy/windows/deploy.cmake index 9e784107a..703779c85 100644 --- a/deploy/windows/deploy.cmake +++ b/deploy/windows/deploy.cmake @@ -31,15 +31,15 @@ set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_PROPER_NAME}") # Menu Entry set(CPACK_WIX_PROGRAM_MENU_FOLDER "${CMAKE_PROJECT_PROPER_NAME}") -set(CPACK_PACKAGE_EXECUTABLES "deskflow" "${CMAKE_PROJECT_PROPER_NAME}") +set(CPACK_PACKAGE_EXECUTABLES "${CMAKE_PROJECT_NAME}" "${CMAKE_PROJECT_PROPER_NAME}") # Default Install Path set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_PROPER_NAME}") # Wix Specific Values set(CPACK_WIX_UPGRADE_GUID "027D1C8A-E7A5-4754-BB93-B2D45BFDBDC8") -set(CPACK_WIX_UI_BANNER "${MY_DIR}/wix-banner.png") -set(CPACK_WIX_UI_DIALOG "${MY_DIR}/wix-dialog.png") +set(CPACK_WIX_UI_BANNER "${CMAKE_SOURCE_DIR}/extra/deploy/windows/wix-banner.png") +set(CPACK_WIX_UI_DIALOG "${CMAKE_SOURCE_DIR}/extra/deploy/windows/wix-dialog.png") # Required Extra Extenstions list(APPEND CPACK_WIX_EXTENSIONS "WixToolset.Util.wixext" "WixToolset.Firewall.wixext") diff --git a/deploy/windows/pre-cpack.cmake.in b/deploy/windows/pre-cpack.cmake.in index 003876058..8e2e3ad0f 100644 --- a/deploy/windows/pre-cpack.cmake.in +++ b/deploy/windows/pre-cpack.cmake.in @@ -8,12 +8,12 @@ if(CPACK_GENERATOR MATCHES 7Z|ZIP) string(REPLACE "*" " " _ITEM ${ITEM}) file(COPY ${_ITEM} DESTINATION ${CPACK_TEMPORARY_INSTALL_DIRECTORY}) endforeach() - file(WRITE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/settings/Deskflow.conf " ") - file(REMOVE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/deskflow-daemon.exe) + file(WRITE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/settings/@CMAKE_PROJECT_PROPER_NAME@.conf " ") + file(REMOVE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_PROJECT_NAME@-daemon.exe) file(WRITE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/README.txt -" Portable Deskflow: @CMAKE_PROJECT_VERSION@ +" Portable @CMAKE_PROJECT_PROPER_NAME@: @CMAKE_PROJECT_VERSION@ - The portable version must have the settings/Deskflow.conf file to save settings, or it will try to use the system settings location. + The portable version must have the settings/@CMAKE_PROJECT_PROPER_NAME@.conf file to save settings, or it will try to use the system settings location. The portable version does not include the daemon, so the client will not work at UAC prompts or the login screen. ") endif() diff --git a/extra/deploy/windows/wix-banner.png b/extra/deploy/windows/wix-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..bb317a0df12cb9fd068e2284da5db895bd791652 GIT binary patch literal 3408 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 literal 0 HcmV?d00001 diff --git a/extra/deploy/windows/wix-dialog.png b/extra/deploy/windows/wix-dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..fa4d5fe8df361368f179b661d8a96bc39c91b7aa GIT binary patch literal 41237 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%0AbLdD%cM`?^^{7md9sNC|C8S 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 literal 0 HcmV?d00001 From e262a6aa3e64dd28f75c17abbd4fc5d912f45362 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 15:31:49 +0100 Subject: [PATCH 122/144] refactor: clarify comments on MSVC runtime version mapping and bundling logic --- extra/cmake/MsvcRuntime.cmake | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extra/cmake/MsvcRuntime.cmake b/extra/cmake/MsvcRuntime.cmake index ee05856c9..85cfcc820 100644 --- a/extra/cmake/MsvcRuntime.cmake +++ b/extra/cmake/MsvcRuntime.cmake @@ -12,9 +12,11 @@ # The bundled module ships the runtime of the compiler's own toolset, but its # path carries only the toolset major (.../Redist/MSVC/v143/MergeModules/...), so # the runtime version isn't in it. Take the minor from the compiler version -# instead (cl 19.x maps to CRT 14.x, with matching minors); that floor is always -# <= the runtime we bundle, so it can never reject the runtime we ship. Builds -# with no bundled module (dev) keep upstream's registry value. +# instead: cl major 19 maps to CRT major 14 and the minor matches (e.g. 19.44 -> +# 14.44), per Microsoft's _MSC_VER / build-tools version table: +# https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170 +# That floor is always <= the runtime we bundle, so it can't reject what we ship. +# Builds with no bundled module (dev) keep upstream's registry value. # # The glob mirrors deploy/windows/deploy.cmake's bundling condition; keep in sync. if(MSVC) From fcf220fd73a6f47050a35d48a7ea04a29d422f0d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 16:08:11 +0100 Subject: [PATCH 123/144] feat: enhance versioning logic to include short git SHA for dev builds --- .github/workflows/ci.yml | 8 ++++---- extra/cmake/Version.cmake | 15 +++++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96f9d5360..f7f5115e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -255,7 +255,7 @@ jobs: - name: Upload uses: actions/upload-artifact@v7 with: - name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} + name: package-${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* build-macos: @@ -424,7 +424,7 @@ jobs: - name: Upload uses: actions/upload-artifact@v7 with: - name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} + name: package-${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* build-linux: @@ -642,7 +642,7 @@ jobs: - name: Upload uses: actions/upload-artifact@v7 with: - name: package-${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} + name: package-${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-${{ matrix.target.name }} path: ${{ github.workspace }}/build/${{ env.PACKAGE_PREFIX }}[-_]*.* build-flatpak: @@ -696,7 +696,7 @@ jobs: - name: Upload uses: actions/upload-artifact@v7 with: - name: package-${{ env.PACKAGE_PREFIX }}-flatpak-${{matrix.flatpak.arch}} + name: package-${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-flatpak-${{matrix.flatpak.arch}} path: ${{github.workspace}}/${{ env.PACKAGE_PREFIX }}[-_]*.flatpak # Upload all built packages to AWS S3 for distribution. Runs after the gate diff --git a/extra/cmake/Version.cmake b/extra/cmake/Version.cmake index 31534a2da..7133acbbb 100644 --- a/extra/cmake/Version.cmake +++ b/extra/cmake/Version.cmake @@ -15,7 +15,7 @@ set(SYNERGY_VERSION_STAGE "beta") # # RELEASE → "X.Y.Z[-STAGE]" (STAGE = SYNERGY_VERSION_STAGE, e.g. beta1) # SNAPSHOT → "X.Y.Z[-STAGE]-snapshot+rN" (N = commits since last v* tag) -# neither (dev) → "X.Y.Z[-STAGE]-dev" +# neither (dev) → "X.Y.Z[-STAGE]-dev+" # # Outputs (PARENT_SCOPE): # OUT_VERSION the composed version string @@ -26,6 +26,7 @@ set(SYNERGY_VERSION_STAGE "beta") # a strict identifier). function(synergy_compute_version SOURCE_DIR OUT_VERSION OUT_TWEAK OUT_BASE) set(_rev_count 0) + set(_git_sha "") find_package(Git QUIET) if(GIT_FOUND) execute_process( @@ -37,6 +38,12 @@ function(synergy_compute_version SOURCE_DIR OUT_VERSION OUT_TWEAK OUT_BASE) if(_git_describe MATCHES "-([0-9]+)-g") set(_rev_count "${CMAKE_MATCH_1}") endif() + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --short=8 HEAD + WORKING_DIRECTORY "${SOURCE_DIR}" + OUTPUT_VARIABLE _git_sha + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) endif() set(_base "${SYNERGY_VERSION_MAJOR}.${SYNERGY_VERSION_MINOR}.${SYNERGY_VERSION_PATCH}") @@ -51,7 +58,11 @@ function(synergy_compute_version SOURCE_DIR OUT_VERSION OUT_TWEAK OUT_BASE) set(_version "${_base}${_stage}-snapshot+r${_rev_count}") set(_tweak ${_rev_count}) else() - set(_version "${_base}${_stage}-dev") + if(_git_sha) + set(_version "${_base}${_stage}-dev+${_git_sha}") + else() + set(_version "${_base}${_stage}-dev") + endif() set(_tweak ${_rev_count}) endif() From d4e69174e2dbb33f12bb97614e8b6d95ac5ec0c7 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 16:17:14 +0100 Subject: [PATCH 124/144] feat: refine human-facing versioning logic to improve snapshot traceability --- extra/cmake/Synergy.cmake | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/extra/cmake/Synergy.cmake b/extra/cmake/Synergy.cmake index c80cead7d..688468b39 100644 --- a/extra/cmake/Synergy.cmake +++ b/extra/cmake/Synergy.cmake @@ -82,13 +82,15 @@ set(CMAKE_PROJECT_VERSION_MAJOR ${SYNERGY_VERSION_MAJOR}) set(CMAKE_PROJECT_VERSION_MINOR ${SYNERGY_VERSION_MINOR}) set(CMAKE_PROJECT_VERSION_PATCH ${SYNERGY_VERSION_PATCH}) -# Human-facing version: release stays clean; dev/snapshot append the short sha -# for traceability. Composed here (not the C++ template) because only the build -# knows the stage. Consumed by VersionInfo.h.in (kDisplayVersion). -if(SYNERGY_VERSION_RELEASE OR NOT GIT_SHA_SHORT) - set(CMAKE_PROJECT_VERSION_DISPLAY "${CMAKE_PROJECT_VERSION}") -else() +# Human-facing version. The composed version already carries its build metadata +# (dev: +, snapshot: +rN), so it is the display string as-is. Snapshot is the +# exception: +rN is a rev count, not the commit, so append the short sha for +# traceability. Dev already embeds the sha (don't double it); release stays clean. +# Consumed by VersionInfo.h.in (kDisplayVersion). +if(SYNERGY_VERSION_SNAPSHOT AND GIT_SHA_SHORT) set(CMAKE_PROJECT_VERSION_DISPLAY "${CMAKE_PROJECT_VERSION} (${GIT_SHA_SHORT})") +else() + set(CMAKE_PROJECT_VERSION_DISPLAY "${CMAKE_PROJECT_VERSION}") endif() if(NOT SYNERGY_VERSION_RELEASE AND NOT SYNERGY_VERSION_SNAPSHOT) From 3be29ddc36559bef897216d8453039a700247f3d Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 17:07:32 +0100 Subject: [PATCH 125/144] feat: adjust version label font to match sibling label for consistent UI appearance --- extra/src/lib/synergy/gui/FeatureHandler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extra/src/lib/synergy/gui/FeatureHandler.cpp b/extra/src/lib/synergy/gui/FeatureHandler.cpp index 98a6726a9..e153bf206 100644 --- a/extra/src/lib/synergy/gui/FeatureHandler.cpp +++ b/extra/src/lib/synergy/gui/FeatureHandler.cpp @@ -183,6 +183,14 @@ void FeatureHandler::setAttribution(QDialog *parent) const void FeatureHandler::tightenVersionRow(QDialog *parent) const { + // The .ui pins lblVersion to a fixed point size, so it renders out of step with its + // "Version:" sibling on systems whose default UI font differs; match the sibling instead. + auto *prefix = parent->findChild(QStringLiteral("label")); + auto *version = parent->findChild(QStringLiteral("lblVersion")); + if (prefix != nullptr && version != nullptr) { + version->setFont(prefix->font()); + } + // The copy button is taller than the version text and padded the centred row; cap it. if (auto *copyButton = parent->findChild(QStringLiteral("btnCopyVersion"))) { copyButton->setMaximumHeight(copyButton->fontMetrics().height() + 4); From f35e7d5004229e78a64ea5e26229c3dd848689b8 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 17:08:24 +0100 Subject: [PATCH 126/144] feat: update Wix banner image for enhanced visual presentation --- extra/deploy/windows/wix-banner.png | Bin 3408 -> 3407 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/extra/deploy/windows/wix-banner.png b/extra/deploy/windows/wix-banner.png index bb317a0df12cb9fd068e2284da5db895bd791652..3b39f0feaae9d25ccc232a7b42d4601a882ffeae 100644 GIT binary patch literal 3407 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 djrf1esx@0Q!#=SPS#xCoaP(-%SS6@q(7~2dH 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 From e1eb685c9914ec012d340db2a3c3704976caab93 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 17:19:07 +0100 Subject: [PATCH 127/144] feat: implement custom package filename scheme for Synergy distribution --- .github/workflows/ci.yml | 5 ++-- deploy/CMakeLists.txt | 5 ++++ extra/deploy/PackageFileName.cmake | 47 ++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 extra/deploy/PackageFileName.cmake diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7f5115e5..3023045c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -608,8 +608,9 @@ jobs: cd build sudo -u build makepkg -s export OSNAME=$(cat /etc/os-release | grep ^ID= | sed 's/ID=//g') + [ "$OSNAME" = "arch" ] && OSNAME="arch-linux" export ARCH=$(uname -m) - mv *.pkg.tar.zst ${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-${OSNAME}-${ARCH}.pkg.tar.zst + mv *.pkg.tar.zst ${{ env.PACKAGE_PREFIX }}_${{ steps.get-version.outputs.version }}_${OSNAME}_${ARCH}.pkg.tar.zst cd .. else cmake --build build --config Release -j8 --target package @@ -684,7 +685,7 @@ jobs: - name: Build uses: flatpak/flatpak-github-actions/flatpak-builder@v6 with: - bundle: ${{ env.PACKAGE_PREFIX }}-${{ steps.get-version.outputs.version }}-linux-${{ matrix.flatpak.arch }}.flatpak + bundle: ${{ env.PACKAGE_PREFIX }}_${{ steps.get-version.outputs.version }}_linux_${{ matrix.flatpak.arch }}.flatpak manifest-path: extra/deploy/linux/flatpak/com.symless.synergy.yml cache-key: flatpak-builder-${{matrix.flatpak.arch}}-2.0 arch: ${{matrix.flatpak.arch}} diff --git a/deploy/CMakeLists.txt b/deploy/CMakeLists.txt index d155d1309..bd3fde6c3 100644 --- a/deploy/CMakeLists.txt +++ b/deploy/CMakeLists.txt @@ -34,6 +34,11 @@ else() endif() set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PACKAGE_VERSION_LABEL}-${OS_STRING}") + +# Synergy overlay: rewrite CPACK_PACKAGE_FILE_NAME to the release-templates scheme +# (synergy___); kept in extra/ since upstream won't adopt it. +include(${CMAKE_SOURCE_DIR}/extra/deploy/PackageFileName.cmake) + message(STATUS "Package Basename: ${CPACK_PACKAGE_FILE_NAME}") include(CPack) diff --git a/extra/deploy/PackageFileName.cmake b/extra/deploy/PackageFileName.cmake new file mode 100644 index 000000000..65eac1a2d --- /dev/null +++ b/extra/deploy/PackageFileName.cmake @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd +# SPDX-License-Identifier: MIT + +# Synergy distribution filename: synergy___., matching the +# release Package Templates that the downloads page / release flow match artifacts +# against. Upstream's deploy/ builds a different scheme (hyphen-joined, win/macos, +# os-release-derived distro tokens) it will never change to ours, so we override +# CPACK_PACKAGE_FILE_NAME here instead of patching deploy/*. Included once from +# deploy/CMakeLists.txt after the per-platform OS detection, so it can reuse the +# DISTRO_* vars deploy/linux/deploy.cmake already parsed from /etc/os-release. +# +# The flatpak bundle and the Arch .pkg.tar.zst are named in +# .github/workflows/ci.yml (built outside CPack; Actions can't include cmake) — +# keep those in step with this scheme. + +set(_os "") +if(WIN32) + set(_os "windows_${BUILD_ARCHITECTURE}") +elseif(APPLE) + # mac uses the short arch token (mac_x64 / mac_arm64), not x86_64. + set(_arch "${BUILD_ARCHITECTURE}") + if(_arch STREQUAL "x86_64") + set(_arch "x64") + endif() + set(_os "mac_${_arch}") +elseif(UNIX) + set(_distro "${DISTRO_NAME}") + set(_ver "") + if("${_distro}" STREQUAL "arch") + set(_distro "arch-linux") # os-release ID is "arch" + elseif("${_distro}" STREQUAL "opensuse") + set(_ver "-${DISTRO_CODENAME}") # rolling: codename (tumbleweed), not the ISO date + elseif(NOT "${DISTRO_VERSION_ID}" STREQUAL "") + string(REPLACE "." "-" _ver "${DISTRO_VERSION_ID}") # 24.04 -> 24-04 + set(_ver "-${_ver}") + elseif(NOT "${DISTRO_CODENAME}" STREQUAL "") + set(_ver "-${DISTRO_CODENAME}") + endif() + if("${_distro}" STREQUAL "") + set(_distro "linux") + endif() + set(_os "${_distro}${_ver}_${BUILD_ARCHITECTURE}") +endif() + +if(_os) + set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${PACKAGE_VERSION_LABEL}_${_os}") +endif() From 3e62ffc47a3071992f19397fa56b83286ba78b5b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 17:28:24 +0100 Subject: [PATCH 128/144] feat: update package file matching patterns for cross-platform compatibility --- .github/actions/test-package/action.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/test-package/action.yml b/.github/actions/test-package/action.yml index b81fe1792..291d6940c 100644 --- a/.github/actions/test-package/action.yml +++ b/.github/actions/test-package/action.yml @@ -15,13 +15,13 @@ runs: shell: bash run: | if [ "$RUNNER_OS" == "macOS" ]; then - dmg=$(ls build/synergy-*.dmg | head -1) + dmg=$(ls build/synergy[-_]*.dmg | head -1) echo "Y" | hdiutil attach "$dmg" -nobrowse -mountpoint /tmp/synergy-dmg /tmp/synergy-dmg/Synergy.app/Contents/MacOS/synergy-core --version hdiutil detach /tmp/synergy-dmg elif [ "$RUNNER_OS" == "Windows" ]; then - 7z x build/synergy-*-portable.7z -otmp/synergy-pkg + 7z x build/synergy[-_]*-portable.7z -otmp/synergy-pkg exe=$(find tmp/synergy-pkg -name "synergy-core.exe" -type f | head -1) exe_dir=$(dirname "$exe") pkg_root=$(dirname "$exe_dir") @@ -30,13 +30,13 @@ runs: elif [ "$RUNNER_OS" == "Linux" ]; then if [ "${{inputs.like}}" == "debian" ]; then - apt-get install -y ./build/synergy-*.deb + apt-get install -y ./build/synergy[-_]*.deb elif [ "${{inputs.like}}" == "fedora" ]; then - dnf install -y build/synergy-*.rpm + dnf install -y build/synergy[-_]*.rpm elif [ "${{inputs.like}}" == "suse" ]; then - zypper install -y --allow-unsigned-rpm build/synergy-*.rpm + zypper install -y --allow-unsigned-rpm build/synergy[-_]*.rpm elif [ "${{inputs.like}}" == "arch" ]; then - pacman -U --noconfirm build/synergy-*.pkg.tar.zst + pacman -U --noconfirm build/synergy[-_]*.pkg.tar.zst else echo "Unknown like: ${{inputs.like}}" exit 1 From 80e92d01f1e894bd76dae6ca6782d700cf2a832e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 18:21:29 +0100 Subject: [PATCH 129/144] feat: add Synergy EULA to the installer package for compliance --- deploy/windows/deploy.cmake | 4 ++++ extra/deploy/windows/synergy-eula.rtf | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 extra/deploy/windows/synergy-eula.rtf diff --git a/deploy/windows/deploy.cmake b/deploy/windows/deploy.cmake index 703779c85..664d3c4f7 100644 --- a/deploy/windows/deploy.cmake +++ b/deploy/windows/deploy.cmake @@ -41,6 +41,10 @@ set(CPACK_WIX_UPGRADE_GUID "027D1C8A-E7A5-4754-BB93-B2D45BFDBDC8") set(CPACK_WIX_UI_BANNER "${CMAKE_SOURCE_DIR}/extra/deploy/windows/wix-banner.png") set(CPACK_WIX_UI_DIALOG "${CMAKE_SOURCE_DIR}/extra/deploy/windows/wix-dialog.png") +# Show the Synergy EULA (which references the canonical copy at synergyapp.io/eula) +# on the license page instead of the GPL; the GPL still ships as the LICENSE file. +set(CPACK_WIX_LICENSE_RTF "${CMAKE_SOURCE_DIR}/extra/deploy/windows/synergy-eula.rtf") + # Required Extra Extenstions list(APPEND CPACK_WIX_EXTENSIONS "WixToolset.Util.wixext" "WixToolset.Firewall.wixext") diff --git a/extra/deploy/windows/synergy-eula.rtf b/extra/deploy/windows/synergy-eula.rtf new file mode 100644 index 000000000..ac78ea055 --- /dev/null +++ b/extra/deploy/windows/synergy-eula.rtf @@ -0,0 +1,6 @@ +{\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil\fcharset0 Segoe UI;}} +\fs20 +This software is governed by the Synergy End User License Agreement, available at https://synergyapp.io/eula\par +\par +Synergy is open-source software licensed under the GNU General Public License, version 2; see the accompanying LICENSE file.\par +} From 7319d260783401922a874195e11eefe40ad88ce2 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 18:21:39 +0100 Subject: [PATCH 130/144] Refactor code structure for improved readability and maintainability --- extra/deploy/windows/wix-dialog.png | Bin 41237 -> 26337 bytes extra/deploy/windows/wix-dialog.svg | 94 ++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 extra/deploy/windows/wix-dialog.svg diff --git a/extra/deploy/windows/wix-dialog.png b/extra/deploy/windows/wix-dialog.png index fa4d5fe8df361368f179b661d8a96bc39c91b7aa..87115d4b8190faa555f4b0774616c65b25b46b01 100644 GIT binary patch literal 26337 zcmdqIWl)=K7&REANYMgCgSW-qokD>Yio3f@aMuFGLveTa;%-Gkad#>14ncx!-tU{; zKfCkI?EcuD*@T2l@;rHNxvq1c`2R|Aj{GEr7 z!tC&WC?;|;UjVQF-E%uj5&!@yz_%}-)jTs#);v7b)E`kV?p6i;I1jPnC*#18h>>qK zB0DQvpj}7hwz>;B)%qUxi}P-$wros`2T};uDr$vy;$J5|%z%XU?;uzSe+$Kj3NgEK z!ov2)v;KDS{qo%(^--jLBgu+7`+%mDrKLYOo8qCXueN}d@yl7JTR`7W8!d0A&+YaN zRS2#P(Wp}huB=1qHX3!}Dm&zz1bwS9m^MVoxeq7Mf@H`|JD;hQ%1RY?iCdZKekCi_H7fkw=YxZDf zv@-JgZCP@EIx}o8lKaEV-7reQhc9*#Ov>XfKqPj|U=CbQuwy^_!KR-n8ug8kljpNK zaeDmAQ74=)OGY63y6ML_ifHVY8) zfJogf_961e08J=OWHT=S5MqQ%44)9?m6h#~0`Emt?%p}mhP?Zri#-K}Usyc3k9TA? zzqa4>#t)YW-g5X@*T|ZneV^lxJ?ZlKefK(Fmy3-UaojI~cKIM#k}bRyOB72v*&If7 z;d7@xp4olWH$ljW@}q~VvYskL$z?fpJH%9aF@1R@PHbK2M+j{+=btDwO`AJrbdAb| zBpqoi2{efAz`DaRx98 zx8;0r99GJGlfegq7((n2GQjz)uyPfDcL6e4DZj7cLr}G22C<-|LnY< z9RvKf`pXs&@c2B@t&@K|sQirKO|*-KD0U8WbFTm{e4s<-2dDOdTaX$_!L&e#(^!&2 zzM@lz_$UNHB3!$T8UTb0ob3yM;URwu5(LTwB2a(uUT@jPrwV&CkPK9&8MYhh*HN}e zD=i^AU5rGSaf@cdByxm^P^|t z-XX1;EJa_~e=-nJ)|!eS?hExYBONkka9R1VQzrdY-fu>^e5#9Hqc6%l65#_h+GY{c zk1K`36QO@1^R1#eviDDU)AqftwWX$zgNUTo*{#@#2|X2(rNEBq)v!KB)9?QT+Seo) zFhU8w;kjn1$bS6x@8PEnjw$Y2h7eqBLj3!UU|b2*xn$seY4s2--6TX>7p({nf3*P9tn&af#KhxN_s@Cfq*$oAPLO;Q>2}0$W^fu&7n--T(62S zRLnB4zAQfRggKbHX5qEB%hr|Fd9{DLf{MfZTdQzw;C)=UO;aOQ@KWVfpuY%WucKFJ zvxoB8di-$tQCnQ!BZWw1tON0ghW%=7#q8m4m0>_`6Dz-!cyD-KXBDCKL|3G`Z4)x$ zyRfzVLuupB8g7j>ZG-!+TCrjlfz7;19W^w#*hM965DLB_iv&5@iiMYsHIsK?n2d}| zhP&=7`q(R7&+1*`U;lXx5}SS}2=VWXQj=#|*NiCg4NAuOt(n`Rsc_l^R1fAe8RFqd*?s15{$+{}5`vp=BxJ_4gmIyCCo-+^w2<`S8 z(}77kyw^1Pa)FSt@~-X%#(~rOix}9I^Yn%%X#P32Yx|oM;Fn;|d7e*+!8L@7=;`;_ zK)}TPQ9;-~vtGUT+I-kq=cuqt7Tf(k?1jhnMNy%hCBHOwHQ?b^GxNPYGib46e?4qn zuhqgu+Iv`&QnaJH^6?CH!}F@Q0!+RX@Pc#v$?$1%Hz4(*M?ZxSI?8GIdM0xX_%|%4 z_&kF2n&!OcRFcVm(%xE8-L)%_(kIPdy?osMTU}rJ_-7QvVxV{C^iyRu6=byO*1N%`9;Nq_2#eZyIKW1e7%28PJ^mfAF_s{JXxpA?wT+8J0qw@0iH$%b!!{|Vn zuJqPb=8W&cr;G6ZpESp`2k5nao^fp+c{$nUGb{Ef@9cuTUWMt_i0LK|)L60v?Dw_8 zpqf~uN*C+h=YPi(OcNvW+3g%_4k?9QPljFqO1ebEeC|VJZ0pL4Top~M?5|;NEAEMU zJhu%M#o5wm9GKe`X(Q;=mJ0k9{lC_GmHKnJ2Tof~@-KmqvGnG)wn4hL!aw;XN@p7K zUinQ!h|;WzZr|HrOBnPjwFY~fe+ro|!&0rprQ%&5^#J513TDjc*=aoV zPX&Y8yr*p^)_CtmN*p?zX^L*LaP}Xc@(7x@|I{m@g(W$DNppR4Dx+Pd-qmKLDbl4W8Ew7*VPnJ6f+_EF(X>u_-vDtcr*eRc0VtY zfyvG!w)lsxyZ}_BN$uyspuV=urhUU_=c|u6@;>&DcX9zN3{{AMH{1)f0qtAGwx6o> zI=$wfasWn0p(TZd{LFPXVb_X?ffE+YtSl9AALDQ*U7x#iMv0I`;W^?xe`QmX!|_RO z>n|PVd#SF5Rb6#ekM$i6qVFUt?es8sMDVN;ySWBv6S`&LNfZabwhbvf{ifm#(SkN5 zy}1H4U#{l{S$t+x^?@?Q&&MNIZ`lxVrSd8}_WIZe5qbphAvcV}%4(-L`#G3^`n}f% zFWcHNM%7Gd6zbUs>{ik7!0j}`y+5Msan(o&l62X6>h8pC%SYjIh~gwUAf)Ws=3WM_ z4x1bD5ZrugZ+rbO$wlzNuhgs8PVOgYHXU`65qbEho<@?bTP*_X&SAcvXy=+p<8Lda z@;Pw|9%i!niOKjjG-<+;xpvvi<)o|F=I3XxY}05bAW=kV2=1`f#MAe|+)~T=(Fuy! z_J;LPfjYH~67$Qt4Lgk`n>fxH)k?xN06~hHK$eC&7c(IPu?qC8W_b99_~)jhme})E z7(A?##RKsSPqDZQ%T z!%h%q&OygC_vFA_*I=u-RATtzczkP_Bj#o2@9>lUEtCIpgUPK$*c#sHVK}ZtR)PF8 zgv9i>WtQK+vaZK1URsEOO1)227O6PDkv#tQf7)3aoO@OrKFAm^m_H=UlwmQXMaDEv zJ;a;*Qd4hzEY{rn1OWd5MNP-0n}zq=36~z?qC}2jTW|i#ukjaquzI>|(W6BCuy=sL z#jqSU0_1V4#_-Q*OLcBF0@M4KmSWY{s9El|F$)1V;r@{51EGK^L5|{ok_-vvPKr(g zdsPFpVYi)!M}Nht7nPLNA;kaT?zQvuXs;+r5qA4+-vp6?-_oHZ>K&=C+B?A0-OQWF z@+G^vAg(Gn8f%grZ71SQ{eeE2-6ci*_==A3@%vR+s6tzp1o}n#Fwz791VJW{w#P{v zt?D}J`7QZ=tfetRIT#V7SV~svTf?r-p^1^7xO{qb-)BYYn@p8O-x9YC!o(xIY zO7-4kXkK^3rrt@xYIlF1Qc~3BuUESXE9%At3>wX)vy5ok*R51p%&QJ>k^V{KluaA^ z#HM#8XRrSoP635p+iVYVX<{J)n}zQvJN(bPb6&4m78EZZ{ve|jjoJ;lgjl7Eo;Dta z^l;EVj%Qrn1Qf4{dOWInY`7b{?nw=%&N14rx7|h6B&XBHyA!2MX#ZY5{JnJ&4G-Ui zWLPF9a>OHJsA0EUoLwSm$hP>U3+=?U!lUjt8*g%q5H0r6KW{M@n2Al=97Hy)?y!hl z>M+$%#g$DuSp%tFGY18*Lv|ZlyRczt@0bkF>Y^6PlT z_?~~%EAm@-ZYY+#{&s_P9aJ7fbAH1U&3frHR+1Qk2UlhFWrV~J!oQMec}-TjH%hW* zWcosDGspMhXFZM|dR&B6C3i|^e5KDf#a<_c8b;*&{AnU2+}pTIW3^Ht+%boO0E9o{ z`9YvP<$zy{m5#X&-x=yxnExZ7vI;)T?I$o z`G8yEKNu-bPyO{diJJ4?DnR(b$vGKtKBR-o4^UEHqmgqVs@AUC zYTk||Iry#HbiUdRO*!2NfMEhk%PqPTOUpTYzkApT$u)jaDqNeGA^BQ(YgRB?IB#Ew~9x|fd+5I<@9r-|{S zJ_RqM=uia@2np?rtR@rjG262!aRzD{Xtl>ge+2lprq{n6N8QhPFsj32FU#|P$ zYa6G?ToD-x!X(zPQwXPYPiF7iQCSR#Q=&@tK0o*xRif?#oL@N$R;u%Ki45%ipw}}0 zHiIP*xbG!D^CoD)tuJBAb9+Dt1$ypvHXBC7@**&>U3nb}Uuh32*(g#zUglS1-}@i3 zDTF0){Kpa=q2oTVzE%&B3-5GD5gE(ckv(+dU8zDPtLf25dlhT>@lW}$tpyvIyw8SI zA(NYy%tC!r_$O`d>U zL}MB*AI7dm1+w@sV~39%V^9#)m3#Xe0H*AlpqfjR>TYKfo5+H;LB8h|N^YK!xJoCa=w( zsa8jahnK%u;!e@7Gyv#ZU_GixJ2)LKIH2#$7_QgRtnCMf4nQ{v#B5EGvp!x z5VAbGtv!-Eq>!IG(uA}P8?UFEqI}gLy}aHKSu0rRIBIaJmk>xf(n!11d*=L`SnK#@ zMUzmw{hRhP8P-oz+pQ|{wA$k9=!$aQTtj~?Wj+T6|MTq!jS64S0V^Bu257JFmh9(e zI4DKt@a72J#_2ln`^*l#FgUJu>zE?K2mDF=MHRR;>|~p*<|xEp5lVt``zAV)w6hL{AW6&Y--9IjrH0a= zC)hNa1m*O=e4QVR5lZv(^USoKMifCrsXC7+_}9VMcbCj}kay~HQp)_9QN54+67-*! z=hVR*0BN&~aq-HE8y9nf+{!#o){8H=Jq}2+D*YcANn8P?{j| zRLB0q-%?Rr7G+@^?3muE)NrXMA|a40pYlZ6n zwxsp{%?TY*?{cTa^!-@S@(Mq=-oANSFB|>CQO+f4lw93K7^0-|2YAj2R7(1GomBr2 zUTQ5JY1LU$!G=zq7|jl8PaS3k;`3OPE>g&#krP(NdT><2$8#C|{})5^zZ4+XcV#*N!7f{DSs=ER=L>K|@1Mzb21A=e`fvM#BVeCf2Y7}pbnXFKT{*O1YR<_ad zpuLZtDShy?E!F>jDD3`UjO+fQWN8!!TsWD+a#`^uhLK8qy)kypz%_A5KoWeuQeJ@T z#-)86gY-of(ivn^VYS9txKrZ${H%{2uC%rL5^KnC6&YPRm7!v{00WYt)U}+;wC3XGbH}MA!I| zH+dunpX{pWFyN9jc0}4*TQ%9wsWi8E1&hJ9VrvWXpZi{)aoK0u1>m3Sm=V&r>MyuG zqm7>>vk#7*i}238`P{QC=MgLbRuZl^GU4rPf7EG%4BK0Ei= zyDp_eNax0af%*%96xU4qW4`|aJ7nsdeDKcLG`g(3yqpomd*KgIrdu^pMSp=uzVF_l z+bY)uXo(`YPIUS`b^E{r5)G=KjlzpkDW{Ljrx%{c2m-1vl^d(^;soIK~gP{-sFj#8}_i*xU zOYtF0PL`38X=G%WiK*g8uMs~16!i)4?+#UKq-4(%zC35F5JpMGe{fkjbZVZGCuXO_D~UQE}_oeap+9u^*Wq zo=^V`k5Cs6!v#nlG>R7~KwnF%rl!WK-C_1{m89G3u+nO33CSwO=LbUlpJ6dTfvbj@ zK^#hs4#|o4o#Zyf0`QaHz)y}}US8JbvaHdnb>0W-&++igsjK@3^qAD9Vo9frq$MZs zeY^D(rii8c$i>CQB*er;OACLdr471#qFTnSGsC!@2{W2K2%M_bR<3ok3*KV=4 z%yYc%yIn;siSoJ;eCOesjuiMH@C_GPJDFo_1qXiFkmAD`1JBFbfbMs1L$|UcztF^e zMFt!l9YN357N*~QQU0!|I59T%O-^y-@89vEp`qd7(V?NQa&lusqoYG#6^CSCUoY0XrS2p1a2N=u7NN=nMg%1TIpzfQ}^Nl8n3fca|n0A;GC+@9NPt8@53pJz%u zBMONk4*foQU{739E%CFAmi9KVV=RzRvtcbvf6aOat^Jy%q4VEud0N|!SJy(r0{*(3 z?&Cm$%0xo^(Pb(b0|SFY^T???pROhkX5UG8{K0uwP)ox=MfuA`%a!&Js&oJ9AVi(%GzdPR+4Q)N*A)H9!c zZ*OmB|G(Sr-O$8&Tux4orkYx6M#c)q!n(c@xM02mTCL5j2yW zVu$q|y0+-CUY8{%Cf?jM%r>Zf{<<>(ol$(LAHdy51Gc1m;|I4?W{wOleKcuyBt-WF zFBTRX_WvP0z&)%m_D4J1^+pQFw=E?52zOMBbe+fLB2EOOf91Zy6a+=GrxD z3_2fi4m@U+k?>*o(~aQFbL5Yi8Wj_4gx?hpjjeD!)9i=*=8W#J#egGdFXK4u)RgwQvk>v2< z;X%ZEXa8>Ke5qdMe7zI9;Ag$V=>(S*uvplH^71kL8q4}k@1MA$kdxzIn z13pZ{RPW&B`RAkL_@PLaSxOYdT1vFYsJrnjE|2M*h%7(rw(8?H`p7!w8vS}`JIM0W z&hI=Wt3D7J?^6?X8^oeMM%cSQ1bur#)Y4f&K|x}CI+Puv9JM!|c6|L{dKXT}x%>Bq zH}sx5-Y(M&$&wJq@q%^GCXn%SF)}8+GXPaYc;_~CXtm_~+@uSWABJy_j?&?ORl3as z{?rrKoj!M@!k&{C@A~L61w}w&tc#3OQJu*0y zJXgk5i)DQShwIvW`3KympxZ0d5QA!;ytvs0SVX(2%Yfu(_C~YGnpKDXHfMRZt)A0U z(_%0;wFCr^Vh86P_DYrtjePvL@om=rbg_%pL9rXXiM1`6cl%@fvg+4U27;I-0KinKOL?h{ z{%NK8vwoT3Pfs{nkEGN9MIKv9UM|1LQC3^}^HoflN-#ri-8P&xu6X(o0D)3!ze0-7 z#jl+Fs!SilQb3_dNGUe9ws)sYhf}oOOsx1CRZoA9ZwJq6b2z8m{_+&=J`f00yzdzn zNT+0fALU&!x0MxVs1c^g!poJV@;KBN`>oFPc24utfzW}Pj6^V^+pp7=W{7B_ot{pO zncp@Yavkd5Z3p?*t>=?J+oVZDDQt#b1G@`qiJIK7P%+=`mMJH*UcZ&X4_HosQooms ztC8ZluB$9%{BWOP|MkmB&zr-?+ithBIUcP#GwTyc>wijjMGH-MTlRmvrkVS|@NeF$ zEHS9Z6cB8CEu(u;R##8@rRvH#>NG1XKLcOFP48N+J;93ePm#`M7YUS#OhEI9HSLa) z`^_*)+s7>>`UbD-D86x=-urh!fv_kOjSZyOftqsq)YGzR0sb;mo$z}CKXZ})Ba2ro3diw{5_G{;w zfEy5lVV$l^sX4JzNTC z-sgrK!H(>+m(PB^wLHl)`Q(3H{c~3`d!1y1V8-`j#)`-JY{I@9WJ8w1N870)1<~GVO*7beq zqW*4UqNQZLM|+uvLhESi;?q0K{*;V(MEC`>%gdd7!X0;Z_yIfE5tP~W-fq6Sc7`Wr zNWeJh8`T#KLZ7fj+~( z>S1A~I;y5NMxeEGAD^_X?bG3wQtEc8SK(jrYub;!M@8=kJ=EA7II$gHUs79#{}SK1MR1#J%t2hd;C!T4^A;~}T; z*jFal(%4k_Av4=%)iFD|5-)%SQqwaIQgm^Da&!AQ&WOPMeXZ15|2M(ya1xdqg(#Ee zkORG|l@Ra3cb|Ta2O1aye$Z6UD4SMk=Q($@iutv-i*9`3ChgD0>p*=(7hvO%187!R z2g@}!O&xzn$ypfaaPzC4o6BYMos`23f<8!Q;iRvLvslD|M3}R!>iv18_aQo@AA{U7 z6BXc=(?zZ$nA7NEgKU4tOIrCl%VT#xc_88a7fvJ_hZ2q={D{#%?N&V{JC3<^E7Qby z>kc+TB7DtQ@$J+eei&9oGOrJ56$IexpPwiiz2ANB<9;3?+JH%6>|iy}UY_&XejN>G z=~*~{AOWsFOoS!tFGfN>KtypfeaVf~%k7Nwe~Zt?e!xb0T3Ww_@t;Bp<*(s3u`a&P zc-XbGgCpnz)$*VD5lxy~<}w(yFjyFTlG;rjDRfS~zy&Clkp1eI8d2PBja|5E?QB+_%SLV`ypgT#LLn|a0+co&qaBuN+K zd>zfCC=4dj*h5dD6T;X{l#r8EjB4+R=d7?;DN2)!WFGTy|GwD%)*SFFCB^5KM^of> zN6bX*hwG|I2}{IjB@K=>Jy=E(2pHAyk_gf8rCiJAGlL8pW(!(bKcmQ-BG zyAY+bQP$Ic1CUs!J<_n8nIKGGE`2l1XHXb31-6ODr;0g;-b1Xcp~`8za=OsdF4IYUtWt64Dw>Y{_1%Ytz*$lxv@EbS70uqmQ&HX>bz zlO;rP8=E6k%C5Vf*ENtoVe3mNwLTvdm&uflNU%+FC#`-8A; z6sQZiM#}raA5o+lXss82FtGQj)==Q^t1P|uZk-~9{4(@?krVhjhKa56@AOr~w4Dxq zpvFrpzWN4r_G3`{R5}XvPO>iO<>orJu8xz$6!f%TRZ-JXeOUMgz1iY+`JhR-bO1O} z59e6S&Jl=!8;_M2p0*r)E0-$8rJD)^o!95LKOE1z_JL>W9vJ`iSpJxCv)UncB-1a@ z_6cpmOT?bmv>=*Rf)cVAmwgQrQcP}<)b}0VihO;D&1mATu9k=fmsfDFoP7#$_4ES< z4$;*9y-W~7j3(joUjMN~2NpsAz=@n!?*kEJ&+gBI-;4c}Ig{t-qZyMVQNUTyxtu8!?!UdLwC+iRSo%? z$x`!t9d!Dh%FaaUaNb~RrtL0B4qw^wC3y5TG`}TzXJ@?jH^V5`PIS=#G?uL`5DZmk z%BRA6Ff~I0KkoXH+k4w8>?zDBrkCrMg%y6t_R+R?*c^o9n&cA0qjWah7VILf=lk5_ z#Nl+U=*-}5+TGmp4V)q3e%1yXH|`5t^Iyp_sJYusF?c?0#(KTqPjK+~QG%)4{;9Z> z)6t5l29vCTyOKD1?REycWJt`z?!0N80gW{iz4E1dde=GIf6zM2YwCZ5qP(<)g&i2R zVtJLqBWkHnWZ$7YBS3H2Jj8LNd|lnz%5`~HW4(SJ*1a{U12{MrZmn_299@c%Jf8Oy zD;Wg@5-@-Y_n*|tER|}s^&TEe8_EWHc`U+_0;OqPMzUL*7EsfYMLGA`Q8%#y$P^8u5!YbPJ7 z@6!y|(RA>eg=KT6t&U5jgRSu_^_6(-6F@H~btP*i(8uL--q|s2tryv^cZ7?vD1mc4 zIp^rrOXPabwx@hj;BAC|iIuv1^J8g-FMr_UW7}b9#cj7OLS69b>>qW({GWE4#^={H zH*zF*`0D2 z#>9q?yczV`{wfCQ*mD+8SNGexEII2s?h1*1uk(5gnz}VAT1N_BG=W@XyFVjHAS2!z)~K*D8J+c6T1@Hw0Nw7p&eezCOLw7)1EUIv8}#KN{o5 zlA@9SNj{t3XWSzg1qU$@)b-*HtrR%z4&<>liDH!0@g_k?o#xUnv2%`X;JQ+IBZ1;| z0-&OB4$@n?muqOJ20U%z5M{_k()lEs!bKcz{c3ilwBNPMn8XP2jJF-R5Uvr%4Szq# zM>ue(zGAWmai^oVeYnxh>ztZqw^j*~8(k)Q6lg0EuCuqETj@N3P;R~9=Xz-QoH#ef zvza;&`p^RYOTKvin*@~vHL(ZUvSL%+WrO`ju=G#_C@M6-Z(B{oy`xBSgxO*F z@d}s)=mMpCUDwYf8Kwy`(w5S9`+^im0hVNOL?!YkT5Hm&QXQ|(QT@1706cfepWoRX z+;8d5EWT6NVQ{Vr{^5O_4Tp`3uU;0y?@<@9?=tJ>y7|5DoMZ0l<$StSti&ul9C-j2 zUxayJcgzr;V2c9is$dHC4b)GDvrtZCl8{M5k1Svjd}b6n^G{=- z;4`hv3YH6aF7h|$q4}#~d@W`Gr}adH7TNViozhEnk&g~i$F{)*oO-mXjHd zjN@V;oH2Tk;T73O}noT5R?aekm z#4{C_2BbV&E(pj>KlXC(TMu;q<*~GQ_7mWE`SJ4l(RC%Ytu>93mUIlYUQ{R{S|zF4RVV4X zP(5)_ADXiVSgEaR3iW7_If^U=CrgyFc{mzxKr{bd$20g9~SoT>Tk(yRrQBh&{v-Grbw#iU4$Dv73@6v(VkU)DE zVm2(@F%f~CUs_5JHP&6(Ud!YXdRgFUk_O(5tU5ibw-J|}duzdA`ScYPU|wV%&~*xq z6?;Smu)?;BWUvI^0vMT?>`^t`s6ycUuDEmH5=mfOyV7LBK%N!L;UuS>tS*~qcu-}6bUudMSo;=`K8ho(E4=L+q^!VU}Q^h|vs~&k*hZLN3zwyoINgRyS zlPxm9RUF{9wWyly`BY}ajs{RypJP+-K?g{adz|!tvqb3WXvKK@_ALZVzVd=z-{i1F zczVFV%NISvaD}<(voezYeDyubid`CQF?7EAL(9M8#jq5G&YfJML1K)szAUawZ z9ahWKdCt0=emizqA~CVYlyEyFe~oTyd3!S1OE@0mTWyB@=88k-wj$q#1vt!Pc4&MY zF_3S=(`O%?1-SSpiSsqS(0aKttscMb*8m#_pFmN5m(#@Z8-Hc%(~1lzQPZSkChon1 zp**OhF)90b7-ZYHr9TxWL(bzk^owvG6k#qcaQx3r8y~I?kbn7ax5e1(0ljJ`4|6oS z?1wL4TrIrmhM~B9L_cVgdeD(v5OUkvmZRDAXSnq#g?H$=S}48lYuDQEgs(14_dL_S zxQ$0CRN$p-xAGp2cFf|}tr`cjR+I{rSn4G6yhJO+b>~uTj|wYl@wwmkP5$V~B&o$= z>%NK&^Y==eUUV{EBi!Q`41als4XmA2EaHjv*{IYP^}iB+k=v25dvyX&iqo_|y747P zfm0O8hJkeoSn8?_1puS^rE%e7S3oLUk^M#o&f#%s63~@2@K=x^EkDLt-PsYb){~W_ zOa)d5w-;I{e2WoSbhBw08 ztMA}N87u(C{$tZPm^4)3Zne2gTcToTyG$WAdCOkCC-E%f-~$BkD+oFP-lNamS`B<5 z7^wf09k)SC3E-B;mvY)F8H5c@w~pGVFxM`IfGsKg?n|k!mTW9(_|g}1=IFRSINJ#GFL-iOkP|yCBv|n z>db9#s0)63R851P@>p}`$Iz4S10Lb-$QV`z%cm;Law1KSqa-&{iOfFOW5D_#cVDTE zmc`e2Gi5v}<&N_j4>rRt3Dr4M8S;dl?Co~l_LUKneUj!2 zpp20wmGf(Q7Qj)b+7k5puLN*~w=9G7=v;WhQ1BKf%mePbUI}m53niKQbX1gyV?P~c z(-$G3Kx0?5pH#2Zi809uMbDql@@#k}OYtRU+}vdEQquqP#A;!US>}~9VXp3Yh=APl z@^G+r(gzX%3J&Rav|1gOZAgyqU|Iz926D4Z9s>cHI&dfX_`2P6~Cg4u1??sF#_Tj z+AEca1TzJ$4QH@+~TDhxQzh2}> z3n;X-yb}k3n8?%iI+{S^`E|=#FYJJp4=;NLt<984r~qMV47twZ&fbIL{qsBUh;vE^ zY5ER4Q^aRCz|-O3m2ZRYd`MT2@vWL`a_(!;ZJewAGVtB~iDRw|m}gl*mnoz5n2y zT<$=9f2%m0PVOW`jtxoWFimE$)r$E0{UXFnn;SB)>N}2S=xC^WYN&ZC#w6yZ;?F4a zRh)|gX0F)A40X~DD_T|`R+Am;il8%V6$qT0WcqhKOxMk$6c;Npu+(c;^a#WZG)N>0w31Ir~DnP zV*{~C!0(f>Hu-wVHi;zhK9IjG&gQI-ic47k-DmK9S#wL8v3!vDq)F}0js@TKEYZju z)&TfgT37n^4To{myvHYLhAQ*}m~Dgn$Kl7dBewR8=Md^hiHU3xyo@#F@^a}E%6K2h zMqoq{kG8^u(Mg%%-S=TdU0r-nbJZsuS7w56+h`&J!c6bw5M+H%kBzVuSeEYTdVAO2f3{1cm!iXUxB+_}!t-f<j68W$Pl zuqJmw59tB2WjL$vXv5fa?){!-EV|zW+vv(QkQX5bHd@N+f&pcQkqNj~#U_0KY_CcA zdfRLE!N58TYzdNv4$m6~d`Rt zYD7fA^=IXb&a3b^z;(GAm#=R&SDZm<7;~7L_9fPVJ(yyPauREP2Z!B4$TUPN)jE69 zeK33$m33zos=+$E`PZ8hfSLrSusBfEg&IG;mOuYHs;e7gd7&sS zDM=$s_@p>GI(*WPae{mG>qp|sr#4VEW&Bx}#SimQz+vgUpz7=Giw}-qtF6zMm2C-) zr*ze@Fl8Os*@(1!f%ULR_AG;?hgyJvlyckO)g~>?&sd|1Q7j_Iu7cwt=O=K6$?YJ< zvv|dK46VeZ3`i0Jg{+41NPg@Aq*k-*8)J%Nv9n0b{pVL}+24=Ew5fFZCXilI7CxM~ z3KSGHvk;DS-mSac>EF2xdrT_NlAGs+9@C1=-jzVR1XUSFRW$5vp#vem$Q(M(@}ss> zYQ9&FWgL}|mu&X?W?%a~S-@51maKrzSBKiUvcD?~coD8)6uWR@ACymD6|2>LI??P4 zJN@20v2j!Od_2c?UVd{!@R)5jLh)e_Gu|AM=fA8P{2fF2h_*Dm-83CWFGL zZLD&`%8UaEi`9@=B+9ULLP!{AXrf7TLv>s1;^@Rvi;)5sg;qFrwK~m6?r}If{AWu| zO(_)we6G&cd<^_{8w*qaG-xy9mie7k9P|xs#l#JeW%!xH@!+g$pUpDU%R9_K5uOMN z{qND}0ue+$d=avJf6XU~zcO5Rv9!0318Po}O+4towgj^g7|RlE1z96_5eHPBSTte-0*6&Aqp<%Dpek z<6xOK-W-Y>T>yPT)b!du=C0%)7hCtfGD4|zUv91nSo=#C8@1xHk^_SKVCNmzUB*BG zml_D(o8DY*E+$*(>N(y@V}bU6Hjl;!t4YI$Mg940g?QB1UyqnOYuUkmZnU$8$9}Mn zyUlL$+0?NjmtXT)Ti&M^qyLy}T*KvnyaP7m;~h_zrHrQY_>3JjqWTpP8n1VzHxImD z6rw=d!$`m+!bJfDDFz^qq4_?m9vdbmB)C8Y#?;h&2noJGY%V)`uD_@Y*-$`}=WQ)9 z(@&`u1qTE7(cwQQV24-5VWv)b+SAG}AmrwE=&8>s9Q-t9U0QVvO()b56l-u51zfQ5 z<)rr(6<=p_!x5le#8*e~{K~QH@QdBS%LJw*(>jSkD?j`|)%1s{(~a))mIQ^915p3$ z?*DAPtkg$Y+>jK5Z1<&ECinB@wabM$(P;ITUHg9mlbC?XNT$x2F6(C|VTuX4nvTg6 z$c{Kw5B@l&Sl=Ys8K|~Dc0l9+wV+9`zuhTuTAqwAR@nQC$;5hM-;{|aNuQ?W{gKff zv{>=o(y#z9WE&w>`Pf`1s<})+0$y6x+OL29nSHRWsqM$JI_^Q+dhx%nii$q3!5*kM z;2hx!p?u7A-JE9a0toQ6`!ENKT(EG-^1YO`XifWw)-PnL5WD4$R(u6+vx8roePNBa z6{&c<m0?m^+T|48`G(`R z8~HLxF8)wyx{t#R3gdRlg1Yc9Nh#ad+ke^1GPwfrC!;+QhZQ zWzb6OX)p0i!@NX{%HzBcNY7_&j#qc9 z!(2Q094>{Auyvm#D2l>PW+t{7v$FOb7j5&A+ zl4FO7u!x8V2?@~&F|mo&6q&dJ)zx($J(^g%|Myw|@{b|k;R$+K5bGk4Wio=Hsl!Yd z#f%h~UtT*y!9G}CQDIwdTV7G#4BF^28DOk8=W{FfMw=*eA3fBfC4E>xYh7Rqz2j`n^J~V0oD>!lcRT!gQ4v z_6izJ-G1|#K&scSUm$CIyraIboY8iZVw<5P^XYuSP<}Q-^yMI-{l%FL@I9N;x_@Z% zdT)GUbo6NjDL}vbrJ1tJQ24A6pVH?k?$B_Y|D<@s2@)IWA4wAer!@CgVAa0TiYnXN z3SBeeL37obaOhjhXt-V9b-O1Nj6!bS^qAxMbcnMK(pv02#~ICO?%BQ+e(9$r()Znz zW9zy)VyYouFDuYv-GA+R;^yNsE_hmzRaaLg5U8#{{x4c>!xm+BbCEw2bDCYiP52uA z-^8uP#JxuBc*^-wWS{@RYu;RQ-B#mUUG{h~w$sbszkmO6#|B4{sXH@1Gj(8FUaIq` zTfSz&Rw48F)_ct7H{f5@Wn}(= z%sE&bAYeLv(z;i_H*G6C!G)5@DUAiMpc`}nn%_`?bVH)W9t!DRFFl{s`m)A>B(s0% z27s^6)7(|t((kW@&z)QK>~lUd36-RknQYGkj`0*q5^OiK_wqwGx^K3a3S=mA()TVs zg-^Agm~qjb&Z8dFBNmQ%i0aV7`ruS7As%n+243>lH-4GKq$v;g%WSJ#hjRy`4-NgG zhx@e?D#P?ee0l*hb(AU1&bayu87#r6jiHMoabG_)wc>in=r35GIAQi#J#tw$=-kO& z7Opz>xe*z-cCDN`?3!l{0>hlb2im^$0$;NPJ61BXsF>;>{(jH5#db~l^9Qji6dOG< zU-)@etN=#<>>@~J@yV%O-|3dJKApg?)H=O&K7NC;w5lp*z4{)Skv8?){~rj41?EHo zsMLpDyG(Q`>FCU7pYP_pdP3okrbB;;@R?1mwj0R2WJd+sk6&?d$~rov@M@Y7)=p?! zMf+KPdLDfw6F&;bq`Rf1 zrKMw}hVF*%{l2$m&CEX&>)um)pMB3$!zuEO<4~ED#YfEhf?svC+{8YAnDsOJ-|f_p zE3qftO(=blyF0q(&fg;*dS>RQf3W*SJHY(0U8!(Tg>MeuMR@)S+2V89tD&$w@=anJ zV!t|Ak5>Vw^I=8Vt?G$ljLGEsIrXBLcRhLR#4fB8KkW;{VrG^CQu^VK$NvCpl}Vls zjpvfX$=RKmo*5Y(fn6gZ^qilcA0F;huw;`{Q9Z>8zC1qG&wMmzUOiR(Dl{3L?==8T zO+9864oz*|Vf9>fejsW7wAZ%Ld%>#PWYEq6#=!+tFU;$61H)Ua0O6oz2fCL$xtMsmp-w zcFqo7R^I0SJO2pNYC4EPp^lBPbLP4{*Ejw_~?mb=9<3Q3RHBDTtk%oUp^9U!ICwfZC>t38eXEdo5_iQz&?uR|j$fZUFYT#Y2RX=6 zED#ncPS0&)koXPXOP)YbXL00fqjbcwg{Ad)HgEmtE}>o6+}ES5yZRd8=k}R#A{Z!P zM&f0?g~-5ADrMUlrcBIRHASol=&5%v?%?pTXXh>fUgf#LF4i_P? z(G12WH*Dn+K*QU>@l$kP=zLEypN`4k7E(Vm-W-QXiHtDmT3(LQ;WRVO3v@^SWo1b_ zPF83=T|Nhh`W-LgKR+!6RJC50>pMQoFgLGt_$_{0F!F`WjpXX7T&%2Q`5t#x>|ge- z#(GngW8agT#l3X7<>J24pK5l@wYo`7)#h5=zd;%q9c5%>uswkDNxXee{HOkh^^h?v zadO+`=_0*^zDUsHaHYAru;b^-!PVQ49w)k=`{V1>3tW)xZ_tOE)b~UpZYwR=Gcz;I zc5CMc_gAdlm+*^8B_glcKosmjl{B{JQt{X00|J09w5ibIC4$UPLto#v_$95jU?_&9 z^S*?eRp@04K4*J>b>cZlP-1Cekrq;^w&I{tPAe)Z+6Fhev5q@E+^aW%bX2&BLkwmb zz~~!?szG8-v+M<#{tk2u3~if(whHpssVOPYl$4ZXRz_xK`+JgX@11?tt`!R53ofO1 zZ$p5MKe>?^rAc%uTvXFsrI^H%K&Qgwdu$vB%f+mz)ax+SQNIctJ*r!__sCp5u30{+ ztSrf*YpXK$zTM)w>>(xOKVSWVucPB%NW4&GYx7 z38ij1EP7e$?{!7Qb7wpG1a4Vj0A zdAV=Q10k$~sND_bLPZ~llr z&8I)T)jwwTaZtnLi#`$(0uPV9ziUf_ZIVdT2gJ9z!U2zehZVG09?MUw?RuUW_UHN{ z;>kpPri&CxW~`s!&BMDFuP2bQg<9SE4!^d z^>oo|STkp)e2o$&v7ASBB)w?A+Jy2Ugqxm;>2fuIj)St?_r_tl$uY6jQV$O}Kyi8? zk@hVF?&Uf)D^P_R?VJngdbJypER0GTACP{ksI?q6ZZ?_2%_}MzFfg*sC2;iE3d$4v zctNM9wU9izU8T2w&B3zP+iO6L7jXYDQ#_vICa`KAQZj~k37GoyJsA58U>qlO0H#uUikBHJz& zj7f0G4tkzGE@;}CkxCH}5z#wTDyyw^thSnaN=p|r(Je7;n-ay=rT(+Ys#hY%LCrMm ze+Bx6lkk;qf+gCL@_pZF2KNDw@IqLTbam2BmNuF3qAt$Q*NZz2R!g*j_Dl!=yLwj_ z1cawb`?tx!p^kGw-@T0wgikVGa3ZByvON`bdU`gT`?~_Z{3J#CEC&K1hfuh2f7pd? zjmLbA<^u)(nL{*i4EC+WsLS^2Bk{9-9T-&JP4|n7NAs$=uE~ep?^-Lj&*&D{Z(u zexb}!;E|D$VAH>&t_oH=yE7#!Bf%~uj}sl<7sch}@Z|tq_K#@cw?%G)p;3T6I2(0< zd{P-v{MY1^mKBvs1fh$q&7VMlLIC6x3#=jJ`%aO#CZ$w@uemP0YM~MTT-Sv+{wA~! zlUZpSW6SyDhziOA${7lJ^Kc4|cU=Aw72MP1xa^0I65)q3?bKN3Yd!p}&>a_$PcKEIRK7C;+zzD}R$C6LPqn zu0hr^G6;o89XI`G5lDx|HVxSyj>~?})e^ew4L;5JUN*WTCp?bIq4o{D?lmVBvg^;L zM97xL;PF(YeaV8o3L&bJFg_tGBIx$Jix`5<<)ZdNlRI}qEzfi6K8c>+kQp0t#~GehMSmub)n$fc)a`7 zhZIlKEq^WCVyvw#Bc7=QLhBnD{UqbwaaBMW?Mz^l$-|Kr^G>{H9WMWN0Z{*#@9lFE zC_;*>s;YwWf^w>=NF`N(PijTAZyIzsMU7kedi~^N=hi)JzzjEKW@hGz9^buSWMELf z%O((Ba)5J1a624Wl#}3kPddy3r>G{eq7Z|BY*N|L<1|irm@~I!0A7zB(bw2;c<-n+ zZ_{220=-YwS2Gyfy-+z6j44U*HhUE@x_#R6Q=X@yA!`)oGQ!Y;yWTu$@*Di!oz}pY zMk@)yNe3+*RP!-XB3Yp`K?@}a?5`~_hA$`2Fkn1do>MHh7?AL*59ZXXf~3xEoBS`_ zR3rkQ=;4s|fh{W=YHwxaX(EjpZCqd7ZEB3)cmCnL9F<;S-f{}urPMN zesI)h!Lc&mGU5-pxrRKBhW5!|FtBWKkR!Xe=*?DGtK4uOlqhDvJ?}${EVaQ*jJVs| z-wOoRMt~MVtZjE0wS~$?zSMv1TOfe+Q?et916Bfb+6glVW#3eXN|>GwD8L5e6OT~J zk3XpEXdr@+Q9C00OnDtw5kWQ0WVo1^{o78f^DWY$G6=}MX6%E5gGlf10eGZV))4UW zwBSFsS2j~Tmct)Ex}Ea45`KAJ#CSD{Xj@v;9v~tuIfdr1G#2#9_Vqv*ZllU)+8b@6 z1$>21a;*q*^!f?s7!M;J-J`>#%ko{H_y*EzHde;7o15 z0^h}69I4Uj)3a=WKrwH_1jgI8ypmU(v+{T#XUHIN5QqsHYFUas7JKVVCbc*)EB7>e z=&Kd*-NYeuVS&krHkY;EY@znZhma?o-;- zo%h{x(-AR`UF2$jWsj?i%gl@@;@fQXjpKl*@bJOAU-daeywwJ^J{Ne^i*cM#cGO_x zFbR#J&(COO8cV?l;>cKpE*1K`3h&;zw!(&ot6zaqQVgxfGblV6PW=+PUe%Y@*Z{th zfwBV%%FS@;IY#Q8%aR)CB6!zeq4=wM% z0_NZ$DeyJA86ZR-ryfHky6D4ez^}DQbqs?c ziOh+Y>m=JS690z&QNRD zJNhdX6uj5S7f(+?pTwGMxzh|$P8`n}T#9m^pSgZpeGD0{X*gVJ_f*kcR(v1vQ1T%z z{I&R>d-P*O1pk`}VN67RfpUk>F1l;4%3*ZqZ^Acy7zP{7j@JV~eNIOn#{B%PFONx< zCMI;vD{nz7EzU{O1$B#Atrx@uiqhlic@)i9Ti8UYrv(LhXB`g`D1mpBrciJ{o`$9z z*snH{7WWkhbo|iAlp+rT@uA^G^ptKQgFu8ippK4mjQ5stnK(*qMu~3y{V=Mel>als&Mswf1WtJA;KP2ubU;Z0(XgRD)r) zu}@#p&=40s`+}M%3#J~CX~jjHJJX7W7ZUNXtB{HwtrcXFN_H7vq|ZEq0=F3r|HWP0 zJw`^-1&JZ|o%V)Z*ydnGfg>C@I`$)uG@>0 zma$gYbjyqXDn80vLr)Kt@IfHdlut;g&Ut|+;WzT#Z)`MxE+jm z5QpC_e3aBX0WbB1;m(53bN&-6i^mQJ`T6-}8mNH&hVRRx|2l+=(7m_+44Woy_sk2} z1oUpA+M(qk>-8X@x)9`3BsH-Q^FkUO*Y7yLKuuxQ$8|Yqi;8l7U1jqYbHUk7)cOD@ zyGu3NOq0&Gyt7p4h^+)Qj=iL$q&z$(mfUT>8lS^8>kcElFLr-tqoTJKRVmXpma37W zBZc&!kqCJG*$A4GnQldg9BjyaoC;z{gtbv&+H)Y8F&i$KCGI1mpY?oS5OeZmu}EO<@vL6gCp#C z+IMWBle-I#NZ;j6TMIw1q|&B5M%w>k8!Jz)wwfr{T+bTc`7KS6G;F{UVLX8ULW|et z)AsVPZtO-F3#I|8S95bJDs$LG_KSA?7CKsC>mx86;PXM?EUM+bv$Bo<0av*%!M z{NUI3Hz!D-y`{M=ZGu=IpEdv+WED< zXM90{fKd;V#K-xIQ6|2{AON=d?fZtV0MTNF)Cr&%wY1*VS=M1E7&}}62;J*-)b_<% zbEKkGbt*ZPmyIT$N;@f`F^@gDZgXx|YHBQiQMb~p1MtftQdPD@Rk$X;9u0vl|+i)W4{#eJl2R0TZ8yq9naV(waE07P~zMT(oU%59E?)$<45J`^{?Y>JcdCRY@Gf8k8paKpOx+ zUhl$Po_8q+fr=H=xqkVG8REYpe(z}ZRc#sf;&v)2uP^+!z@5kO^c}!65Y77%5)hDz zxR?MHJ}xfw*mfaw1+CNnD14zfcBD0L(A?CN)Pt7#cFQtRw!xyz)x$$sZL1a#OuH4u2d@B7RSN8XK?jc)mS)hAl%> zheQ%R@V=&r(8PYOwEbs1P?uUy=CqnWR2g;plv-7YB6j`*iW7;Y9W#UC4jj}X$8M2E zl}6k3V{JLd8EZvu&I3w(C-YhzAcbQP-y5mHN49O-J$x|8Ob_Y<;@g)6l$6GOZ>VPH z=3rY}Uwwr%5K+VGEXpn_|17^xDzUb(Fss$U#>Q4xhfzo(WoBf+_u!>7`&UeP~cGl#Y{<*8XAWkxBV$53P|kF=S&|- zAEyWJk^u!8m2@0$Rs(f)-Nh~Rp*~lL%egHEMzumJD_z;2V#>$?GoToO7mpurB7%gX zAtFu*|sa1V<&6A8;1s_bk3d3*`Kk>7IANy2-x0{N{I=S9ekfrJ-uX8uZ+tR zot^XCbB{lfB>IVkWo^2SFCVnE-1GbrV8AjxY21TnXTDVp=@Z${m;Dz{!IZUi@QBF9(A!XU^P^__Rc8PL(w0aa z@U&=06l(o6puzgq+j|@d&Ih=em>9GV1Rr#@sy!l1Z$}z81SH$#2m1T@*Z}#7#${LM z!_bn7lkV2P!#bU^Rr&+#v#l^Wlxm)zH)!qH!6x63|Jxt%SHJv&GcBo1B_bqH{}3$z zLa6F4+G}dqSXo7Hx3>QzEbBMh>z&j#SsA?|jW<52onIV?8iHxwkgCy(JU!8Gk1wMW zE%0_mYI9nR?fFh^)>}=U3VQn-rbPk7onuwH7^d6cy5mXveEM$mwGWw3&39qK4?x}FYF*BTH^tUS1NPv48WCu%f5P@ z;2dXO`xjb~a`LyeazdJ4t;{WHL5qs2uW0n2LSreiVVhg83EOxJVwK#(_0emDCm@t6 zC@B%yif&4%I@SAGHjs$e0jX5b(@R@pb}G7nK-`^po3>ExOQw4{HBDEr>Bq7wuO~!A z#7^&|txivE&U?QD8DrxBI2TfGXP0U73D1o|pdG)E^YrcBLn1b9?0WkUFnF`oxa)D| za_01Mi9AtWnfS)`KJVaF;t*a%&tBXgKhd9n# zo3GN5;4QZ0JjnfZ+_w(T3DH2YUpI$y*o)_tSCTYIQQ)mb&Sz{IWq_Mh;yHE%dWERP z&633_=bhE68HmWgu+WTR7f0VHsA5M`6LPI|N}^sVZo0JEdkBk+IQ)cac9RG;)4Sua zqzZls4(aOh-w1PMXsIsbVTElyJxvaEEB-QJ3wS!IW-$~j| z3YDNpl(pdIh?TY_)ZD(f18TueOdKJv(;rVr*9_I&;A}~Z2GTl{%|~Dxc^7{GTBjaW zI@Bv7@r(ERDE5nGY5-!ma!j!`Z2X#NIdpBvgeQ3Gq8g3q3<_)<2ia!68W{M*YSdrU z1q@mFtthX^LQj9Exx@i9@DgR+1e9VWfc*XStA)EjwjzysDGn7Cm84|IdlEr9BgJqq zrMHi_4tmYG&I!NebOYye$=3yLbu4PEFSC1}+1cYYnxLF7d$f`EoX|fkj-$Ja}&S0*#p=Cd7JtiXV5Po>NGYsqM@TF z^MV~3Y{r0@;cs33yGOwB0&`}QfWQPHyGzTt^cN0UK#K#uSC5beYF9lyA0Ychp8hUV zw?YF_PN#KAaj{iFiK&f^)ArJ*1x*K*356T7Qvyyaw*n>ZtmI@*h8A~)o?ZNL?Ju7i zEJ*@hSTxI`ouj~Y6OfRgpq}v9gJj-saKPvQf2%j!uKVrl8BZ*DDR4Lb*c#npP({h} zg$u6xGUHKjIN$2nf{hoqVsSw8=+NJnCIb{jP~kth!Eip*1X*hPdfbw+=Cq8A`O3_q z^Y9k0O*J2%`=Ah!@tpHKN)xt^z7L;+#CPlJ>Tp$HeK6-Pq-&)tKL3J4XlxlHsFWVo?fH9?rT(20hbGt5`qQnKyLb=5uN=Ys_kf22Yb0*8Hd$YsfC+umieX z{<05Zb<%yAN*IMR_HPE9PQ$Nq0OVxff2EQuk|_f5Ao%sg=MHPEjovi8{7oCFHU*w% z%j}A}Dn)rA*N5A2)P@b)XSYNhIRVm#OM>?i?E-yDvE`~q@4&y@$bU&L&Cf7=$X(gm zgtv{XzC6-1Ry?D3poVV(n@Ym(H)hpxWMxdojjB03qk*zC|AK}qlUi!C{jEvbzXu5v zSXNfc5z{R%f4o{zQdFfD^TKZNhcJY^dvhyn#+7)tTBnKOcKVHL^tyH;?BWhcw$jqz z)@pZGpQh{tw*H>rf zsBp3-3^gU*j{h?~-R5_dFGlXTA&#D=}L$-cZExdhc9iG$QuZ@r^jR1|t{4_y)P6lx3=cnEE-roG+;NIZ>uHMJT#~2lY uurMi!yi;ut8!kB1c5h8D8L#izaFv8f@c#hC?V&9I literal 41237 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%0AbLdD%cM`?^^{7md9sNC|C8S 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 diff --git a/extra/deploy/windows/wix-dialog.svg b/extra/deploy/windows/wix-dialog.svg new file mode 100644 index 000000000..0d81c92ca --- /dev/null +++ b/extra/deploy/windows/wix-dialog.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 28b05df9e9694b31167abd0d843020437b894f5e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 19:09:12 +0100 Subject: [PATCH 131/144] Update wix-dialog assets: change background color in SVG and modify dialog position --- extra/deploy/windows/wix-dialog.png | Bin 26337 -> 25242 bytes extra/deploy/windows/wix-dialog.svg | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/deploy/windows/wix-dialog.png b/extra/deploy/windows/wix-dialog.png index 87115d4b8190faa555f4b0774616c65b25b46b01..4bc12dd296ee44fab5dff7dc0a5edc358ad043b0 100644 GIT binary patch literal 25242 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 DO#V9B literal 26337 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 diff --git a/extra/deploy/windows/wix-dialog.svg b/extra/deploy/windows/wix-dialog.svg index 0d81c92ca..cc31fcadb 100644 --- a/extra/deploy/windows/wix-dialog.svg +++ b/extra/deploy/windows/wix-dialog.svg @@ -1,6 +1,6 @@ - - + + From 6db316622c2c3416de60008f7c40ca174852e20e Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 19:16:27 +0100 Subject: [PATCH 132/144] Update wix-dialog assets: replace PNG and modify SVG content --- extra/deploy/windows/wix-dialog.png | Bin 25242 -> 25122 bytes extra/deploy/windows/wix-dialog.svg | 4 ---- 2 files changed, 4 deletions(-) diff --git a/extra/deploy/windows/wix-dialog.png b/extra/deploy/windows/wix-dialog.png index 4bc12dd296ee44fab5dff7dc0a5edc358ad043b0..584f7a2d3f6465dee9d6ac5ae274b2f5dd1b5633 100644 GIT binary patch delta 6562 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 delta 6683 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 diff --git a/extra/deploy/windows/wix-dialog.svg b/extra/deploy/windows/wix-dialog.svg index cc31fcadb..56ab48dc4 100644 --- a/extra/deploy/windows/wix-dialog.svg +++ b/extra/deploy/windows/wix-dialog.svg @@ -1,10 +1,6 @@ - - - - From 3064fdebc723717ce0a535c744a0207255bfe811 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 19:38:32 +0100 Subject: [PATCH 133/144] Refactor code structure for improved readability and maintainability --- extra/deploy/windows/wix-dialog.png | Bin 25122 -> 6517 bytes extra/deploy/windows/wix-dialog.svg | 86 ---------------------------- 2 files changed, 86 deletions(-) diff --git a/extra/deploy/windows/wix-dialog.png b/extra/deploy/windows/wix-dialog.png index 584f7a2d3f6465dee9d6ac5ae274b2f5dd1b5633..a1c5e5bd119cd4619d0a4cf90e329ef058964f26 100644 GIT binary patch literal 6517 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 diff --git a/extra/deploy/windows/wix-dialog.svg b/extra/deploy/windows/wix-dialog.svg index 56ab48dc4..758adeef5 100644 --- a/extra/deploy/windows/wix-dialog.svg +++ b/extra/deploy/windows/wix-dialog.svg @@ -1,90 +1,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From c5068dd65854380cec4bca8f77d906daafe7f99b Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 20:35:56 +0100 Subject: [PATCH 134/144] Update Synergy EULA text in installer package for clarity and compliance --- extra/deploy/windows/synergy-eula.rtf | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extra/deploy/windows/synergy-eula.rtf b/extra/deploy/windows/synergy-eula.rtf index ac78ea055..630be2c27 100644 --- a/extra/deploy/windows/synergy-eula.rtf +++ b/extra/deploy/windows/synergy-eula.rtf @@ -1,6 +1,4 @@ {\rtf1\ansi\ansicpg1252\deff0{\fonttbl{\f0\fnil\fcharset0 Segoe UI;}} \fs20 -This software is governed by the Synergy End User License Agreement, available at https://synergyapp.io/eula\par -\par -Synergy is open-source software licensed under the GNU General Public License, version 2; see the accompanying LICENSE file.\par +By using this software you agree to the Synergy End User License Agreement (EULA).\par } From 88eb0e553e066a2cfd6547f4051fa8960b312ac7 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 20:36:00 +0100 Subject: [PATCH 135/144] Remove unused image assets from wix deployment --- extra/deploy/wix/images/banner.png | Bin 8593 -> 0 bytes extra/deploy/wix/images/common_background.png | Bin 18998 -> 0 bytes extra/deploy/wix/images/dialog.png | Bin 86987 -> 0 bytes extra/deploy/wix/images/welcome_background.png | Bin 115370 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 extra/deploy/wix/images/banner.png delete mode 100644 extra/deploy/wix/images/common_background.png delete mode 100644 extra/deploy/wix/images/dialog.png delete mode 100644 extra/deploy/wix/images/welcome_background.png diff --git a/extra/deploy/wix/images/banner.png b/extra/deploy/wix/images/banner.png deleted file mode 100644 index cbf9b4675daac5bf6ea432666246c20bde3f946e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8593 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 af6APkH@o2!`^)yQ>sL(wAn4!u^S=Ob2FI%a diff --git a/extra/deploy/wix/images/common_background.png b/extra/deploy/wix/images/common_background.png deleted file mode 100644 index 647e8b40ce5bab287b874b4870fce3dc467ef619..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18998 zcmeHvX;_lk`@fA%)-+Dp=1f*LHo0bIxipeGEvC6-;}WHUrIov6DyXQmmzr9+Yf7f* zAPJe^5+H3#x$lSymWJkvi2DM+$FYaz`|khp{{pVdOP2E-?sMOttNieJBY z@kikGx~mrF17&131ug%q;$Be>k&!u7bLzxVhYEviayQ4GJk)5CGLeW9)qJMOD3B6YEq|oLGETgj{ ziv2z@^OOAHeXDQ8-nm?Bw#njY)BW-T#w~|9#t?D)qgCgBm9MoHhs_J|Lqmb}yPy7r za9ye&Ec?=lPT2&6xcpe%lEzrI@^tr>qe?5!u0LGuxAIKp*e3F~kJp}RTwXiy*!_oe z;7TTBjt%{r&57+RuXjI)8?zhY>OBcFK-gx&GVD+je+R3(g)vZM*$M?7EVV}ycT=bmU+?dsR=s>dNJM{M9sc4pB z!Ts)}!5sWS$M86uw^3$tb=#tSdyHg!dGGvqg?36lHyegW%|V=AM?xy6cx_n;URQb_ z9bzRbXV#fs)Lz%QGX3lH!%9VRRYW*T>pjaUm?a=zZ4-KlqTKNljaqnQwNkf_83>fK zjsbUOUzZS^uWed5wsmvYRM+X;dHMX7UfaCpMN_QxdO0;0tBPt6Y@c9Sc|~%l?nDC} zjDh!i6YS6pcYIi1QPnFrG4Br3HoqLPOP4~ol#JgwtSO)WCetuaROHj8d5O`alrw$4 zvK2X(VXo9e3k1{n!mX0hn}O<1oiBkW+je&K%3*COKDJ*t!`|i|_Hz)X62h|xI< z#nw`V8RCHiZU=l7$dOsx-D@BGS%xp6Zh~nnu9K%G94lPcrI_b)X<^E>)@LQH*RfjM zkGrV-EqFxDWqeLC1Upd^l(ox(;u9&*9t{lU|7MZ#%Y!`JHSM;zx_#pwy=TkaG|@p~V?63h zbNJinByW5L`XGV}XT^zH(OuJt+lKM;-PR_!3*ZF@lIYP!@xC7k>sd`bnQI~kn#psf zF8)pn@?a@E1C3?7nx}_NGYt4;8rX6FEvz`yK3cIM6OHIfzH5Sk^~L&02Ky`*qlzec zfFEf)J6Ii)q@_wW7kypAi#MYuPA!0v^VDW5^g;Xx^-wU9+8ajMYTwB}D^{D)BktBA z?m_L>l+RZ^}T9VTk$ml6~!LTt+;GJYOne$gubW;YD?nLJh9Qw?di2NwPhooh5tZ5baR z*}!se;V%Q$eoNJhqg!;*Bk8vJ^<%M$1~~*+pc{uN@tY}h$QZq{ zicRIfw%4mYPr(%y+mw4Rx|r4OnqOK9E!CCGx5|Y)b6S_WE;^Myu4H=J3g>O&MI;)_ zdVM;S(hnQn{B3PdE9FNk^rsM1RsK1hO8vg1p2&mNwI&3G(1l+wW?V-CP$n3PxOojVm%YML+v$rGMBV#V`LpKp%0 zoBHZ&;$;IShy0I9xBn!nIOYuf@Peh2I@U#Gl2$;{#^_k%a6Dm>I``L}Rz{&fPA2*T z?77uD^n|UBbp+F)0A^CL_-C4}8zHkG_oL1Z-}j?u_kOGHUoo8%6;y*Sj(tgZKy@RY z3t+cLlB$N2)a6J0Z;$p;7mVNGi%lLHX>vS>3Ox>1Q-vgb)uNbGI+^kW<2anUP%qG1 zpbSh9r@&M;-UMi+8w*f@@8qgL8aQp5aD|C=YPr6IG}zgZZa~NQ4AK3ZO}rv1+xlWm z)2gZMDw1e4h}(6zbz>v@c?saqt}}>dF|_c0tX01=!`9he z%hi~x|8i+wr(b_Xm>PeGCR7>v`E%Cws$6QIO$@S6O>!qjV7n6F8# zO+5*(W`6$CGFuN%s|YCz&LcyFhAWqEJg1upb1~#vs$#|PFkTcXuX?fwph&B&W3vxN z)UsR_JGx6tyYw|k3eQ#VsE9hktalQAvO2-lkCsAjs*4y1Nnd>X#sQ*_ zwRO{SLu$cN_Jeh~3Q$-3P^Ph=z&dR1BPp+voiKN)Pb-h2#uz`b$G`NZn0L%&~ePB(3tq|~zL znU^t7Ib+qBK(azfPFK(cpP}-DBEA;rQ9mek2e`v49Y|-J38qwrHn*nY41lB-%zRKm z3SsV#yyn6#q2_*Y_)Sf2{n&5!m3GFY8jJc!ICg?9Z*#p*d zt^^)_*pD;SQsv<}a&AZ?NoZSt?)hZ?pnx;T^FUbWn4%)mGj`RPo;NnDHAsuNIB!yN zNIty3nHF;y?_!fL9xtjwod7w%n#_wp;Y5C{FPz_~Gp0|Fu&G<*VAkjGYZq1FG!=Tt zS7>ulzD{`WLP{Et>b`e(hC$3JTFoKi@+J)9`(gWFd%8$sH~5O8%d84QrZj)RF_Mjp zs7ecNKP|jwG9FLU#Z)}T^I1U2rb<$10~%}5m15dwPCVUai4lFQu6q))&qjGJk{gh{ z4RbIF0A((5Cx4N#SyX;f6rnuYRk)pHpqcY zBwu{A0E%w@QlrKAfqFL0aBUr4RDzMTanFevj_$@eqtV6krR! zBpf!9K>q#fjx<7*4ugkiX)6^sRE1Ht@ck4q4ZVE&rYY~enc+jEmgWgVzO~@=)Fjp< z6IGPx&&h%of!egu0_t@*>F$i)O(9R&<$Mr43b^v|KQLMv%-T~p)*4rz$0ix4paK$q z@jbQkjVg1!q!0(ogjGRpvGLEn0J)#_pbgOFfZnJsD_oNfhy$&UlMYCsgRxNtN*A!E zmW9$UupN?%UtZ=X2Yq@sGr;MjigGZIURm*I5QdvkM%&f^OX^HX2rCY30yds#cwI7i z!xf`$qn-6Z&4(0XzmV>_SO%^$ep4Aig( zOLWh^rx8y>!xUcHc#tvy2;_y8r=#i=1)TvBT9gS&+YtvBTo(Li{o!kM;^T<|m*53xp43v$@ zX0%U@js$K+cp*5!hxyhXq!ds4&Pqz3G4z)Km{6h+uPdPqJV)Wru@xP&2ATAVXEt6a z{;m&%e$Z=oU!NauqIqY1E^?1Ww6KQ>sm;Vi;YXT}$!4&>&uHCmv^gOIkMMjZfTekh z7*^EtheP@q5v#$}j|b+UweYR34b=X0<2i@E>VS3@(9&K=t0)m2dCw6pR_mhPJtYq3E&OcOPB&(lBu|Pl4QH{jD{>uG*=0#ug_E*S-?F6+-K3aYqXBguK+q+)ZLOF(;6e++(vF`%OUX`1A|k+Ij2kTj4Wu~UQg!V z5XX;%{-N!DBK|rMXzBmxZQSDtt!}=!1;AH<4ko?O{=ZiyL+cLpEsccyuzGZ@q!NLL z)X)>r>O;JK@zHzV-+uN+H=%6tS#PJ{%JBC8h9`3~YWyxzQgrg42v6qbi^m{8fKhxu zCGZTy-3Ws#nBATs?AZveuFh{rksJjc8eCT`NX)>F7^F4!AMwub7Z!v{0(*9c-LFP% z{!5#5{~2I-&`}$3Ed~SCf)FEFS~lImU!)u)nL6r96?&o|)KYfJVH4%u*ROm4GESP% z|Cux`LARIJUsOqZ#$I-N$3T!Ijs2QDBF&rFL{D1&49Q&ENwKdJ8~+ZHb93DTW@_Uu z4)q~a&l!4srg;67y)d**vMbkXdh7dO-DsgI`Q80%ypV{ulIDgIwI^eupu7aG;%VL# z$h!*HhjNs_jR-f72?SjZ1Wg8T&u!ZGw;M7WKpiRF99*xeQ|hR7(zOS^YMzF!4O?|@ zP6OsXtCt}Gqk-U<1}WWVpzS8Ic*?~9sPoo~MpTKcBxFW!BF{A7>@dcB&%5>7|7A+! z#>>a3SLb%NgjR+PvrDyt{p2^1H`oHGlO*O6bHh}>qi~e^XVoQth~~~RO;S1u>1{$v zw~|Dd@f-gX5MdD11p*I0=L%Dl$8lvB>2nS2J`}%-#tN?lK^F&v^Jpgso^Lru);?Lr9BM>1+;?(1+k5tK4vQt5$ zFrZ&>(%RX9eRc9~z3;Y#hH%5C-isg-LvHomn}18ofvMjt(HNZ)2nR>rzP(^+RYyhA zkOiCimqu=aDLhIW7x%hA{4LQDG%urt=d2Q55RhwoXS4fV4VO*=;d3+c ziW|~7S&GAm&-aQ%7>T327sgknHSXVH-)YDVG~%E0qnC2WV_MjE!l0JL!=^YG8n5Pv z{jGueeOkARCB=eduotHZVX?`0Y$FuqFi`Qn~WIJdut3x0& z4f@=cE5V7sjM&m^u%(m(9c$}+{Jg@ghBQa5l5W`!BpjBkIkybC_&(kp27#9i+ULWJ zD}Dmne!0j#sBr9uEzo$<#n%09NM>(Em1AEMQAtN){bGCJ+4+jXf2#N%(OPD?6UB!g;Gccd-JYmQ zv=~hCX>c)v-yRae%;8V;Ri@(-Ps1YnSz8#>J9vDP$56rd>AYih;xW0&C9Vd*E`W^^ z`np@9uh7)6S4}vOs^)PI(=-&}`P%jvS&h2byva^jSLcBg;!4cV6Lk8(Q_Z8o)GS{k7KD2e)}l}VvhjH=w^f#3G#*ezwaRyn5JFH&?3$xzGd(@1EtCDv@%C%$a9yPTE&kx^e1#U z{a4zqRyudy6ML!i-Kb;4kCRkalgDeA+k_vhSakP&v5O*pZu5;LUrnmMp!K8C^(i}rF>YuwtMC;cqQw9cD)-N{EGGj&+Nlx z0u5C{$16i^T!djm-W$IduiiU$PUibxZ>z5mop-l!J%332T@IUO|XqYj&2Sb+RJsVX0k5r2U5q zUxXD*M|3i#fu4GWFOK9=;^M4`bd(0jux&5Ao@PbP)ac~eVTN6dBTdYnU`B=&ae(e_ zAGQda9<|Jvc1^dGo6fK2wr8kga^}PL+K2{Sar0k-OuTl2lqWx>16gzx-`X6?n*YW0 zaOyAOmNR?&4Xt!zVMbRSHAgXsJ)Vdy#XX|PboA^6E@c~tTc_50k$i_&K<#Nh+I@j3 zxR2G9HCUKe5tlW)rC)8E{pDxrsu(AK+6--WF(y_>vAaE-Wu;>xq~B1PKEK}9G$T*U z?BT(NCEb_VR%m>W5SJ5cNO3-@P>4;Vxz7ndW93HH|9xU~C`#af4mN5#{lxouNwxx6tE#>xRFOdx$UEo*Cp18tCtXFb z0^8FA39%B$$>-kh6pu3@z{#X>z|a`^9KQzAUgOgfvb*i{g8@zz*mJ#Jv+M=)fFfj} zh6nDXIX4T~!$6z|1AAn>J`Z{b{oTR3U63_e{B$8~R_CP9KVnB(#a_}mon^t*j90Fv z#!6Z#`JsC=@wv>3ioN{yvjXJwZ1?3*W|+Dgat=@}l#)c=lsepdI$rModtom$mPBA9 zFqn7D@9LrCqCjEI+xQ_oNLb+Ds{G!DeomDt*eEKgb6Z6VrtC+WJj$ut2}!WfN+&#! zh*s!WS0myIR?;;Y2;Sz%4mx9k0@8ou*Q*gcinuAz=gUlwO{D{Hoy#=_4Yl#4DqOuV ztTn5UxTM4xeIDjUQz-VS=@~r{- z7WUFdpo~SR*VyM9LK^#los!RqQB>fMa~6Y@v|f%n@mK8Pge!%IW)HOdk9;=f zZL5Y2vhT^IFd!`@H3Rmj20PKmI~vf^TtRuwL|jq5u%G@U>XZGj5De^X-`5EY{zd!FdE2FmUm7SC6fgP;p#t^?~efx8-Q`_4K>pWWz61k2e3W%07{#TOs$5E8%vd;uiz>v(P4f9!Z!b43AGYT2%`2s_0a6b@_M5-^tW0{rq-0xi_E84IGG2coU zjE2BBl#Onq#5^<>H*Q`oIQIj5FNlj5-qA1Uf#UFG)g=cb_S)2A4lafC5`*0Q-Pdqc z+@M&rZk_SsZJ$ckb+CTVqf|UyLl>+&k;gS`W;hvHPs;uf=T2=!xa%BQ@=eDqdbM=; zNN%CEyC{@mc!nhFTZY_(ETc>(e>ZCCd-&l=-vSe{UBNy~Ovmu?Z@`ynuOnvW4Bj$y zO(W9#^7Kqg_8QycahS{y?Yezug{}(66<+u-nM(E@ZJYC5)VP0u%V^Xy`@{_voHIJ& zc)A$I!4EXY*w3J=>{#_ht!(FNOk1as5{aM_pWhi}544wx_7T^N#EDnHGl=UCjl!(( zXy&;->hQBSt)YMkZ2!%B1Tt^wadU>v7pfl%u7+l?;F+f>9|x-(KTq+dnir2b9nsBV z5jVtJg3<5JtzY~G1(|HHcrv&D08e=~J_b|xmjxKP%!v zX@ewfkfaULzfvHjf|LqUDoClYoB`>{yY$Ayzf6!eNKz_Dsqp_qg>i9Lsk@5IgOnH4mSk;+u(m4b{DQ`@jB3L>_1 zN9*4wFRL4YPhPB%l-hD+C-3Biy=v@-Pi1OD-ApidJUzgg~_6zvQy!hXc|LrB{|3HYY+WHOI z@+Q=tBx~VSTQjN;x2Be#X?=q~)jB0pbrq~{5Z?HPEVzdVW?e+^yv-10z6WZkFM@nR zD}n|gAvIpJUzck^BPXOC_%wab7CzeH73`NJw(C9$+8}=$e6O9;ITsq!_2zL#nk!ei z#n&TVHjRmU!tg;0&oG7rx5?#!!4cx1+RN)QoGBwlmfbChpo}?pZP*6UUR1WrAeB)F zTl6~1Duc6?C8#Mu5iD~*nM~Mk`48BHa4)xZ6wL?x{NZRq3K@Ok(IJU*_iEYMrNzst zl&C!}8`83WdPG#I+lp%9bhb=z=52p#+mA!;<(K}MYiN3tv=@wU&>b0}3%@}78CsmS zuU+}5aMM(O@>`FNNXO@U39O3l;F*`JmcNz5@B6LY z0najr?{>Lzvy5Ri`pTig!Dl-M0wwV|RQwq*B#ZFZ@&>nq8Km_=K_y9bp()bD7<*$L6YOq=puS#4N$tDtAXgMtI6p#L2g)(&sK)&d1&4B-y# zs^!Yo96{&mjI@01dI*j<=Y#%@bq_2hOJuI02Zk#gxfg*CX`*w}ijWRA3j~_c?~i1< zY-aqC>ZztT2jza&Pu*w!8xh@)oI`BPc50z;Y9hM;ftl~9H|}=$8Bd>7Sf9j$$&_En z-0a+V%E#5wW`4E&>c?c+LkK#m%K!SgYWUz)iEL$*zb|ygG%wyJ!?M6mEyOlsMY`px zK)~_jZAvT8e)v$?h+)J?RskK_B1AgTCyRA$%9m=7svW7nk?!TR2}fIA_*( zsXU;omHORnLsjIuDLAXWt3+|iG02l)`Dj>AtB1++23KL!-0q}8olR4rcwCTOLxvk9 zHHf%C?II9{m#=X2g>P9|!#v;bu!(2S!`Z$Bl>#XH12WrB&7c3n3{|$HT)8+};{P+EFEO%|4sNtnkKuu-j>_eld1$DbL3zLdo}ORwVdkVa4jB?0@D2#{GoxO;$aw@J+8x|Cf?G zCwG*8+KV5o^bIna^Fa$x9?gw^9 zcX)NS9TO^K)gtC?50v}PW|Z*V(mJ`QWovSvnUXoQK=}@$7cbonrBYz2Mq2U3r}G-94AH7p^30K9Ur#dyx6bPws8w1xET~7c|(Ti zR#<_Qda*WTS$mppAoL`weS%_q2=>9gm(7rqFpn;<=oBOLE&@8J%b~JsGyr`?c3Vs0 zX}zX&;)KQYVUa{mRstfpR_z;|uRpNk}jl1gif^;~AD z6pS1nM}<#&Gd8v9*I@A_NpcYvo7^tJuAp(h0V(+m@LfT(X)AVea1|Wf?drlBkVM!7 zTh95(nmlI_P$tkWJ4rm9PzFIfN8f#?7J8f08_?=TOl;E=Np!Ju9Fj8vS5Xo3+!=p^ z=PH#8xxZwaWq)-pE4*Psdi=L$-j?6Y5R}{x;?}L$c4cFR-k^V6LzDH0d;lu!{7v;r z6!0Zh#HFb94Dgyw$=^NwTn`KrS*rUEGPd|z=utpHqG-)-F zy(akN-#C~IY=wbBYg@*k=-aFE3&t(+w$WUl;qkIw zS=my4P?SrfZzY?y?Cvdzcjg9S+jRIPKTMuAou0J7>YUB94`HA=AJkX|VOQmT*|D>; z$!{SoH4Q~WOT7VPFVV?#WLr~br-fI>UcBK!Fn5lXCFRJO4w#?CUECI{?uBT|=2y$R zM!roU&vsIdM$*M5{7oSm;8F)AZ3p9)9~oC#cJ`qr63K)?i#LGMO{^mt=-QLa^wufc z8404)5pcT|JV?b5^ z4BCd((&d;8b58pP?RWlIWw-ovo7Uw2ZD84Z?f#w9bRkZoT-1TgP7M)tyyIpsmiN93 zDhCdwe0bqD)HB{1rhKJKg4ql!C|yX2;kXBA53i<^9r{~$sUh;M$?Q<*dj(VeR=x8jl1!h>Lk8~sJhRj;p`c@F9P0%DApG$J3y}1d^ zz^Je6J&`EMl1Nf#e2A=iIcZIpJ=|)`=ZnYJ6|?TfX2v-a34_Dw^SR%*wB3qW9m(R2 zW4kr^uIQuOE=rK0Ki0RU6J9FZ;?Ma|EAL8N8IpJ}u2NOJ_mje1Si6?uZ|yvw?bG1r zJDk)6nJwv)dmbO&CBZSLF$EOG&MM!UA}u}s8iqgzhj*@E^12o()j1vGkQLkVKBv$; zqjurv0#qXUg0y6c5@Q(e{q#+@rx-mUa z4s{fsXQqD=v)>*`si??IL8P*L|23UzM3JDOZ#UIT|LCi97cfRUgSV6npaAZ^63#&A zckZm+zH8FBxLbeRmPcD5^VJ=N@5ss-NkwvzQ7f}d0

!-7O4Os21t?X&1Ujh{LY% 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

9jy39PrRdC#na;0G`~I3ATr>D9j{^vuZ-fJ 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 H)HBw>v^mT~KT}fq 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 From e3f839908bb11badbde63e16efb24a5b1ff3cd64 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 21:29:24 +0100 Subject: [PATCH 136/144] Update developer ID and homepage URL in metainfo file; enhance release description with security and reliability improvements --- .../linux/com.symless.synergy.metainfo.xml | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/extra/deploy/linux/com.symless.synergy.metainfo.xml b/extra/deploy/linux/com.symless.synergy.metainfo.xml index 01a9211a0..3a351e788 100644 --- a/extra/deploy/linux/com.symless.synergy.metainfo.xml +++ b/extra/deploy/linux/com.symless.synergy.metainfo.xml @@ -6,22 +6,21 @@ CC0-1.0 GPL-2.0-only Synergy - + Synergy App Ltd Share one keyboard and mouse between computers

- Use the keyboard, mouse, or trackpad of one computer to control nearby computers, and work seamlessly between them. + Use the keyboard, mouse, or trackpad of one computer to control nearby computers, and work + seamlessly between them.

com.symless.synergy.desktop - https://synergyapp.io - https://symless.com/synergy - - com.symless.synergy.desktop + https://symless.com/synergy + + com.symless.synergy.desktop + Utility @@ -38,9 +37,49 @@ -

Synergy re-forked from Deskflow 1.26. See the release notes for the full changelog.

+

+ This release folds a large number of upstream improvements into Synergy from Deskflow, + much of it built by the Synergy team and contributed upstream. + Highlights since the previous release: +

+

Stronger security

+
    +
  • TLS fingerprints are now kept in a fingerprint database, with a clearer comparison + dialog that shows both the local and remote prints side by side.
  • +
  • A new option for servers to require a client certificate before accepting a + connection.
  • +
+

Reliability

+
    +
  • The app now connects to the running core process over a dedicated IPC channel and + receives status updates directly, instead of inferring the connection state by parsing + log output.
  • +
  • Only one copy of the core runs at a time, so duplicate instances no longer fight over + the connection.
  • +
  • On Windows, clipboard changes are tracked with a modern format-listener API for more + reliable clipboard sharing.
  • +
+

Features and interface

+
    +
  • A single combined core binary replaces the separate client and server processes.
  • +
  • A hotkey to cycle through computers, and an option to start with the cursor locked + to the current computer.
  • +
  • An option to show the version in the window title.
  • +
  • The server address can be a list of host names or IP addresses, with a more accurate + suggested address that updates as the network changes.
  • +
  • A new client configuration dialog, a searchable log you can dock and hide, millisecond + timestamps, and a refreshed about dialog and status area.
  • +
  • A setting to choose the network protocol (Synergy or Barrier) for compatibility with + different clients.
  • +
  • Live interface translation without restarting, with Spanish, Italian, Japanese, + Chinese (simplified), Korean, and Russian added.
  • +
  • Mono or colorful tray icons with matching light and dark icon themes.
  • +
  • A new scroll scaling option replaces the old scroll-speed setting.
  • +
  • On Linux, server and client configuration honors XDG_CONFIG_HOME.
  • +
+

See the release notes for the full changelog.

- https://synergyapp.io/release-notes + https://symless.com/synergy/download
From 4d820dfd23e2bb6f4cbfb3dfc1441efa9741f2f1 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 22:02:54 +0100 Subject: [PATCH 137/144] Add git configuration to handle dubious ownership in CI environment --- .github/actions/get-version/action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/actions/get-version/action.yml b/.github/actions/get-version/action.yml index d47a5babd..e18aa5030 100644 --- a/.github/actions/get-version/action.yml +++ b/.github/actions/get-version/action.yml @@ -15,6 +15,10 @@ runs: - id: compute shell: bash run: | + # Container jobs bind-mount the workspace owned by the host user while + # running as root, so git rejects it as dubious-owned and the SHA lookup + # in Version.cmake fails silently, degrading the version to a no-SHA form. + git config --global --add safe.directory "$GITHUB_WORKSPACE" mkdir -p build cmake \ -DVERSION_FILE=build/VERSION \ From 2761dd3aff2cb0ba0c83cab0a2a240fa572001b5 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 23:00:21 +0100 Subject: [PATCH 138/144] Add EULA and update DMG background image for macOS deployment --- CMakeLists.txt | 1 + deploy/mac/deploy.cmake | 3 ++- extra/deploy/mac/dmg-background.tiff | Bin 0 -> 72810 bytes extra/deploy/mac/synergy-eula.txt | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 extra/deploy/mac/dmg-background.tiff create mode 100644 extra/deploy/mac/synergy-eula.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dd6215b4..65ed3e83f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ if (WIN32) elseif(BUILD_OSX_BUNDLE) set(CMAKE_INSTALL_BINDIR ${CMAKE_PROJECT_PROPER_NAME}.app/Contents/MacOS) set(CMAKE_INSTALL_LICENSE_DIR ${CMAKE_PROJECT_PROPER_NAME}.app/Contents/Resources) + set(CMAKE_INSTALL_DOCDIR ${CMAKE_PROJECT_PROPER_NAME}.app/Contents/Resources) else() set(CMAKE_INSTALL_LICENSE_DIR ${CMAKE_INSTALL_DATADIR}/licenses/${CMAKE_PROJECT_NAME}) set(CMAKE_INSTALL_I18N_DIR ${CMAKE_INSTALL_DATADIR}/${CMAKE_PROJECT_NAME}/translations) diff --git a/deploy/mac/deploy.cmake b/deploy/mac/deploy.cmake index d4bcafce7..57b7adc92 100644 --- a/deploy/mac/deploy.cmake +++ b/deploy/mac/deploy.cmake @@ -15,9 +15,10 @@ if (OSX_BUNDLE) -timestamp -codesign=- )") set(CPACK_PACKAGE_ICON "${MY_DIR}/dmg-volume.icns") - set(CPACK_DMG_BACKGROUND_IMAGE "${MY_DIR}/dmg-background.tiff") + set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/extra/deploy/mac/dmg-background.tiff") set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${MY_DIR}/generate_ds_store.applescript") set(CPACK_DMG_VOLUME_NAME "${CMAKE_PROJECT_PROPER_NAME}") + set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/extra/deploy/mac/synergy-eula.txt") set(CPACK_DMG_SLA_USE_RESOURCE_FILE_LICENSE ON) set(CPACK_GENERATOR "DragNDrop") endif() diff --git a/extra/deploy/mac/dmg-background.tiff b/extra/deploy/mac/dmg-background.tiff new file mode 100644 index 0000000000000000000000000000000000000000..e13ab35b3f939a65cd59ad2618e7efa6ccd8d006 GIT binary patch literal 72810 zcmeFZXH-GKhePph%J|C?F!BARtLZ zK?DRvJ?*>q*!#U_-1B{BocrgFd-v)w77ugHS~V-IT5Hays`T{%KL9#}XuQ$7*XK|Y zyhJr;qaHVDX0lWb7h~C43nAJ}Emz~7txzRajJBJJ9A}X}Pk}DVq}Q$6UZnV%yQy5Q z=xmd(mLarAaxvygm64~JJQq#Y)mjrTvqvcD3d2T?$|GJbt-F@37B|cvZHGQ_Y`5~Z zkV{+(qN{k}UGx~$z3SIv=WD4@xA=*?X>k2v)b`oWn}bgNR*Kv-`R)~lqDy`5GR!#> zZh_YFN;@qD)9yjmeLG=Fl@B(B=2A zF$3%V;dV+Prp_Z?9z^G9 ziCreKP2}-=mx~FQWpPdBd50k<=QHd0hUbP^nRWA~9|#R}CiaRwS(23D0yUw6o|(CjzG-#WCZ(Y{wU52gLSYQbdfwcG+`^)8Cgh5R z)M||5%xNc?ofVBxmdNXFr9#x?A_a+ybx~FwtF?5+-5aB4{t!=Fnom#2R$85)P_PbY z2$Rr&7$^R<5epdr2)sbF3KEf6nKHQvSQ|#qqAJFgKr}1vIf!N64H-z)eyg z1+f$Oxq}CQzdZBX!FHR1%5|?}z*1eE4p?>|BY>5g?^W6kK7iuD;#Hky!!@|6iIUZw zXU`M0xG2*#UFO0^+=V^O9EQXU;Ot?#O@RU4vH{7L9 z2du|ay0DbYMk=0!L4ZrB&s(x@D$vjy8pI`$xhwyCPs3nA^ZJA=$@HK<15UNXGhIPq zHzh9j<;9q)^EuuuTF;fmCmr5zELLvnO0B1AbIxxVAt%8Xor8$!@!oCHYP`69M**{a z7YGzB3-Gcxe5@_i3$~t;D5Ke0scplsxI? ze2Gg{&ESYSz*Q~wwk>h6F4}%ZU7u>x8|ziNv)mt`{$|Epkdq8yBu_0oL4Ov#f!h%; z0PyJBm`zU!wvBj@yhgi@mYcj0`N^{&y11=bgnXoS4k=SrHy6Win19SBJ0j889iIp9 zumcqEA?NZ#+qH}Ss)%&flEb&G1j9#f<$qK>V3WNml*(Z-nB>``8myQ7-8$R2aouT* zV%m9KIJMG!X!Dao>cqNaIug(HRaJ2|&l0<=q9%pr8v`+wyXT>dq+v)%t9QAMh}T+Z z0bbmOkWxcV<^|K$>b<^8DDzOH!$8Nu4~G?WwQQCXMqYIYTtBSdPJ=Q4Jh|3z-gP=9 zS12dG>&?eJo{MfA>{XT7x$(r}7;_AuF`4E>H&QltNOG#j;V?XP@<-z?YHGGOBT%Av zf*u%(_{L~J_6x*U8R5<4NrrwfL13SI5>*`nri9{9Rpc5ci{4YV>^ZU;y1)r5Rs5u* z(Pa+F>uRj;nNxhK`4zZFKEhOt03w}20|}U6du7NDAv?83tB#iors<8T$OKMBS~Yc^ zjK(K)DeDJswK>V+MX#l+S_#9do(&{mB0gU&KGATfCW)N;tfEbigIvC!F2-i2QHbQm zCn7*lJ1>!QbP|CZ@};>2+UPyY@<0t74prERP^pE>8Awd$RtXi7F4=5tF~BG(Jzyx;yf- z^5riyr!}!N&m0~r#{vXEl_w=>9nv3&hm^v zAZ+3RKeHxVzHWuNoR4`UNsRvjRg&SxMBsMdbRi?Km(KHK~?qgkCnlk zYU%V1+B%*y{u-PvL3-8|#HjF|A}Q^7*dT`KF4Jt-e32odI;hsY!6?_`r}h?=O)?oG8XX;axP0LZTHTj^7-K089a$o&2`%+R1@5(T#_ zvWK~;RajMBQlb`rB-NJS`Qevj@!peyxHqB@A8oxZ26d`yE?cHK+FAX@U8T+z^906A zpO!*pyyLd7UD0bw5+5ju@pgie#C*j?O)pnUQrKQPsVx=6BP9uQI@x)il8~@+tRxm` zwYilxk?T*`OZB_BeL||Wlr6L-%-p!2+o`2H5qkIC`-TV!dAD+hZh8M+%I2lXtF!*i zvAO$E!*{#FQBf6+NTbR$GRe4GIW?~}i7XQ-3SvP`?~cuh ztk3e3t^*S2J$ZIm@eP0hf{FNyyz5N#&?*2^m*muYKb^e(SX`62{yf9zVMM?%SF1S5 z&^(;74Z)LZ86aIc&k#gmYM`vr0~cGjq`8g|Cc-;9OGO$Pjr8l`lYYM z)c0YYm8yqdaeq)zmFTae+K4$0be%dR-k;~7xr#YC!H_+9e<`YRcbAmyrdgb&{Jxrd z=ICd!GHc6NrFw14mtSA}QhFhZG?W4eA}~v8eW)TTa_y8%<0KwgwNF8f{+uVk0BT&` zj2=POC(hRKVf?p|cPEf4G|cS8EL-x-L&Vh3J{EX42kH68M)YHmx5yX!r9R4A#62VW z^T-#aY&SG6{MLT^aehDKec85wN4oVRL$_;3BZByPGA049rC@Rsw~&_ymu%^9BALyP z-C2t+W%4%K1wYNhvsJL>qh1tYRwSG68wg@eRum2p1f+HJK`Y{}nN>i>8~6760vDAJLli z+SWPk@7orBrz}jYNq-XD4okI2sv#$t?aY|C ziyP12>J*-^floZlU&vc@F+2a;*yz0>Eg5;6w9z(cZUZZ7W$F9Y1_ri|*ZxdZ)pC<3L$hDEQfKUZnYw$1V7>t+OI!0pv4N z^#`O{AIk>~q>*CNaXS~=4t1LuD_gl_ji;--JR!QuwGojgg3ID+6t`K6g6X#T5wksi zO0CaIUBit(G2ACyekn5c;{}VRhSjqghOo5Gp^wAnUekrVwWI8$$3p8!y$1YYhKGDJ zRKwInV>I5o{@I8}aom&=#+t(OA(HVwx$9FT@0&`QS_mD7i}BG2BFo8M7=hztwrX!C zHmGj(H|F?EmZ3;`&Su*26B%(vaY4yvIHX-o2BF!V?1J)Wp@XL`)Up!|cqVL8`JFZeiX52?maDikoPQE{lLNhqq@^UQv?SPHzd-WOP(1e6)|0(1% z-H3=DMXO?ld}#V(jxnpSyyHxjOni@p*F#&DleY^+YuOq+%cNK$%sC9CbUC|L&0d+B z8}}Nxz7a6qWFtS<3!yPHyKf%iX9=6-xN}2_I>_=>>{sVsNaTN9{QtQ4|3@x<_s`LQ zVT)J2f%df5aQs&-ecoQYo#A>Hv4$a8j(kV^tRsBV^3~6qeU|-@xb>{li}(#Xy|OrG zD_^Err}J2@wC-?szKr*jmzcSCJ!GlKqk*j}_9yyxmBatULAcxVd#7Qj0R`_ZiZa>aN?gl{M_IJnu@mUb^<= z_6>$#qpcqwv&lEXQ>Rh5G1(GnY<3J(rx#=SfN zm>N{y0@^Y9T0q0qn@`k!uao;ylW5~7EgG-D4X}J^1kvLFa=|>>@o^Y70RRCT8rWq5 z$o#eF9u*3kJ~q5+f1pODwhn;|b_RF-WSTt!dX%GGI+S`hGR@L7^Z6GeXV8$BG7~09 zYB@BJ{3MIcH#^y`4BCSzS2{tysE!-2bE!{Kn4PswcG;9Fkknv+aQZL_v)(`ud_*sQ z`-$ki>AlRChAnugYk(z08{nIx;b_=GVMbf*A{ysfC;&0Rz-YbDtVW~SeGVNjq21(Z zLU^6OA|=1>>$;O8lJ|2m2o1}XlpED&&*5l*G?L|{`4jO~A>(y|ydeWPE-HY^g8ILT z_+qZqtKiu!0Ge_D3F=Ke`xEi?xOa5k_2_r?UlCtP&}&|$0;w9*t@vBS*YLLA)gF*- zeg!QUYDyd_PGf>h9j-;Ah#(7=H-})>VKa*H@C15tjg~K9v2}9H4Nl0>QQ5 zqZ!)?GWpKMkXttE={9n#{T3uD(MsApaerukZ^_7*kINIH-QwgOX4_*v^#S?VL8e@p zQ*4_kIkDHyz(c0u#hz(Jg(y&H1^A(nN{<&qbKV}==JC4n{<%$fjKhG(!-an5E;R1f zdB4?f-G^-tZ;qe!1q}i~1VIBc;@_%>Yt%R`JY7tH)jt&w4`bW}3m#8jijdTzoRt;O z5Ls0st(;&W7E1RN*pS8_AGbIJprHGdp*67*x8pdc!Zok$HtP9;)M z`4LYVcho!Q1$x)>*()%CVs$OmQPqu0yDXsk(eR;Fe8PAaIQ)Y&%15l zOde&09tIh#Jo?{Y@3Nmq5mjj2B+IX{vz zTeYyt3UPGnGA}H_j=;=*o;@M+G7dIbuWpLlN`LAbSz5^IAF~c)B=@j;t-tWlCd^jz z28YGy+nG0^B)d`X2u`26ISR}?i*1$B!oCjD%-_U!(8fzdI1_DuOx~nR7m3-_J^h{D zv_vD8^*&r#_Oi|IO;XO?^H`nycRlq+^_v$qH?vU7S*tnjy3eUHolBbU<$G9vsxI`H zJhQE;vrbOCvlLN}sdnG%52|aV->Gg|u)%F2=570RuV#v2?TML}&3D`SYMa=R!hKui zS91GrY*ed1O+3%)`JAu(^wF3#u~+UPfx*lEBkF6*u1CB(8^cv)mE@yij01F82dM@# z<9BR-^F13<@Dq(Yy7Zu)IU)PTJX>{{PCj{(DkHRTS|oxO5TbZU`{MLT%bmHiRdKR~ z&{apDcMqdqt1bsHH^jYv7&Crj6}h^5<6Q(xhUP|4EDzE9NQY;d+adifM7tsVDG$DM zI5dcT^j99AJneAUwLU~v;#q%*schc(8ul;*b#6`1J~fwwq;&m9{F>*}YA#Q2Wf*n}kA<(&*%7H+k>8+l_@$em*VQfH1?(1^0Kr} z>(R;7UiPA`n!LP_N0Vm*yeKoJf^2qEr4lJYXSY`0yxx0y1Odd`Wl2T%bZwuVs|%N9 z_T32l?yANYzwXXBSA1U@|61~@cxm5sDaPHShoI(|yZjCMtM!AKx#3Km{XNm>|oh8*-H1hKlT16yvKgeItbMf(~{|fWW>@dgP0#aa1CSZV5v;5kb+;+ zUT(HAJcp&n2K)~^=SIXHt5aMxK8XH2D)z&ahCMepabZkcv5>3#OIPvOxOfyTi>^s) z>+FR1VW@~`CCB_2>M@nt_@)tOHn`KjA1Ng`T1Td=BRSWTs`(ma=%#BAJr zf5zB+_T-6G`AhRiKZ8Z{)^ElZQ&PGq3#U_A=9Y`Zqn1`WC|8#{^SWHrl=*0J@oUeJ zD35w+oTeLNXI$;MOJ^(UDBIV>h8}i0nl;HQVf@B7>@B_Nee5S5JPxn9h4Ue5;5FA# z*4sVQ%a}W1?vS`Aw8{9%YvJ;=QO;I39>zFN6n;;7cb?7_=d$!HE5X&uKqA@InkXsN zP1}Jx$)%T{HWOui!#LYj_Dl$aT5i$Fb6=LHEZ7;uwZ6Saoa~(8Zk6p-^?^7vpmbMT zE27NfyFu-cXLxo-is$#+T~E9sLQ4}qT(sS}dytXbl5+jr_kQQ6jL^qLANQo6-tvy} zJ!tnn_kEe<6V-p&>T_YS349}bKMs5$%>J6>7q0wc&DS%9n(eZnEL9DEwkG&KpLhvla|)2F9mQ|oVlUlJ zZ%Gyy{YFVs47c+lkCc!(AMUxsu$I~JU_dMB3$_inH7gRSX1*8>A0 zbv`ijvRZc*ni%g^(cIu$tZgdtVYd*Ww~KL$uFeyO+CN^_?dBSOam$qY_On;3BC`cU zMIznq*SxN$)b(zAQVL)1{80YbJzl8P@UyZ0>j<&MzDeu+z#wt!{znajn&tl7fzMxC z%G1>DTL*PUi8ZbDdZgRm_IHSU@mgJ(Ci=TI<#B=-{jZ)z!jLNeqWIa@9BMQuLaQ$E z4AEwm9?$fw8vp0%bFay@XzUrR%mVX8<&_^c5!TlR;O5OQ9_rF)@>@xEmWiZ0bbmxAVB!5)dyGFVv)dx`snw67v=ch9jOWZGVc@Ik^9d`{hyKg|Mera zr}b}WHRI4$#BJ=9gGiQj$W%~8qvu~vvI!*&%ZiMD>Pu(7zwy|B`oXc?RL8?@+f+#J z_T5x*;^80Tc*AA5b7O#MTxSi0C|U3J1Ki&3KtHTI8Oc(%PX%fe9uXQ*JjDP;TvZvV z>EmKcp@gq2jCjpfJW|wx z6fx8$T`@R7m!J)BuoN-?WU+}!`<*;(!Bsci4VNh{HJ%cI$!OKKJ zgqtwTCfaBx&PbXv!Y;;JZX!UFG0fB!<_Si8bpX@nTmC?tmH<#TQThW}27*z|J1@Om zZb+9s=aFc7dwiWrt!%S(*zVh7emWd&I%iISfVmoHU;e8$xAEpb_o zzoxBh-Ir@!rLTXgvnwet#FDR9|8k$5XG9zW23#Zu32Kwz`>{;}j`!A~O^RUvHdNJ^ zIpx*#{#f$mcVd$i&8-u83A1>62e2QKjhmMl+;bJcdhB_rH6=lS9J2}z)N1?vw@c+K zB9-&x=|Qey?;PJ9(-xpgQqG|0S?-9cIAkg}Vf^~25W`h?oD~`{Hw01xs+aA6V$}Hv z;8OU57QezorO^bJl+$NM1={t(&PCfpf%drHyaAJ|N9M8O3n!pR!LsqzpIQZ?p-1X+ z!L6IOHKy||R!v9R#Le-{!L)EGoWZJSdot9)WTm@nMpAzkH1ed>HQ{7hQcnX~9Af~O z$2Rmy(zY+QlhWTVbw^gtQQ)WZsX)VPg;k*C6{YH!1>Ru7&XK18&-m2T{#Me5=qkRc z(AzgT&!mkT_BBk*(bsM2$cmbqyW3ipC4Lsm<+cL`hgne^t2RurB1m)=GcqJofr=*3 z8?7*;RyC@wgaDl7QG(k7MCOP}avAx>29BOHrj!;={0IE0GJq7iQvCBX2@FvH*izWp9Coa^?xB zKrt<%0EXz{sIOI#%TqFG2O)hXl{1zJq=u$gxXQ4`ViZHFUg@o22 z_FruLWadwvS)4bXEdRP(@c8lD?A2+SXot`=2I}159qzP~oIXD>0!Sqa4WD)p>rv5- zouTDPhM&YwXbByGLq#&JquVS@K#~ARFuT{zkTbnW&X<(G7Cd*yGEg>*e#ps%4GsHI z8o9P6a(j0h8|NvKQqDGc^~krOIna0u4g0v`RZS2INQ&}ToNFmntP^yV=$?mRiaBui zOmqY{B74J9HDvE`^L|oLJqe*1gk;)>EhRAf0i2TFgQx_j3GT1`hJ}ZF{hkeV-<_Ug zW2L`wPW)I3B}8Xs0GYN85ELM{woD``Z><7~<26d;E(DMByOuHsq0bG5`LYJ6>W?R) zX5u}L!gIL4r@+3tsH zR%7q8mfoTcNadP_5lrQpTsLbPOWMlK)0i$ldSv-LK|-*>8dG%tQF!l{XwfkYh(+nI zPQ!@6vf{CvCWMCR;+F~1#g}Z3>GP9#JlwLqTuSI@0WaE#aPS;3KFw^18xyS2_=twA z%lqagE!;&9@kX6c6!h!7(#_=jR9BB1Zc6ArVDsJeT5DP!J+gCq5RCIL%uY<_(TeXA z&@eo8^S@u!%Sg5E*jjUxx)nTI&gU%*A4J8XkH-ZOHJQ)1lwoy zhKmUB`O)4DeptKz_$}1Wp^bS=N9@DB3};Jg!J+cX`$~|GWV#=MX#|~~_~sdqM1YHZ z;c#+edPJFeS9FA6PG(2oX)3h*NI$rQFR5^(xBF2ReOL8GAIB*9}ScSIrHH+u?Lhb`D+Qu z)8#jc6^t;a;pV4+hGnEQo!FkN;Comt^@=MEZ3xquH1d4?mxE#_aROXlNtJ;_SVfpa)J;x0RFPNY8c2Ffs6whS7b;v=k zX#DJqX2lO#M!)39Guy-fseOZTrcfys42M|ELTmwcmK<|oFJ&vKR%chDrX${TpQ=V` z>QV`hX8@;hr|j(?;!U<0h?$D9o)2h>*-JoZY$d9K`$_3LVlRNjKvUAFc+4cN-Zzlw zk>NeQ*&lAxA97oz`Hdda!Q`8$gF}^6aDNU#$#AqIC<4QkZQie{ssuO)b)Hu;?JGIm zdGMuMCPfxZYqZ~T$fE`XTX)#xh$A>wcf-E=n|eRN;(6w@hD{qoU^BE40^v{MJJSC<2~xu+vA_|@D3 zb)-L#)}n1+SlCS&>2Zpe$>A>^h8ob%kjcOH(y2@NapNxf5$#X7{U!dDI|{N062Ake z+iQx-+}MwvnWM6V)b|E~H%Z4N>-K&Z zM`cx{)n+)Z`4HSG^CY>rpI0!e3{Z^lsGl3VTlF%fTX2a4}1prYx2Mj)} zN)!NGXrVno5`Zwt=y~D5-1}4rEjSm9Da6}=Hcwnt*!9bJgyl~BF-a`$Y%KSSSZ&dS zfMdnDvH*mD!*-VCSuMgs3rY2oBUdlXBRs5zFJ4SDo}4=LTL9tged#d2U<3pLu#-%2 zpp+t@N>vOz$5~eu=+x5LMG0?1to`oQjz}iKW>L;YKPn2xl$MAG*fJ zqQeR@OXm7`*UsvE^c9^dyt8L#E=@|J<;dw)Js@Hc{BA(PDf*G!vT(w3w~}>YEm(@NW>@3 z1sZC?LUS+FxKsn{uzf~=u4@s0{GGsAynp1Kq@@rE8g;-|8rz4e3oi)Pe#D?`|; zpl_-fvY%L0*7FenA}5Ld+MY8hOON=T-vuWV{i^N6jN)0%u^1&lBnzU|V6(#cCD-Uj zP7K}Ik&-_Ms&c8wH&CjjDxwMaor!3%!}Mx9x(nJgYkKMkMl45_FpH$KG6KLI4?i<; z?;7H@n8dAy;Ep66=S2cOH$wk%yj*vhcsOZ9Vp0_jz$!MS7c6Jeshkxb0kYma3d_@s zhjd}P);!Qa9<|Wo<=b>Yj#q04v9mS`1L~Yk%?mdsZ}D<6PspMjX zX7E$R+#on2W5L4*1#W#P3H^FNOWs7DchpqLyETg2!d2Q^5^W&6O>b`fFV`g8}Vsr)j=oPk&}7B zkq~q9yFRwBDK)YI^MgHri*?huoL@@|lnBU$N)^1%;=q2;lW>4Mz+Ys4_r@EVXC4|< zNzQ0Mu90hN``ondp~C!0{8(oRE(*CSG>G-?FzW0S-<2m~HUa)7o#zeqHXH+P zlwHH&qE>Ieyh+iPs!5nW2d5GQl-q1UW)gE*T}cmj{XzIvVJbHOB?eatw@ZBO(pcqE zLAZP2&v-%)`q<#IWs0$|%KG^5JVrE3WspGHzKx74FC;yV9FyVj8kNJA|t9;7$_B*ITAhOsf4%wqg<-K4?b*myTH60nJ|4u zt+r%Ow2>%=dQp%nO|#UBGuux;CF!13G$B%^aO5??H-$XTTeW>05zUQ|NlsAQHh&yv zs_PF_sZ>Z*Wr0&B{gbp@BoktjCD}MzT+3>Pe(6#$3p+zlYiX5n1~Eq$Z{4jdTkDt! z9@8FuBA`tbg0DAbB0Q{0uLO?$O8k0>C(Im?DmwkHs3)FW+d-qF`>YDHl#x5HCabfo z#Ip^r=FM$3*0t8>+tz4`^q(zkj4u+!RAt=2F=`%}tx9l_P!Jm}1QP`2B0KP*V&g|9 z*W|Lqb~GkSu13f;nDc8aXqebLK|-5wzW*Mff`|ZH5EYee?f<>lmSdb27dAC${r4+$}~RENS;J>Pm>^#;(Mq{5qymD z;}2KY>6n~Z0F7tVfFSqiFuBE)j&})b&9d`MEUQ($r148P-XB()H^IcR&x5f0%N`{xur_o*>>7MjAL?M2kafZ01S2pZyvjQeiea(yM zTXt_Mf+jRa9?=@?+q4vDd^&yg*wXivy+Ci+Ax_$(E!~4P#+ir%{k12wZD$q=V;X)! zpZYDIcdWI1h&de9c(SE-{;urj&3&%3K{_(15z(3YAvO;yVYHJVeZo=V*x;4|e1l0S zX-As$DV-mw)+LXTuRHUIRko>ft4`V%4c4by5`>%O_8A+v++%bv7$j$9NfSEg>EfMe zT@i5m5_DW9#Lwi?%EN|ZJ)a_qXYsFKdlJ%-oq=`?XT%e;#9aqx-(vZ zv^l;TLeU*Iqiz>Urqwrzy%fCm$5*+#PL7wV851+kCJK<&!LJ2|eezV8`Y>!#S8Py2 zlLVyzvaYSlApUiqov%qsdE>1{i`nuRw^Z`rMvWGA1kB$v_33$-YTT?c?~IJtX%~Fi zZNR?iXVkq{4@lT>6dTKsnTTr!y-V(!j;wb(;|c0tn%SBkAN{0BJo_v=wJZ|wWld4@ zp-`DifSxvHaD-+S?N6c=m2riX)Wu2_jb9DFCne?l}F-V-`;EtirNPEqRk z#4FSLba(kugYEzl_gYzNYBOGM#u|2t9~q6?%x+vDzTE_Gr=}P%9o{}zoHvc6CxTUZ z_XlS!t>EP9)48n5AJ)Cqa&t6Wy>z%@(2MII@`Zq~T8(g2c=Zn3GY>y!DkL4oj8FmZ z%AS%(KgJEV^6b5-XdG4BY3%!L{MP=8$#+qEHRR8uSQS5gmuOXY5nEqx?4|9%={`PZ zl6yA9?4X@QzBh5QyhM1x=J00x3~%G`Cti1KvJueX6xj@ccL5gh>`QyqV+9@DO>Mt) z)|q>w-P=2FMhyJ}e$QOYw*~hy<#Ys^(Dc8F9sfL~n$|wQFDJj&OSxH?)gd8;_n!+z&v*C{k*cG?(opj<5gz+`z}o`A1img02zKadW#O}(A7{gb)%JvkzreR>afF9W5P^Vn z5jsl_F*s-EcQQkWv+-h#k(ZQd$ltSo9swsrSHRxGSrL&7P~LY!{-1ZaRYWTQ7aIbE z$Bl5W)R5zlx{C>Y#xd#xh#^FW@XeY&^|rw?)#4gN1GL>!a0}zzu1;yxk0TlZ5v7SG z>rO|>Z??N!3JhPma9-Y@L3HDmIY=Vqs%4QbOe6 zI^EqI8ny3>F}eaaL~|_p0PzStqj506UGwGRylqgl zu13bViHPnPn@wyMB59j5&b+`hzuTl(d{MsYg4n2z(toJBn(x!uw($IXaH`IYe#R#ppAi zIv-id`b%RY#gk7bW+83M0RW+0Cs^(;Nd<7!(C63`H~@cy90R$_kByi&wL^?Re^7it2npPI}BVA9*OR?7O6WO@SYL_HcDk`)j+ANZs5ID7GCfhnZ zw9ikGLlTWm)ocYR03vf;e1LUQ(M&9iJawBPXx78;h3tjb;`XJ{M}eVKiGxR25~Nz! zK#VBsx=m*b;mcDfw>EN2y_eLq|Cgd6M;WPl`^goAh8WdV3XKj)Ln=)%GmA`E-0UYx zy2L?i8m*2lKc=f<3t=o*J5FvQwZ*AY*tI()b2xRx&2Dk)bOu*)Rj!}g2=~t?-V@Xl ze_6WEM*6t(o_ebZdsUzkF(7lT`%1wsySfssQ&z1V%oMQsrRER!;0aA zq7)J5Zr)yhov%PPV{)AQv@E&;*L6l2Eq0@Wy`5LBf)Ppw;H?(wW`MCjrId8THzz#R4g({GHg5K zwca#wP}z7j5Bs->M5U8pcHOKOZR!U0F)Uf0hBiNfGxuaDMLa)~+6p1wJijGW=4kY! z`IqH}l`*7v$Ki+C!$ihTE3Y<|;Umt2XEL5$Ld;jLzcnXKOL|irrep0QcPnR0m#)v1 z{z5sdaxYT(R>OW||HFF+5pv!w2S54`9^GK1EKxSOrTy$6k53){VMQK%xXz^J2e1B&+T?W^Yz1masF~G6*py2V+y-U-ni#z*1-6fPi4I(Iz{^(f4 zKx!}vGr>+)eG16#R-_;nRNj45HcA}5Ix?Tk3SaOr5f0_NdzxNqLs_Hn0@2u8-v*#l zH|g)7f+;($^H)T>We#^r@>%>MQ`1Xj;h2bQP5#Zi6t_j_EXiClA>U3YF~Ha2wIQrS z&G!}x3o_C?)O*-?y!345Rz9xtHMuk~cggV*N^TT2PxrGOyBDq<#!39kTIa4HH|7g( zmp&JS#<4XYKm}EC)xzD=WFOH^!%Oo66fd0a8w8oA2Py;_au;Os^BJizvYi{LJWV9w z+?}W*iMP-uHT_BUjGYZlptwog^OR;&S_KgF>FnlBa9p)Yn)QrqN6oXCW7ZX!;x)7i z2@B!KK9RhDeo0N@Y} z)oS$auB;PA^Qr|xb(e~1B>FaLY;5&5TppLD=%(k^S!?hX8pfd{`QaZd4RjPXxSY#n zOHi>pwM}~piRrcN=1qIPo(mV1fdX*<@>b5`Im@H`w{>`p6x zS!&@W{yMkJC5PH&pr`TI`{r%L28lp=?v45-y9H~%*ZM>JmR1#SGa4s-twVYx(Vti2 z4MXcXHG?dZ7uOzVW~{j~KDjk^;i&K-#_z$mlC!o;$KD8X)Ds(z_p^^)zsx37m1XW^ zoBt(_ud#XLKsHR%`GQ?RHPOGD_9>kQiF2Rq#eCV@r#(q|z0Kc;N~>ic1r=Vk9_FlLH%%S6)YlRMi5#WPpB~`?FSbQkO zrb54Z&8cM%Ayi-QEr;g@8uaQS?$~QV(jG3>u9EJ3%Vj~Kewx;CWIKWz6+zu$FX$_! zJjyRBgU)VFSWl99=tHW5rBmi;*KcAF|8=D4zu8zrR$f8NDlN`!@jn}D{QquajsL8= z(K}VTiLwcKQt6`}sx%N{a`xn1jKVji-$}Y*{j6D%;mTJE)z14JD+Di;Z`LV9^jF^H zh*TbUAo0Eb-4o^?%D+eXqXt-KXrixNdBtA2hyR}^@jvU|{<;25eEc-baf!;@JK;4A zNtWxg-S1EUs38Er000oE74`)A2Zmt&2>!xw0Cbrkuz$z_4pxr)PmE0rA^a!C-ULDl zuo6Ix)%S*w18h2gkHs1giofYl{}W@cLTLVprLmayU;3O7hCjIg>>YK9A&mdRR1l`W z-G}#|_>T=@f8~q7>c>Nr{w6p18#et5|0#=GN)W*AyhE&w#d!civWLwJi~r>J&%gLs zyZ`(E8Frf%SUmuEMOZ4X82|(}UQKZwD@z9e%7WGZqw^5}%3J^#c>qApVJSi^Td(d6 zfYwLs9sm0IIJ+8}die$gJ9~Sh0$n_W{e3+E2=;P!4+-}2Mutj=$q8et0Bg}TFJC_> zwj4gb!GR`M)sR-!Hb}hZ*wPUITp$L3vulvQzNwZORvGAMsw0E2N`LUbkF!N=p4j81 zkhT#L`EU9E79w}`55y7|5F@O%gc~Zz6^j$G*gGuP|Bw6#i>X|0{K3#atxFY%H4uxj zu@XS#@eeHcm(D-1${+0JQ9g7vQ4M6PiH|+8^9E=Lb+KFuoKmUk8 zFAvXPB(EzUQcO-x7O9O2^F{>+3mG}P-f#|dL#q4v_&fVX0Pv48|49WX{?siJTglR5 za?;X5;=B3n~EnG!5XC zL;z-Y8+!%vuXejeVDX1529~V5|LFHvjJ^KX`Tuf)=3xJY26=fP|A^I1%#f}jfmmAl z&jJ5)!U2SU3{U}jzyde`FAxIaKn5rP6`%=pfgvyh*1!R{0#D!r0znvv25}%6WMDTH zDFmgU64Zhw&;~leL(m6?z!-Q2X2C1)2CRc^@DUt=Gw=fff#5+%Ae0b#2rGmOA^;JG z$U>AMnviP{6Noj$3E~0qg@iz&AqkKSNFJmVQVnT>bU=C`Ly$?x9Ap`?0r>zqhJ1&@ zpu|urC^M8BDgu>-szUXkW>9;m2Q&a02~C7%LyMs`&{k+SbO`zk`U<)Z{Rln9zKV!o zG%z-p5KI=P4l{(=z}#Vhuvl0+tO!;EYlA(4jl*8T)?o**3pg&E5{`rm!4=@zaC5jT zJOCaG&xDu48{ysX5%^2^|6uPe+@fs%e%*VB8HO0TySux)yOB^jq!ADVbm;DuknUDO z1f@$#L=gm~L1~au(0$|c{9^5Qt#|MDSjSp>?_>KXFw9)>z0S}1SwTBQ`-zT&PKC~i zE`hF&ZieoJejhywJqx`8y%l{B{T=!S`WX}oC4sU+MWJfYThP1EAZP-#0NM!cfxdyR zK~G^Y7&(j+CJobt*}!~ZQLtQC9qbis2DSmaz`(+w#Sp+y0p|%G7-1M$7&RC#F=jBf zFn+-C;LLCdxGvlt9tcl{m%%&XQ}7M=4@`VaR!nJ3Bh0&)p_nLwZg|NhU|;Ko&>VNcN8GJ2@S>GPx^xDtSBk5(OFs2ZcVxeTqVgL5f{U zB1%b0JIXl97Rm)G6e*!I&Y8p^;&U5tC7j(UCEO zv5)bPiIz#5DVV92X@MEeEXM52oXtGUe9pqkV#*TD($2EYO2(?r`hc~Tb&(B+O_t4@ zt%Pls9m+1o?#f=kKFxu`AB3pS`GyOfOPtGttAuNg8=G66+n>9Z zdxeLDM~f$nr=4e?mxfjog(K^#F9 z!AF7}f=5D}LJmTOLLY<)g|&sFh5LlBMMOn>L>ffCh%$*jld+a5l3A9em$i{C zkzJKzl(UzsklT`Hm%l4tC%>=2r{JT|s&K9-p%|jrqlBuYsuZU*2};(Cl^-jAQejYW zQmIurR25bYQteSgSJPBWRhv_%R<~2HRzK7b)d)O@VDs>Pw@qt&U6qOGBw zuDz(ktmC26t_$d@>!#~2>9Oj0>%Gv2>g(y}>2DhF8w48+8xk7cF|08>Gg2^0Hu`AH zX6$F&XM$^DX;N)+ZmMLOX1Z*~YZhYm`WD44ms=g?Fmn_03iDG7Ws6LU4NGCmD9iU& ztX2V5qqiw;yWQ@-gL}v3&Qoi2Yg6kQ>nj^wn-ZH-TXoxf+e14=yKK81dpY|I`!5dC z4rvZsj?#{4j@wSsPU%ixoMoM}oOfLmT^_p}-Br8$-SMt2N%Yxn0K z1Rky)1D-UV0iJKXIK860mc1psGrbReG<_=WLGD@I>+mH44^!iQY<`h`EB-S6dHxso zP3}Jpzz^^U7!TwKj1Am+pz@$R2rbAis4tj4I5c=AL_VbWA>^U$!@ft1k0KszAXSl7 zp_rkrp%Y_s%b?4M%lMXg zCv!AQDClhN>pH7Nyp& z_Dh{*-E_TDeMbX#LvbTXV|)|P^m@gw<{(AM`)oG7c&u*_%?^@rTzK{K8 z{j&r51CxWAgCj#KL;b_@!>>l9N4iEOMxT#~jkUiPdEGWHJl;AXJkdHSGTAmII@K{P zKHd37>dng;*_qzAN^gf|)n{M7(|Pyiz47}GbCz?&0crIqnj!_|*#c56H9_tvjAA~tb0v$klrs<#EUyS}J=nf_|= zbz{eC=f`f;9{yhbKHL7Y1G$6oL$kw;BcG#R#|bCoCzaoXzV)5zoqjrVKl^bWcR_wp zbt!f^{N42X){pyFu&bPF_Uq1{nm-qRx&Qk0KXcvxjd(d3K41UsH~5FqRy7ebYklC5 z-Ok`2k==hc+DbBKO*lO>cQ<+A0RM#&X7QKN_I?-WH+bYX`Ht*DU`q+Wz(%xcM7iX-gD8UG%tb(Y^V%(N=~fThV*<_{L~^<2Ueq zU?o$b=olOLQ)o-Ki`PHu;Jj|Yx9z8A` z)ZQDvL2rs{&-e1FE^5$dTgQ#nk2fxiQ>Vuyjyr&lePguc(#Gn8yD<}jMq4Y=A^Zdf z^46wrE|kOg<9@%5wx!DK_NKHWX~sbRg_>#;$#G_T>iOm{2O_rxLc zX74k9BVJ1e>BfE&A$LBI;6yT+_){m^&68+nVw<{f2RST|B$m4@Bx}dt{YWy=>i#it z>2cbvv%Nv@#cb~JVe@n8cYaIR?u$(pU#R=n_z33!gzl|EYz>-7)nu|fKz`P{zX@MN%5SC{R+4X&#$N#kPfU1%E^>ALC z)6*e_2J0396yfE0~50}$khQ(rCXvO~)gx?dDQBrrVC?@zdFxd`#;l=MjV`L@`&M+q|DqRq`_Ll*0i8OUAcZ0Xi{{DpjZ9_NaG;S&O=^YJgJ+a2aCcbI<7d)Fr)aba_N=l|N!5ijD}>JPF1xuLsJ z!}{G{skpICziX}k@H^#eL>S%!B+x^x zZ}PtBULu&qtf?EEg{pa4_T*7LU_uy%0g=(2alhHnL1EFGjJtc(?EA{p>N6Sj+@PZV zWb`XU z6KUc`;HT``Yu&eURVG{joN1NNNPx7+@Y(Uhjs5ijNIAoVVR<=#zd0qof9T{!Wh(k;U7O zdB6>w)cr^!6gj?tfyO}b!njqnc=D{!Nj#^3HZiUWB@)g?#5y5~?o&UAge*$z1ZiA| z;jkl?Q|bs`Yv*h_Ha`vhA_;Ejhg7&|&$q@LLwk>?bcw+Y-IIE_VZn-AZ`O}n*OF5V z0liNd*#R+KuO;D1+P$X+#S|PHjJ3N(VsOZhWy=T1B34(|UH~Y1H8PG_ujoHt(BMX{ ze4^sW?!PG1&D^*w^eos~w9F(qhSQ<4k<_w+rgXFKGsln{Q#v%kh7amuuSo$0xJ$TFY}2R`M7%F!~E(m5o00VJqo(FrXLCK~fr zC?zXu@iQ;YHE+3dZwx#)Xf9w1o%SWQ=`kcK1J8veoYbW~!^FiCVYp1IO0bkQvSv&d zNHi{9fsma>zgIRmuOWN=z3v*7XxcZG=B8F78bRRAGSC=o9dY9D@ zI;U=$)z>%7wx!QN>1zozx&Lu$eTb32YSBlMCV7NOfSoE$lO=tOS)kgo#eye$oJEj5 zf99X5^&pui!%tR@T>O5?l}`1o90dSL!PWpoZp+Q#>=GRy)395H>>p#w-)^WVf_z%E zFkrT(E20AM_ZIbn$PJ&*3J`|JUp(TtTT00h( zYPchqH@UUVW~3pisG}nEQAr6}7rC%b2#|e!TSh!Np@pPv%uY5U(#18>poQBF61)Xu zKGKZ#<9j86Umh20C6wh|X8@OR{7ge`S=fvN{vK^zJgEujqbVC3ZEYf+KDk`=h$uH0t|dIGQ?Yuw3hJ`MRi{5{SlCr%^0SPsQGl?rb`D1~|K;x3gY znhk~^09*NBq3HZw$(L5W!yl_+Kd&CW%Eq9n1$Z(5WBSmPA7 zP&B>e$>g!CYv{>`SPxMSy1XO2Y)J;JQBJ$3wK%c+o6!Vv$s&7c1-h$ii_wPAij6Hx z^|246t#iPAou{}72xEvdM{br+d*5X4A$mHvbeKW3;GT6iNkoSZm3Lf)-dF8~%}S0Z zld!uD$HH0tbDUZF30>pn00b;-`9g;nUT23n6}5UXDVnr)iBL^jztk~JYP!^WrUAZ) z?cP~RGgNA`(ejB5jFS^?xy{gT)Each^kIP@ODyD->;&G4g)>0r&#I<#kar+8m5N=S zOFuK;wiZg~7MwsgWOd&{QN^YwwX3bJ?}B=8Ae-#qzELzEBof*dSYfv<;);iF*Z^Uz98vzp+w#T+}TCTV2#dD!h1{% z3#Dg&?kozU+ocKL3>hGJW`{w~#S(E?Ley7fiUp?Dy?AWKP%C9X#bO>a!?tP|0EkA( z`>8l|bs*)WP)5}bao3~(+};6c_1Xa3DX+K}6*G%@rQ9qB)30&}*nS$}GsUw2FS~NSsf#UE`T<8 zDJ7bvur_m)fbSMt$+oVx@YXVMKn5m{afg*@w=jKQ0-vBtosF5RBICHkXsp_lli960 zibsnTb~FY6(;PJGWyaCg61St_?kd(?Tw!zoiUJ=6Gvl}pP7xrZ!lB0!Xa6&18VI*# zC-aQYeb``YL-(X~ko+!WEzOK*ZzRE3VZ=IYD3D9Ztbd<*Ml5a=CF$x(D1LGcS47XU zS7DhANlFBXVy=Q?sa-ElArpZM>!6Ghn>^zaG!<+ZYd>dIJxR$o>-8}PLw2?an$mA! zDKi|Z>g?PsY{$K$=*?_pa?ZrG_GO^SRxJqtKg}Kh3Ki_pfK^p!+bvpE_;}03YPmWH z(|GACh)Ru=6I9#Zg*UKqL;`4J;Yu`q-2(E&BLp}QK|Sb%El=s)pjY(jtMWP*0}j*E zB9=W9isxV|uy+Lt8F@2I+Pb5}e&E16v=57e-Xu^}9-Lq#@UsjFHqbC)zp zz8>&^$cuqJJ5o|8GX~O;;Vv|NgU`|<1@8<{W3e?JtYj)d1W?h_T`Qt-2gRvkxpVukh#)fU|-o% zg4LHc#ocB^i-5rr|Bl)}OXp+Lcl&jwuRkFhl?UO!iy(r5_@}VbBh&H;$GNf<|``|h>gctmMUu7(n z?||V1?@D2s&OK)$BAgSS`jMH&5M`Za%E0mrYnz&fVHZUu@{k7_d+NeCD@7P_*g)DZ z=80I@;xry3bk=Zh?Pyn9{q*rixYt_-M{Ri3^ec7JnAipyy8-@2^`KcLBe!&hJebrQ z>E}6Z9^Ld;`AOBOW&BD**&3%(5grOSWNYlK+xDa-DTOHtd@S2j_J)P<2mNMe?;mVN z+|Sv^fOe@lrqW+?$nbE?z4aY>vr1k1?5tF~HR#}X+X`={+W7g8R!g$K{ z5864NAB;F-&;^yRn*wcRJ_IT(KBsI(t5E1tQ@p!}Hyq z-gwm|JS>G&k7yqA;=M}x_E2YV|CinFb&2>#bzILMkHQas@(y3hauXl$2zDbet+e2B zPp}2CgOjb%5&<>koMkRp{`gq#!ftoYRq!gy@Y=KRwsG(( zJ_>u&KS)J+(DvZA|0fmvCUxPAP#bg?C19K3ITH6pmk3~=!#m_6$P^;Plp+jjagY9!EHN%2z?=WS-%Ig+SEv1J(CkLF_hg#@4(p3i^W|-_@yZC0sIV~s(H@&ycnS>x3`$YU z02B*os9u)MU4~qEGahkRis3NCK&U|LDVj1-(M>HATS6f9FkAIM!oJ&@YHu0Bi!QVQ zbQ^nHp7@r-Zr=D4f!>z7HLt;^TK)8V8YvchLxS#aB?jqFB4+c(e2&@Pi`2VTfa2fO zI4u!(ql*-w38u3Jy=`eiK8ExlVM>F4-Agm}q>80=?V8SIh_1B_-w|sY8~;6MPIS2S z*Pt0u#}Hc7`g)o$dMgE>OH?(i2`((c19TJQi9Jvv*x6{LBewaVArh>YyI_tMEKiaV z0>CTtxSR;)YaY!$FhD(hW6M(DSQpo^%a75+GVg@eZWmcnCjzJui%lm|U++wBY3BTWhBT^p+7X`eVV|tQ zTX2P`YJ>sc;M@R{ESj5Hh*9@(d%`xipp#ktKLXX&QPShRvtSntPbXBY{ z(3wnxdcM#U;YdIfv!9^wApyYc?|9Cvp5|K=H*zdZ^3M7B#zI_*lDaxQQTcmHUVXI! zW&yrjsxR@{;u1JgEir|bV21hV@kNbBg+k=HS`yE2#Xi@19QRI4&GXJesJBY_t$qJE zpAM>pSdE#1Rvvd*-FK(gPdu0EzWUMBp8tH(xcB21?bPY@uO|mrzm_39ZeP!URK7N5 zEF=y}>F(EdJz()&01C=YhXxpORpY~B>hPVRkPh+pJV5Y zQa{P4ez*o5@S!ON2nDrU*!6T&kguE13nW^%`*+vbgGm=N* zUDNgGx#C%4v|(ag0br3COg{&J9UhvgddsmfW2iB#*mDg#j6ZLe*cvGv3|Pq#gA*v+ z+%?%9*tU5Uv+ikNE5*PQUaJsLoQ&KJBRE`->C3t6H=l7eyKW>`nZXfX zH0?AlG&8%wdy0T;$UXMOr-Who?3BA~eCrWRDT5&rPzr;w} zR4o?+{0q(jh?s1WVM_~%UIh*k3E-ygwqz{BVb(nW(3F8Y;xk9rqvbZ{qNm1eAV3j= z41nXC1v=M1y!zESuEYk@XttRrquz4CJKtjHfJWQ;b3hf2%D_DFOYklpLJrryB_fXizq z4-ZL7V{Mo4#Gg5)uTL^zLKr9e6#X%A*4gNDL}OtwyhGkuFYHzJ-^FiGHsa&;+UuOm zy3V+dpYC>{saU>eAU7Y;b{Ckd(}kr=rYHY|3$#!`dW#1mrN3_J@lR36YjUe6p~@yj8@J60*95pGC? zKeX{M_2sK+bquJRMkK6Qt##a zFt6JwbpxW8Gt1JCTKsQM%>Phz+0&iVnbwp5!~&Pg0Kg^Q zi!Mg*^2i)510799>KU-IL1WuGkDC{T4E%EO8Ef&&?A1Kwhs$@HWs-SmpM2z>&l4Q6 zKH<@FZ#WvM%zm6#v`ZP0w5LI!$+gAL<@93(WM?fGhPHWt>x_3-m^ZO*0wn&JJQ zu-=P1xU0c;5<*_MqlW77abV=AEqruYUvP|A7T^N`fGT}hgF76^`94q`sjQ{a#If^ za37(2*+BUmpk6M}hL@KdM2bpc3I|t`#W&?ZOub*xmjSBcujnNNH1Y%P#TQp)_S8^< zb*+?Pn=6`Ke?X3)wFruW!8RlZeaGO4E~m1#V=j|Yww|Pw#qKEYsin?)CXsB^U_<2h z<+Rl61hs#P0OuyUJvr4q0n`2@dz|hbxqOPc)1fbYZI+CqL)5FKhh^|wgRm`B>eU%hhD|yG1PQ` z84N`dQb*mB#zDUbSN5QhS0h0JH+4ki0(w{pk>hB$wL86gbWp21w)>%TVI#gAQv`~g zb;cgQEYRZEHAmhU}1>0Kw$ zoU_Q@=Mz&RiD)P+VSAm>wXoYK(8$s~Z0QekAn{>mXkjN-v?5O9X7mwfY>{R>mNRL- zTbx`}YJFAO!-9sLA$}A@dK5&a?Dn49@qN8V39b@7q7}lgLky9anuwT;>)Wxbc!l+? zrYaFua*K#9E7<&(<4-+W}pag;;{7s9(zRcpZJNoU(LYQXAA?y1S7@n#fmk&72w3>>=U2{Aj|?L z480}P$T9%ZMgxQyAJ4PI;}b?9Y8M^5{UC!f;9Ook1dAPQrJn+s%msetM zH)QV8^R2=81;5A&p32_4l&vKe$6|&dAut6_3BgKwQ# zF^Yjx6JK5B87L|Is9X}R{B2O{+Z%16ciIk;Dm(m~qa>+CIvDu=bSvlFbO1?j51zmq zZY63=`L86!kz_|{Pl;IQz46)iUN$o9FO?3=GxTW)l`(r`(|_aC&g3^_$ej8(YV+25^V25!R;~V1z0~4mRKW%+YpAE< zrCJX_3n|Pf{zfshW@=d2C&aI497TUh$UpS~bcOMHb1+XnBKZ0Sf?9ivfl|rusdXE+ z9I+V7skHSOxlJ3j%}JnzI~N)<7zG?r(K)}X*JJB#qtz{uIJIR>$*0Yrw>-eLn&70e zZ3G05Md5wkTMYE*fVKTHRp^)v`)8`7K?h%jE)jaCGo*|^0rPuOXOTqbvs4#;ZCf40 zJM?KluVZM}4B(L)Qoy4kK)w$&{Y_X4K{k_2iS<1^-R z&;|;k;%{8T-*{M(-jHR98&%owNxA<*#Yg9}LvF~j`rZ1)FMe->{zAp!gKNm;93BuA z$1pz5`W&DGqT*zUTcN9F4InB`yiby4bJL8vPL87)XMc+aM8yfCL^9W&>wx}&S^_qsf~Yu-Ojd?V=j6=< zA7{nW&4LF+#j!rMrSH7Z0a0N){AQIa}yy7~q|u2I7+?rhCXLUO|^8E&3en})fw`+?F=bhn8ph!L^M3E}d{ z6o^-i96+iufQQbIsZwjaL;U!i$8)Pj(_Iou;-pcBr?>X-w*-q90f(m_tnIMM3w z?2CWby&u$ta~{lLeR@qFlD3jZHx@Sm#DU4J6>`Xr9w0e`02}%Ppp)Yrz@51MC3}d5 z3XyXcoPOA|3;`)W*enG=i-&}uKB*C-fFg0MS>}ZKm?Uu(zqq>P4d63r5;AJo<_lvh z7*v!E9DxNtD&A$Tl&m}E3z`xQ@s*Ur$7pH?e05UcjB1BiY-in?uG4W4gK(or`ZT3l zPL1P%r`()di=6b&n6}U2M^WA*LT}N@dnJ8fKW=(Vwa(M^AF{gN z&{2{zB5xr9e5O4y~r{n;E z@9q(e#Gw!)0ZbD=X;*_u;nsU{qU*8P0!T=eEy0`#bG)6|nL?jio)aCwmpG7V^nm6} zyH;QSD^|a&g|F-My^qU~18)Gpc`dAsw0i4db>HFReIwQKHDlxG>m7FFG9Vfv>-p~U zBc;s2U!jWSHZoG+i0V(F~~miSWV-6c3^)iX)06wMiX zl=$U)sZ-8dVsr;{SBZufjjlgX6mtwtwXHE++Bq3+oX}`=-+0`A44rg) zIydA1E*4uTPrtICRrY%AKsE*em?@=?WluX;o*<>l`P%19s51mf(fu{wBuHms+}B0t zGcN!%T3GkaS%~QHxW|i^YtOGfJAFJ?%7?n>;QoWmweT4FgioSgLfO7T}G#u2<#IK!=y?Aa^OH_CM8ek@4|;NLP>F+-lX*=5M8h)V84*)S^@hLsr35GInjdMF5-xbeE@BR zPCRIt41pfnIn4h?7h|>RSC~ygWX_;G-a!kZ%)W~B(henWNpHq$62Jxfpi8$Jjt&BET^}ugAgBe?r; z9@duq?1SK)lHGtDQv#FkA!xEz}> zYy~wExlV!BN34xXFAy1eSV5vN2Sca#`4G0Ah22ja55ui3kDA`65g1Vc=((izyh`X< z#7yyRmDKI|`j|Nb9M$RTw2hipah2KXiQh}f#_-#44ybjclt|r4&`-2g8W}0G4P=bt zFOXJN^P^;p@YYIonNwwoBrR=WNZ{X;QJrlhFFkfjWQr3WA*!c*LKT&2z>QEssEbwXQVGZgTqeO{A^m_(ceVd|7bB5H+%NL zz2tW_yhwp={>>U(3$DRce`nd0Jj`t9YndPZBRUOM!*_s5D{;!ldvJo=9@#h1X-lxo zBKwMk6el`wbVDq`@!zs+h*ATA%dXA)lm<209Pbj&z{R-3%pyyYrxdJ)|GOQ7@P&9I zY5hwuIvufW?R6VQn)r0k`=3cGo7}=|)B3_`&~k!Fgo4XPzHhY#2U7|k@VssMlWqd!E2GyvuD1EPILW|j!`&1 zS2*06z3fQ&rgZ#@pXzs(&1k+y-_r7X&zC$^(xK!Cs_;Jiy1$~+g^FxaqO>E4zVZyC zI9!@cqiJqnbQ(j;f^94@!ZugMr|#LD%C^uw?r}owLH@25OdR_0bd7n&3B$W@m?z%e zM5hxYDkY|{A{ugD3yxp4YZjrs`YSqJoz4dl(kHs>DxyCdXD4Q0bd$7#GaYEXjf<<; z6Ux|A%GVR3Rt1SRtuM;@nZ}mU2E-xukp`IsU^@muh^XaUOxanEZtGq|>}62X8BxhTQ2FAM6h<@&3)L^S)wbogxOrAmBmHk=V0^HcmGkuS4k=Inw#jf*p6iI zod%}Pvki(}$(N_yBY7g1DDIL!&f9M8!xSD;Qj(oadF{mbo>GsPM|<+l*}fl2y_o8| zXL%rX?Jac$A7To=uZ->^Eo=O)ld9e9=tMfGd=gxY-PU}iyKHoOiE;f|Sm?#l!X&oEhumYoEE&ceO=LeY*-moh)>HiPf*jyFD#l`QzmB-1{- z{X_QA#%1Nh^X=JS*%zY6kM0~?X?@oGCED1OBD0O=MVkNvlU50`&SVa>xM4S#T0N(G5rfFPFEGb+aE2V~?Uia0j?pCg*q zI~8IAENm=gV|0B;2Lxpf@YCQiqh8H)r-=ls;ZxDxO0E6dRi-7_rtv!O83tQ^V8`qV z#Sf8C4vCGgm*hL~LE(5qRY(m+U+;iae{quS9#uvnKOX%oY1Q{r6n00NAl3GN{}uDk zs`6)5`LnA0Syle5Du2!@f6gj@&MJS-Dt~T3e{Mj3Za{x-K>t$G{h6!$nXCMntNcGO zR{``CqK<0_{b>nmvXa@!6@JgC*$22?gNSdXSYVHchHX|no z$wtW+gN+lRKraK?96nshGf9_#cUWMFthh@jX-Gj(wc~1@(dZGo-PM0?#`xGwH zxLzlR1)oDczp~T~Me&ZMrC#Z^jI)|TLVKTiA1`j@ZJpfCF4(5Iovl30YQ1F+5HP8| znLCqFoj57Jel>k`e#fHx)Ai@%^|gRI`M;m3_fRg?Jb5;6TYsRmOa0OfYXhKNs1F)^ z1D}U|!jzY6AufrKe$~OXcgKiS{Gbg74?x3&p@sDp9mZ&JkYY#m7au|Ma9Hu;21|}& z#_x(!B@LGz!?EL3nbJnfj$^fIOu4dNm!H5B#+-y6PgI=5bbwq){#4~ROdQN;rIK-! z7{lPf9BuM9)u)(=8kI&hqZXMloP*offC#0B#)-=ia8G=x2-+R?+Uaq4Xi*XR%T*2{ z*uE*&;1I~WLc>xl&QR!%1G|xMw9YQzy;}zopo)Ri3~Rn?xlEXJ70sXAdios)5w9w8 z&!wnDTPNML?)^^d;oL>Axz(dxkspbZJNyK39_LpGT&(DU)uWE9ByIEi(i``lUn3HD zQZ;f6*_U{xT-)pVy5$h6+{G#5u50`~&>;0Tz@vSMkO~iF=l*3#<1fk9opYf_dq~La zLzbQ!apF8wJ;R}3L;wf06Di%&nbNdI@Q|k3`viDVWo5y#mlE+HBd)L>?W7(DLQe z+_@90ee6uTj&X}@#D!}-T_Gi(8o->12ug8$-r}BDaoL+4S&U5cE~xa=p>=nPJr2<; z3H%;fR3$+WS)6{7=}tLZg*`8d&;ux-Aemjr1PYia6N$(^MORjTTb1%F{TfzYtrgCr zovJm8_bPQ#F=to(4&Aaarh~efr+PeKja_h zPpee;J#!Bdc@X0s?^9UB{V{B%qd_vYffmCy1>n-W$BaV{EA|2i2U3>AD?Kb$-&CcY z-@bfR^u1>%6pN4`lN~Ds5^3{hY^JS&?@~KB;&s|t9r-YUdWzhLCL7S~7W9jLq(v&t z{n@mnDf3v#3s=t9B^Hl($Dc~Q6@J?AqHU{FKmO%W*MZT@`NT7^mp?Qcmk8CJMrV>S z=tf#H$uZ0IP+IFx#m!XkGIlk%v*{q;9*1Ia5vKz73LOy@F5R5&AKR{bl}4wzD!rHT zrP}X*%DWm4`rQ5#HPo3cv&fdRIX!1vGDZKwpX4x$JjOO9|LnM%kQys&Ge3l!dq(_a zC`V%*794&@81y26jwe*}InvzEr2rQt4jN8BUk<=Js{gw9;KeUg#l;?+x!$8p>aF49 z46F0kCtbf3hnZuK7Xjwc)%nne*b2)@@@84Q%L*=ct#5$K_S|m|+wK%-ivN0gWKw$w zNE4{a$Vbm3QD&>@l1J|w_SKnTGXb;_YZP=^-VV*@8>mIWc&fZOtG#3#>affIL|pb? zKH%#)aTY89{FG^OFu~#_xDjiz9HeJjA4h_gP!8R8G!_mGhtQ#$2kZklQ3^h9oWFfk zyurLALFQH-lV1D8#74;Jevh@6nG_2N;#U|FQMtb4ZQCD_I-*F2;y~xs?`WG7!*4$n z1qhHQA_15dV8xk|6>sADFU+fb(ZkupeVs}D)sj~L$lN{$lZW0n4H+VVtQjYr|732h zo`>C%X$P3!zIN6&Y1FSC#Da1|6peS-^$7)M;t)tv+KW{Lv7x80fk9ljaAYem= zhLLxEr5V_%-Mz;e4}e#?{R&UzCd~0$ zs9mbPGX7Z%e*hF}W5g$gHVa@UGL9f3Qknn4ya?hkORBzVWK~peD^AyNjMTAX!4;}5 z0IF#I4-pY7Y+~PKdH||sCf>zz@yXOWw3obZ>rSN`&;mibHCn>Yh|py(B7me^AMBz; zPIJo8W3Lg()ZYF2@+}hWl0zJTG}#jx5dYnfQLL)JtPVD0+>NAvF6`%UtCGOj#bS%&W}bIVs#^P#BO5SREi%TJ_ENH28v{cK7*4rrRq-n;5CW>b7*{P}|WzL{Ew2=T4= zA8y~oo|2fJfC%o-3ic6oAvML6W5s(*BKEA9#UkbP&gN?C8?hT%OP#qr&pjN_0E;~}QM55sn85B3x?t@Vj z4#CXbu7vFDid)IduiFN`sP^I6-@%J8B=xB{(IBPX6dz9);>8x`HlMjVp-y&~KZ}Fb zg}YKW`mXL2d=6gTr3S~57X@#c-1FlUGTYooJXQf5MZ63!+Y)IySK86Q%SF#*0~J7` z6cy4`NQvV2dL@T%h{*ixV;>246<;~VBGta}qjCTjSL@K18M}nYAVGhWzxbIU$qa^~ zj&dUZmD~qv)W!IA0I`W7Z?Jk`{fwNA7m6eQK^it2W)!Gym5>@;NQ9yXYR%l8E7q5l zpk`B)UWpGvD=t3gL&WS=J0d!Usa9iZ4jCja#q7_&1$^MG!tja5~zvL%&BvkR9|35`_HX1}Z>i|toS~*w9p3^DUq9~j@>aiRhBM$=4++fo~j){m5mZWH5u(o9Mho+ zfWqrC;<|ZnfN)HC1Y*`~)2|37g@9RGX5eP215!-n6nr~FbQ*f==hEUTM>LeN9RR%O zl5I8c*z@}QiiVc-p)ggQ>bi`qY2*A}I{&=3HN<}ONdy%RGahX1bU*gV&W}z0lpaWm zBP9SfpB5?owasO)WT{)`{n$m*}C zj>^%1rCHXd8X$u=$bk8`r@w!O!G~(t8c-wH2y*_&SOR_>k!Uv19d4X>M|pf@4oqk$ zQVlRp@9zL`!s#Ak?n}NIUlZ4Hxi?nVO;~m!t+M__Wj@8;vM)?t4WC3_Vy3Q*-l7i8u8(nw>DD-J&tt6Vj2jMbfv}p_x^(|qdHR(^6nPr zD4lZS07Pbc@fB~^13MMhy@*$v2z_w<9Syo@`$vP%{ zjJbhCXzUlTCWV(jE(%+&p#xAm7TJ-64<*Z8oc%gVrzjjtz2vaIyXA|Iuu0&wI#h#7 zKER6ty>HfT^ZRDc?1B~X08e?l`;Vx#=Zs_;u*m(OLF1SM$qm)`gDx0qua|e8!W;0Q zBrs8w`{H8KG*-nf7>NoN8GIsFM6JU@C4v_eHI&wgCM$i0zcqQ_lUU_@ZSQzD4+a*~ zCZVN3o)q~qf6)1G%((QRM~?|7gzo;=Mx@_M)%1-(Z;!8`9zUH*5eE;mYq1Z0wm;nq zdbn!hkh}Q-^5$EY0pZXqsSsj<*B~n_Pj`>%_3@8aWfrql-FJ4b`OAWf066KD0wv~- z_oVUbVw17bJjJ{2$@?(53wktl%}G#j4wt_NJ*69< zI+%OWYZku?NE!b$DnYY|PxX7=tHw=Qimwu`pCm}IEls{=WPyb6utqikR24B% zk#hN&{a`5ozI1l6smzpBi&kO_(No2~JGf;+@gnd`R(M3o)`*-fmJJyITiz$dr-Hlf z^FQl1qhw_MKid21xTvt7+81XIluEe_x;@Wz45vCuX~2i49~2+cFfv)?Y-7Fo`o@HSJ-XOv>b>{dMiK#z3`g)|j&T=zFwd z?AJS4pKg#v@iIh$7L|t0Mp?(C0;L_P!1P9Vu%@4 z<7Mj0vVq;NOTph|z+fVQFy=^syL09$fttp}sw+COLyO86XD?O9-V8ICe}07vA$uo~ zq{1+?0H2e{KXsFZn#X|`RdcGE-Da4p7gn=sm-!_`ks5ZVa|)KYE5sf(bEVt7Tb^;G zI@ktIO+ETda$7^RIQcBtsPO2j^Oj7b?#(ijd44?)?3kvE+xi4$H}Dnn8&D8ECOJK2 zXnKWcy1shfkXxPl-e8{R%=lf(t;z1+~ZU<$}jr% z!ZTkld(ZGr-78NQUp=sC!0zu#^AN<@cKiGWAJKw>4>M&8eB+E8OfYuHF?(1uL_=?D zIrMyK`t!7s)wSlj!7H`tx_sEOp^u2PEe&q2r2w3sMTXqC!1lSPM1-iz_;^>k`P(^T zkvR)J`P8@;<3y+RKk-Aw32|(`qoxx$w;8LE8)7lW6MDk8Zzi(h^}MT7_Znih3HJ1x zV!>r&c$81*pqza{$_S&jfL{p!2O`o202@LW05>4nWq^g}PImN?Ks2}GATQ#KJE(jH zsdCFm{Rw8Qz^KP2rEd4|s+o7!B|gXRJ@-ANlgPw)qHD67ax<*5r7+ucZ)pk=)s$H$ z2>P@+j}ZroBWbL8m!s$83(S^o&lN#&>CdMgl&f;mPi5UlJ7}iHsj`}j2DY+A)%?_0 zCle*;{U%N^KpDMg8mpz&Dzz7(+uJf!>lNy4EXwfs{%LNjQW*nR!bdr|5k}qmosL~T zEtl1g85|Qu*!p(v4^3)$ZDh0~KAE==rn*vLDhEgvQ=ijRsIrQVgwo*evfx$;8D3}O z``OAfc-xR=PJe!}Gx*|TlZW#M4J(=krXQ@1o@mB>d{Sm8h<96Hf>_A6zmPOSlrdhd zgCAGNKj*%py`_@u^=C!mQp4%XjUBR6_=|-K-kYgh905-XFy>zV2^#K^2Iih?w>$ID zk2jvpd!4-1NVxX#s_%pb5xawCmdmIaKfA##4=D?UVgWC5wGa!hqJw$AHN8z~{MAkN zYkEy!cSyF!hserCYDzfNzLe(CV9+MFE6zO?laT~ERH1NcdT)bRV~q{5)9ui=Kl(ER zIyPByh5}sH@gH(ORXHr79Qnvh)RFLibN*wG@T>g}-UeJM<90MhLR8ENHAWKUa(s=z z=suPDR?uT>NKicR;gt@KAV)gS-R zsab{fJdzCt#6uQ-W)hF*4!W&{Wg%q^doq29<+nYjCwvBfDg^$Sb7(9s9z>2g4ElZ*N^|M>6(x6h z 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 bCTH8 Date: Wed, 17 Jun 2026 23:04:56 +0100 Subject: [PATCH 139/144] Update project references from Deskflow to Synergy in source files --- extra/src/integtests/CMakeLists.txt | 2 +- extra/src/lib/synergy/gui/CancelActivationDialog.cpp | 2 +- extra/src/lib/synergy/gui/CancelActivationDialog.h | 2 +- extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp | 2 +- extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h | 2 +- extra/src/unittests/CMakeLists.txt | 2 +- extra/src/unittests/gui/license/LicenseHandlerTests.cpp | 2 +- extra/src/unittests/gui/license/license_notices_tests.cpp | 2 +- extra/src/unittests/license/LicenseTests.cpp | 2 +- extra/src/unittests/license/ProductTests.cpp | 2 +- extra/src/unittests/license/SerialKeyTypeTests.cpp | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/extra/src/integtests/CMakeLists.txt b/extra/src/integtests/CMakeLists.txt index be5c31e32..d68fb0b15 100644 --- a/extra/src/integtests/CMakeLists.txt +++ b/extra/src/integtests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Deskflow -- mouse and keyboard sharing utility +# Synergy -- mouse and keyboard sharing utility # Copyright (C) 2024 - 2026 Synergy App Ltd # # This package is free software; you can redistribute it and/or diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.cpp b/extra/src/lib/synergy/gui/CancelActivationDialog.cpp index 3155b14cd..466612d03 100644 --- a/extra/src/lib/synergy/gui/CancelActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.cpp @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or diff --git a/extra/src/lib/synergy/gui/CancelActivationDialog.h b/extra/src/lib/synergy/gui/CancelActivationDialog.h index 8484de2d1..9735e1b07 100644 --- a/extra/src/lib/synergy/gui/CancelActivationDialog.h +++ b/extra/src/lib/synergy/gui/CancelActivationDialog.h @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or diff --git a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp index 3fedfbde6..f36e0ca61 100644 --- a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp +++ b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.cpp @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2022 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or diff --git a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h index da650ad81..848aaa4f0 100644 --- a/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h +++ b/extra/src/lib/synergy/gui/dialogs/UpgradeDialog.h @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2022 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or diff --git a/extra/src/unittests/CMakeLists.txt b/extra/src/unittests/CMakeLists.txt index e8e872979..0982bbb53 100644 --- a/extra/src/unittests/CMakeLists.txt +++ b/extra/src/unittests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Deskflow -- mouse and keyboard sharing utility +# Synergy -- mouse and keyboard sharing utility # Copyright (C) 2024 - 2026 Synergy App Ltd # # This package is free software; you can redistribute it and/or diff --git a/extra/src/unittests/gui/license/LicenseHandlerTests.cpp b/extra/src/unittests/gui/license/LicenseHandlerTests.cpp index 202efe96e..4fe43a6c1 100644 --- a/extra/src/unittests/gui/license/LicenseHandlerTests.cpp +++ b/extra/src/unittests/gui/license/LicenseHandlerTests.cpp @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or diff --git a/extra/src/unittests/gui/license/license_notices_tests.cpp b/extra/src/unittests/gui/license/license_notices_tests.cpp index 14043ed16..86d1ae127 100644 --- a/extra/src/unittests/gui/license/license_notices_tests.cpp +++ b/extra/src/unittests/gui/license/license_notices_tests.cpp @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2024 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or diff --git a/extra/src/unittests/license/LicenseTests.cpp b/extra/src/unittests/license/LicenseTests.cpp index c4948712a..199211ca8 100644 --- a/extra/src/unittests/license/LicenseTests.cpp +++ b/extra/src/unittests/license/LicenseTests.cpp @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or diff --git a/extra/src/unittests/license/ProductTests.cpp b/extra/src/unittests/license/ProductTests.cpp index 6d38d142e..87dba0f73 100644 --- a/extra/src/unittests/license/ProductTests.cpp +++ b/extra/src/unittests/license/ProductTests.cpp @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or diff --git a/extra/src/unittests/license/SerialKeyTypeTests.cpp b/extra/src/unittests/license/SerialKeyTypeTests.cpp index ba9e7de9b..0f2ec725d 100644 --- a/extra/src/unittests/license/SerialKeyTypeTests.cpp +++ b/extra/src/unittests/license/SerialKeyTypeTests.cpp @@ -1,5 +1,5 @@ /* - * Deskflow -- mouse and keyboard sharing utility + * Synergy -- mouse and keyboard sharing utility * Copyright (C) 2016 - 2026 Synergy App Ltd * * This package is free software; you can redistribute it and/or From 14d3e5040204a6c9dd76a41f4e2e628514ad3d7a Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 23:07:53 +0100 Subject: [PATCH 140/144] Update local network usage description to use project name placeholder --- src/apps/res/deskflow.plist.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/res/deskflow.plist.in b/src/apps/res/deskflow.plist.in index c95e4d4d3..da3cbced2 100644 --- a/src/apps/res/deskflow.plist.in +++ b/src/apps/res/deskflow.plist.in @@ -41,7 +41,7 @@ NSLocalNetworkUsageDescription - This app uses local network permissions for communication between a Deskflow server and clients. + This app uses local network permissions for communication between a @CMAKE_PROJECT_PROPER_NAME@ server and clients. com.apple.security.networking.local From 4132561e8b3dda2423cf4f28c8d3381e50d827fc Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 23:26:31 +0100 Subject: [PATCH 141/144] Refactor macOS signing conditions in CI workflow to use MAC_SIGNING environment variable --- .github/workflows/ci.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3023045c3..47f677db9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -264,6 +264,10 @@ jobs: name: ${{ matrix.target.name }} runs-on: ${{ matrix.target.runs-on }} timeout-minutes: ${{ matrix.target.timeout }} + + env: + MAC_SIGNING: ${{ secrets.APPLE_CODESIGN_ID != '' }} + strategy: fail-fast: false matrix: @@ -312,7 +316,7 @@ jobs: # set-key-partition-list grants codesign passwordless access so the later # sign step doesn't prompt. - name: Set up signing keychain - if: env.SYNERGY_VERSION_RELEASE == 'true' + if: env.MAC_SIGNING == 'true' shell: bash env: APPLE_P12_CERTIFICATE: ${{ secrets.APPLE_P12_CERTIFICATE }} @@ -323,8 +327,6 @@ jobs: security create-keychain -p "$keychain_pass" "$keychain" security set-keychain-settings -lut 3600 "$keychain" security unlock-keychain -p "$keychain_pass" "$keychain" - # Prepend to keychain search list so codesign finds the cert without - # changing the user's default keychain. security list-keychains -d user -s "$keychain" $(security list-keychains -d user | tr -d ' "') cert_file="$RUNNER_TEMP/cert.p12" echo "$APPLE_P12_CERTIFICATE" | base64 --decode > "$cert_file" @@ -339,7 +341,7 @@ jobs: # signatures inward. `--deep --options runtime` are required for # notarization to accept the bundle later. - name: Sign macOS bundle - if: env.SYNERGY_VERSION_RELEASE == 'true' + if: env.MAC_SIGNING == 'true' shell: bash env: APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} @@ -376,7 +378,7 @@ jobs: # submit to notarytool (synchronous via --wait) and staple the returned # ticket so Gatekeeper can verify offline. - name: Sign + notarize + staple DMG - if: env.SYNERGY_VERSION_RELEASE == 'true' + if: env.MAC_SIGNING == 'true' shell: bash env: APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} From 4ab27c35154640eede7c8217549d3b0ae3f8a557 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Wed, 17 Jun 2026 23:44:34 +0100 Subject: [PATCH 142/144] Enhance macOS notarization process by adding codesigning for app bundle and updating CI workflow to include APPLE_CODESIGN_ID --- .github/workflows/ci.yml | 48 ++++++++++++++++++---------------------- deploy/mac/deploy.cmake | 19 ++++++++++++++++ 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47f677db9..34039d0da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -303,7 +303,7 @@ jobs: uses: ./.github/actions/get-version - name: Configure - run: ${{ env.CMAKE_CONFIGURE }} ${{ matrix.target.config-args }} -DSYNERGY_VERSION_RELEASE=${{ env.SYNERGY_VERSION_RELEASE }} -DSYNERGY_VERSION_SNAPSHOT=${{ env.SYNERGY_VERSION_SNAPSHOT }} + run: ${{ env.CMAKE_CONFIGURE }} ${{ matrix.target.config-args }} -DSYNERGY_VERSION_RELEASE=${{ env.SYNERGY_VERSION_RELEASE }} -DSYNERGY_VERSION_SNAPSHOT=${{ env.SYNERGY_VERSION_SNAPSHOT }} -DAPPLE_CODESIGN_ID="${{ secrets.APPLE_CODESIGN_ID }}" - name: Build shell: bash @@ -336,33 +336,16 @@ jobs: echo "SIGN_KEYCHAIN=$keychain" >> "$GITHUB_ENV" echo "SIGN_KEYCHAIN_PASS=$keychain_pass" >> "$GITHUB_ENV" - # Sign the inner .app BEFORE the DMG is built — CPack DragNDrop embeds - # the bundle into the DMG, and signing the DMG afterward doesn't propagate - # signatures inward. `--deep --options runtime` are required for - # notarization to accept the bundle later. - - name: Sign macOS bundle - if: env.MAC_SIGNING == 'true' - shell: bash - env: - APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} - run: | - bundle=$(find build -maxdepth 4 -name "*.app" -type d | head -n1) - if [ -z "$bundle" ]; then - echo "ERROR: no .app bundle found under build/" >&2 - find build -maxdepth 4 -name "*.app" -o -name "Info.plist" 2>/dev/null - exit 1 - fi - security unlock-keychain -p "$SIGN_KEYCHAIN_PASS" "$SIGN_KEYCHAIN" - /usr/bin/codesign \ - --deep --force --timestamp --options runtime \ - --sign "$APPLE_CODESIGN_ID" "$bundle" - /usr/bin/codesign --verify --strict --verbose=2 "$bundle" - echo "Bundle signed: $bundle" - - # CPack on macOS is flaky; retry a handful of times before failing. + # CPack runs macdeployqt and, when a Developer ID is configured, codesigns + # the bundle that gets embedded in the DMG (see deploy/mac/deploy.cmake), so + # the keychain must be unlocked first. CPack on macOS is flaky; retry a + # handful of times before failing. - name: Package shell: bash run: | + if [ -n "$SIGN_KEYCHAIN" ]; then + security unlock-keychain -p "$SIGN_KEYCHAIN_PASS" "$SIGN_KEYCHAIN" + fi for i in $(seq 1 5); do if cmake --build build --config Release -j8 --target package 2>&1; then echo "Package successful" @@ -397,11 +380,22 @@ jobs: security unlock-keychain -p "$SIGN_KEYCHAIN_PASS" "$SIGN_KEYCHAIN" /usr/bin/codesign --force --timestamp --sign "$APPLE_CODESIGN_ID" "$dmg" - xcrun notarytool submit "$dmg" \ + result=$(xcrun notarytool submit "$dmg" \ --apple-id "$APPLE_NOTARY_USER" \ --password "$APPLE_NOTARY_PASSWORD" \ --team-id "$APPLE_TEAM_ID" \ - --wait + --output-format json --wait) + echo "$result" + id=$(echo "$result" | python3 -c "import sys, json; print(json.load(sys.stdin)['id'])") + status=$(echo "$result" | python3 -c "import sys, json; print(json.load(sys.stdin)['status'])") + if [ "$status" != "Accepted" ]; then + echo "Notarization $status; per-file detail follows:" >&2 + xcrun notarytool log "$id" \ + --apple-id "$APPLE_NOTARY_USER" \ + --password "$APPLE_NOTARY_PASSWORD" \ + --team-id "$APPLE_TEAM_ID" + exit 1 + fi xcrun stapler staple "$dmg" xcrun stapler validate "$dmg" diff --git a/deploy/mac/deploy.cmake b/deploy/mac/deploy.cmake index 57b7adc92..35cf98a71 100644 --- a/deploy/mac/deploy.cmake +++ b/deploy/mac/deploy.cmake @@ -14,6 +14,25 @@ if (OSX_BUNDLE) \"\${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_PROPER_NAME}.app\" -timestamp -codesign=- )") + + # macdeployqt only ad-hoc signs the staged bundle above. When a Developer ID + # is provided, re-sign every nested binary with hardened runtime and a secure + # timestamp so the app embedded in the DMG passes notarization; an ad-hoc + # signature is rejected by the notary service as Invalid. + if(APPLE_CODESIGN_ID) + install(CODE " + execute_process( + COMMAND /usr/bin/codesign --force --deep --options runtime --timestamp + --sign \"${APPLE_CODESIGN_ID}\" + \"\${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_PROPER_NAME}.app\" + RESULT_VARIABLE result + ) + if(NOT result EQUAL 0) + message(FATAL_ERROR \"codesign of the app bundle failed (\${result})\") + endif() + ") + endif() + set(CPACK_PACKAGE_ICON "${MY_DIR}/dmg-volume.icns") set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/extra/deploy/mac/dmg-background.tiff") set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${MY_DIR}/generate_ds_store.applescript") From 6c34f2177fafc53007587914020f867e46439519 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 18 Jun 2026 00:00:00 +0100 Subject: [PATCH 143/144] Add CPack pre-build script for app bundle codesigning to ensure notarization compliance --- deploy/mac/deploy.cmake | 26 +++++++++++--------------- extra/deploy/mac/codesign.cmake.in | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 extra/deploy/mac/codesign.cmake.in diff --git a/deploy/mac/deploy.cmake b/deploy/mac/deploy.cmake index 35cf98a71..ae074ad04 100644 --- a/deploy/mac/deploy.cmake +++ b/deploy/mac/deploy.cmake @@ -15,22 +15,18 @@ if (OSX_BUNDLE) -timestamp -codesign=- )") - # macdeployqt only ad-hoc signs the staged bundle above. When a Developer ID - # is provided, re-sign every nested binary with hardened runtime and a secure - # timestamp so the app embedded in the DMG passes notarization; an ad-hoc - # signature is rejected by the notary service as Invalid. + # macdeployqt above only ad-hoc signs the staged bundle, and later install() + # steps (e.g. LICENSE into Resources) would invalidate any signature applied + # at install time. So when a Developer ID is provided, defer signing to a CPack + # pre-build script that runs once the staging tree is fully populated, right + # before the DMG is built; otherwise the notary service rejects it as Invalid. if(APPLE_CODESIGN_ID) - install(CODE " - execute_process( - COMMAND /usr/bin/codesign --force --deep --options runtime --timestamp - --sign \"${APPLE_CODESIGN_ID}\" - \"\${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_PROPER_NAME}.app\" - RESULT_VARIABLE result - ) - if(NOT result EQUAL 0) - message(FATAL_ERROR \"codesign of the app bundle failed (\${result})\") - endif() - ") + configure_file( + ${CMAKE_SOURCE_DIR}/extra/deploy/mac/codesign.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/mac-codesign.cmake + @ONLY + ) + set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_CURRENT_BINARY_DIR}/mac-codesign.cmake) endif() set(CPACK_PACKAGE_ICON "${MY_DIR}/dmg-volume.icns") diff --git a/extra/deploy/mac/codesign.cmake.in b/extra/deploy/mac/codesign.cmake.in new file mode 100644 index 000000000..559aa30eb --- /dev/null +++ b/extra/deploy/mac/codesign.cmake.in @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: (C) 2012 - 2026 Synergy App Ltd +# SPDX-License-Identifier: MIT + +# CPack pre-build script: sign the app bundle as the final packaging step, after +# every install() has populated it (frameworks, LICENSE, translations) and just +# before the DMG is built. Signing earlier and then adding files breaks the seal, +# which the notary service rejects with "the signature of the binary is invalid". + +set(app "${CPACK_TEMPORARY_INSTALL_DIRECTORY}/@CMAKE_PROJECT_PROPER_NAME@.app") + +execute_process( + COMMAND /usr/bin/codesign --force --deep --options runtime --timestamp + --sign "@APPLE_CODESIGN_ID@" "${app}" + RESULT_VARIABLE result +) +if(NOT result EQUAL 0) + message(FATAL_ERROR "codesign of the app bundle failed (${result})") +endif() From c7b72e80ebb4a5ef491efe505171883700dac5ee Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Thu, 18 Jun 2026 00:18:48 +0100 Subject: [PATCH 144/144] Replace qFatal with qCritical for improved error handling in various components --- .../src/lib/synergy/gui/ActivationDialog.cpp | 3 ++- .../synergy/gui/license/LicenseApiClient.cpp | 15 ++++++++----- .../synergy/gui/license/LicenseHandler.cpp | 20 +++++++++++------ .../synergy/gui/license/license_notices.cpp | 4 ++-- src/lib/gui/config/ServerConfig.cpp | 2 +- src/lib/gui/core/CoreProcess.cpp | 22 ++++++++++++------- src/lib/gui/dialogs/SettingsDialog.cpp | 3 ++- src/lib/gui/validators/LineEditValidator.cpp | 2 +- 8 files changed, 45 insertions(+), 26 deletions(-) diff --git a/extra/src/lib/synergy/gui/ActivationDialog.cpp b/extra/src/lib/synergy/gui/ActivationDialog.cpp index ab157feff..5b4827c7f 100644 --- a/extra/src/lib/synergy/gui/ActivationDialog.cpp +++ b/extra/src/lib/synergy/gui/ActivationDialog.cpp @@ -187,7 +187,8 @@ void ActivationDialog::showResultDialog(LicenseHandler::SetSerialKeyResult resul break; default: - qFatal("unexpected change serial key result: %d", static_cast(result)); + qCritical("unexpected change serial key result: %d", static_cast(result)); + break; } } diff --git a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp index 502c9a02c..6c02f0db6 100644 --- a/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseApiClient.cpp @@ -221,23 +221,28 @@ void LicenseApiClient::handleResponse(QNetworkReply *reply) QByteArray LicenseApiClient::getRequestData(const Data &data, std::optional takeover) const { if (data.machineSignature.isEmpty()) { - qFatal("cannot create license request, no machine id"); + qCritical("cannot create license request, no machine id"); + return {}; } if (data.hostnameSignature.isEmpty()) { - qFatal("cannot create license request, no hostname"); + qCritical("cannot create license request, no hostname"); + return {}; } if (data.serialKey.isEmpty()) { - qFatal("cannot create license request, no serial key"); + qCritical("cannot create license request, no serial key"); + return {}; } if (data.appVersion.isEmpty()) { - qFatal("cannot create license request, no app version"); + qCritical("cannot create license request, no app version"); + return {}; } if (data.osName.isEmpty()) { - qFatal("cannot create license request, no os name"); + qCritical("cannot create license request, no os name"); + return {}; } QJsonObject requestData; diff --git a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp index d35025f52..49bf5f1d7 100644 --- a/extra/src/lib/synergy/gui/license/LicenseHandler.cpp +++ b/extra/src/lib/synergy/gui/license/LicenseHandler.cpp @@ -100,7 +100,8 @@ LicenseHandler::LicenseHandler() resetGracePeriod(); if (m_pCoreProcess == nullptr) { - qFatal("core process not set"); + qCritical("core process not set"); + return; } // Key entry activations must never start the core; the customer chose nothing yet. @@ -166,14 +167,15 @@ void LicenseHandler::handleMainWindow(QMainWindow *mainWindow, deskflow::gui::Co qDebug("main window create handled"); if (!loadSettings()) { - qFatal("failed to load license settings"); + qCritical("failed to load license settings"); } } bool LicenseHandler::handleAppStart() { if (m_pMainWindow == nullptr) { - qFatal("main window not set"); + qCritical("main window not set"); + return false; } if (!m_enabled) { @@ -313,11 +315,13 @@ bool LicenseHandler::handleCoreStart() } if (m_pMainWindow == nullptr) { - qFatal("main window not set"); + qCritical("main window not set"); + return false; } if (m_pCoreProcess == nullptr) { - qFatal("core process not set"); + qCritical("core process not set"); + return false; } // The role is only reliably known at core start; start optimistically, a deactivated verdict stops the core. @@ -553,7 +557,8 @@ LicenseHandler::SetSerialKeyResult LicenseHandler::setLicense(const QString &hex using enum LicenseHandler::SetSerialKeyResult; if (hexString.isEmpty()) { - qFatal("serial key is empty"); + qCritical("serial key is empty"); + return kInvalid; } qDebug() << "changing serial key to:" << hexString; @@ -630,7 +635,8 @@ void LicenseHandler::clampFeatures() const auto isSystemScope = (Settings::settingsFile() == Settings::SystemSettingFile); if (isSystemScope && !m_license.isSettingsScopeAvailable()) { - qFatal("settings scope not available"); + qCritical("settings scope not available"); + return; } qDebug("committing default feature settings"); diff --git a/extra/src/lib/synergy/gui/license/license_notices.cpp b/extra/src/lib/synergy/gui/license/license_notices.cpp index ce7ac3fa2..1ec4a9d24 100644 --- a/extra/src/lib/synergy/gui/license/license_notices.cpp +++ b/extra/src/lib/synergy/gui/license/license_notices.cpp @@ -34,8 +34,8 @@ QString licenseNotice(const License &license, const QString &linkColor) } else if (license.isSubscription()) { return subscriptionLicenseNotice(license, linkColor); } else { - qFatal("license notice only for time limited licenses"); - return ""; // Workaround for no return warning on Windows. + qCritical("license notice only for time limited licenses"); + return ""; } } diff --git a/src/lib/gui/config/ServerConfig.cpp b/src/lib/gui/config/ServerConfig.cpp index 75f38e0a6..0f976fd30 100644 --- a/src/lib/gui/config/ServerConfig.cpp +++ b/src/lib/gui/config/ServerConfig.cpp @@ -258,7 +258,7 @@ QTextStream &operator<<(QTextStream &outStream, const ServerConfig &config) outStream << "\t" << "heartbeat = " << config.heartbeat() << Qt::endl; if (config.protocol() == NetworkProtocol::Unknown) - qFatal("unrecognized protocol when writing config"); + qCritical("unrecognized protocol when writing config"); outStream << "\t" << "protocol = " << networkProtocolToOption(config.protocol()) << Qt::endl; outStream << "\t" diff --git a/src/lib/gui/core/CoreProcess.cpp b/src/lib/gui/core/CoreProcess.cpp index de1b5c2e5..83be94011 100644 --- a/src/lib/gui/core/CoreProcess.cpp +++ b/src/lib/gui/core/CoreProcess.cpp @@ -105,7 +105,7 @@ CoreProcess::CoreProcess(const ServerConfig &serverConfig) { m_appPath = QStringLiteral("%1/%2").arg(QCoreApplication::applicationDirPath(), kCoreBinName); if (!QFile::exists(m_appPath)) { - qFatal("core server binary does not exist"); + qCritical("core server binary does not exist"); return; } @@ -216,7 +216,8 @@ void CoreProcess::startForegroundProcess(const QStringList &args) using enum ProcessState; if (m_processState != Starting) { - qFatal("core process must be in starting state"); + qCritical("core process must be in starting state"); + return; } // only make quoted args for printing the command for convenience; so that the @@ -245,7 +246,8 @@ void CoreProcess::startForegroundProcess(const QStringList &args) void CoreProcess::startProcessFromDaemon() { if (m_processState != ProcessState::Starting) { - qFatal("core process must be in starting state"); + qCritical("core process must be in starting state"); + return; } const auto configFile = Settings::settingsFile(); @@ -271,11 +273,13 @@ void CoreProcess::startProcessFromDaemon() void CoreProcess::stopForegroundProcess() const { if (m_processState != ProcessState::Stopping) { - qFatal("core process must be in stopping state"); + qCritical("core process must be in stopping state"); + return; } if (!m_process) { - qFatal("process not set, cannot stop"); + qCritical("process not set, cannot stop"); + return; } qInfo("stopping core desktop process"); @@ -291,7 +295,8 @@ void CoreProcess::stopForegroundProcess() const void CoreProcess::stopProcessFromDaemon() { if (m_processState != ProcessState::Stopping) { - qFatal("core process must be in stopping state"); + qCritical("core process must be in stopping state"); + return; } auto sendStop = [this] { @@ -398,7 +403,7 @@ void CoreProcess::start(std::optional processModeOption) if (m_mode == Settings::CoreMode::Server) { const auto [hasNeededPermissions, configFilename] = persistServerConfig(); if (configFilename.isEmpty()) { - qFatal("config file name empty for server args"); + qCritical("config file name empty for server args"); return; } if (!hasNeededPermissions) { @@ -661,7 +666,8 @@ void CoreProcess::clearSettings() } if (processMode != ProcessMode::Service) { - qFatal("invalid process mode"); + qCritical("invalid process mode"); + return; } qInfo("clearing core settings through daemon"); diff --git a/src/lib/gui/dialogs/SettingsDialog.cpp b/src/lib/gui/dialogs/SettingsDialog.cpp index 8a3409e06..11514fc14 100644 --- a/src/lib/gui/dialogs/SettingsDialog.cpp +++ b/src/lib/gui/dialogs/SettingsDialog.cpp @@ -365,7 +365,8 @@ bool SettingsDialog::isClientMode() const void SettingsDialog::updateKeyLengthOnFile(const QString &path) { if (!QFile(path).exists()) { - qFatal("tls certificate file not found: %s", qUtf8Printable(path)); + qCritical("tls certificate file not found: %s", qUtf8Printable(path)); + return; } auto length = TlsUtility::getCertKeyLength(path); diff --git a/src/lib/gui/validators/LineEditValidator.cpp b/src/lib/gui/validators/LineEditValidator.cpp index c4278df2f..b9203110c 100644 --- a/src/lib/gui/validators/LineEditValidator.cpp +++ b/src/lib/gui/validators/LineEditValidator.cpp @@ -21,7 +21,7 @@ LineEditValidator::LineEditValidator(QLineEdit *lineEdit, ValidationError *error { if (!m_pLineEdit) { - qFatal("validator line edit not set"); + qCritical("validator line edit not set"); } }