409 Commits

Author SHA1 Message Date
d4ff55da13 Release 1.25.0
Some checks are pending
Continuous Integration / ci-passed (push) Blocked by required conditions
Continuous Integration / test-results (push) Blocked by required conditions
Continuous Integration / lint-reuse (push) Waiting to run
Continuous Integration / lint-clang (push) Blocked by required conditions
Continuous Integration / analyze-valgrind (push) Blocked by required conditions
Continuous Integration / windows-2022-x64 (push) Blocked by required conditions
Continuous Integration / windows-2022-arm64 (push) Blocked by required conditions
Continuous Integration / macos-arm64 (push) Blocked by required conditions
Continuous Integration / macos-x64 (push) Blocked by required conditions
Continuous Integration / archlinux-x86_64 (push) Blocked by required conditions
Continuous Integration / debian-13-arm64 (push) Blocked by required conditions
Continuous Integration / debian-13-x86_64 (push) Blocked by required conditions
Continuous Integration / fedora-41-arm64 (push) Blocked by required conditions
Continuous Integration / fedora-41-x86_64 (push) Blocked by required conditions
Continuous Integration / fedora-42-arm64 (push) Blocked by required conditions
Continuous Integration / fedora-42-x86_64 (push) Blocked by required conditions
Continuous Integration / fedora-43-arm64 (push) Blocked by required conditions
Continuous Integration / fedora-43-x86_64 (push) Blocked by required conditions
Continuous Integration / opensuse-arm64 (push) Blocked by required conditions
Continuous Integration / opensuse-x86_64 (push) Blocked by required conditions
Continuous Integration / ubuntu-25.04-arm64 (push) Blocked by required conditions
Continuous Integration / ubuntu-25.04-x86_64 (push) Blocked by required conditions
Continuous Integration / ubuntu-25.10-arm64 (push) Blocked by required conditions
Continuous Integration / ubuntu-25.10-x86_64 (push) Blocked by required conditions
Continuous Integration / unix-freebsd (push) Blocked by required conditions
Continuous Integration / flatpak-aarch64 (push) Blocked by required conditions
Continuous Integration / flatpak-x86_64 (push) Blocked by required conditions
Continuous Integration / release (push) Blocked by required conditions
Continuous Integration / winget-publish (push) Blocked by required conditions
2025-11-21 10:09:53 -05:00
d8bdad4e1d fix: translate MainWindow::coreProcessError warning 2025-11-21 09:30:36 -05:00
fb119f3c56 refactor: skip wl-clipboard test when not on wayland but wl-clipboard is installed 2025-11-21 08:27:19 -05:00
525123573e chore: use U for wlclipboard setting name 2025-11-21 10:38:50 +00:00
858a5d71bd fix: enable translations for validators 2025-11-20 16:18:16 -05:00
f88f79ef8d chore: update japanese translation 2025-11-20 16:18:16 -05:00
b547493bc5 refactor(ipc): Add newline in utility write function 2025-11-20 15:08:57 +00:00
cace2892ba fix: append newline to logPath response in DaemonIpcServer 2025-11-20 15:08:57 +00:00
82e23716ac docs(build): Fix minor nits in build docs 2025-11-20 08:15:34 -05:00
92e3e9ac64 refactor: wlClipboard fix some sonar issues 2025-11-20 07:57:31 -05:00
50fd3365e8 chore: WlClipboard remove unused process control methods 2025-11-20 07:57:31 -05:00
4ebf5ff479 refactor: WlClipbaord::empty use QProcess 2025-11-20 07:57:31 -05:00
ab4feaa037 refactor: WlClipbaord::add use QProcess to set the clipboard 2025-11-20 07:57:31 -05:00
21900ade68 refactor: make WlClipboard a QObject subclass 2025-11-20 07:57:31 -05:00
7b0e8e8188 chore: WlClipboard remove unused executeCommand 2025-11-20 07:57:31 -05:00
c5f7a3792a refactor: WlClipboard::get use QProcess 2025-11-20 07:57:31 -05:00
7a60f0cc9e refactor: WlClipboard, use QProcess to get available mimeTypes 2025-11-20 07:57:31 -05:00
9ed0b06a42 refactor: WlClipboard mimetypes use QStrings 2025-11-20 07:57:31 -05:00
5ef4f64b11 refactor: use QDateTime::currentMSecsSinceEpoc behind WlClipboard::getCurrentTime 2025-11-20 07:57:31 -05:00
c7e90d3cf5 refactor: WlClipboard::isAvailable use QStandardPaths::findExecutable to check if the apps exist. remove WlClipboard::checkCommandExists 2025-11-20 07:57:31 -05:00
d157713a85 feat: Settings expose settings to use wl-clipboard backend. Disable clipboard on wayland if this settings is disabled 2025-11-20 07:57:31 -05:00
90e7d475f8 refactor: WlClipboardCollection minor sonar cleanup 2025-11-20 07:57:31 -05:00
ecb03297fd refactor: EiScreen Sonar cleanup for new clipboard items 2025-11-20 07:57:31 -05:00
a09dfad22a chore: WlClipboard remove unused private setClipboardData and setClipboardData methods 2025-11-20 07:57:31 -05:00
9960b657e3 chore: rename EiClipboard -> WaylandClipboardCollection 2025-11-20 07:57:31 -05:00
bbabb91d42 chore: rename src/lib/platform/WaylandClipboard -> src/lib/platform/WlClipboard 2025-11-20 07:57:31 -05:00
e55c67fa2f feat: add wayland clipboard support via wl-clipboard
this is currently based on wl-copy/wl-paste.
One could probably just implement wl-copy/wl-paste without adding much
complexity and better performance.

Co-authored-by: KoljaFrahm <GitHub.Kolja@dfgh.net>
2025-11-20 07:57:31 -05:00
56bb41a291 refactor: also check qt6 paths for translations 2025-11-20 07:29:01 -05:00
4285361413 refactor: replace Q_OS_MAC -> Q_OS_MACOS 2025-11-20 07:29:01 -05:00
37177f8e45 refactor: macos move bundle resources to Deskflow.app/Resources/translations 2025-11-20 07:29:01 -05:00
6eadba1ab2 feat: mac os new build option BUILD_OSX_BUNDLE can be disabled to build a non app bundle 2025-11-20 07:29:01 -05:00
aa11dc94ba build: macos, copy translations post build into the bundle no need to install them 2025-11-20 07:29:01 -05:00
82c18e26e5 doc: fix dev documents mis aligned quotes 2025-11-20 07:07:32 -05:00
e7018bd75a fix: Fix broken <br/> tag in the tooltop of suggested IP addresses 2025-11-20 06:29:54 -05:00
10585e6afd build: fix desktop file lint 2025-11-19 20:30:41 +00:00
35d26645a7 build: place core built runtime on mac os in the app bundle, must use generator expression here 2025-11-19 20:30:41 +00:00
2376c61292 feat: Add russian translation 2025-11-19 12:12:16 -05:00
43a04308f0 ci: use macos15 runners 2025-11-19 09:43:58 -05:00
0463518baa build: do not use generator expressions to see install paths 2025-11-19 14:13:21 +00:00
8e7a950d11 feat: Added Chinese (simplified) translation 2025-11-19 07:34:48 -05:00
ae5dc085d5 feat: Added Japanese translation 2025-11-18 06:13:59 -05:00
bc9bc906a5 refactor: EventLogger sonar suggestion to define index2 in the if 2025-11-17 12:07:00 +00:00
0890bcac42 chore: logger make deconstructor an override 2025-11-17 12:07:00 +00:00
22d27f7245 fix: Settings, add a new private cleanScreenName method to update the screenName
Fixes: #9156
Names: contain only A-Z a-z 0-9 or - or . or _
       replace any spaces with _
       are < 256 characters in length
       can not start or end with - _ or .
Set a screen name if the settings does not have one do this instead of returning a default value
2025-11-17 10:21:31 +00:00
94b7e2ffff github: Add more options to the issue config 2025-11-14 10:46:15 -05:00
1a9e468c86 refactor: Settings store envvar checks so they only need to be read once when checking 2025-11-14 09:55:28 -05:00
204a6cca8a refactor: Settings make the name checking regex static in the method 2025-11-14 09:55:28 -05:00
4df7c54afb refactor: Settings make the internal setting objects children 2025-11-14 09:55:28 -05:00
999f174441 fix: Gui save and restore the window when closing and restoring to the tray 2025-11-14 09:55:28 -05:00
aca08a5e74 feat: save geometry into into a state file
fixes: #9132
2025-11-14 09:55:28 -05:00
ddcd2f0ff1 refactor: Logger store a local copy of the used setting value for guiDebug, update it went the settings have changed 2025-11-14 09:22:40 -05:00
ec9f7efcff refactor: Logger is now a proper singleton 2025-11-14 09:22:40 -05:00
8256a3ba43 refactor: allow for movable events
based on: 2f5c612ade
2025-11-14 09:05:37 -05:00
e011351aad refactor: remove event.cpp, event code is just the header now
basedon: 6b7d45e9c4
2025-11-14 09:05:37 -05:00
ad33114e0c chore: remove unused Client::sendEvent data argument
based on: 307fdfe1b3
2025-11-14 09:05:37 -05:00
4728525ece fix: Fix memory leak in EventQueue
Previously empty event targets were never fully removed from EventQueue
even after unregistration. This may lead to unbounded increase of memory
use if new event targets are allocated to new memory locations.
ported: c960360106
2025-11-14 09:05:37 -05:00
cbc74d99b0 refactor: server Store button info by value
based on: 55e727be14
2025-11-14 09:05:37 -05:00
f372ccd2b8 refactor: only include the ISO639Table with X11 support enabled 2025-11-14 13:28:41 +00:00
4d2597c31b refactor: XDGKeyUtil no need to add std:: for uint32_t 2025-11-14 13:28:41 +00:00
281cda1d14 refactor XDGKeyUtil::mapKeyToKeyID cleanup default case make i in the if and not case the already right type returned 2025-11-14 13:28:41 +00:00
4a67694676 build: new build option BUILD_X11 to control building of X11 backend 2025-11-14 13:28:41 +00:00
f45df39032 refactor: XDGKeyUtil, make s_keySymToUCS4 static map 2025-11-14 13:28:41 +00:00
186a20a1ac feat: XDGKeyUtil, use Xkbcommon Keysym names
this removes the need for any x11 releated headers when using EI screens
2025-11-14 13:28:41 +00:00
7eee265010 refactor: make s_map1008FF private static array 2025-11-14 13:28:41 +00:00
b3cd759a4e chore: remove unused KeySyms Tyepdef 2025-11-14 13:28:41 +00:00
93beb491db fix: Split XKB code out of XWindowsUtil to allow building without X11
the xprotoheaders are still required for EI, but nothing more.
2025-11-14 13:28:41 +00:00
6d89ee0660 chore: StreamChunker remove unused includes 2025-11-13 11:40:24 +00:00
b2cdf38fca chore: remove unneeded using namespace std 2025-11-13 11:40:24 +00:00
567766508a ci: do not force ci to rerun if a pr was marked ready for review without additional code changes 2025-11-13 10:17:24 +00:00
1b67293d9c ci: name sonar and codeql jobs after their respective tools making them easier to distinguish on the ci report 2025-11-13 10:17:24 +00:00
75922cb944 ci: Skip CodeQL when the changes will not will change the scan results 2025-11-13 10:17:24 +00:00
e712bf6c8e ci: Skip sonarcloud when the changes will not change the scan results 2025-11-13 10:17:24 +00:00
f26ea08469 docs: Highlight that Input Leap has become inactive
The Input Leap project (an attempt to continue Barrier development, a dead fork) has been inactive for a long while now.

This PR updates the readme to reflect this.
2025-11-11 15:02:56 -05:00
6a86c2990e fix: Adjust OS X MouseMove handling to use live cursor position. 2025-11-11 11:15:41 -05:00
859af720c9 feat: add a new core option --new-instance to skip the core check for a running instance 2025-11-11 09:33:35 -05:00
5441464a1d fix: do not make windows users select .sgc files, enable i18n for the server config dialog 2025-11-11 12:30:36 +00:00
de63751516 refactor: CoreArgParser use kCoreBinName in place of class created static 2025-11-11 12:11:34 +00:00
39382bfd8c fix: Prevent out-of-bounds access for Qt string when building TLS fingerprint
Only happens when using debug build of Qt lib because release Qt is optimized and doesn't check for bounds for the sake of efficiency.
2025-11-10 21:18:56 +00:00
477c7b07e5 chore: Fix typos for var names in formatSSLFingerprintColumns 2025-11-10 21:18:56 +00:00
63026752b5 ci: Use deskflow version in the title for continuous release 2025-11-10 15:59:52 -05:00
5bcefbe582 build: install runtime depends on windows to CMAKE_INSTALL_LIBDIR, set CMAKE_INSTALL_LIBDIR to . on windows 2025-11-10 07:51:12 -05:00
8e754b2ce2 build: linux deploy use CMAKE_INSTALL_DATADIR in place of hard coded share 2025-11-10 07:51:12 -05:00
1556908ef6 build: use CMAKE_INSTALL_BINDIR when installing runtime artifacts
On windows override this to be .
On macOS set it to the MacOS folder in the bundle
2025-11-10 07:51:12 -05:00
57d36b51af build: Simplify the install of translation files 2025-11-10 07:51:12 -05:00
c72cdedd4f build: place the install steps for the license data in the main CMakeLists.txt 2025-11-10 07:51:12 -05:00
759457f739 build: remove option to build gui
The Gui must always be built. The core can not function completely standalone
 it is unable to add new clients or setup screens for example until such a time the gui is not optional.
2025-11-10 07:51:12 -05:00
78c90fe7c6 fix: Crash caused by hostnames with invalid characters returned by machine name.
fixes: #9006

Only cleans the hostname if the system is using the default
Replace Spaces with _
Remove any other non word characters
2025-11-10 07:32:48 -05:00
1875c599a4 refactor: define EventQueueTimer to its own header not once per IEventQueue impl 2025-11-10 07:08:29 -05:00
ab1a87ba04 chore: remove MsWindowClipboardTextConverter 2025-11-10 11:52:42 +00:00
d492ddfbbb chore: LogOutputters remove unused includes 2025-11-07 15:54:19 -05:00
cad92d2ff7 chore: IEventQueue remove unused includes 2025-11-07 15:54:19 -05:00
9737f23f5c chore: FinalAction include utility 2025-11-07 15:54:19 -05:00
e26c737776 chore: EventQueue remove unused includes 2025-11-07 15:54:19 -05:00
d36576a079 chore: StreamChunker include string_view not string 2025-11-07 15:54:19 -05:00
e48ee3d368 chore: ServerApp remove unused includes 2025-11-07 15:54:19 -05:00
891d5c9bb7 chore: Screen remove unused includes 2025-11-07 15:54:19 -05:00
cbf3627ae2 chore: ProtocolUtil remove unused includes 2025-11-07 15:54:19 -05:00
44882cfa7a chore: ProtocolTypes remove unused includes 2025-11-07 15:54:19 -05:00
c91e051082 chore: PlatformScreen remove unused includes 2025-11-07 15:54:19 -05:00
e01837579d chore: PacketStreamFilter remove unused includes 2025-11-07 15:54:19 -05:00
0a5b39be7a chore: OptionTypes removed unused includes 2025-11-07 15:54:19 -05:00
19f49e746b chore: MouseTypes include cstdint directly 2025-11-07 15:54:19 -05:00
8d34bfdcc6 chore: ISecondaryScreen remove unused includes 2025-11-07 15:54:19 -05:00
725c2e96a4 chore: IScreenSaver remove unuse includes 2025-11-07 15:54:19 -05:00
113801f967 chore: IScreen remove unused includes 2025-11-07 15:54:19 -05:00
c3e017bd68 chore: IPrimaryScreen remove unused includes 2025-11-07 15:54:19 -05:00
71bb8eb750 chore: IKeyState remove unused includes 2025-11-07 15:54:19 -05:00
01cdbf48d4 chore: IClipboard remove unused includes 2025-11-07 15:54:19 -05:00
04bea86254 chore: DisplayInvalidException only include stdstring on apple systems 2025-11-07 15:54:19 -05:00
176b5c2459 chore: Chunk remove unused includes 2025-11-07 15:54:19 -05:00
dad46a5a34 chore: AppUtil remove unused includes 2025-11-07 15:54:19 -05:00
997b0c5f1b chore: X11LayoutParser remove unused includes 2025-11-07 15:54:19 -05:00
a13e3f895d chore: DeskflowXkbKeyboard remove unused includes 2025-11-07 15:54:19 -05:00
6a895f5c45 chore: VersionChecker remove unused includes 2025-11-07 15:54:19 -05:00
98a49b7f7a chore: StyleUtils include missing QIcon 2025-11-07 15:54:19 -05:00
4b3399d951 chore: LogDock remove unused includes 2025-11-07 15:54:19 -05:00
2743e1dbae chore: KeySequenceWidget remove unused includes 2025-11-07 15:54:19 -05:00
aff73bbded chore: StreamBuffer remove unused includes 2025-11-07 15:54:19 -05:00
f8b299ff67 chore: IStream remove unused includes 2025-11-07 15:54:19 -05:00
f294daa077 chore: TCPSocketFactoryy remove unused includes 2025-11-07 15:54:19 -05:00
e389b2ed56 chore: TCPSocket remove unused includes 2025-11-07 15:54:19 -05:00
9b145c2739 chore: SocketMultiplexer remove unused includes 2025-11-07 15:54:19 -05:00
bc8dcf76ad chore: SecureUtils remove unused includes 2025-11-07 15:54:19 -05:00
93f42df4db chore: SecureListenSocket remove unused includes 2025-11-07 15:54:19 -05:00
00f10bdb14 chore: NetworkAddress remove unused includes 2025-11-07 15:54:19 -05:00
2ca2500954 chore: ISocket remove unused includes 2025-11-07 15:54:19 -05:00
b20201007e chore: IListenSocket remove unused includes 2025-11-07 15:54:19 -05:00
959e6b2d0d chore: IDataSocket remove unused includes 2025-11-07 15:54:19 -05:00
65a7fbb90b chore: Server remove unused includes 2025-11-07 15:54:19 -05:00
c2ae5eae08 chore:InputFilter remove unused includes 2025-11-07 15:54:19 -05:00
06ac0a747f chore: ClientProxyUnknown remove unused includes 2025-11-07 15:54:19 -05:00
e2a78ac5ac chore: ClientProxy1_7 remove unused includes 2025-11-07 15:54:19 -05:00
9f213fa1dd chore: ClientProxy1_5 remove unused includes 2025-11-07 15:54:19 -05:00
6924c4f09d chore: ClientProxy1_4 remove unused includes 2025-11-07 15:54:19 -05:00
58bb8a0fcf chore: ClientProxy1_3 remove unused includes 2025-11-07 15:54:19 -05:00
e8702cd716 chore: ClientProxy1_1 remove unused includes 2025-11-07 15:54:19 -05:00
fb5e532ff6 chore: ClientProxy remove unused includes 2025-11-07 15:54:19 -05:00
1e371ad84a chore: ClientListener remove unused includes 2025-11-07 15:54:19 -05:00
7ce5ce5d41 chore: XWindowsUtils remove unused includes 2025-11-07 15:54:19 -05:00
68a15486ae chore: XWindowsScreen remove unused includes 2025-11-07 15:54:19 -05:00
bb42cfb8fd chore: XWindows Clipboard remove unused includes 2025-11-07 15:54:19 -05:00
af0321ce77 chore: XDGPowerManager remove unused includes 2025-11-07 15:54:19 -05:00
fbdee8e10e chore: PortalRemoteDesktop remove unneeded includes 2025-11-07 15:54:19 -05:00
489d984ab6 chore: PortalInputCapture remove unused includes 2025-11-07 15:54:19 -05:00
1e672bb8a1 chore: EiScreen remove unused includes 2025-11-07 15:54:19 -05:00
6147e9604b chore: EiEventQueueBuffer remove unneeded includes 2025-11-07 15:54:19 -05:00
97367a20c5 chore: OSXUchrKeyResouce remove unused includes 2025-11-07 15:54:19 -05:00
5a029110a3 chore: OSXScreenSaver remove unused includes 2025-11-07 15:54:19 -05:00
e4557c6b95 chore: OSXScreen remove unused includes 2025-11-07 15:54:19 -05:00
685312866b chore: OSXKeyState remove unused includes 2025-11-07 15:54:19 -05:00
a0959e0334 chore OSXClipboardTextConvertor remove unused includes 2025-11-07 15:54:19 -05:00
a376a714e8 chore: OSXClipboardAnyBitmapConverter remove unused includes 2025-11-07 15:54:19 -05:00
21a7d7db4b chore: OSXClipboard remove unused includes 2025-11-07 15:54:19 -05:00
b4649b4f9c chore: AppUtilWindows remove unused includes 2025-11-07 15:54:19 -05:00
0a407b6726 chore: Remove checkfor windows < xpSp3 we need windows 7 or higher to runthe app now 2025-11-07 15:54:19 -05:00
8b89489d40 chore: MSWindowsHook inclue Directional Types directly not thru protocol types 2025-11-07 15:54:19 -05:00
e8073de989 chore: MSWindowsEventQueueBuffer remove unused includes 2025-11-07 15:54:19 -05:00
74e4f1a1e1 chore: MSWindowsClipboardTextConvertor remove unused includes 2025-11-07 15:54:19 -05:00
d9493da173 chore: MsWindowsClipboardFacade, remove unused includes 2025-11-07 15:54:19 -05:00
0e8fdb18d8 chore: MsWindowsClipboard, remove unused includes 2025-11-07 15:54:19 -05:00
e07a9f58f7 refactor: MSWindowsClipBoard, use Text converter as last option 2025-11-07 15:54:19 -05:00
1f6f6d5dc6 chore: remove unused Common/Common.h 2025-11-07 15:54:19 -05:00
19814c61d7 chore: MSWindowsProcess remove unused includes 2025-11-07 15:54:19 -05:00
66efc9c70e chore: Unicode remove use of common/Common and use system headers needed directly 2025-11-07 15:54:19 -05:00
c4fd8699f9 chore: ArchNetworkBsd Remove unneeded includes 2025-11-07 15:54:19 -05:00
5dc8263e54 chore: ServerProxy remove unused includes 2025-11-07 15:54:19 -05:00
52b23e6609 chore: Client remove unused includes 2025-11-07 15:54:19 -05:00
90fb80872c chore: deskflow-gui remove unneeded includes 2025-11-06 09:12:18 -05:00
754210e8f4 chore: SettingsDialog remove uneeded includes 2025-11-06 09:12:18 -05:00
5966b8f1b8 chore: SecureSocket remove unused includes 2025-11-06 09:12:18 -05:00
72496548de chore: PrimaryClient remove unneeded includes 2025-11-06 09:12:18 -05:00
0091cc5e9a chore: Path remove unneeded includes 2025-11-06 09:12:18 -05:00
241be6abd8 chore: MainWindow remove unneeded includes 2025-11-06 09:12:18 -05:00
f0eaa3efaa chore: IKeyState Remove unneeded includes 2025-11-06 09:12:18 -05:00
f56073a090 chore: FileSystem remove unneeded includes 2025-11-06 09:12:18 -05:00
ca2a7d14f0 chore: DaemonApp remove unneeded includes 2025-11-06 09:12:18 -05:00
3ffe8d7c86 chore: CoreProcess remove unneeded includes 2025-11-06 09:12:18 -05:00
e130cbb706 chore: server/Config remove unneeded includes 2025-11-06 09:12:18 -05:00
e6792b00d7 chore: remove unused includes 2025-11-06 09:12:18 -05:00
f89e857223 refactor: Add deskflow::platform::isMac 2025-11-06 09:12:18 -05:00
5256f05963 refactor: addNew PlatformInfo::isWindows() 2025-11-06 09:12:18 -05:00
8e83b16f5e chore: move platform/Wayland.h -> common/PlatformInfo.h 2025-11-06 09:12:18 -05:00
c18e3c9ef2 refactor: platform/wayland move consts kHasEI etc into wayland Warnings 2025-11-06 09:12:18 -05:00
0a67f63af6 fix: ServerApp use the port configured by the user 2025-11-06 13:30:23 +00:00
8ef2319b0d chore: ServerApp, remove unused includes 2025-11-06 13:30:23 +00:00
0e98702944 chore: ClientApp: remove unused includes 2025-11-06 13:30:23 +00:00
080a6e8b54 fix: ClientApp, use the port in settings in place of default port 2025-11-06 13:30:23 +00:00
8fadbecf00 chore: make TMethodJob const void* 2025-11-05 22:07:16 +00:00
eab38dc23e fix: High Cpu use on windows
port: 95f2a840be
Fixes: #8970
2025-11-05 21:46:20 +00:00
3e76a39326 chore: remove unused IEventQueue::isEmpty
port: 8ab6ad64f9
2025-11-05 21:46:20 +00:00
3af83dd9f2 refactor: remove DotEnv 2025-11-05 11:31:29 +00:00
aba38b949f feat: add new setting Log::GuiDebug to control when the Gui shows debug messages no longer controled by an ENV var 2025-11-05 11:31:29 +00:00
67ceedcad0 refactor: Settings use static list of settings that default to true and another that default to false 2025-11-05 11:31:29 +00:00
6c8c6f5208 feat: Show milliseconds in time values
fixes: #7861
2025-11-04 10:47:14 -05:00
6ab1579d38 refactor: Log remove unneeded includes 2025-11-04 10:47:14 -05:00
fc6e9f1447 refactor: Simplify log::makeMessages method for getting current time by using QDateTime object 2025-11-04 10:47:14 -05:00
1750dd9149 chore: AboutDialog remove unneeded includes 2025-11-04 10:47:14 -05:00
8134521cca chore: remove unused MainWindow::getTimeStamp 2025-11-04 10:47:14 -05:00
b204204920 refactor: Logger::PrintLine use Qt::IsoDate for timestamp 2025-11-04 10:47:14 -05:00
8066ac8e2f refactor: logger::PrintLine use less streams and use the one we need directly 2025-11-04 10:47:14 -05:00
43989dc964 refactor: logger: use QStringLiterals 2025-11-04 10:47:14 -05:00
c066e394a8 refactor: Logger::instance return a pointer to the logger static object 2025-11-04 10:47:14 -05:00
78eae652fd refactor: directly set logger::m_debug using when in debug mode 2025-11-04 10:47:14 -05:00
e1c0803018 chore: remove unused kDebugBuild 2025-11-04 10:47:14 -05:00
1d82be270a chore: remove logger:m_verbose and related method 2025-11-04 10:47:14 -05:00
b22fa1550a refactor: do not mark the protocol names Barrier and Synergy for translation 2025-11-04 10:26:51 +00:00
dc0a85d34f fix: net: use an exception in NetworkAddress::resolve()
Instead of asserting that the number of resolved ipv4 addresses is nonzero,
throw an exception. This will prevent the core from aborting if the host has
no ipv4 addresses.

The host can get into this state if the remote device loses its ipv4
advertisement midway through resolving, such as if an mdns host unpublishes
its ipv4 address as a result of going into a low-power state.

Signed-off-by: Sean Cross <sean@xobs.io>
2025-11-03 10:04:43 -05:00
9f669dbbce ci: Set OSX_DEPLOYMENT_TARGET as a configuration option 2025-11-03 09:40:59 -05:00
a5bcc90988 feat: Added Italian translation 2025-11-03 09:17:37 -05:00
acd4b59b4c refactor: Center drag point for screen management 2025-11-03 08:56:23 -05:00
50240c1fc3 refactor: Arch do not include common/Common.h use config.h where neeed as a result 2025-11-03 13:31:28 +00:00
091d309444 chore: deskflow-core remove unused iostream include 2025-11-03 13:31:28 +00:00
90d6fc6f08 refactor: Assume inet_atom on unix
ported ed2d44c346\#diff-1e7de1ae2d059d21e1dd75d5812d5a34b0222cef273b7c3a2af62eb747f9d20a
2025-11-03 13:31:28 +00:00
6ff8b053af refactor: fail without posix sigwait, remove from Config.h.in
based on 998a9d1735
2025-11-03 13:31:28 +00:00
1f950f4c2a refactor: define SYSAPI_WINDOWS and WINAPI_WINDOWS in cmake, remove the define from Common.h 2025-11-03 13:31:28 +00:00
4afc20e2c3 chore: remove unused _THREAD_SAFE define 2025-11-03 13:31:28 +00:00
550f7c3e06 refactor HAS_FORMAT => HAVE_FORMAT 2025-10-31 12:21:47 +00:00
135fe27007 refactor: look for cxx20 format support on all oses 2025-10-31 12:21:47 +00:00
7e6a674210 refactor: forward HAS_FORMAT define into c++ 2025-10-31 12:21:47 +00:00
dc997a80d5 ci: Add Ubuntu 25.10 2025-10-31 10:21:08 +00:00
293c321ba5 ci: Add Fedora 43 2025-10-31 10:21:08 +00:00
4816608c50 chore: add new .qtcreator user dir to ignore list 2025-10-30 17:38:09 +00:00
14fa29505d refactor: Allow for colorful tray icon but fall back to symbolic with fallback 2025-10-30 09:40:00 +00:00
eee4efd59d refactor: Only use symbolic tray icon 2025-10-29 13:23:47 -04:00
066e63cc86 refactor: make sure flatpak and snap use packed icons, they can not detect panel color 2025-10-29 13:23:47 -04:00
f4ca17ba3d refactor: Remove RetryOnFailure, Client will always retry and Server will never retry 2025-10-29 08:32:58 -04:00
8c6fa880b4 refactor: SecureSocket do not set retry on failure to false when disconnecting 2025-10-29 08:32:58 -04:00
83e0a6b1ea refactor: SSL_Options, ignore client disconnect w/o close message 2025-10-29 08:32:58 -04:00
495a5e6479 refactor: MainWindow, do not reinit serverconfig 2025-10-29 08:14:39 -04:00
def479bc7b chore: add missing es translation 2025-10-29 08:14:39 -04:00
0bf6e1e9f8 feat: Apple codesign support for development builds 2025-10-29 07:51:40 -04:00
a140b3bcca refactor: MainWindow update window icon on palette change, prevent issues where the palette change does not change the icon and make its blend weirdly 2025-10-28 07:39:14 -04:00
8b513efc95 feat: mainWindow check panel color on windows when deciding upon try icon color 2025-10-28 07:39:14 -04:00
17392a8e06 refactor: rename symbolic icon to full fqdn name. 2025-10-28 07:39:14 -04:00
ddc827e6f1 feat: support symbolic icon deployment and recoloring linux, unify setTrayIcon to use theme icons only 2025-10-28 07:39:14 -04:00
72792e7d4d chore: simplify deskflow svg icon files 2025-10-28 07:39:14 -04:00
1b6a5ced08 refactor: rename deskflow linux deployed icon to org.deskflow.deskflow 2025-10-28 07:39:14 -04:00
9906421460 feat: Dynamic translation of GUI, able to restart the application without exiting 2025-10-28 07:18:53 -04:00
a62613f219 refactor: deskflow-gui add newline after the lang is written to console 2025-10-28 07:18:53 -04:00
91a82b22b2 refactor: MainWindow make menu items private members 2025-10-28 07:18:53 -04:00
73e44916e7 refactor: SettingsDialog, support LanguageChange Event 2025-10-28 07:18:53 -04:00
84d2c56b54 refactor: LogDock, support LanguageChangeEvent 2025-10-28 07:18:53 -04:00
18d90aa0bf refactor: use curly initilize for list of QDirs in I18N 2025-10-28 07:18:53 -04:00
40c20e3f5e chore: rm Unused gui/DataDownloader 2025-10-28 09:30:36 +00:00
1e0bd5822a chore: remove unused AddClientDialog 2025-10-28 08:59:45 +00:00
7b0d74aace chore: rm unused ServerConfig::ScreenAddResults 2025-10-28 08:59:45 +00:00
becbb01286 chore: rm unimplimented ServerConfig::getClientAddress and ServerConfig::setClientAddress 2025-10-28 08:59:45 +00:00
fc1769ce66 chore: rm Unused ServerConfig::addToFirstEmptyGrid 2025-10-28 08:59:45 +00:00
31573bf6e8 chore: rm Unused ServerConfig::m_pMainwindow 2025-10-28 08:59:45 +00:00
2efcc76933 chore: rm unused ServerConfig::showAddClientDialog 2025-10-28 08:59:45 +00:00
3580b4ead9 chore: rm unused ServerConfig::autoAddScreen 2025-10-28 08:59:45 +00:00
a82e9a4a74 chore: remove unused MainWindow::autoAddScreen 2025-10-28 08:59:45 +00:00
2d7174c3b2 feat: expose language settings, populate a basic spanish translation 2025-10-27 17:54:32 -04:00
c3f0b18df6 feat: support translation generation 2025-10-27 17:54:32 -04:00
71c1bb87ca refactor: Mainwindow prepare for translations by simplifying MainWindow::serverClientsChanged to use plural form always 2025-10-27 17:54:32 -04:00
bf3fd82630 chore: use kAppName and kAppId more to avoid unneed translations of Deskflow / deskflow 2025-10-27 17:54:32 -04:00
bdacd6a994 chore: prepare for translation remove tr calls from placeholder strings in ui files 2025-10-27 17:54:32 -04:00
Gio
f2a3c708a8 refactor: Update footer text in FingerprintDialog
Fix up wording in connection / fingerprint dialog
2025-10-27 16:32:37 +00:00
77bf32cb0e chore: update dmg-volume icon used for mac os dmg 2025-10-27 16:15:33 +00:00
0ca87f7576 docs: Remove github logo image on main page 2025-10-27 15:54:12 +00:00
b96e94be14 docs: Set a fav icon and logo for generated documentation 2025-10-27 15:54:12 +00:00
091dbc0729 chore: IClient.h remove unneeded overrides of nothing 2025-10-27 15:36:40 +00:00
29f7a57d97 chore: App.h remove unneeded forward declration 2025-10-27 15:36:40 +00:00
8d0c1af032 chore: IListenSocket remove unneeed overrides of nothing 2025-10-27 15:36:40 +00:00
2b83a0ac0f chore: IDataSocket remove unneeed overides of nothing 2025-10-27 15:36:40 +00:00
453686655b chore: IPlatformScreen remove unneeed overrides of nothing 2025-10-27 15:36:40 +00:00
61f3113d61 chore: remove unused Arch::setInstance 2025-10-27 15:36:40 +00:00
bf6986bcba chore: remove unused Arch::Arch(Arch *arch) 2025-10-27 15:36:40 +00:00
6b2c3c1844 chore: remove unused ILogOutputters:show 2025-10-27 15:36:40 +00:00
023e8dc78e chore: remove unused ARCH->showLog 2025-10-27 15:36:40 +00:00
b5c188abbc refactor: use theme icon for tray on windows 2025-10-24 16:26:38 +01:00
8226c4162b fix: [build]Fix build issue without format support
add C++20 <format> support checks in Unix libraries and update logging format usage.

Log: Fix build issue without format support.
2025-10-24 15:08:11 +00:00
2016ce887b fix: stuck modifiers after screen unlock on windows
Fix stuck modifiers (like the windows key) after unlock on Windows
by updating keyboard state after every desk switch.
2025-10-24 15:38:27 +01:00
4a0e2c492c docs: Add CODEOWNERS file to for default PR review requests 2025-10-24 14:04:01 +00:00
a34971fee7 refactor: call xkb_keymp_num_levels_for_key, to apply shift in EiState::mapKeyfromKeyval 2025-10-24 14:46:49 +01:00
5fc4e9dba6 fix: ctrl+alt+fn issue 2025-10-24 00:30:38 +00:00
42b90a2b14 feat: update icon theme on theme change 2025-10-21 11:05:10 -04:00
6b05c2abc0 refactor: use QPushButtons and fake toolbutton style without using a style sheet 2025-10-21 11:05:10 -04:00
697cd57adb refactor: rename MainWindow::setIcon -> MainWindow::setTrayIcon to better reflect what the method does 2025-10-21 11:05:10 -04:00
88e385d050 refactor: move icon theme settings to styleUtils::updateIconTheme 2025-10-21 11:05:10 -04:00
91b836a486 fix: SettingsDialog, set the UAC hidden by default unless on windows
fixes: #9054
2025-10-21 14:36:17 +01:00
b8bd6903e4 fix: client to server modifier press/release
Pressing a modifier on the client and moving to the server and releasing
it breaks the modifier on the client. This resolves the issue.
2025-10-21 13:54:40 +01:00
ea7a493c13 fix: dangling pointer from scoped var 2025-10-21 13:32:13 +01:00
e108afdb80 chore: remove Cpp file for ArchDaemonNone 2025-10-17 09:41:56 +01:00
4be0dd3d9d chore: remove unneeded ArchDaemonUnix subclass 2025-10-17 09:41:56 +01:00
55a9596bd5 chore: use Settings::setingsFile in place of instance()->.. 2025-10-17 09:07:21 +01:00
71fc1a47fb chore: update generated pkgbuild to be more like the aur deskflow-git one 2025-10-17 09:07:21 +01:00
d036c5a07c chore: set CMAKE_PROJECT_HOMEPAGE_URL value 2025-10-17 09:07:21 +01:00
e1fbcb379e chore: Sub classes and overloads not usings parameters of the base methods have the varible names removed 2025-10-17 09:07:21 +01:00
ed8e71072e refactor: use first(n) in place of mid(0, n) 2025-10-17 09:07:21 +01:00
dbc3229b40 refactor: ArchNetworkBSD, set addr to nullprt after delete to avoid returning invalid pointer 2025-10-17 09:07:21 +01:00
8b489b8301 chore: remove unneeded semi colons from several places 2025-10-17 09:07:21 +01:00
d32b98ec34 refactor: EiScreen Define Spaceship op for EiScreen::HotKeyItem 2025-10-17 09:07:21 +01:00
78e394a210 fix: apply scroll lock setting on initialization
Co-authored-by: Nick Bolton <nick@symless.com>
2025-10-17 03:50:53 -04:00
c11a1caf59 fix: handle xkb_keymap_mod_get_mask returning 0 2025-10-16 11:57:11 -04:00
debfd4dc69 refactor: deskflow core print any parser errors 2025-10-15 15:04:08 +01:00
41d5359f4d Refactor: deskflow-core remove non essential options move CoreArgs and CoreArgParse to apps/deskflow-core 2025-10-15 15:04:08 +01:00
34f56af6d6 refactor: set Remote host when it changes on the line edit. 2025-10-15 09:38:11 -04:00
687fd5411a refactor: Rename setSettingFile to setSettingsFile for consistency 2025-10-15 08:59:09 -04:00
bb1394ceeb feat: Change to local user ini (not native)
Windows registry is horrible to deal with and makes it impractical to pass settings to the Core when run via daemon on Windows.

- Pass settings path to Core when launched via daemon
- Introduce portable mode detection logic on Windows
- Generalize `m_settingsFile` use
- Reduce #ifdef size for Settings ctor path logic
2025-10-15 08:59:09 -04:00
3ece50e292 fix: Invert noHooks to useHooks to match new config 2025-10-14 09:04:00 -04:00
497813e198 docs: Move Windows redist instructions up 2025-10-14 12:37:32 +01:00
a4aa540bc8 ci: make vcpkg pull from master on ci 2025-10-13 20:13:12 +01:00
58c750471f ci: Use Qt 6.10 2025-10-13 10:19:42 -04:00
7c3ec372df chore: use std::ignore in diagnostic reset 2025-10-13 10:19:42 -04:00
0f335d46bb fix: Store server config window state in memory instead of on disk 2025-10-13 08:50:30 -04:00
2b203c8cdd refactor: use CoreProcess::CorrectedAddress in CoreProcess::setAddress 2025-10-13 08:34:12 -04:00
ac7bd1ceca refactor: CoreProcess make wrapIpv6, Simplify wrapIpV6 method 2025-10-13 08:34:12 -04:00
de6be901b9 refactor: CoreProcess use Settings::defaultvalue for server Config in place of constructing in CoreProcess 2025-10-13 08:34:12 -04:00
0ab65410cc refactor: CoreProcess::restart simplify method 2025-10-13 08:34:12 -04:00
a057437e1a refactor: MainWindow::coreProcessStarting move logic into coreProcessStateChanged 2025-10-13 08:34:12 -04:00
f9c007cc3a refactor: CoreProcess Remove the starting signal and use it like the others via CoreProcess::coreProcessStateChanged 2025-10-13 08:34:12 -04:00
d45196304f chore: CoreProcess remove unsed includes 2025-10-13 08:34:12 -04:00
004089f887 refactor: Coreprocess remove single use one liner persistLogDir, make dir in start if needed 2025-10-13 08:34:12 -04:00
13990438d3 refactor: CoreProcess::processStateToString use QVariant conversion to string, make static member of CoreProcess 2025-10-13 08:34:12 -04:00
a120441a10 refactor: CoreProcess::processModeToString use QVariant::fromValue().toString().toLower() to create the string, make method static member of CoreProcess 2025-10-13 08:34:12 -04:00
26c2f672be chore: remove unimplimented CoreProcess::processModeString 2025-10-13 08:34:12 -04:00
48195f9347 refactor: CoreProcess::start seed args with value of coreMode, use it when checking if server 2025-10-13 08:34:12 -04:00
5a632bbb6d refactor: remove CoreProcess::modeString 2025-10-13 08:34:12 -04:00
173638d6f5 refactor: CoreProcess remove addSeverArgs handle server config in CoreProcess::start 2025-10-13 08:34:12 -04:00
bd0c5a68f1 refactor: CoreProcess append log option in start Method, remove now unused addClientArgs , and args parameter from addServerArgs 2025-10-13 08:34:12 -04:00
e3f940c70a refactor: Simplify CoreProcess::QuoteArgs, use QString::Simplify and simplify logic in the loop 2025-10-13 08:34:12 -04:00
bb1e2ecc78 refactor: makeQuotedArgs is now a static private member or CoreProcess 2025-10-13 08:34:12 -04:00
8d0c368c8f refactor: CoreProcess::start return early if called with coreMode of None 2025-10-13 08:34:12 -04:00
9e94a4fe0e refactor: CoreProcess::start, early return if process is already started 2025-10-13 08:34:12 -04:00
8d577aaa93 fix: Always rune code scanning, even if draft PR 2025-10-13 08:10:30 -04:00
47662c359e chore: Use new SonarCloud action
refactor: Rename job from 'sonarcloud-analysis' to 'analyze' in SonarCloud workflow

refactor: Reorder SonarQube scan and build wrapper installation steps in SonarCloud workflow

refactor: Update SonarQube scan step to use new action and streamline options

fix: Update SonarQube scan step to correctly pass coverage report paths and environment variables

fix: Set CPU core count to a fixed value in SonarCloud analysis workflow

refactor: Move 'Install Build Wrapper' step to the correct position in the SonarCloud analysis workflow

refactor: Remove unused Sonar Scanner environment variables from analysis workflow
2025-10-13 08:10:30 -04:00
4fb76bec41 refactor: Remove matrix strategy for CodeQL analysis and set language directly 2025-10-13 08:10:30 -04:00
5945114b7b refactor: Run SonarCloud workflow standalone and cleanup comments
fix: Prevent SonarCloud analysis from running on draft pull requests
2025-10-13 08:10:30 -04:00
559b7b5a17 chore: Standardize quotes and formatting in CI workflow 2025-10-13 08:10:30 -04:00
a346ff6161 chore: X11LayoutsParser remove stray semi colon 2025-10-13 12:45:44 +01:00
eed4b49ea0 chore: remove unused member App::configSection 2025-10-13 12:45:44 +01:00
88c0c49ba3 chore: remove unused member App::s_helpVersionArgs 2025-10-13 12:45:44 +01:00
f64d430f96 fix: Remove assertion for unused name parameter in daemonize function 2025-10-13 07:23:59 -04:00
f4a6d3d43d chore: fix up feature request yml 2025-10-09 09:28:19 -04:00
04b3f5c769 chore: remove unused name parameter from ARCH->daemonize 2025-10-09 13:30:41 +01:00
aae3067f03 fix: stop retry if core has crashed
fixes: #9003
2025-10-07 14:57:22 +01:00
0820c5a188 chore: add note to feature request for up or down vote 2025-10-07 08:25:47 -04:00
8ea2671e65 refactor: cleanup daemonApp args 2025-10-07 13:01:47 +01:00
12ea23a3bd refactor: App::run remove unused args 2025-10-07 13:01:47 +01:00
0bc82c3a57 refactor App::runInner remove unused argc and argv 2025-10-07 13:01:47 +01:00
3270b40455 refactor: App, remove args from static startup methods to make StartupFunc no longer take args 2025-10-07 13:01:47 +01:00
9f2327932d refactor: IApp::start remove unused args 2025-10-07 13:01:47 +01:00
1b44a24d75 refactor: IApp::initApp remove unused args 2025-10-07 13:01:47 +01:00
8db65da345 refactor: use QString to set log level value
fixes: #8999
2025-10-07 12:40:18 +01:00
558833ca05 fix: windows, unable to use settings until a QApp is made
fixes: #9000
2025-10-07 12:22:46 +01:00
06263ceaad feat: drop cli11 2025-10-01 10:55:03 -04:00
4a5f173422 feat: remove toml config file only use our QSettings based format
remove deskflow/Config and related tests
2025-10-01 09:41:12 -04:00
e30025ea8b feat: SettingsDialog, add control to adjust scrollSpeed option for clients 2025-09-30 06:39:54 -04:00
59d860874e refactor: App::ParseArgs remove all unneeed arguments 2025-09-30 06:39:54 -04:00
5978694d95 chore: remove unused ArgParser 2025-09-30 06:39:54 -04:00
d98f8a524d remove: argsBase 2025-09-30 06:39:54 -04:00
37827f0540 refactor: remove ClientArgs 2025-09-30 06:39:54 -04:00
6fa8ba087a refactor: move remoteHost to coreArgs 2025-09-30 06:39:54 -04:00
a78959e66f refactor: move invert-scroll to coreArgs 2025-09-30 06:39:54 -04:00
5fc00f7af4 refactor: use bool for invert scrolling 2025-09-30 06:39:54 -04:00
2cb36777f9 refactor: move languageSync to coreArgs 2025-09-30 06:39:54 -04:00
b0fe79d527 refactor: move yscroll to coreArgs 2025-09-30 06:39:54 -04:00
efebe9ca56 refactor: remove ServerArgs 2025-09-30 06:39:54 -04:00
bf7a50ab0e refactor: move -c --config to coreArgs 2025-09-30 06:39:54 -04:00
cfedfc2c1e refactor: move peerCert option to coreArgs 2025-09-30 06:39:54 -04:00
cfd580fb21 refactor: move ArgsBase:m_pname to App 2025-09-30 06:39:54 -04:00
1884ab3555 refactor: move --no-hooks to coreArgs 2025-09-30 06:39:54 -04:00
eeaf139bd9 refactor: move no-restart to CoreArgs
chore: Client remove unused ClientArgs member m_args
2025-09-30 06:39:54 -04:00
295137dbd2 refactor: CoreArgs Add display option 2025-09-30 06:39:54 -04:00
c2ee366e23 refactor: move preventsleep option into CoreArgs 2025-09-30 06:39:54 -04:00
a8348b1ccb refactor: move tls-cert to coreArgs 2025-09-30 06:39:54 -04:00
ff4c9dc421 refactor: move enable-crypto to new core option secure to set the TLS setting 2025-09-30 06:39:54 -04:00
53b36801e1 refactor: move Log option to coreArgs 2025-09-30 06:39:54 -04:00
f3a1bbaf5b refactor: move setting of log level to CoreArgParser 2025-09-30 06:39:54 -04:00
05f377e21b refactor: Handle Name option in CoreArgParser 2025-09-30 06:39:54 -04:00
ead49c4025 refactor: move address option to deskflow-core, split into --interface (-i) and --port (-p) options 2025-09-30 06:39:54 -04:00
5e02adc772 refactor: deskflow-core take over --version arg for App class
change the serverAppTest to not call version
2025-09-30 06:39:54 -04:00
10f34f214f refactor: new Classes CoreArg and CoreArgParser to use to create and parse core options 2025-09-30 06:39:54 -04:00
6f574a3076 refactor: set const for Reverse FQDN name to use for deployment on linux 2025-09-30 06:17:23 -04:00
dc86538456 feat: Set app id on wayland this is required for restore tokens to work correctly 2025-09-30 06:17:23 -04:00
7698980576 refactor: simplify desktop::platform::isWayland 2025-09-30 06:17:23 -04:00
9fe445ebd0 feat: add methods to check if running from a sandbox, flatpak and or snap 2025-09-30 06:17:23 -04:00
087afd22b2 refactor: use CoreProcess::m_appPath to store app 2025-09-28 10:53:16 -04:00
8192d7b2d8 refactor: remove Settings::Client::Binary and Settings::Server::Binary use kCoreBinName, instead 2025-09-28 10:53:16 -04:00
d9ce9c4f10 chore: remove unused CoreProcess::extracted 2025-09-28 10:53:16 -04:00
4dfc98c93c fix: SettingsDialog, ensure debug warning is shown without changing the combobox 2025-09-28 10:36:23 -04:00
abbfc91192 chore: PriorityQueue::comp add no_unique_address attritribute 2025-09-25 15:01:17 +01:00
e3f5e045b7 chore: FileLogOutputter::write use Static QFile::rename in place of local file method 2025-09-25 15:01:17 +01:00
7f7e7de841 chore: SecureSocket serviceConnect / serviceAccept make anonymous ISocketMultiplexer a pointer const 2025-09-25 15:01:17 +01:00
bd63d54a9e chore: EiScreen::m_powerManager add no_unique_address 2025-09-25 15:01:17 +01:00
9dae55fc6f chore: remove unused ClientProxy1_5::m_events 2025-09-25 15:01:17 +01:00
e35a2b1c29 chore: remove unused ClientProxy1_7::m_events 2025-09-25 15:01:17 +01:00
4157367aaa chore: remove unused InputFilter::RestartServer::m_events 2025-09-25 15:01:17 +01:00
3945dc927c chore: remove unused ArgsBase::m_disableTray 2025-09-25 14:41:35 +01:00
801ada88b7 fix: correct spelling of convertLayoutToISO in X11LayoutsParser 2025-09-25 09:18:06 -04:00
c234f2fc66 fix: Do not reuse listen sockets on Windows 2025-09-25 14:00:48 +01:00
7b866ec632 refactor: make sure to use static args on ArgParsers::NameTest 2025-09-24 16:28:19 +01:00
ff1726677b chore: remove unused --service and --relaunch options for windows 2025-09-24 16:28:19 +01:00
57e34ded54 chore: rename IApp::standardStartup -> IApp::start 2025-09-24 16:28:19 +01:00
03e014c753 feat: Remove daemon / no-daemon option 2025-09-24 16:28:19 +01:00
fd7950bc04 ci: use mac os 14 large to build x86 2025-09-23 07:33:41 -04:00
9f8c45b449 fix: XWindowsScreen: stop centering panned screens on client when leaving screen
panned screens still needs `xtestIsXineramaUnaware` option enabled. this change
forcefully uses XTest only when leaving
useful examples:
* double 1 FullHD screen (main output) by:
	`xrandr --fb 3840x1080 --output DP-1 --panning 3840x1080+0+0/3840x1000+0+80`,
	leaving top 80 pixels for moving mouse out without actual panning
* dedicate right half of framebuffer to FullHD TV (second output):
	`xrandr --fb 3840x1080 --output HDMI-0 --panning 1920x1080+1920+0`
2025-09-23 12:12:59 +01:00
bf5b0de6ee fix: XWindowsScreen: properly calculate xrandr/xinerama screens
this allows screens to overlap, use panning, etc
2025-09-23 12:12:59 +01:00
3aef2f4309 chore: XWindowsScreen: remove duplicate function 2025-09-23 12:12:59 +01:00
134c8fd1c1 ci: fix names of suse and arch architecture postfix 2025-09-16 12:46:18 +01:00
35c7fe7fc4 fix: screensettingsDialog, do not allow aliases to be ip addresses
fixes: #8952
2025-09-15 16:31:52 +01:00
612ca91f9c chore: rename XWindowsPowerManager => XDGPowerManager
fixes: 8949
2025-09-15 13:21:51 +01:00
4632bec405 ci: be sure to always run CI checks 2025-09-15 12:58:10 +01:00
fcdea215be Reapply "refactor: App use Uniqueprt for SocketMultiplexer"
This reverts commit 6f18cf74c5.
2025-09-12 16:03:22 +01:00
6f18cf74c5 Revert "refactor: App use Uniqueprt for SocketMultiplexer"
This reverts commit 4d738b4784.
2025-09-12 15:03:52 +01:00
162cb85811 fix: Improve output loop error handling in MSWindowsWatchdog
Prevents high CPU usage while waiting for data (ERROR_NO_DATA)
2025-09-12 13:17:50 +01:00
5ea8d0326a fix: Use QString for Windows error functions
Was returning only first char of error message (mild-mojibake)
2025-09-12 13:17:50 +01:00
7a2591a1fa fix: wayland sleep inhibit on client
Fixes #8827
Adds the XWindowsPowerManager to the EiScreen as, despite the name, this
works on Wayland as well.
2025-09-12 10:54:25 +01:00
356 changed files with 12701 additions and 7145 deletions

2
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,2 @@
# Apply to all files
* @nbolton @sithlord48

View File

@ -1 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Ask a question
url: https://github.com/deskflow/deskflow/discussions/new?category=q-a
about: Where to ask general questions.
- name: Show and Tell
url: https://github.com/deskflow/deskflow/discussions/new?category=show-and-tell
about: Show off things you have done with Deskflow
- name: Chat with us
url: https://matrix.to/#/#deskflow:matrix.org
about: Join us in live chat.

View File

@ -14,3 +14,14 @@ body:
Please describe the feature you have in mind.
validations:
required: true
- type: textarea
id: votes
attributes:
label: Voting Instructions
description: Do not modify these instructions
value: |
React with :+1: or :-1: to vote on this request
validations:
required: true

View File

@ -38,26 +38,22 @@ 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 libtomlplusplus-dev libcli11-dev \
help2man -y >/dev/null
libei-dev libportal-dev help2man -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 \
gtk3-devel gtest-devel gmock-devel \
libei-devel libportal-devel tomlplusplus-devel \
cli11-devel help2man
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 \
gtk3-devel gtest-devel gmock-devel libei-devel libportal-devel help2man
elif [ ${{inputs.like}} == "suse" ]; then
zypper refresh
zypper install -y --force-resolution \
cmake make ninja gcc-c++ rpm-build libopenssl-devel \
glib2-devel libXtst-devel libxkbfile-devel qt6-base-devel qt6-tools-devel gtk3-devel \
googletest-devel googlemock-devel libei-devel \
libportal-devel tomlplusplus-devel cli11-devel help2man
glib2-devel libXtst-devel libxkbfile-devel qt6-base-devel qt6-tools-devel \
qt6-linguist-devel gtk3-devel \
googletest-devel googlemock-devel libei-devel libportal-devel help2man
elif [ ${{ inputs.like }} == "arch" ]; then
pacman -Syu --noconfirm base-devel cmake ninja \
gcc openssl glib2 libxtst libxkbfile gtest libei libportal \
qt6-base qt6-tools qt6-svg gtk3 tomlplusplus cli11 help2man doxygen graphviz rsync
qt6-base qt6-tools qt6-svg qt6-translations qt6-declarative gtk3 help2man doxygen graphviz rsync
else
echo "Unknown like"
fi
@ -85,6 +81,7 @@ runs:
extra-args: --classic --host-triplet=${{inputs.vcpkg-triplet}}
triplet: ${{inputs.vcpkg-triplet}}
token: ${{ github.token }}
revision: master
- name: Install Wix
if: ${{ runner.os == 'Windows' }}

View File

@ -1,42 +1,33 @@
name: "CodeQL Analysis"
# According to the docs, the CodeQL workflow should be triggered directly by push to master
# and by pull requests (we only run this on open PRs as it's very slow). We also use the
# `workflow_dispatch` event is also enabled to allow manual triggering of the workflow for testing.
#
# We should not trigger this workflow with `workflow_call` as this causes the error:
# "1 configuration present on `master` was not found"
#
# Sadly, this means we can't roll it into our monolithic CI workflow.
# This is best run as a standalone workflow, not as part of another workflow like CI
# because of how GitHub understands the code scanning workflows in it's UI.
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- '**/*.md'
- '.github/ISSUE_TEMPLATE/**'
- '.editorconfig'
- '.env-example'
- '.gitignore'
- '.gitattributes'
- 'cspell.json'
paths:
- '.github/workflows/codeql-analysis.yml'
- 'cmake/Libraries.cmake'
- 'CMakeLists.txt'
- 'src/**'
- '!src/res/**'
- '!src/unittests/**'
push:
branches: [master]
paths:
- '.github/workflows/codeql-analysis.yml'
- 'cmake/Libraries.cmake'
- 'CMakeLists.txt'
- 'src/**'
- '!src/res/**'
- '!src/unittests/**'
jobs:
analyze:
if: ${{ !github.event.pull_request.draft }}
name: Analyze
codeql:
runs-on: ubuntu-latest
container: debian:trixie-slim
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
language: ["cpp"]
steps:
- name: Install container dependencies
run: |
@ -54,7 +45,7 @@ jobs:
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
languages: cpp
- name: Autobuild
uses: github/codeql-action/autobuild@v3

View File

@ -8,21 +8,8 @@ on:
push:
branches: [master]
tags:
- 'v*'
- "v*"
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
paths-ignore:
- '**/*.md'
- '.github/ISSUE_TEMPLATE/**'
- '.editorconfig'
- '.env-example'
- '.gitignore'
- '.gitattributes'
- 'cspell.json'
env:
GIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
@ -31,33 +18,6 @@ env:
CMAKE_CONFIGURE: "cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DSKIP_BUILD_TESTS=ON -DCMAKE_COMPILE_WARNING_AS_ERROR=ON"
jobs:
# Always run this job, even if not on PR, since other jobs need it.
pr-comment-flags:
runs-on: ubuntu-latest
needs: lint-clang
outputs:
no-sonar: ${{ steps.check.outputs.no-sonar }}
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Check PR comment for flags
if: ${{ github.event_name == 'pull_request' }}
id: check
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
no_sonar="{no-sonar}"
if echo $PR_BODY | grep -q "$no_sonar"; then
echo "Flag $no_sonar found in PR body."
echo "no-sonar=true" >> $GITHUB_OUTPUT
else
echo "No $no_sonar flag found in PR body."
fi
# Quality gate to allow PR merge, used in the branch protection rules.
ci-passed:
runs-on: ubuntu-latest
@ -104,18 +64,11 @@ jobs:
- name: Lint Checker
uses: ./.github/actions/lint-clang
analyse-valgrind:
analyze-valgrind:
needs: lint-clang
if: ${{ github.event_name == 'pull_request' }}
uses: ./.github/workflows/valgrind-analysis.yml
analyse-sonarcloud:
needs: pr-comment-flags
if: ${{ needs.pr-comment-flags.outputs.no-sonar != 'true' }}
uses: ./.github/workflows/sonarcloud-analysis.yml
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
main-build:
needs: lint-clang
name: ${{ matrix.target.name }}
@ -133,7 +86,6 @@ jobs:
runs-on: "windows-2022"
timeout: 30
config-args: "-G Ninja"
qt-version: 6.9.0
vcpkg-triplet: x64-windows-release
arch: "amd64"
@ -141,21 +93,18 @@ jobs:
runs-on: "windows-11-arm"
timeout: 30
config-args: "-G Ninja"
qt-version: 6.9.1
vcpkg-triplet: arm64-windows
arch: "arm64"
- name: "macos-14-arm64"
runs-on: "macos-14"
- name: "macos-arm64"
runs-on: macos-15
timeout: 10
config-args: "-DCMAKE_OSX_ARCHITECTURES=\"arm64\" -DCMAKE_OSX_SYSROOT=/Applications/Xcode_15.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
qt-version: 6.9.1
config-args: '-DCMAKE_OSX_ARCHITECTURES="arm64" -DCMAKE_OSX_DEPLOYMENT_TARGET=14 -DCMAKE_OSX_SYSROOT=/Applications/Xcode_16.4.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk'
- name: "macos-13-x64"
runs-on: macos-13
- name: "macos-x64"
runs-on: macos-15-intel
timeout: 20
config-args: "-DCMAKE_OSX_ARCHITECTURES=\"x86_64\" -DCMAKE_OSX_SYSROOT=/Applications/Xcode_15.0.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
qt-version: 6.9.1
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-13-x86_64"
runs-on: ubuntu-latest
@ -171,6 +120,20 @@ jobs:
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-42-x86_64"
runs-on: ubuntu-latest
container: fedora:42
@ -199,7 +162,7 @@ jobs:
timeout: 20
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
- name: "opensuse-x86_84"
- name: "opensuse-x86_64"
runs-on: ubuntu-latest
container: opensuse/tumbleweed:latest
like: "suse"
@ -213,7 +176,7 @@ jobs:
timeout: 20
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
- name: "archlinux-x86_84"
- name: "archlinux-x86_64"
runs-on: ubuntu-latest
container: archlinux:latest
like: "arch"
@ -234,24 +197,38 @@ jobs:
timeout: 20
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
- name: "ubuntu-25.10-x86_64"
runs-on: ubuntu-latest
container: ubuntu:25.10
like: "debian"
timeout: 20
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
- name: "ubuntu-25.10-arm64"
runs-on: ubuntu-24.04-arm
container: ubuntu:25.10
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
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
@ -265,12 +242,11 @@ jobs:
with:
arch: ${{matrix.target.arch}}
- name: Install dependencies
id: get-deps
uses: ./.github/actions/install-dependencies
with:
qt-version: ${{ matrix.target.qt-version }}
qt-version: 6.10.0
vcpkg-triplet: ${{matrix.target.vcpkg-triplet}}
like: ${{ matrix.target.like }}
@ -429,7 +405,7 @@ jobs:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "continuous"
prerelease: true
title: 'Continuous Build'
title: "Continuous v${{env.DESKFLOW_VERSION}}"
files: |
deskflow-*
sums.txt
@ -443,7 +419,7 @@ jobs:
files: |
deskflow-*
sums.txt
winget-publish:
needs: release
if: contains(github.ref, 'tags/v')

View File

@ -1,14 +1,32 @@
name: "SonarCloud Analysis"
# This is best run as a standalone workflow, not as part of another workflow like CI
# because of how GitHub understands the code scanning workflows in it's UI.
on:
workflow_dispatch:
workflow_call:
secrets:
SONAR_TOKEN:
required: true
pull_request:
paths:
- '.github/workflows/sonarcloud-analysis.yml'
- 'sonar-project.properties'
- 'cmake/Libraries.cmake'
- 'CMakeLists.txt'
- 'src/**'
- '!src/res/**'
- '!src/unittests/**'
push:
branches: [master]
paths:
- '.github/workflows/codeql-analysis.yml'
- 'cmake/Libraries.cmake'
- 'CMakeLists.txt'
- 'src/**'
- '!src/res/**'
- '!src/unittests/**'
jobs:
sonarcloud-analysis:
sonar:
# This job would fail for contributors who open PRs as the workflow runs outside of our repo
# in this scenario. Having a var that only we set to true prevents this job from running.
if: ${{ vars.SONAR_SCANNER_ENABLED }}
runs-on: ubuntu-latest
@ -16,10 +34,7 @@ jobs:
timeout-minutes: 20
env:
SONAR_SCANNER_VERSION: 6.1.0.4477
SONAR_SCANNER_OPTS: -server
SONAR_SCANNER_URL_BASE: https://binaries.sonarsource.com/Distribution/sonar-scanner-cli
CPU_CORE_COUNT: ${{ vars.SONAR_SCANNER_CPU_COUNT || 4 }}
CPU_CORE_COUNT: 4
steps:
- name: Install container dependencies
@ -35,8 +50,8 @@ jobs:
with:
like: "debian"
- name: Install sonar-scanner and build-wrapper
uses: sonarsource/sonarcloud-github-c-cpp@v3
- name: Install Build Wrapper
uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v6
- name: Configure
run: |
@ -71,11 +86,11 @@ jobs:
fi
echo "csv=$paths" >> $GITHUB_OUTPUT
- name: Run SonarScanner
run: |
export PATH=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux-x64/bin:$PATH
sonar-scanner \
-Dsonar.coverageReportPaths=${{ steps.coverage-paths.outputs.csv }} \
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v6
with:
args: >
-Dsonar.coverageReportPaths=${{ steps.coverage-paths.outputs.csv }}
-Dsonar.cfamily.threads=${{ env.CPU_CORE_COUNT }}
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

3
.gitignore vendored
View File

@ -25,6 +25,9 @@ deskflow-config.toml
/*.user
*.ui.autosave
#Qt creator 18 user files
/.qtcreator
# generated vcpkg file
vcpkg.json

View File

@ -18,7 +18,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Fallback for when git can not be found
set(DESKFLOW_VERSION_MAJOR 1)
set(DESKFLOW_VERSION_MINOR 24)
set(DESKFLOW_VERSION_MINOR 25)
set(DESKFLOW_VERSION_PATCH 0)
set(DESKFLOW_VERSION_TWEAK 0)
@ -82,13 +82,16 @@ project(
deskflow
VERSION "${DESKFLOW_VERSION_MAJOR}.${DESKFLOW_VERSION_MINOR}.${DESKFLOW_VERSION_PATCH}.${DESKFLOW_VERSION_TWEAK}"
DESCRIPTION "Keyboard and mouse sharing utility"
LANGUAGES C CXX)
HOMEPAGE_URL "https://deskflow.org"
LANGUAGES C CXX
)
# Define Additional "PROJECT" vars for packaging and metadata
set(CMAKE_PROJECT_PROPER_NAME "Deskflow")
set(CMAKE_PROJECT_VENDOR "${CMAKE_PROJECT_PROPER_NAME} Devs")
set(CMAKE_PROJECT_COPYRIGHT "(C) 2024-2025 ${CMAKE_PROJECT_VENDOR}")
set(CMAKE_PROJECT_CONTACT "${CMAKE_PROJECT_PROPER_NAME} <maintainers@deskflow.org>")
set(CMAKE_PROJECT_REV_FQDN "org.deskflow.deskflow")
#Unset the vars used in the project call
unset(DESKFLOW_VERSION_MAJOR)
@ -105,6 +108,7 @@ set(REQUIRED_LIBPORTAL_VERSION 0.8)
set(REQUIRED_QT_VERSION 6.7.0)
if (WIN32)
add_definitions(-DSYSAPI_WIN32 -DWINAPI_MSWINDOWS)
# VSCMD_ARG_TGT_ARCH is set on CI
if ("$ENV{VSCMD_ARG_TGT_ARCH}" STREQUAL "")
# NOT on CI
@ -144,11 +148,6 @@ if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DNDEBUG)
endif()
# Set required macOS SDK
if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 12)
endif()
# Set Output Folders
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
@ -157,38 +156,48 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
# Instead use Q_SIGNAL, Q_SLOT and Q_EMIT
# prevents issues when used with glib for libportal
add_definitions(-DQT_NO_KEYWORDS)
include(cmake/Libraries.cmake)
include(GNUInstallDirs)
#Options for Linux platform support
if(UNIX AND NOT APPLE)
option(BUILD_X11_SUPPORT "Build with x11 support" ON)
elseif (APPLE)
option(BUILD_OSX_BUNDLE "Build mac os bundle" ON)
endif()
include(cmake/Libraries.cmake)
configure_libs()
# setup install paths
include(GNUInstallDirs)
if (WIN32)
set(CMAKE_INSTALL_BINDIR .)
set(CMAKE_INSTALL_LIBDIR .)
set(CMAKE_INSTALL_LICENSE_DIR .)
set(CMAKE_INSTALL_I18N_DIR translations)
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)
endif()
add_subdirectory(doc)
add_subdirectory(src)
# Install License, License is in the App Bundle on mac os (src/gui)
if(WIN32)
install(
FILES ${PROJECT_SOURCE_DIR}/LICENSE
DESTINATION .
)
install(
FILES ${PROJECT_SOURCE_DIR}/LICENSES/LicenseRef-OpenSSL-Exception.txt
DESTINATION .
RENAME LICENSE_EXCEPTION
)
elseif(UNIX AND NOT APPLE)
install(
FILES ${PROJECT_SOURCE_DIR}/LICENSE
DESTINATION share/licenses/deskflow
)
install(
FILES ${PROJECT_SOURCE_DIR}/LICENSES/LicenseRef-OpenSSL-Exception.txt
DESTINATION share/licenses/deskflow
RENAME LICENSE_EXCEPTION
)
endif()
add_subdirectory(translations)
option(BUILD_INSTALLER "Build installer" ON)
if(BUILD_INSTALLER)
add_subdirectory(deploy)
endif()
install(
FILES ${PROJECT_SOURCE_DIR}/LICENSE
DESTINATION ${CMAKE_INSTALL_LICENSE_DIR}
)
install(
FILES ${PROJECT_SOURCE_DIR}/LICENSES/LicenseRef-OpenSSL-Exception.txt
DESTINATION ${CMAKE_INSTALL_LICENSE_DIR}
RENAME LICENSE_EXCEPTION
)

View File

@ -22,6 +22,11 @@ TLS encryption is enabled by default. Wayland is supported. Clipboard sharing is
[![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)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[![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)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[![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/vs/17/release/vc_redist.x64.exe) [`vc_redist.arm64.exe`](https://aka.ms/vs/17/release/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).
@ -59,11 +64,6 @@ For instructions on building Deskflow, use the wiki page: [Building](https://git
We support all major operating systems, including Windows, macOS, Linux, and Unix-like BSD-derived.
> [!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/vs/17/release/vc_redist.x64.exe) [`vc_redist.arm64.exe`](https://aka.ms/vs/17/release/vc_redist.arm64.exe)
Windows 10 or higher is required.
macOS 12 or higher is required.
@ -115,11 +115,10 @@ 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.
- [**Input Leap**](https://github.com/input-leap/input-leap) -
Deskflow/Synergy-derivative with the goal of continuing what Barrier started, after Barrier
became a dead fork.
- [**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

View File

@ -26,6 +26,8 @@ path = [
, "src/apps/deskflow-server/deskflow-server.exe.manifest"
, "src/apps/res/manpage.txt"
, "src/apps/res/deskflow.plist.in"
, "src/apps/res/entitlements-dev.plist"
, "translations/*.ts"
]
SPDX-FileCopyrightText = "Deskflow Developers"
SPDX-License-Identifier = "MIT"
@ -34,7 +36,7 @@ SPDX-License-Identifier = "MIT"
path = [
"deploy/mac/dmg-background.tiff"
, "deploy/mac/dmg-volume.icns"
, "deploy/linux/deskflow.png"
, "deploy/linux/org.deskflow.deskflow.png"
, "deploy/windows/wix-banner.png"
, "deploy/windows/wix-dialog.png"
, "src/apps/res/icons/deskflow-**/apps/64/deskflow*.svg"

View File

@ -49,6 +49,20 @@ macro(configure_libs)
message(STATUS "Qt version: ${Qt6_VERSION}")
# Check if <format> header is available
check_cxx_source_compiles("
#include <format>
int main() {
char buffer[100];
std::format_to_n(buffer, 100, \"test {}\", 42);
return 0;
}
" HAVE_FORMAT)
if(HAVE_FORMAT)
add_definitions(-DHAVE_FORMAT)
endif()
option(ENABLE_COVERAGE "Enable test coverage" OFF)
if(ENABLE_COVERAGE)
message(STATUS "Enabling code coverage")
@ -89,6 +103,7 @@ macro(configure_unix_libs)
include(CheckIncludeFileCXX)
include(CheckSymbolExists)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)
check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
if (NOT HAVE_SYS_SOCKET_H)
@ -102,30 +117,8 @@ macro(configure_unix_libs)
endif()
check_function_exists(sigwait HAVE_POSIX_SIGWAIT)
check_function_exists(inet_aton HAVE_INET_ATON)
# For some reason, the check_function_exists macro doesn't detect the
# inet_aton on some pure Unix platforms (e.g. sunos5). So we need to do a more
# detailed check and also include some extra libs.
if(NOT HAVE_INET_ATON)
set(CMAKE_REQUIRED_LIBRARIES nsl)
check_c_source_compiles(
"#include <arpa/inet.h>\n int main() { inet_aton (0, 0); }"
HAVE_INET_ATON_ADV)
set(CMAKE_REQUIRED_LIBRARIES)
if(HAVE_INET_ATON_ADV)
# Override the previous fail.
set(HAVE_INET_ATON 1)
# Assume that both nsl and socket will be needed, it seems safe to add
# socket on the back of nsl, since socket only ever needed when nsl is
# needed.
list(APPEND libs nsl socket)
endif()
if (NOT HAVE_POSIX_SIGWAIT)
message(FATAL_ERROR "Missing posix sigwait")
endif()
# pthread is used on both Linux and Mac
@ -149,10 +142,12 @@ macro(configure_unix_libs)
${lib_Foundation} ${lib_Carbon} ${lib_UserNotifications}
)
add_definitions(-DWINAPI_CARBON=1 -D_THREAD_SAFE)
add_definitions(-DWINAPI_CARBON=1)
else()
configure_xorg_libs()
if (BUILD_X11_SUPPORT)
configure_xorg_libs()
endif()
include(FindPkgConfig)
find_package(PkgConfig)

View File

@ -21,6 +21,7 @@
"dotenv",
"Evenson",
"Feder",
"Flatpaks",
"Fourdan",
"gdrive",
"Hadzhylov",
@ -78,6 +79,8 @@
"Serhii",
"shemp",
"SNAPPROCESS",
"sonarcloud",
"sonarqube",
"Sorin",
"subproject",
"subprojects",

View File

@ -2,15 +2,16 @@
# SPDX-License-Identifier: MIT
# Maintainer: Deskflow Developers
pkgname=deskflow
_basename=@CMAKE_PROJECT_NAME@
pkgname=${_basename}-git
pkgver=@CMAKE_PROJECT_VERSION@
pkgrel=1
pkgdesc="Mouse and keyboard sharing utility"
url='https://deskflow.org'
pkgdesc="@CMAKE_PROJECT_DESCRIPTION@"
url="@CMAKE_PROJECT_HOMEPAGE_URL@"
arch=('i686' 'x86_64' 'armv7h' 'aarch64')
license=(LicenseRef-GPL-2.0-only-WITH-OpenSSL-Exception)
conflicts=('synergy-git' 'synergy-1.6' 'synergy1-bin' 'synergy2-bin' 'synergy3-bin' 'synergy3-beta-bin' 'synergy3-stable-bin' 'barrier' 'barrier-git' 'barrier-headless' 'barrier-headless-git' 'input-leap' 'input-leap-git' 'input-leap-headless-git' 'input-leap-headless' 'waynergy' 'waynergy-git' 'qsynergy' 'slim-synergy' 'quicksynergy' 'deskflow')
provides=("deskflow-git${pkgver}")
provides=("$_basename")
depends=(
gcc-libs
glib2
@ -33,7 +34,7 @@ depends=(
openssl
qt6-base
qt6-svg
tomlplusplus
qt6-translations
)
options=('!debug')

View File

@ -7,21 +7,23 @@ set(MY_DIR ${CMAKE_CURRENT_LIST_DIR})
# Install our desktop file
install(
FILES ${MY_DIR}/org.deskflow.deskflow.desktop
DESTINATION share/applications
FILES ${MY_DIR}/${CMAKE_PROJECT_REV_FQDN}.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
)
# Install our icon
install(FILES ${MY_DIR}/org.deskflow.deskflow.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/512x512/apps/)
# Install our symbolic icon
install(
FILES ${MY_DIR}/deskflow.png
DESTINATION share/icons/hicolor/512x512/apps/
RENAME org.deskflow.deskflow.png
FILES ${CMAKE_SOURCE_DIR}/src/apps/res/icons/deskflow-light/apps/64/org.deskflow.deskflow-symbolic.svg
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/symbolic/apps/
)
# Install our metainfo
install(
FILES ${MY_DIR}/org.deskflow.deskflow.metainfo.xml
DESTINATION share/metainfo/
FILES ${MY_DIR}/${CMAKE_PROJECT_REV_FQDN}.metainfo.xml
DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo/
)
# Prepare PKGBUILD for Arch Linux
@ -31,7 +33,6 @@ configure_file(
@ONLY
)
set(CPACK_DEBIAN_PACKAGE_SECTION "utils")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2")

View File

@ -17,7 +17,6 @@ cleanup:
- /lib/cmake
- /lib/pkgconfig
- /share/pkgconfig
- /share/tomlplusplus
- /share/cmake
- /share/doc
- /share/gir-1.0
@ -63,22 +62,6 @@ modules:
commit: 8f5dc8d192f6e31dafe69e35219e3b707bde71ce
- type: patch
path: libportal-qt69.patch
- name: cli11
buildsystem: cmake-ninja
config-opts:
- -DCLI11_BUILD_TESTS=OFF
sources:
- type: git
url: https://github.com/CLIUtils/CLI11
tag: v2.5.0
commit: 4160d259d961cd393fd8d67590a8c7d210207348
- name: tomlplusplus
buildsystem: cmake-ninja
sources:
- type: git
url: https://github.com/marzer/tomlplusplus
tag: v3.4.0
commit: 30172438cee64926dc41fdd9c11fb3ba5b2ba9de
- name: gtest
buildsystem: cmake-ninja
sources:

View File

@ -11,3 +11,9 @@ Icon=org.deskflow.deskflow
Terminal=false
Categories=Utility;
Keywords=keyboard;mouse;sharing;network;share;
Name[zh_CN]=Deskflow
Comment[zh_CN]=键鼠共享工具
Keywords[zh_CN]=键盘;鼠标;网络;共享;
Name[ru]=Deskflow
Comment[ru]=Приложения чтобы использовать одну мышку и клавиатуру с разными устройствами
Keywords[ru]=Передача;Трансляция;barrier;input-leap;

View File

@ -8,10 +8,14 @@
<name>Deskflow Developers</name>
</developer>
<summary>Software Keyboard and mouse sharing</summary>
<summary xml:lang="zh_CN">用软件共享键鼠</summary>
<description>
<p>
Use the keyboard, mouse, or trackpad of one computer to control nearby computers, and work seamlessly between them.
</p>
<p xml:lang="zh_CN">
使用一台计算机的键盘、鼠标或触控板来控制附近的其它计算机,并在它们之间无缝工作。
</p>
</description>
<launchable type="desktop-id">org.deskflow.deskflow.desktop</launchable>
<url type="homepage">https://deskflow.org</url>
@ -51,8 +55,10 @@
<category>Utility</category>
</categories>
<keywords>
<keyword translate="no">Input</keyword>
<keyword translate="no">Sharing</keyword>
<keyword>Input</keyword>
<keyword xml:lang="zh_CN">输入</keyword>
<keyword>Sharing</keyword>
<keyword xml:lang="zh_CN">共享</keyword>
<keyword translate="no">KVM</keyword>
<keyword translate="no">Synergy</keyword>
</keywords>
@ -62,6 +68,36 @@
</branding>
<content_rating type="oars-1.0" />
<releases>
<release version="1.25.0" date="2025-11-21" urgency="high">
<description>
<p>This stable release fixes known issues and adds a few new features. Most notable symbolic icon support, I18N support and experimental support for wl-clipboard to access clipboards on wayland. This release also continues our trend of cleaning up the codebase. For the full changelog, see the release page.</p>
<ul>
<li>Removed the ability to use toml config and env based config files</li>
<li>Set XDG app ID (app_id) on Wayland</li>
<li>Support symbolic icon deployment and recoloring</li>
<li>Added Spanish translation</li>
<li>Added Italian translation</li>
<li>Added Japanese translation</li>
<li>Added Simplified Chinese translation</li>
<li>Added Russian translation</li>
<li>Translate the GUI without needed to restart the application</li>
<li>Expose setting to adjust clients scroll speed</li>
<li>Expose setting to show the GUI debug messages in the log</li>
<li>Expose setting to allow use of wl-clipboard backend on Wayland</li>
<li>Fixed the port settings not being used from settings</li>
<li>Save the geometry info into a state file</li>
<li>The Core (deskflow-core) has a new CLI interface</li>
<li>Fixed Wayland sleep inhibit on client</li>
<li>Fixed XWindowsScreen: properly calculate xrandr/xinerama screens</li>
<li>Fixed XWindowsScreen: stop centering panned screens on client when leaving the screen</li>
<li>Fixed stop retying to launch the Core if its crashed</li>
<li>Fixed various input issues</li>
<li>Fixed apply scroll lock setting on initialization</li>
<li>Fixed crash caused by hostnames with invalid characters returned default value</li>
</ul>
</description>
<url>https://github.com/deskflow/deskflow/releases/tag/v1.25.0</url>
</release>
<release version="1.24.0" date="2025-09-11" urgency="high">
<description>
<p>This stable release fixes issues found in the previous version and adds a few new features. This release also uses more C++20 features. For the full changelog, see the release page.</p>

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -4,17 +4,20 @@
# HACK This is set when the files is included so its the real path
# calling CMAKE_CURRENT_LIST_DIR after include would return the wrong scope var
set(MY_DIR ${CMAKE_CURRENT_LIST_DIR})
install(CODE "execute_process(COMMAND
${DEPLOYQT}
\"\${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_PROPER_NAME}.app\"
-timestamp -codesign=-
)")
set(OSX_BUNDLE ${BUILD_OSX_BUNDLE})
set(OS_STRING "macos-${BUILD_ARCHITECTURE}")
set(CPACK_PACKAGE_ICON "${MY_DIR}/dmg-volume.icns")
set(CPACK_DMG_BACKGROUND_IMAGE "${MY_DIR}/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_DMG_SLA_USE_RESOURCE_FILE_LICENSE ON)
set(CPACK_GENERATOR "DragNDrop")
if (OSX_BUNDLE)
install(CODE "execute_process(COMMAND
${DEPLOYQT}
\"\${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_PROPER_NAME}.app\"
-timestamp -codesign=-
)")
set(CPACK_PACKAGE_ICON "${MY_DIR}/dmg-volume.icns")
set(CPACK_DMG_BACKGROUND_IMAGE "${MY_DIR}/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_DMG_SLA_USE_RESOURCE_FILE_LICENSE ON)
set(CPACK_GENERATOR "DragNDrop")
endif()

Binary file not shown.

View File

@ -6,7 +6,7 @@
set(MY_DIR ${CMAKE_CURRENT_LIST_DIR})
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION ${CMAKE_INSTALL_LIBDIR})
include(InstallRequiredSystemLibraries)
configure_file(${MY_DIR}/pre-cpack.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/pre-cpack.cmake @ONLY)

View File

@ -12,6 +12,8 @@ if (DOXYGEN_FOUND)
set(DOXYGEN_STRIP_FROM_PATH ${CMAKE_SOURCE_DIR})
set(DOXYGEN_QUIET YES)
set(DOXYGEN_PROJECT_NAME ${CMAKE_PROJECT_PROPER_NAME})
set(DOXYGEN_PROJECT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/deskflow-logo.png")
set(DOXYGEN_PROJECT_LOGO "${CMAKE_CURRENT_SOURCE_DIR}/deskflow-logo.png")
if (BUILD_USER_DOCS)
add_subdirectory(user)

BIN
doc/deskflow-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -7,76 +7,104 @@ To build Deskflow you will a minimum of:
- [libportal] 0.8+ (linux, bsd)
- [libei] 1.3+ (linux, bsd)
- [google_test] ^
- [tomlplusplus] ^
- [cli11] ^
> ^ Will be fetched if not found on the host system.
By default a build of Deskflow will:
- The GUI application deskflow
- The Core application deskflow-core
- The GUI application `deskflow`
- The Core application `deskflow-core`
- Documentation if [doxygen] was found on your system
- Tests that will be run as part of the build process.
- Tests that will be run as part of the build process
## Configuration
Deskflow supports the following build options
CMake options:
Deskflow supports the following CMake options:
| Option | Description | Default Value | Additional requirements |
:-------------------------:|:---------------------------------------:|:------------------:|:-----------------------:|
| BUILD_GUI | Build GUI | ON | |
| BUILD_USER_DOCS | Build user documentation | DOXYGEN_FOUND | `Doxygen` |
| BUILD_DEV_DOCS | Build development documentation | OFF | `Doxygen` |
| BUILD_INSTALLER | Build installers/packages | ON | |
| BUILD_TESTS | Build unit tests and legacy tests | ON | `gtest`|
| BUILD_X11_SUPPORT | Build X11 backend (Linux and BSD only) | ON | `x11 libs`|
| BUILD_OSX_BUNDLE | Build an app bundle (macOS only) | ON | |
| ENABLE_COVERAGE | Enable test coverage | OFF | `gcov` |
| SKIP_BUILD_TESTS | Skip running of tests at build time | OFF | |
| VCPKG_QT | Build Qt w/ vcpkg (windows only) | OFF | |
| VCPKG_QT | Build Qt w/ vcpkg (Windows only) | OFF | |
| CLEAN_TRS | Remove obsolete strings from tr files | OFF | |
| APPLE_CODESIGN_DEV | Apple codesign cert ID for development | Not set | |
Example cmake configuration.
Example cmake configuration:
`cmake -S. -Bbuild -DCMAKE_INSTALL_PREFIX=<INSTALLPREFIX>`
### Windows Configuration
It is recommended to use vcpkg to install the dependencies. The first time you configure Deskflow, all dependencies other than Qt will be built. If you don't want to use vcpkg, you must manually setup the dependencies. However, that will not be covered by this document.
#### Windows and Qt
There are two ways you can install [Qt] on Windows (vcpkg or Qt online installer). The default configuration expects you to use the Qt online installer. You should not install Qt in both ways, as having both can cause some weird things to happen, like Qt getting libs from one install and plugins from the other. When switching between them, remove the previous install first.
##### System Qt
1. Download and install the [Qt] online installer from their website.
2. Add the path of Qt's cmake files to your system path. (Skipping this may require you provide this path to cmake via `Qt6_DIR` at configure time)
- Often `C:\Qt\<version>\<msvcinfo>\lib\cmake`
2. Add the path of Qt's cmake files to your system path (skipping this may require you provide this path to cmake via `Qt6_DIR` at configure time).
- Often: `C:\Qt\<version>\<msvcinfo>\lib\cmake`
3. Add the path of Qt's binary tools to your system path.
- Often `C:\Qt\<version>\<msvcinfo>\bin`
- Often: `C:\Qt\<version>\<msvcinfo>\bin`
##### vcpkg managed Qt
##### Vcpkg managed Qt
1. Add the option `-DVCPKG_QT=ON` to your cmake configuration command (i.e `cmake -S. -Bbuild -DVCPKG_QT=ON ...`) or if using an IDE, look for the option where you configure the project, have the IDE run cmake again.
2. Once the configuration starts, you should see a lot more packages vcpkg will build. Building Qt takes a long time (potentially hours), so go find something else to do for a while.
3. If you want to use the system Qt again, you must delete the `vcpkg.json` generated in the project root and the `build` folder and reconfigure the project from scratch.
### macOS codesign
The code signing option `APPLE_CODESIGN_DEV` is only for local development and not intended for distributed bundles.
Signing for local development and signing for the distribution bundle must be different because of development entitlements which are unlikely to be safe for use in production. It is impractical (i.e. very slow and cumbersome) to use the distribution bundle for local development. When developing locally, the app bundle is partial and does not contain dependencies and uses external libs, e.g. installed with Homebrew; the entitlements allow those external libs to be loaded which is not allowed by default.
For development codesign:
1. Install Xcode
2. Go to Settings -> Accounts
3. Add your account (requires a free Apple Developer ID)
4. Manage certificates -> Add -> Apple Development
5. To get your ID, run: `security find-identity -v -p codesigning login.keychain-db`
6. Pass the ID to CMake, e.g. `-DAPPLE_CODESIGN_DEV=Apple Development: bob@exmaple.com (KLGSJHLFXY)`
7. Configure and build
8. To verify, run: `codesign -d -r- build/bin/Deskflow.app`
## Build
After configuring you should be able to run make to build all targets.
`cmake --build build`
## Install
To test installation run `DESTDIR=<installDIR> cmake --install build` to install into `<installDir>/<CMAKE_INSTALL_PREFIX>` <br>
To test installation run `DESTDIR=<installDIR> cmake --install build` to install into `<installDir>/<CMAKE_INSTALL_PREFIX>`
Running `cmake --install build` will install to the `CMAKE_INSTALL_PREFIX`
## Making Deskflow packages
Deskflow can generate several packages using cpack.
To generate packages build the `package` or `package_source` target.
Example: ` cmake --build build --target package package_source` would generate both package and package source packages.
Deskflow can generate several package types depending on the system. Archive-based packages should work on all platforms. On Linux deb and rpm info is set up, flatpaks can be generated from the included file in deploy/linux and a PKGBUILD for Arch linux is generated in the build folder. On macos a dmg file will be created and signed. For windows wix can be used to create an installer.
Deskflow can generate several packages using `cpack`.
To generate packages build the `package` or `package_source` target.
Example: ` cmake --build build --target package package_source` would generate both package and package source packages.
Deskflow can generate several package types depending on the system.
Archive-based packages should work on all platforms. On Linux deb and rpm info is set up, Flatpaks can be generated from the included file in deploy/linux and a `PKGBUILD` for Arch linux is generated in the build folder. On macos a dmg file will be created and signed. For windows WiX can be used to create an installer.
[Qt]:https://www.qt.io
[doxygen]:http://www.stack.nl/~dimitri/doxygen/
[cmake]:https://cmake.org/
[openssl]:https://www.openssl.org/
[google_test]:https://github.com/google/googletest
[tomlplusplus]:https://github.com/marzer/tomlplusplus
[cli11]:https://github.com/CLIUtils/CLI11
[libei]:https://gitlab.freedesktop.org/libinput/libei
[libportal]:https://github.com/flatpak/libportal

View File

@ -802,43 +802,3 @@ section: links
down = larry
end
```
# Example file for `--config-toml` arg
```
[server.args]
no-daemon = true
no-tray = true
debug = "DEBUG"
name = "moe"
address = ":24800"
[client.args]
no-daemon = true
no-tray = true
debug = "DEBUG2"
name = "larry"
_last = "moe:24800"
```
# Example `.env` file
```
#
# App
#
# Shows the test menu in the GUI (on by default in debug mode)
# DESKFLOW_TEST_MENU=true
# Version checker URL to use (useful for testing)
# DESKFLOW_VERSION_URL="https://api.deskflow.org/version?fake=1.100.0"
# Enable debug logging in the GUI (on by default in debug mode)
# DESKFLOW_GUI_DEBUG=true
# Enable verbose logging in the GUI (always off by default)
# DESKFLOW_GUI_VERBOSE=true
```

View File

@ -1,5 +1,3 @@
![Deskflow](https://github.com/user-attachments/assets/f005b958-24df-4f4a-9bfd-4f834dae59d6)
**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.

View File

@ -2,8 +2,8 @@ sonar.organization=deskflow
sonar.projectKey=deskflow_deskflow
sonar.sources=src/apps,src/lib
sonar.tests=src/unittests
sonar.exclusions=subprojects/**,build/**
sonar.coverage.exclusions=subprojects/**,src/unittests/**,src/apps/deskflow-gui/**,src/apps/res/**
sonar.exclusions=subprojects/**,build/**,translations/**
sonar.coverage.exclusions=subprojects/**,src/unittests/**,src/apps/deskflow-gui/**,src/apps/res/**,translations/**
sonar.cpd.exclusions=**/*Test*.cpp
sonar.host.url=https://sonarcloud.io
sonar.cfamily.compile-commands=build/compile_commands.json

View File

@ -29,11 +29,5 @@ function(generate_app_man TARGET NAME)
endfunction()
add_subdirectory(deskflow-core)
## Only used on windows
add_subdirectory(deskflow-daemon)
option(BUILD_GUI "Build GUI" ON)
if(BUILD_GUI)
add_subdirectory(deskflow-gui)
endif(BUILD_GUI)
add_subdirectory(deskflow-daemon) #Only used on windows
add_subdirectory(deskflow-gui)

View File

@ -5,7 +5,13 @@
set(target ${CMAKE_PROJECT_NAME}-core)
add_executable(${target} "${target}.cpp")
add_executable(${target}
"${target}.cpp"
CoreArgs.h
CoreArgParser.h
CoreArgParser.cpp
)
if(WIN32)
# Generate rc file
set(EXE_DESCRIPTION "${CMAKE_PROJECT_PROPER_NAME} combined server and client application")
@ -32,21 +38,19 @@ target_link_libraries(
app
${libs})
if(APPLE)
install(
TARGETS ${target}
RUNTIME_DEPENDENCY_SET coreDeps
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(BUILD_OSX_BUNDLE)
set_target_properties(${target} PROPERTIES
BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_RPATH "@loader_path/../Libraries;@loader_path/../Frameworks"
RUNTIME_OUTPUT_DIRECTORY $<TARGET_BUNDLE_CONTENT_DIR:${CMAKE_PROJECT_PROPER_NAME}>/MacOS
)
elseif(UNIX)
install(TARGETS ${target} DESTINATION bin)
generate_app_man(${target} "${CMAKE_PROJECT_DESCRIPTION}")
elseif(WIN32)
install(
TARGETS ${target}
RUNTIME_DEPENDENCY_SET coreDeps
DESTINATION .
)
elseif (WIN32)
install(RUNTIME_DEPENDENCY_SET coreDeps
PRE_EXCLUDE_REGEXES
"api-ms-win-.*"
@ -54,6 +58,8 @@ elseif(WIN32)
"^hvsifiletrust\\.dll$"
POST_EXCLUDE_REGEXES
".*system32.*"
RUNTIME DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
else()
generate_app_man(${target} "${CMAKE_PROJECT_DESCRIPTION}")
endif()

View File

@ -0,0 +1,104 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "CoreArgParser.h"
#include "CoreArgs.h"
#include "VersionInfo.h"
#include "common/Constants.h"
#include "common/ExitCodes.h"
#include "common/Settings.h"
#include "deskflow/ProtocolTypes.h"
const QString CoreArgParser::s_headerText = QStringLiteral("%1: %2\n").arg(kCoreBinName, kDisplayVersion);
CoreArgParser::CoreArgParser(const QStringList &args)
{
m_parser.setApplicationDescription(kAppDescription);
m_parser.addPositionalArgument("coremode", "The mode to start in either: server or client", "coremode");
m_parser.addOptions(CoreArgs::options);
m_parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
m_parser.setOptionsAfterPositionalArgumentsMode(QCommandLineParser::ParseAsOptions);
m_parser.parse(args);
m_helpText = m_parser.helpText().replace("<executable_name>", kCoreBinName);
m_helpText.replace("[options] coremode", "coremode [options]");
m_singleInstance = !m_parser.isSet(CoreArgs::multiInstanceOption);
}
void CoreArgParser::parse()
{
auto posArgs = m_parser.positionalArguments();
if (posArgs.isEmpty()) {
showHelpText();
exit(s_exitSuccess);
}
const QString mode = posArgs.takeFirst();
m_serverMode = (mode.compare("server", Qt::CaseInsensitive) == 0);
m_clientMode = (mode.compare("client", Qt::CaseInsensitive) == 0);
if ((!m_clientMode && !m_serverMode) || mode.isEmpty()) {
showHelpText();
exit(s_exitSuccess);
}
if (m_parser.isSet(CoreArgs::configOption)) {
Settings::setSettingsFile(m_parser.value(CoreArgs::configOption));
}
}
[[noreturn]] void CoreArgParser::showHelpText() const
{
QTextStream(stdout) << helpText();
exit(s_exitSuccess);
}
QString CoreArgParser::helpText() const
{
return QStringLiteral("%1%2").arg(s_headerText, m_helpText);
}
QString CoreArgParser::versionText() const
{
const static auto vString = QStringLiteral("%1 v%2, protocol v%3.%4\n%5\n");
return vString.arg(
kCoreBinName, kDisplayVersion, QString::number(kProtocolMajorVersion), QString::number(kProtocolMinorVersion),
kCopyright
);
}
QString CoreArgParser::errorText() const
{
return m_parser.errorText();
}
bool CoreArgParser::help() const
{
return m_parser.isSet(CoreArgs::helpOption);
}
bool CoreArgParser::version() const
{
return m_parser.isSet(CoreArgs::versionOption);
}
bool CoreArgParser::serverMode() const
{
return m_serverMode;
}
bool CoreArgParser::clientMode() const
{
return m_clientMode;
}
bool CoreArgParser::singleInstanceOnly() const
{
return m_singleInstance;
}

View File

@ -0,0 +1,46 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include <QCommandLineParser>
/**
* @brief The CoreArgParser class
* This class processes the argments for the "core" app
*/
class CoreArgParser
{
public:
/**
* @brief CoreArgParser calling this funciton will parse apps and set the setting and version options
* For any other settings to be set you must call parse();
* @sa parse();
* @param args List of args to parse
*/
explicit CoreArgParser(const QStringList &args = {});
/**
* @brief parse
* This method will parse all options other then help and version
*/
void parse();
QString helpText() const;
QString versionText() const;
QString errorText() const;
bool help() const;
bool version() const;
bool serverMode() const;
bool clientMode() const;
bool singleInstanceOnly() const;
private:
[[noreturn]] void showHelpText() const;
QCommandLineParser m_parser;
QString m_helpText;
bool m_clientMode = false;
bool m_serverMode = false;
bool m_singleInstance = true;
static const QString s_headerText;
};

View File

@ -0,0 +1,24 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include <QCommandLineOption>
/**
* @brief The CoreArgs class
* This class contains args for the CoreArgParser
*/
struct CoreArgs
{
inline static const auto helpOption = QCommandLineOption({"h", "help"}, "Display Help on the command line");
inline static const auto versionOption = QCommandLineOption({"v", "version"}, "Display version information");
inline static const auto multiInstanceOption =
QCommandLineOption("new-instance", "Skip the check for a running instance, always makes a new instance");
inline static const auto configOption =
QCommandLineOption({"s", "settings"}, "override configuration file to use", "configFile");
inline static const auto options = {helpOption, versionOption, multiInstanceOption, configOption};
};

View File

@ -6,10 +6,12 @@
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "VersionInfo.h"
#include "CoreArgParser.h"
#include "arch/Arch.h"
#include "base/EventQueue.h"
#include "base/Log.h"
#include "common/Constants.h"
#include "common/ExitCodes.h"
#include "deskflow/ClientApp.h"
#include "deskflow/ServerApp.h"
@ -19,89 +21,17 @@
#include <QCoreApplication>
#endif
#include <QFileInfo>
#include <QSharedMemory>
#include <iostream>
#include <QTextStream>
const static auto kHeader = QStringLiteral("%1-core: %2\n").arg(kAppId, kDisplayVersion);
void showHelp()
void showHelp(const CoreArgParser &parser)
{
std::cout << qPrintable(kHeader) << qPrintable(kAppDescription) << "\n\n";
std::cout << "Usage: deskflow-core <server | client> [...options]" << std::endl;
std::cout << "server - start as a server (deskflow-server)" << std::endl;
std::cout << "client - start as a client (deskflow-client)" << std::endl;
ServerApp sApp(nullptr);
sApp.help();
ClientApp cApp(nullptr);
cApp.help();
}
bool isHelp(int argc, char **argv)
{
for (int i = 0; i < argc; ++i) {
if (argv[i] == std::string("--help") || argv[i] == std::string("-h"))
return true;
}
return false;
}
void showVersion()
{
std::cout << qPrintable(kHeader) << qPrintable(kCopyright) << std::endl;
}
bool isVersion(int argc, char **argv)
{
for (int i = 0; i < argc; ++i) {
if (argv[i] == std::string("--version") || argv[i] == std::string("-v"))
return true;
}
return false;
}
bool isServer(int argc, char **argv)
{
return (argc > 1 && argv[1] == std::string("server"));
}
bool isClient(int argc, char **argv)
{
return (argc > 1 && argv[1] == std::string("client"));
QTextStream(stdout) << parser.helpText();
}
int main(int argc, char **argv)
{
Arch arch;
arch.init();
Log log;
if (isHelp(argc, argv)) {
showHelp();
return s_exitSuccess;
}
if (isVersion(argc, argv)) {
showVersion();
return s_exitSuccess;
}
// Create a shared memory segment with a unique key
// This is to prevent a new instance from running if one is already running
QSharedMemory sharedMemory("deskflow-core");
// Attempt to attach first and detach in order to clean up stale shm chunks
// This can happen if the previous instance was killed or crashed
if (sharedMemory.attach())
sharedMemory.detach();
// If we can create 1 byte of SHM we are the only instance
if (!sharedMemory.create(1)) {
LOG_WARN("an instance of deskflow core is already running");
return s_exitDuplicate;
}
#if SYSAPI_WIN32
// HACK to make sure settings gets the correct qApp path
QCoreApplication m(argc, argv);
@ -110,16 +40,61 @@ int main(int argc, char **argv)
ArchMiscWindows::setInstanceWin32(GetModuleHandle(nullptr));
#endif
EventQueue events;
Arch arch;
arch.init();
if (isServer(argc, argv)) {
ServerApp app(&events);
return app.run(argc, argv);
} else if (isClient(argc, argv)) {
ClientApp app(&events);
return app.run(argc, argv);
} else {
showHelp();
Log log;
QStringList args;
for (int i = 0; i < argc; i++)
args.append(argv[i]);
CoreArgParser parser(args);
// Print any parser errors
if (!parser.errorText().isEmpty()) {
QTextStream(stdout) << parser.errorText() << "\n";
}
if (parser.help()) {
showHelp(parser);
return s_exitSuccess;
}
if (parser.version()) {
QTextStream(stdout) << parser.versionText();
return s_exitSuccess;
}
if (parser.singleInstanceOnly()) {
// Before we check any more args we need to check for a duplicate process.
// Create a shared memory segment with a unique key
// This is to prevent a new instance from running if one is already running
QSharedMemory sharedMemory(kCoreBinName);
// Attempt to attach first and detach in order to clean up stale shm chunks
// This can happen if the previous instance was killed or crashed
if (sharedMemory.attach())
sharedMemory.detach();
// If we can create 1 byte of SHM we are the only instance
if (!sharedMemory.create(1)) {
LOG_WARN("an instance of deskflow core is already running");
return s_exitDuplicate;
}
}
parser.parse();
EventQueue events;
const auto processName = QFileInfo(argv[0]).fileName();
if (parser.serverMode()) {
ServerApp app(&events, processName);
return app.run();
} else if (parser.clientMode()) {
ClientApp app(&events, processName);
return app.run();
}
return s_exitSuccess;

View File

@ -30,7 +30,7 @@ if(WIN32)
install(
TARGETS ${target}
RUNTIME_DEPENDENCY_SET deamonDeps
DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(RUNTIME_DEPENDENCY_SET daemonDeps
PRE_EXCLUDE_REGEXES
@ -39,6 +39,6 @@ if(WIN32)
"^hvsifiletrust\\.dll$"
POST_EXCLUDE_REGEXES
".*system32.*"
RUNTIME DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
endif()

View File

@ -15,12 +15,12 @@ if(WIN32)
set(EXE_ICON "IDI_ICON1 ICON DISCARDABLE \"${CMAKE_SOURCE_DIR}/src/apps/res/deskflow.ico\" ")
configure_file(${CMAKE_SOURCE_DIR}/src/apps/res/windows.rc.in deskflow.rc)
set(platform_extra deskflow.rc)
elseif(APPLE)
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_GUI_IDENTIFIER "org.deskflow.deskflow")
set(BUNDLE_GUI_IDENTIFIER "${CMAKE_PROJECT_REV_FQDN}")
set(BUNDLE_ICON_FILE ${target}.icns)
set(BUNDLE_INFO_STRING "${CMAKE_PROJECT_DESCRIPTION}")
set(BUNDLE_COPYRIGHT "${CMAKE_PROJECT_COPYRIGHT}")
@ -31,13 +31,7 @@ elseif(APPLE)
@ONLY
)
file(COPY_FILE
${PROJECT_SOURCE_DIR}/LICENSES/LicenseRef-OpenSSL-Exception.txt
${CMAKE_CURRENT_BINARY_DIR}/LICENSE_EXCEPTION
ONLY_IF_DIFFERENT
)
set(platform_extra ../res/Deskflow.icns ${CMAKE_SOURCE_DIR}/LICENSE ${CMAKE_CURRENT_BINARY_DIR}/LICENSE_EXCEPTION)
set(platform_extra ../res/Deskflow.icns)
set_source_files_properties(${platform_extra} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
endif()
@ -55,13 +49,16 @@ target_link_libraries(
Qt6::Widgets
Qt6::Network)
install(
TARGETS ${target}
RUNTIME_DEPENDENCY_SET guiDeps
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
BUNDLE DESTINATION .
)
if(WIN32)
set_target_properties(${target} PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT")
install(
TARGETS ${target}
RUNTIME_DEPENDENCY_SET guiDeps
DESTINATION .
)
install(RUNTIME_DEPENDENCY_SET guiDeps
PRE_EXCLUDE_REGEXES
"api-ms-win-.*"
@ -69,7 +66,7 @@ if(WIN32)
"^hvsifiletrust\\.dll$"
POST_EXCLUDE_REGEXES
".*system32.*"
RUNTIME DESTINATION .
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
set(QT_DEPENDS_DIR ${CMAKE_BINARY_DIR}/qt-depends)
@ -91,17 +88,46 @@ if(WIN32)
install(
DIRECTORY ${QT_DEPENDS_DIR}/
DESTINATION .
DESTINATION ${CMAKE_INSTALL_LIBDIR}
PATTERN "dx*.dll" EXCLUDE
)
elseif(APPLE)
set_target_properties(${target} PROPERTIES
INSTALL_RPATH "@loader_path/../Libraries;@loader_path/../Frameworks"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/deskflow.plist"
)
install(TARGETS ${target} BUNDLE DESTINATION .)
if (BUILD_OSX_BUNDLE)
set_target_properties(${target} PROPERTIES
INSTALL_RPATH "@loader_path/../Libraries;@loader_path/../Frameworks"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/deskflow.plist"
)
# Warning: Do not use for CI/production, as the `entitlements-dev.plist` file adds special
# entitlements that are only appropriate for local development.
#
# macOS made TCC stricter so that if you don't sign your local dev builds properly, macOS will
# nag you to remove and re-approve the app every time you make a change to the binary which is
# extremely annoying during development.
#
# If you were to use ad-hoc signing (i.e. not specify a certificate), TCC would still nag you
# because the binary identity is anchored not on the app ID, but on the CD hash (which changes
# based on the binary contents).
#
# To use, simply generate a personal certificate for free with Xcode and pass the ID to CMake.
# Full instructions are in the docs.
if (NOT "${APPLE_CODESIGN_DEV}" STREQUAL "")
message(STATUS "Apple codesign ID for development only: ${APPLE_CODESIGN_DEV}")
add_custom_command(
TARGET ${target} POST_BUILD
COMMAND /usr/bin/codesign
--force
--options runtime
--entitlements "$<SHELL_PATH:${CMAKE_SOURCE_DIR}/src/apps/res/entitlements-dev.plist>"
--sign "${APPLE_CODESIGN_DEV}"
"$<TARGET_BUNDLE_DIR:${target}>"
VERBATIM
)
endif()
else()
set_target_properties(${target} PROPERTIES MACOSX_BUNDLE FALSE)
endif()
else()
install(TARGETS ${target} DESTINATION bin)
generate_app_man(${target} "${CMAKE_PROJECT_DESCRIPTION} \\(GUI\\)")
endif()

View File

@ -9,10 +9,10 @@
#include "VersionInfo.h"
#include "common/Constants.h"
#include "common/ExitCodes.h"
#include "common/I18N.h"
#include "common/PlatformInfo.h"
#include "common/UrlConstants.h"
#include "gui/Diagnostic.h"
#include "gui/DotEnv.h"
#include "gui/Logger.h"
#include "gui/MainWindow.h"
#include "gui/Messages.h"
#include "gui/StyleUtils.h"
@ -23,7 +23,7 @@
#include <QMessageBox>
#include <QSharedMemory>
#if defined(Q_OS_MAC)
#if defined(Q_OS_MACOS)
#include <Carbon/Carbon.h>
#include <cstdlib>
#endif
@ -32,9 +32,13 @@
#include <QLoggingCategory>
#endif
#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
#include "platform/XDGPortalRegistry.h"
#endif
using namespace deskflow::gui;
#if defined(Q_OS_MAC)
#if defined(Q_OS_MACOS)
bool checkMacAssistiveDevices();
#endif
@ -47,14 +51,21 @@ int main(int argc, char *argv[])
QLoggingCategory::setFilterRules(QStringLiteral("*.debug=true\nqt.*=false"));
#endif
#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
deskflow::platform::setAppId();
#endif
QCoreApplication::setApplicationName(kAppName);
QCoreApplication::setOrganizationName(kAppName);
QCoreApplication::setApplicationVersion(kVersion);
QCoreApplication::setOrganizationDomain(kOrgDomain); // used in prefix, can't be a url
QGuiApplication::setDesktopFileName(QStringLiteral("org.deskflow.deskflow"));
QGuiApplication::setDesktopFileName(kRevFqdnName);
QApplication app(argc, argv);
// Ensure the I18N object is made before strings
QTextStream(stdout) << "initial language: " << I18N::currentLanguage() << '\n';
// Add Command Line Options
auto helpOption = QCommandLineOption({"h", "help"}, "Display Help on the command line");
auto versionOption = QCommandLineOption({"v", "version"}, "Display version information");
@ -83,9 +94,10 @@ int main(int argc, char *argv[])
return s_exitSuccess;
}
const auto shmId = QStringLiteral("%1-gui").arg(kAppId);
// Create a shared memory segment with a unique key
// This is to prevent a new instance from running if one is already running
QSharedMemory sharedMemory("deskflow-gui");
QSharedMemory sharedMemory(shmId);
// Attempt to attach first and detach in order to clean up stale shm chunks
// This can happen if the previous instance was killed or crashed
@ -96,38 +108,27 @@ int main(int argc, char *argv[])
if (!sharedMemory.create(1)) {
// Ping the running instance to have it show itself
QLocalSocket socket;
socket.connectToServer("deskflow-gui", QLocalSocket::ReadOnly);
socket.connectToServer(shmId, QLocalSocket::ReadOnly);
if (!socket.waitForConnected()) {
// If we can't connect to the other instance tell the user its running.
// This should never happen but just incase we should show something
QMessageBox::information(nullptr, QObject::tr("Deskflow"), QObject::tr("Deskflow is already running"));
QMessageBox::information(nullptr, kAppName, QObject::tr("%1 is already running").arg(kAppName));
}
socket.disconnectFromServer();
return s_exitDuplicate;
}
#if !defined(Q_OS_MAC)
// causes dark mode to be used on some DE's
if (qEnvironmentVariable("XDG_CURRENT_DESKTOP") != QLatin1String("KDE")) {
if (!deskflow::platform::isMac() && qEnvironmentVariable("XDG_CURRENT_DESKTOP") != QLatin1String("KDE")) {
QApplication::setStyle("fusion");
}
#endif
// Sets the fallback icon path and fallback theme
const auto themeName = QStringLiteral("deskflow-%1").arg(iconMode());
if (QIcon::themeName().isEmpty())
QIcon::setThemeName(themeName);
else
QIcon::setFallbackThemeName(themeName);
QIcon::setFallbackSearchPaths({QStringLiteral(":/icons/%1").arg(themeName)});
updateIconTheme();
qInstallMessageHandler(deskflow::gui::messages::messageHandler);
qInfo("%s v%s", kAppName, kDisplayVersion);
dotenv();
Logger::instance().loadEnvVars();
#if defined(Q_OS_MAC)
#if defined(Q_OS_MACOS)
if (app.applicationDirPath().startsWith("/Volumes/")) {
QString msgBody = QStringLiteral(
@ -154,7 +155,7 @@ int main(int argc, char *argv[])
return QApplication::exec();
}
#if defined(Q_OS_MAC)
#if defined(Q_OS_MACOS)
bool checkMacAssistiveDevices()
{
// new in mavericks, applications are trusted individually

View File

@ -57,8 +57,8 @@
<file>icons/deskflow-dark/actions/32/document-save-as.svg</file>
<file>icons/deskflow-dark/actions/32/help-about.svg</file>
<file>icons/deskflow-dark/actions/32/view-refresh.svg</file>
<file>icons/deskflow-dark/apps/64/deskflow.svg</file>
<file>icons/deskflow-dark/apps/64/deskflow-symbolic.svg</file>
<file>icons/deskflow-dark/apps/64/org.deskflow.deskflow.svg</file>
<file>icons/deskflow-dark/apps/64/org.deskflow.deskflow-symbolic.svg</file>
<file>icons/deskflow-dark/devices/64/video-display.svg</file>
<file>icons/deskflow-dark/places/64/user-trash.svg</file>
<file>icons/deskflow-dark/status/32/software-updates-release.svg</file>
@ -130,8 +130,8 @@
<file>icons/deskflow-light/actions/32/document-save-as.svg</file>
<file>icons/deskflow-light/actions/32/help-about.svg</file>
<file>icons/deskflow-light/actions/32/view-refresh.svg</file>
<file>icons/deskflow-light/apps/64/deskflow.svg</file>
<file>icons/deskflow-light/apps/64/deskflow-symbolic.svg</file>
<file>icons/deskflow-light/apps/64/org.deskflow.deskflow.svg</file>
<file>icons/deskflow-light/apps/64/org.deskflow.deskflow-symbolic.svg</file>
<file>icons/deskflow-light/devices/64/video-display.svg</file>
<file>icons/deskflow-light/status/32/software-updates-release.svg</file>
<file>icons/deskflow-light/status/64/dialog-error.svg</file>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>com.apple.security.cs.disable-library-validation</key><true/>
<key>com.apple.security.get-task-allow</key><true/>
<key>com.apple.security.cs.allow-jit</key><true/>
</dict></plist>

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="400"
viewBox="0 -960 16000 16000"
width="400"
fill="#e8eaed"
version="1.1"
id="svg58"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs62" />
<g
id="g312"
transform="matrix(0.95521236,0,0,0.88584237,358.301,773.57086)">
<path
style="fill:#f2f2f2;fill-opacity:1;stroke-width:22.0314"
d="m 10822.775,14277.69 c -1480.6355,-205.266 -2703.2156,-1149.093 -4024.7185,-1775.333 -745.9215,-412.95 -1640.7516,-671.66 -2479.9222,-411.91 -844.9777,264.031 -1378.4045,1001.612 -1991.5999,1582.679 -747.0662,380.92 -732.8666,-1875.959 -957.1362,-1286.712 396.2288,-871.302 1186.3232,-1519.256 1987.7363,-2012.518 1172.4366,-680.7295 2625.6591,-459.9056 3774.9177,151.009 1203.0592,540.157 2286.8972,1374.106 3578.5308,1696.524 888.521,187.313 1759.981,-291.864 2291.813,-984.522 293.133,-563.979 1138.577,-1336.7036 1564.647,-433.065 320.451,648.351 -932.467,2288.278 -494.032,1806.312 -768.024,1006.215 -1931.603,1805.177 -3250.236,1667.536 z"
id="path3393" />
<path
style="fill:#f2f2f2;fill-opacity:1;stroke-width:26.9658"
d="M 10691.975,9232.2954 C 8846.2487,8912.0018 7433.3401,7506.5916 5652.3423,7010.2615 4761.8774,6790.4246 3822.1342,7124.1431 3186.0428,7766.5861 2779.8147,8179.8677 2076.0345,9244.1299 1514.3929,8423.1235 1079.2106,7830.3042 2354.0198,6103.9484 1854.8774,6562.2615 2749.3313,5511.0149 4101.33,4683.576 5528.9402,4933.985 c 1825.4065,308.1723 3248.3604,1635.6508 4981.4158,2196.167 786.252,212.7419 1654.268,-19.2507 2198.637,-635.8586 489.708,-366.0972 693.581,-1248.5159 1405.833,-1192.9521 749.104,302.2549 726.809,1381.0589 243.424,1914.1731 -761.756,1116.6719 -1965.665,2136.6365 -3401.044,2036.4798 l -140.36,-6.6924 -124.875,-13.0105 z"
id="path1767" />
<path
style="fill:#f2f2f2;fill-opacity:1;stroke-width:26.9658"
d="M 10576.744,4172.4544 C 8781.32,3857.6985 7413.9341,2483.8453 5681.7925,2001.6118 4813.0936,1790.1465 3885.6762,2059.0545 3243.5441,2676.9291 2755.2378,3026.8587 2296.4145,4124.678 1601.3049,3504.7347 718.55095,2717.1148 2282.4679,796.203 1795.7242,1592.2603 2696.3129,485.67774 4095.5242,-387.1285 5571.7883,-103.57995 7431.0096,214.26172 8850.9468,1625.9548 10643.731,2118.8133 c 1007.057,248.4056 1967.172,-377.7119 2523.479,-1176.70049 225.605,-637.03713 1128.579,-1020.154865 1434.5,-217.3221 318.499,687.05509 -995.822,2368.70809 -558.726,1862.82939 -805.909,1049.9999 -2097.781,1861.9671 -3466.24,1584.8343 z"
id="path970" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="128.00002"
height="127.99999"
viewBox="0 0 33.866671 33.866665"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><g
id="layer1"
transform="translate(-32.279167,-138.64166)"><g
style="fill:#e8eaed"
id="g1"
transform="matrix(0.00196863,0,0,0.00196863,33.463433,141.71593)"><g
id="g312"
transform="matrix(1.002973,0,0,0.93013448,-23.783992,460.24941)"><path
style="fill:#3366cc;fill-opacity:1;stroke-width:22.0314"
d="m 10822.775,14277.69 c -1195.5832,-143.508 -2220.2063,-841.148 -3268.3667,-1379.127 -718.8533,-355.301 -1431.1297,-838.976 -2251.4363,-896.085 -491.0057,-34.634 -993.9142,16.701 -1419.8658,282.946 -632.9097,323.689 -1041.4867,924.538 -1556.5718,1387.702 -543.283,324.938 -1024.416,-324.503 -999.9439,-831.854 -66.7515,-706.46 526.7775,-1215.106 971.4799,-1675.328 796.7042,-788.985 1908.9735,-1339.2985 3051.7899,-1198.3588 1348.2477,151.6528 2490.0786,954.1998 3671.9639,1551.7868 812.8473,430.368 1732.2728,937.309 2680.6698,656.999 865.182,-240.211 1345.317,-1049.536 1918.072,-1669.709 419.319,-369.53 999.56,99.027 1037.303,575.62 144.849,591.439 -228.81,1099.793 -584.858,1527.872 -674.991,878.758 -1646.404,1612.499 -2788.132,1678.492 -153.996,19.794 -308.698,6.257 -462.104,-10.956 z"
id="path3393" /><path
style="fill:#33b2cc;fill-opacity:1;stroke-width:26.9658"
d="M 10691.975,9232.2954 C 9345.1884,8998.8534 8209.7623,8175.6727 7013.3992,7567.553 6317.9006,7189.0731 5542.8247,6853.4818 4732.6528,6969.3648 3791.0852,7073.1473 3096.3863,7808.0894 2511.2819,8487.0616 2153.4997,8962.5979 1492.1857,8662.9151 1369.9995,8151.7319 1161.0166,7603.1278 1417.243,7018.9363 1804.1792,6620.924 2646.9373,5624.9864 3867.0135,4790.8306 5227.0245,4902.123 c 1538.2424,122.1926 2802.7979,1096.8978 4150.0464,1747.2218 689.4831,373.3537 1480.8271,695.5213 2275.6811,498.2159 843.443,-225.4675 1355.155,-986.6873 1874.463,-1625.928 303.839,-429.2412 935.004,-202.6984 1073.134,247.0946 229.503,525.6439 50.159,1127.049 -311.918,1545.1618 -731.881,1031.1947 -1847.889,1971.3437 -3180.435,1938.7364 -138.853,1.6775 -278.273,-1.5834 -416.021,-20.3301 z"
id="path1767" /><path
style="fill:#33cc99;fill-opacity:1;stroke-width:26.9658"
d="M 10576.744,4172.4544 C 8874.8637,3865.0547 7544.5841,2635.7395 5940.4635,2069.4233 5180.2383,1837.8115 4305.6379,1885.6382 3648.4341,2367.3105 c -522.046,315.7633 -870.3773,828.5536 -1307.695,1235.7633 -408.0241,287.6051 -882.8065,-100.3925 -976.5864,-516.0856 -207.011,-566.4578 84.2445,-1166.9024 491.559,-1564.2835 824.1797,-978.84256 2034.8379,-1776.07292 3363.7164,-1666.23087 1553.6459,128.553637 2838.511,1104.93638 4193.6746,1778.01277 748.5153,409.2855 1666.3233,736.8711 2500.3373,374.9559 742.862,-301.9425 1184.93,-1003.6082 1669.344,-1599.36398 390.513,-404.4477409 1011.637,22.50986 1078.781,494.20116 143.129,510.48212 -76.578,1022.79062 -407.346,1408.75032 -715.856,1002.3301 -1795.1,1907.7734 -3088.381,1906.4911 -197.264,2.2737 -394.6,-14.3348 -589.094,-47.0667 z"
id="path970" /></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,10 @@
<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<defs id="defs62">
<style type="text/css" id="current-color-scheme">.ColorScheme-Text { color: #fcfcfc; } </style>
</defs>
<g fill="currentColor" class="ColorScheme-Text">
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10822.775 14277.69c-1195.5832-143.508-2220.2063-841.148-3268.3667-1379.127-718.8533-355.301-1431.1297-838.976-2251.4363-896.085-491.0057-34.634-993.9142 16.701-1419.8658 282.946-632.9097 323.689-1041.4867 924.538-1556.5718 1387.702-543.283 324.938-1024.416-324.503-999.9439-831.854-66.7515-706.46 526.7775-1215.106 971.4799-1675.328 796.7042-788.985 1908.9735-1339.2985 3051.7899-1198.3588 1348.2477 151.6528 2490.0786 954.1998 3671.9639 1551.7868 812.8473 430.368 1732.2728 937.309 2680.6698 656.999 865.182-240.211 1345.317-1049.536 1918.072-1669.709 419.319-369.53 999.56 99.027 1037.303 575.62 144.849 591.439-228.81 1099.793-584.858 1527.872-674.991 878.758-1646.404 1612.499-2788.132 1678.492-153.996 19.794-308.698 6.257-462.104-10.956z"/>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10691.975 9232.2954c-1346.7866-233.442-2482.2127-1056.6227-3678.5758-1664.7424-695.4986-378.4799-1470.5745-714.0712-2280.7464-598.1882-941.5676 103.7825-1636.2665 838.7246-2221.3709 1517.6968-357.7822 475.5363-1019.0962 175.8535-1141.2824-335.3297-208.9829-548.6041 47.2435-1132.7956 434.1797-1530.8079 842.7581-995.9376 2062.8343-1830.0934 3422.8453-1718.801 1538.2424 122.1926 2802.7979 1096.8978 4150.0464 1747.2218 689.4831 373.3537 1480.8271 695.5213 2275.6811 498.2159 843.443-225.4675 1355.155-986.6873 1874.463-1625.928 303.839-429.2412 935.004-202.6984 1073.134 247.0946 229.503 525.6439 50.159 1127.049-311.918 1545.1618-731.881 1031.1947-1847.889 1971.3437-3180.435 1938.7364-138.853 1.6775-278.273-1.5834-416.021-20.3301z"/>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10576.744 4172.4544c-1701.8803-307.3997-3032.1599-1536.7149-4636.2805-2103.0311-760.2252-231.6118-1634.8256-183.7851-2292.0294 297.8872-522.046 315.7633-870.3773 828.5536-1307.695 1235.7633-408.0241 287.6051-882.8065-100.3925-976.5864-516.0856-207.011-566.4578 84.2445-1166.9024 491.559-1564.2835 824.1797-978.84256 2034.8379-1776.0729 3363.7164-1666.2309 1553.6459 128.55364 2838.511 1104.9364 4193.6746 1778.0128 748.5153 409.2855 1666.3233 736.8711 2500.3373 374.9559 742.862-301.9425 1184.93-1003.6082 1669.344-1599.364 390.513-404.44774 1011.637 22.50986 1078.781 494.20116 143.129 510.48212-76.578 1022.7906-407.346 1408.7503-715.856 1002.3301-1795.1 1907.7734-3088.381 1906.4911-197.264 2.2737-394.6-14.3348-589.094-47.0667z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,7 @@
<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<g>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10822.775 14277.69c-1195.5832-143.508-2220.2063-841.148-3268.3667-1379.127-718.8533-355.301-1431.1297-838.976-2251.4363-896.085-491.0057-34.634-993.9142 16.701-1419.8658 282.946-632.9097 323.689-1041.4867 924.538-1556.5718 1387.702-543.283 324.938-1024.416-324.503-999.9439-831.854-66.7515-706.46 526.7775-1215.106 971.4799-1675.328 796.7042-788.985 1908.9735-1339.2985 3051.7899-1198.3588 1348.2477 151.6528 2490.0786 954.1998 3671.9639 1551.7868 812.8473 430.368 1732.2728 937.309 2680.6698 656.999 865.182-240.211 1345.317-1049.536 1918.072-1669.709 419.319-369.53 999.56 99.027 1037.303 575.62 144.849 591.439-228.81 1099.793-584.858 1527.872-674.991 878.758-1646.404 1612.499-2788.132 1678.492-153.996 19.794-308.698 6.257-462.104-10.956z" fill="#36c"/>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10691.975 9232.2954c-1346.7866-233.442-2482.2127-1056.6227-3678.5758-1664.7424-695.4986-378.4799-1470.5745-714.0712-2280.7464-598.1882-941.5676 103.7825-1636.2665 838.7246-2221.3709 1517.6968-357.7822 475.5363-1019.0962 175.8535-1141.2824-335.3297-208.9829-548.6041 47.2435-1132.7956 434.1797-1530.8079 842.7581-995.9376 2062.8343-1830.0934 3422.8453-1718.801 1538.2424 122.1926 2802.7979 1096.8978 4150.0464 1747.2218 689.4831 373.3537 1480.8271 695.5213 2275.6811 498.2159 843.443-225.4675 1355.155-986.6873 1874.463-1625.928 303.839-429.2412 935.004-202.6984 1073.134 247.0946 229.503 525.6439 50.159 1127.049-311.918 1545.1618-731.881 1031.1947-1847.889 1971.3437-3180.435 1938.7364-138.853 1.6775-278.273-1.5834-416.021-20.3301z" fill="#33b2cc"/>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10576.744 4172.4544c-1701.8803-307.3997-3032.1599-1536.7149-4636.2805-2103.0311-760.2252-231.6118-1634.8256-183.7851-2292.0294 297.8872-522.046 315.7633-870.3773 828.5536-1307.695 1235.7633-408.0241 287.6051-882.8065-100.3925-976.5864-516.0856-207.011-566.4578 84.2445-1166.9024 491.559-1564.2835 824.1797-978.84256 2034.8379-1776.0729 3363.7164-1666.2309 1553.6459 128.55364 2838.511 1104.9364 4193.6746 1778.0128 748.5153 409.2855 1666.3233 736.8711 2500.3373 374.9559 742.862-301.9425 1184.93-1003.6082 1669.344-1599.364 390.513-404.44774 1011.637 22.50986 1078.781 494.20116 143.129 510.48212-76.578 1022.7906-407.346 1408.7503-715.856 1002.3301-1795.1 1907.7734-3088.381 1906.4911-197.264 2.2737-394.6-14.3348-589.094-47.0667z" fill="#3c9"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="400"
viewBox="0 -960 16000 16000"
width="400"
fill="#e8eaed"
version="1.1"
id="svg58"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs62" />
<g
id="g312"
transform="matrix(0.95521236,0,0,0.88584237,358.301,773.57086)">
<path
style="fill:#1a1a1a;fill-opacity:1;stroke-width:22.0314"
d="m 10822.775,14277.69 c -1195.5832,-143.508 -2220.2063,-841.148 -3268.3667,-1379.127 -718.8533,-355.301 -1431.1297,-838.976 -2251.4363,-896.085 -491.0057,-34.634 -993.9142,16.701 -1419.8658,282.946 -632.9097,323.689 -1041.4867,924.538 -1556.5718,1387.702 -543.283,324.938 -1024.416,-324.503 -999.9439,-831.854 -66.7515,-706.46 526.7775,-1215.106 971.4799,-1675.328 796.7042,-788.985 1908.9735,-1339.2985 3051.7899,-1198.3588 1348.2477,151.6528 2490.0786,954.1998 3671.9639,1551.7868 812.8473,430.368 1732.2728,937.309 2680.6698,656.999 865.182,-240.211 1345.317,-1049.536 1918.072,-1669.709 419.319,-369.53 999.56,99.027 1037.303,575.62 144.849,591.439 -228.81,1099.793 -584.858,1527.872 -674.991,878.758 -1646.404,1612.499 -2788.132,1678.492 -153.996,19.794 -308.698,6.257 -462.104,-10.956 z"
id="path3393" />
<path
style="fill:#1a1a1a;fill-opacity:1;stroke-width:26.9658"
d="M 10691.975,9232.2954 C 9345.1884,8998.8534 8209.7623,8175.6727 7013.3992,7567.553 6317.9006,7189.0731 5542.8247,6853.4818 4732.6528,6969.3648 3791.0852,7073.1473 3096.3863,7808.0894 2511.2819,8487.0616 2153.4997,8962.5979 1492.1857,8662.9151 1369.9995,8151.7319 1161.0166,7603.1278 1417.243,7018.9363 1804.1792,6620.924 2646.9373,5624.9864 3867.0135,4790.8306 5227.0245,4902.123 c 1538.2424,122.1926 2802.7979,1096.8978 4150.0464,1747.2218 689.4831,373.3537 1480.8271,695.5213 2275.6811,498.2159 843.443,-225.4675 1355.155,-986.6873 1874.463,-1625.928 303.839,-429.2412 935.004,-202.6984 1073.134,247.0946 229.503,525.6439 50.159,1127.049 -311.918,1545.1618 -731.881,1031.1947 -1847.889,1971.3437 -3180.435,1938.7364 -138.853,1.6775 -278.273,-1.5834 -416.021,-20.3301 z"
id="path1767" />
<path
style="fill:#1a1a1a;fill-opacity:1;stroke-width:26.9658"
d="M 10576.744,4172.4544 C 8874.8637,3865.0547 7544.5841,2635.7395 5940.4635,2069.4233 5180.2383,1837.8115 4305.6379,1885.6382 3648.4341,2367.3105 c -522.046,315.7633 -870.3773,828.5536 -1307.695,1235.7633 -408.0241,287.6051 -882.8065,-100.3925 -976.5864,-516.0856 -207.011,-566.4578 84.2445,-1166.9024 491.559,-1564.2835 824.1797,-978.84256 2034.8379,-1776.07292 3363.7164,-1666.23087 1553.6459,128.553637 2838.511,1104.93638 4193.6746,1778.01277 748.5153,409.2855 1666.3233,736.8711 2500.3373,374.9559 742.862,-301.9425 1184.93,-1003.6082 1669.344,-1599.36398 390.513,-404.4477409 1011.637,22.50986 1078.781,494.20116 143.129,510.48212 -76.578,1022.79062 -407.346,1408.75032 -715.856,1002.3301 -1795.1,1907.7734 -3088.381,1906.4911 -197.264,2.2737 -394.6,-14.3348 -589.094,-47.0667 z"
id="path970" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="128.00002"
height="127.99999"
viewBox="0 0 33.866671 33.866665"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><g
id="layer1"
transform="translate(-32.279167,-138.64166)"><g
style="fill:#e8eaed"
id="g1"
transform="matrix(0.00196863,0,0,0.00196863,33.463433,141.71593)"><g
id="g312"
transform="matrix(1.002973,0,0,0.93013448,-23.783992,460.24941)"><path
style="fill:#3366cc;fill-opacity:1;stroke-width:22.0314"
d="m 10822.775,14277.69 c -1195.5832,-143.508 -2220.2063,-841.148 -3268.3667,-1379.127 -718.8533,-355.301 -1431.1297,-838.976 -2251.4363,-896.085 -491.0057,-34.634 -993.9142,16.701 -1419.8658,282.946 -632.9097,323.689 -1041.4867,924.538 -1556.5718,1387.702 -543.283,324.938 -1024.416,-324.503 -999.9439,-831.854 -66.7515,-706.46 526.7775,-1215.106 971.4799,-1675.328 796.7042,-788.985 1908.9735,-1339.2985 3051.7899,-1198.3588 1348.2477,151.6528 2490.0786,954.1998 3671.9639,1551.7868 812.8473,430.368 1732.2728,937.309 2680.6698,656.999 865.182,-240.211 1345.317,-1049.536 1918.072,-1669.709 419.319,-369.53 999.56,99.027 1037.303,575.62 144.849,591.439 -228.81,1099.793 -584.858,1527.872 -674.991,878.758 -1646.404,1612.499 -2788.132,1678.492 -153.996,19.794 -308.698,6.257 -462.104,-10.956 z"
id="path3393" /><path
style="fill:#33b2cc;fill-opacity:1;stroke-width:26.9658"
d="M 10691.975,9232.2954 C 9345.1884,8998.8534 8209.7623,8175.6727 7013.3992,7567.553 6317.9006,7189.0731 5542.8247,6853.4818 4732.6528,6969.3648 3791.0852,7073.1473 3096.3863,7808.0894 2511.2819,8487.0616 2153.4997,8962.5979 1492.1857,8662.9151 1369.9995,8151.7319 1161.0166,7603.1278 1417.243,7018.9363 1804.1792,6620.924 2646.9373,5624.9864 3867.0135,4790.8306 5227.0245,4902.123 c 1538.2424,122.1926 2802.7979,1096.8978 4150.0464,1747.2218 689.4831,373.3537 1480.8271,695.5213 2275.6811,498.2159 843.443,-225.4675 1355.155,-986.6873 1874.463,-1625.928 303.839,-429.2412 935.004,-202.6984 1073.134,247.0946 229.503,525.6439 50.159,1127.049 -311.918,1545.1618 -731.881,1031.1947 -1847.889,1971.3437 -3180.435,1938.7364 -138.853,1.6775 -278.273,-1.5834 -416.021,-20.3301 z"
id="path1767" /><path
style="fill:#33cc99;fill-opacity:1;stroke-width:26.9658"
d="M 10576.744,4172.4544 C 8874.8637,3865.0547 7544.5841,2635.7395 5940.4635,2069.4233 5180.2383,1837.8115 4305.6379,1885.6382 3648.4341,2367.3105 c -522.046,315.7633 -870.3773,828.5536 -1307.695,1235.7633 -408.0241,287.6051 -882.8065,-100.3925 -976.5864,-516.0856 -207.011,-566.4578 84.2445,-1166.9024 491.559,-1564.2835 824.1797,-978.84256 2034.8379,-1776.07292 3363.7164,-1666.23087 1553.6459,128.553637 2838.511,1104.93638 4193.6746,1778.01277 748.5153,409.2855 1666.3233,736.8711 2500.3373,374.9559 742.862,-301.9425 1184.93,-1003.6082 1669.344,-1599.36398 390.513,-404.4477409 1011.637,22.50986 1078.781,494.20116 143.129,510.48212 -76.578,1022.79062 -407.346,1408.75032 -715.856,1002.3301 -1795.1,1907.7734 -3088.381,1906.4911 -197.264,2.2737 -394.6,-14.3348 -589.094,-47.0667 z"
id="path970" /></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,10 @@
<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<defs id="defs62">
<style type="text/css" id="current-color-scheme">.ColorScheme-Text { color: #232629; } </style>
</defs>
<g fill="currentColor" class="ColorScheme-Text">
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10822.775 14277.69c-1195.5832-143.508-2220.2063-841.148-3268.3667-1379.127-718.8533-355.301-1431.1297-838.976-2251.4363-896.085-491.0057-34.634-993.9142 16.701-1419.8658 282.946-632.9097 323.689-1041.4867 924.538-1556.5718 1387.702-543.283 324.938-1024.416-324.503-999.9439-831.854-66.7515-706.46 526.7775-1215.106 971.4799-1675.328 796.7042-788.985 1908.9735-1339.2985 3051.7899-1198.3588 1348.2477 151.6528 2490.0786 954.1998 3671.9639 1551.7868 812.8473 430.368 1732.2728 937.309 2680.6698 656.999 865.182-240.211 1345.317-1049.536 1918.072-1669.709 419.319-369.53 999.56 99.027 1037.303 575.62 144.849 591.439-228.81 1099.793-584.858 1527.872-674.991 878.758-1646.404 1612.499-2788.132 1678.492-153.996 19.794-308.698 6.257-462.104-10.956z"/>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10691.975 9232.2954c-1346.7866-233.442-2482.2127-1056.6227-3678.5758-1664.7424-695.4986-378.4799-1470.5745-714.0712-2280.7464-598.1882-941.5676 103.7825-1636.2665 838.7246-2221.3709 1517.6968-357.7822 475.5363-1019.0962 175.8535-1141.2824-335.3297-208.9829-548.6041 47.2435-1132.7956 434.1797-1530.8079 842.7581-995.9376 2062.8343-1830.0934 3422.8453-1718.801 1538.2424 122.1926 2802.7979 1096.8978 4150.0464 1747.2218 689.4831 373.3537 1480.8271 695.5213 2275.6811 498.2159 843.443-225.4675 1355.155-986.6873 1874.463-1625.928 303.839-429.2412 935.004-202.6984 1073.134 247.0946 229.503 525.6439 50.159 1127.049-311.918 1545.1618-731.881 1031.1947-1847.889 1971.3437-3180.435 1938.7364-138.853 1.6775-278.273-1.5834-416.021-20.3301z"/>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10576.744 4172.4544c-1701.8803-307.3997-3032.1599-1536.7149-4636.2805-2103.0311-760.2252-231.6118-1634.8256-183.7851-2292.0294 297.8872-522.046 315.7633-870.3773 828.5536-1307.695 1235.7633-408.0241 287.6051-882.8065-100.3925-976.5864-516.0856-207.011-566.4578 84.2445-1166.9024 491.559-1564.2835 824.1797-978.84256 2034.8379-1776.0729 3363.7164-1666.2309 1553.6459 128.55364 2838.511 1104.9364 4193.6746 1778.0128 748.5153 409.2855 1666.3233 736.8711 2500.3373 374.9559 742.862-301.9425 1184.93-1003.6082 1669.344-1599.364 390.513-404.44774 1011.637 22.50986 1078.781 494.20116 143.129 510.48212-76.578 1022.7906-407.346 1408.7503-715.856 1002.3301-1795.1 1907.7734-3088.381 1906.4911-197.264 2.2737-394.6-14.3348-589.094-47.0667z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,7 @@
<svg version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<g>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10822.775 14277.69c-1195.5832-143.508-2220.2063-841.148-3268.3667-1379.127-718.8533-355.301-1431.1297-838.976-2251.4363-896.085-491.0057-34.634-993.9142 16.701-1419.8658 282.946-632.9097 323.689-1041.4867 924.538-1556.5718 1387.702-543.283 324.938-1024.416-324.503-999.9439-831.854-66.7515-706.46 526.7775-1215.106 971.4799-1675.328 796.7042-788.985 1908.9735-1339.2985 3051.7899-1198.3588 1348.2477 151.6528 2490.0786 954.1998 3671.9639 1551.7868 812.8473 430.368 1732.2728 937.309 2680.6698 656.999 865.182-240.211 1345.317-1049.536 1918.072-1669.709 419.319-369.53 999.56 99.027 1037.303 575.62 144.849 591.439-228.81 1099.793-584.858 1527.872-674.991 878.758-1646.404 1612.499-2788.132 1678.492-153.996 19.794-308.698 6.257-462.104-10.956z" fill="#36c"/>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10691.975 9232.2954c-1346.7866-233.442-2482.2127-1056.6227-3678.5758-1664.7424-695.4986-378.4799-1470.5745-714.0712-2280.7464-598.1882-941.5676 103.7825-1636.2665 838.7246-2221.3709 1517.6968-357.7822 475.5363-1019.0962 175.8535-1141.2824-335.3297-208.9829-548.6041 47.2435-1132.7956 434.1797-1530.8079 842.7581-995.9376 2062.8343-1830.0934 3422.8453-1718.801 1538.2424 122.1926 2802.7979 1096.8978 4150.0464 1747.2218 689.4831 373.3537 1480.8271 695.5213 2275.6811 498.2159 843.443-225.4675 1355.155-986.6873 1874.463-1625.928 303.839-429.2412 935.004-202.6984 1073.134 247.0946 229.503 525.6439 50.159 1127.049-311.918 1545.1618-731.881 1031.1947-1847.889 1971.3437-3180.435 1938.7364-138.853 1.6775-278.273-1.5834-416.021-20.3301z" fill="#33b2cc"/>
<path transform="matrix(.0035792 0 0 .00332131 3.3640502 8.5103867)" d="m10576.744 4172.4544c-1701.8803-307.3997-3032.1599-1536.7149-4636.2805-2103.0311-760.2252-231.6118-1634.8256-183.7851-2292.0294 297.8872-522.046 315.7633-870.3773 828.5536-1307.695 1235.7633-408.0241 287.6051-882.8065-100.3925-976.5864-516.0856-207.011-566.4578 84.2445-1166.9024 491.559-1564.2835 824.1797-978.84256 2034.8379-1776.0729 3363.7164-1666.2309 1553.6459 128.55364 2838.511 1104.9364 4193.6746 1778.0128 748.5153 409.2855 1666.3233 736.8711 2500.3373 374.9559 742.862-301.9425 1184.93-1003.6082 1669.344-1599.364 390.513-404.44774 1011.637 22.50986 1078.781 494.20116 143.129 510.48212-76.578 1022.7906-407.346 1408.7503-715.856 1002.3301-1795.1 1907.7734-3088.381 1906.4911-197.264 2.2737-394.6-14.3348-589.094-47.0667z" fill="#3c9"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -7,12 +7,6 @@
// clang-format off
/* Define if you have the `inet_aton` function. */
#cmakedefine HAVE_INET_ATON @HAVE_INET_ATON@
/* Define if you have a POSIX `sigwait` function. */
#cmakedefine HAVE_POSIX_SIGWAIT @HAVE_POSIX_SIGWAIT@
/* Define to 1 if you have the <X11/extensions/Xrandr.h> header file. */
#cmakedefine HAVE_X11_EXTENSIONS_XRANDR_H @HAVE_X11_EXTENSIONS_XRANDR_H@

View File

@ -26,11 +26,6 @@ Arch::Arch()
s_instance = this;
}
Arch::Arch(Arch *arch)
{
s_instance = arch;
}
#if SYSAPI_WIN32
void Arch::init()
{

View File

@ -25,8 +25,6 @@
#pragma once
#include "common/Common.h"
#if SYSAPI_WIN32
#include "arch/win32/ArchDaemonWindows.h"
@ -36,7 +34,7 @@
#elif SYSAPI_UNIX
#include "arch/unix/ArchDaemonUnix.h"
#include "arch/ArchDaemonNone.h"
#include "arch/unix/ArchLogUnix.h"
#include "arch/unix/ArchMultithreadPosix.h"
#include "arch/unix/ArchNetworkBSD.h"
@ -63,7 +61,6 @@ class Arch : public ARCH_DAEMON, public ARCH_LOG, public ARCH_MULTITHREAD, publi
{
public:
Arch();
explicit Arch(Arch *arch);
~Arch() override = default;
#if SYSAPI_WIN32
@ -86,11 +83,6 @@ public:
*/
static Arch *getInstance();
static void setInstance(Arch *s)
{
s_instance = s;
}
/**
* @brief blocks calling thread for timout seconds
* @param timeout - blocking time in seconds. if < 0 not blocked if == 0 then caller yields the CPU

View File

@ -1,27 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "arch/ArchDaemonNone.h"
#include <QString>
//
// ArchDaemonNone
//
int ArchDaemonNone::daemonize(const QString &name, DaemonFunc const &func)
{
// simply forward the call to func. obviously, this doesn't
// do any daemonizing.
auto t = name.toStdString();
const char *n = t.c_str();
return func(1, &n);
}
QString ArchDaemonNone::commandLine() const
{
return {};
}

View File

@ -1,5 +1,6 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers.
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
@ -24,6 +25,14 @@ public:
~ArchDaemonNone() override = default;
// IArchDaemon overrides
int daemonize(const QString &name, DaemonFunc const &func) override;
QString commandLine() const override;
int daemonize(DaemonFunc const &func) override
{
// simply forward the call to func. obviously, this doesn't
// do any daemonizing.
return func();
}
QString commandLine() const override
{
return {};
}
};

View File

@ -22,8 +22,6 @@ if(WIN32)
elseif(UNIX)
set(PLATFORM_CODE
unix/ArchDaemonUnix.cpp
unix/ArchDaemonUnix.h
unix/ArchLogUnix.cpp
unix/ArchLogUnix.h
unix/ArchMultithreadPosix.cpp
@ -38,7 +36,6 @@ endif()
add_library(arch STATIC ${PLATFORM_CODE}
Arch.cpp
Arch.h
ArchDaemonNone.cpp
ArchDaemonNone.h
ArchException.h
IArchDaemon.h

View File

@ -20,7 +20,7 @@ implement this interface.
class IArchDaemon
{
public:
using DaemonFunc = std::function<int(int, const char **)>;
using DaemonFunc = std::function<int()>;
virtual ~IArchDaemon() = default;
//! @name manipulators
@ -53,7 +53,7 @@ public:
\c ArchMiscWindows::daemonFailed() to indicate startup failure.
</ul>
*/
virtual int daemonize(const QString &name, DaemonFunc const &func) = 0;
virtual int daemonize(DaemonFunc const &func) = 0;
//@}

View File

@ -37,15 +37,6 @@ public:
*/
virtual void closeLog() = 0;
//! Show the log
/*!
Causes the log to become visible. This generally only makes sense
for a log in a graphical user interface. Other implementations
will do nothing. Iff \p showIfEmpty is \c false then the implementation
may optionally only show the log if it's not empty.
*/
virtual void showLog(bool showIfEmpty) = 0;
//! Write to the log
/*!
Writes the given string to the log with the given level.

View File

@ -1,115 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "arch/unix/ArchDaemonUnix.h"
#include "arch/ArchException.h"
#include "arch/unix/XArchUnix.h"
#include "base/Log.h"
#include <cstdlib>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <QString>
//
// ArchDaemonUnix
//
#ifdef __APPLE__
// In Mac OS X, fork()'d child processes can't use most APIs (the frameworks
// that Deskflow uses in fact prevent it and make the process just up and die),
// so need to exec a copy of the program that doesn't fork so isn't limited.
int execSelfNonDaemonized()
{
extern char **NXArgv;
char **selfArgv = NXArgv;
setenv("_DESKFLOW_DAEMONIZED", "", 1);
execvp(selfArgv[0], selfArgv);
return 0;
}
bool alreadyDaemonized()
{
return std::getenv("_DESKFLOW_DAEMONIZED") != nullptr;
}
#endif
int ArchDaemonUnix::daemonize(const QString &name, DaemonFunc const &func)
{
#ifdef __APPLE__
if (alreadyDaemonized()) {
auto t = name.toStdString();
const char *n = t.c_str();
return func(1, &n);
}
#endif
// fork so shell thinks we're done and so we're not a process
// group leader
switch (fork()) {
case -1:
// failed
throw ArchDaemonFailedException(errorToString(errno));
case 0:
// child
break;
default:
// parent exits
exit(0);
}
// become leader of a new session
setsid();
#ifndef __APPLE__
// NB: don't run chdir on apple; causes strange behaviour.
// chdir to root so we don't keep mounted filesystems points busy
// TODO: this is a bit of a hack - can we find a better solution?
if (int chdirErr = chdir("/"); chdirErr)
// NB: file logging actually isn't working at this point!
LOG_ERR("chdir error: %i", chdirErr);
#endif
// mask off permissions for any but owner
umask(077);
// close open files. we only expect stdin, stdout, stderr to be open.
close(0);
close(1);
close(2);
// attach file descriptors 0, 1, 2 to /dev/null so inadvertent use
// of standard I/O safely goes in the bit bucket.
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
if (int dupErr = dup(1); dupErr < 0) {
// NB: file logging actually isn't working at this point!
LOG_ERR("dup error: %i", dupErr);
}
#ifdef __APPLE__
return execSelfNonDaemonized();
#endif
// invoke function
auto t = name.toStdString();
const char *n = t.c_str();
return func(1, &n);
}

View File

@ -1,24 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "arch/ArchDaemonNone.h"
#undef ARCH_DAEMON
#define ARCH_DAEMON ArchDaemonUnix
//! Unix implementation of IArchDaemon
class ArchDaemonUnix : public ArchDaemonNone
{
public:
ArchDaemonUnix() = default;
~ArchDaemonUnix() override = default;
// IArchDaemon overrides
int daemonize(const QString &name, DaemonFunc const &func) override;
};

View File

@ -23,11 +23,6 @@ void ArchLogUnix::closeLog()
closelog();
}
void ArchLogUnix::showLog(bool)
{
// do nothing
}
void ArchLogUnix::writeLog(LogLevel level, const QString &msg)
{
// convert level

View File

@ -21,6 +21,5 @@ public:
// IArchLog overrides
void openLog(const QString &name) override;
void closeLog() override;
void showLog(bool) override;
void writeLog(LogLevel, const QString &) override;
};

View File

@ -672,12 +672,8 @@ void *ArchMultithreadPosix::threadSignalHandler(void *)
// we exit the loop via thread cancellation in sigwait()
for (;;) {
// wait
#if HAVE_POSIX_SIGWAIT
int signal = 0;
sigwait(&sigset, &signal);
#else
sigwait(&sigset);
#endif
// if we get here then the signal was raised
switch (signal) {

View File

@ -11,7 +11,6 @@
#include "arch/ArchException.h"
#include "arch/unix/ArchMultithreadPosix.h"
#include "arch/unix/XArchUnix.h"
#include "common/Common.h"
#include <arpa/inet.h>
#include <cstring>
@ -25,10 +24,6 @@
#include <netinet/tcp.h>
#endif
#if !HAVE_INET_ATON
#include <stdio.h>
#endif
static const int s_family[] = {
PF_UNSPEC,
PF_INET,
@ -37,27 +32,6 @@ static const int s_family[] = {
static const int s_type[] = {SOCK_DGRAM, SOCK_STREAM};
#if !HAVE_INET_ATON
// parse dotted quad addresses. we don't bother with the weird BSD'ism
// of handling octal and hex and partial forms.
static in_addr_t inet_aton(const char *cp, struct in_addr *inp)
{
unsigned int a, b, c, d;
if (sscanf(cp, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) {
return 0;
}
if (a >= 256 || b >= 256 || c >= 256 || d >= 256) {
return 0;
}
unsigned char *incp = (unsigned char *)inp;
incp[0] = (unsigned char)(a & 0xffu);
incp[1] = (unsigned char)(b & 0xffu);
incp[2] = (unsigned char)(c & 0xffu);
incp[3] = (unsigned char)(d & 0xffu);
return inp->s_addr;
}
#endif
//
// ArchNetworkBSD::Deps
//
@ -485,6 +459,7 @@ ArchNetAddress ArchNetworkBSD::newAnyAddr(AddressFamily family)
}
default:
delete addr;
addr = nullptr;
assert(0 && "invalid family");
}

View File

@ -12,6 +12,7 @@
#include "arch/win32/ArchMiscWindows.h"
#include "arch/win32/XArchWindows.h"
#include "base/Log.h"
#include "common/Constants.h"
//
// ArchDaemonWindows
@ -52,9 +53,8 @@ void ArchDaemonWindows::daemonFailed(int result)
throw ArchDaemonRunException(result);
}
int ArchDaemonWindows::daemonize(const QString &name, DaemonFunc const &func)
int ArchDaemonWindows::daemonize(DaemonFunc const &func)
{
assert(name != nullptr);
assert(func != nullptr);
// save daemon function
@ -62,7 +62,7 @@ int ArchDaemonWindows::daemonize(const QString &name, DaemonFunc const &func)
// construct the service entry
SERVICE_TABLE_ENTRY entry[2];
entry[0].lpServiceName = const_cast<wchar_t *>(name.toStdWString().c_str());
entry[0].lpServiceName = const_cast<wchar_t *>(QString(kAppName).toStdWString().c_str());
entry[0].lpServiceProc = &ArchDaemonWindows::serviceMainEntry;
entry[1].lpServiceName = nullptr;
entry[1].lpServiceProc = nullptr;
@ -297,7 +297,7 @@ void ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR *argvIn)
try {
// invoke daemon function
m_daemonResult = m_daemonFunc(static_cast<int>(argc), reinterpret_cast<const char **>(argv));
m_daemonResult = m_daemonFunc();
} catch (ArchDaemonRunException &e) {
setStatusError(e.m_result);
m_daemonResult = -1;

View File

@ -68,7 +68,7 @@ public:
static UINT getDaemonQuitMessage();
// IArchDaemon overrides
int daemonize(const QString &name, DaemonFunc const &func) override;
int daemonize(DaemonFunc const &func) override;
QString commandLine() const override
{
return m_commandLine;

View File

@ -33,11 +33,6 @@ void ArchLogWindows::closeLog()
}
}
void ArchLogWindows::showLog(bool)
{
// do nothing
}
void ArchLogWindows::writeLog(LogLevel level, const QString &msg)
{
if (m_eventLog != nullptr) {

View File

@ -24,7 +24,6 @@ public:
// IArchLog overrides
void openLog(const QString &name) override;
void closeLog() override;
void showLog(bool showIfEmpty) override;
void writeLog(LogLevel, const QString &) override;
private:

View File

@ -11,6 +11,7 @@
#include "arch/IArchMultithread.h"
#include "arch/win32/ArchMultithreadWindows.h"
#include "arch/win32/XArchWindows.h"
#include "base/Log.h"
#include <malloc.h>
@ -284,7 +285,7 @@ void ArchNetworkWinsock::bindSocket(ArchSocket s, ArchNetAddress addr)
assert(s != nullptr);
assert(addr != nullptr);
if (bind_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == SOCKET_ERROR) {
if (bind_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr), addr->m_len) != ERROR_SUCCESS) {
throwError(getsockerror_winsock());
}
}
@ -294,7 +295,7 @@ void ArchNetworkWinsock::listenOnSocket(ArchSocket s)
assert(s != nullptr);
// hardcoding backlog
if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) {
if (listen_winsock(s->m_socket, 3) != ERROR_SUCCESS) {
throwError(getsockerror_winsock());
}
}
@ -606,23 +607,8 @@ bool ArchNetworkWinsock::setNoDelayOnSocket(ArchSocket s, bool noDelay)
bool ArchNetworkWinsock::setReuseAddrOnSocket(ArchSocket s, bool reuse)
{
assert(s != nullptr);
// get old state
BOOL oflag;
int size = sizeof(oflag);
if (getsockopt_winsock(s->m_socket, SOL_SOCKET, SO_REUSEADDR, &oflag, &size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
// set new state
BOOL flag = reuse ? 1 : 0;
size = sizeof(flag);
if (setsockopt_winsock(s->m_socket, SOL_SOCKET, SO_REUSEADDR, &flag, size) == SOCKET_ERROR) {
throwError(getsockerror_winsock());
}
return (oflag != 0);
LOG_ERR("socket re-use not supported on windows");
return false;
}
ArchNetAddress ArchNetworkWinsock::newAnyAddr(AddressFamily family)

View File

@ -7,24 +7,30 @@
*/
#include "arch/win32/XArchWindows.h"
#include "base/String.h"
std::string windowsErrorToString(DWORD error)
#include <QString>
QString windowsErrorToQString(DWORD error)
{
char *cmsg;
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, 0, error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&cmsg, 0, nullptr
) == 0) {
cmsg = nullptr;
return deskflow::string::sprintf("Unknown error, code %d", error);
LPWSTR buffer = nullptr; // Using FORMAT_MESSAGE_ALLOCATE_BUFFER
DWORD size = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPWSTR>(&buffer), 0, nullptr
);
if (size == 0) {
return QString("Unknown Windows error: %1").arg(error);
}
std::string smsg(cmsg);
LocalFree(cmsg);
return smsg;
QString message = QString::fromWCharArray(buffer, int(size));
// Gotcha: Was allocated by FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER)
LocalFree(buffer);
return QString("[%1] %2").arg(error).arg(message.trimmed());
}
std::string winsockErrorToString(int error)
QString winsockErrorToQString(int error)
{
// built-in windows function for looking up error message strings
// may not look up network error messages correctly. we'll have
@ -197,8 +203,19 @@ std::string winsockErrorToString(int error)
for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) {
if (s_netErrorCodes[i].m_code == error) {
return s_netErrorCodes[i].m_msg;
return QString("[%1] %2").arg(error).arg(s_netErrorCodes[i].m_msg);
}
}
return "Unknown error";
return QString("Unknown Winsock error: %1").arg(error);
}
std::string windowsErrorToString(DWORD error)
{
return windowsErrorToQString(error).toStdString();
}
std::string winsockErrorToString(int error)
{
return winsockErrorToQString(error).toStdString();
}

View File

@ -8,10 +8,13 @@
#pragma once
#include <QString>
#include <string>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
QString winsockErrorToQString(int error);
QString windowsErrorToQString(DWORD error);
std::string winsockErrorToString(int error);
std::string windowsErrorToString(DWORD error);

View File

@ -7,10 +7,10 @@ add_library(base STATIC
BaseException.cpp
BaseException.h
DirectionTypes.h
Event.cpp
Event.h
EventQueue.cpp
EventQueue.h
EventQueueTimer.h
EventTypes.h
FinalAction.h
FunctionEventJob.cpp

View File

@ -1,81 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2004 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "base/Event.h"
#include <assert.h>
#include <cstdlib>
//
// Event
//
Event::Event(EventTypes type, void *target, void *data, Flags flags)
: m_type(type),
m_target(target),
m_data(data),
m_flags(flags)
{
// do nothing
}
Event::Event(EventTypes type, void *target, EventData *dataObject)
: m_type(type),
m_target(target),
m_dataObject(dataObject)
{
// do nothing
}
EventTypes Event::getType() const
{
return m_type;
}
void *Event::getTarget() const
{
return m_target;
}
void *Event::getData() const
{
return m_data;
}
EventData *Event::getDataObject() const
{
return m_dataObject;
}
Event::Flags Event::getFlags() const
{
return m_flags;
}
void Event::deleteData(const Event &event)
{
switch (event.getType()) {
using enum EventTypes;
case Unknown:
case Quit:
case System:
case Timer:
break;
default:
if ((event.getFlags() & EventFlags::DontFreeData) == 0) {
free(event.getData());
delete event.getDataObject();
}
break;
}
}
void Event::setDataObject(EventData *dataObject)
{
assert(m_dataObject == nullptr);
m_dataObject = dataObject;
}

View File

@ -9,6 +9,9 @@
#include "EventTypes.h"
#include <assert.h>
#include <cstdlib>
using deskflow::EventTypes;
class EventData
@ -20,7 +23,7 @@ public:
//! Event
/*!
A \c Event holds an event type and a pointer to event data.
\c Event holds an event type and a pointer to event data. It is movable, but not copyable
*/
class Event
{
@ -34,6 +37,10 @@ public:
};
Event() = default;
Event(const Event &) = delete;
Event(Event &&other) = default;
Event &operator=(const Event &) = delete;
Event &operator=(Event &&) = default;
//! Create \c Event with data (POD)
/*!
@ -44,15 +51,19 @@ public:
\p target is the intended recipient of the event.
\p flags is any combination of \c Flags.
*/
explicit Event(EventTypes type, void *target = nullptr, void *data = nullptr, Flags flags = EventFlags::NoFlags);
explicit Event(EventTypes type, void *target = nullptr, void *data = nullptr, Flags flags = EventFlags::NoFlags)
: m_type(type),
m_target(target),
m_data(data),
m_flags(flags)
{
// do nothing
}
//! Create \c Event with non-POD data
/*!
\p type of the event
\p target is the intended recipient of the event.
\p dataObject with event data
*/
explicit Event(EventTypes type, void *target, EventData *dataObject);
Event(EventTypes type, void *target, EventData *dataObject) : m_type(type), m_target(target), m_dataObject(dataObject)
{
// do nothing
}
//! @name manipulators
//@{
@ -61,14 +72,35 @@ public:
/*!
Deletes event data for the given event (using free()).
*/
static void deleteData(const Event &);
static void deleteData(const Event &event)
{
switch (event.getType()) {
using enum EventTypes;
case Unknown:
case Quit:
case System:
case Timer:
break;
default:
if ((event.getFlags() & EventFlags::DontFreeData) == 0) {
free(event.getData());
delete event.getDataObject();
}
break;
}
}
//! Set data (non-POD)
/*!
Set non-POD (non plain old data), where delete is called when the event
is deleted, and the destructor is called.
*/
void setDataObject(EventData *dataObject);
void setDataObject(EventData *dataObject)
{
assert(m_dataObject == nullptr);
m_dataObject = dataObject;
};
//@}
//! @name accessors
@ -78,19 +110,28 @@ public:
/*!
Returns the event type.
*/
EventTypes getType() const;
EventTypes getType() const
{
return m_type;
}
//! Get the event target
/*!
Returns the event target.
*/
void *getTarget() const;
void *getTarget() const
{
return m_target;
}
//! Get the event data (POD).
/*!
Returns the event data (POD).
*/
void *getData() const;
void *getData() const
{
return m_data;
}
//! Get the event data (non-POD)
/*!
@ -98,13 +139,19 @@ public:
\c getData() is that when delete is called on this data, so non-POD
(non plain old data) dtor is called.
*/
EventData *getDataObject() const;
EventData *getDataObject() const
{
return m_dataObject;
}
//! Get event flags
/*!
Returns the event flags.
*/
Flags getFlags() const;
Flags getFlags() const
{
return m_flags;
}
//@}

View File

@ -54,8 +54,8 @@ void EventQueue::loop()
LOG_DEBUG("event queue is ready");
while (!m_pending.empty()) {
LOG_DEBUG("add pending events to buffer");
const Event &event = m_pending.front();
addEventToBuffer(event);
Event &event = m_pending.front();
addEventToBuffer(std::move(event));
m_pending.pop();
}
@ -171,7 +171,7 @@ bool EventQueue::dispatchEvent(const Event &event)
return false;
}
void EventQueue::addEvent(const Event &event)
void EventQueue::addEvent(Event &&event)
{
// discard bogus event types
switch (event.getType()) {
@ -188,24 +188,24 @@ void EventQueue::addEvent(const Event &event)
dispatchEvent(event);
Event::deleteData(event);
} else if (!(*m_readyCondVar)) {
m_pending.push(event);
m_pending.push(std::move(event));
} else {
addEventToBuffer(event);
addEventToBuffer(std::move(event));
}
}
void EventQueue::addEventToBuffer(const Event &event)
void EventQueue::addEventToBuffer(Event &&event)
{
std::scoped_lock lock{m_mutex};
// store the event's data locally
auto eventID = saveEvent(event);
auto eventID = saveEvent(std::move(event));
// add it
if (!m_buffer->addEvent(eventID)) {
// failed to send event
removeEvent(eventID);
Event::deleteData(event);
auto removedEvent = removeEvent(eventID);
Event::deleteData(removedEvent);
}
}
@ -270,10 +270,12 @@ void EventQueue::removeHandler(EventTypes type, void *target)
HandlerTable::iterator index = m_handlers.find(target);
if (index != m_handlers.end()) {
TypeHandlerTable &typeHandlers = index->second;
TypeHandlerTable::iterator index2 = typeHandlers.find(type);
if (index2 != typeHandlers.end()) {
if (auto index2 = typeHandlers.find(type); index2 != typeHandlers.end()) {
typeHandlers.erase(index2);
}
if (typeHandlers.empty()) {
m_handlers.erase(index);
}
}
}
@ -282,15 +284,10 @@ void EventQueue::removeHandlers(void *target)
std::scoped_lock lock{m_mutex};
HandlerTable::iterator index = m_handlers.find(target);
if (index != m_handlers.end()) {
index->second.clear();
m_handlers.erase(index);
}
}
bool EventQueue::isEmpty() const
{
return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0);
}
const EventQueue::EventHandler *EventQueue::getHandler(EventTypes type, void *target) const
{
std::scoped_lock lock{m_mutex};
@ -304,7 +301,7 @@ const EventQueue::EventHandler *EventQueue::getHandler(EventTypes type, void *ta
return nullptr;
}
uint32_t EventQueue::saveEvent(const Event &event)
uint32_t EventQueue::saveEvent(Event &&event)
{
// choose id
uint32_t id;
@ -318,7 +315,7 @@ uint32_t EventQueue::saveEvent(const Event &event)
}
// save data
m_events[id] = event;
m_events[id] = std::move(event);
return id;
}
@ -331,7 +328,7 @@ Event EventQueue::removeEvent(uint32_t eventID)
}
// get data
Event event = index->second;
Event event = std::move(index->second);
m_events.erase(index);
// save old id for reuse

View File

@ -8,7 +8,6 @@
#pragma once
#include "base/EventTypes.h"
#include "base/IEventQueue.h"
#include "base/PriorityQueue.h"
#include "base/Stopwatch.h"
@ -40,24 +39,23 @@ public:
void adoptBuffer(IEventQueueBuffer *) override;
bool getEvent(Event &event, double timeout = -1.0) override;
bool dispatchEvent(const Event &event) override;
void addEvent(const Event &event) override;
void addEvent(Event &&event) override;
EventQueueTimer *newTimer(double duration, void *target) override;
EventQueueTimer *newOneShotTimer(double duration, void *target) override;
void deleteTimer(EventQueueTimer *) override;
void addHandler(EventTypes type, void *target, const EventHandler &handler) override;
void removeHandler(EventTypes type, void *target) override;
void removeHandlers(void *target) override;
bool isEmpty() const override;
void *getSystemTarget() override;
void waitForReady() const override;
private:
const EventHandler *getHandler(EventTypes type, void *target) const;
uint32_t saveEvent(const Event &event);
uint32_t saveEvent(Event &&event);
Event removeEvent(uint32_t eventID);
bool hasTimerExpired(Event &event);
double getNextTimerTimeout() const;
void addEventToBuffer(const Event &event);
void addEventToBuffer(Event &&event);
//!
//! \brief processEvent Internal event proccessing

View File

@ -0,0 +1,15 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2023 Inputleap Contributors
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
class EventQueueTimer
{
public:
virtual ~EventQueueTimer() = default;
};

View File

@ -86,9 +86,6 @@ enum class EventTypes : uint32_t
*/
SocketDisconnected,
/// This is sent when the client doesn't want to reconnect after it disconnects from the server.
SocketStopRetry,
OsxScreenConfirmSleep,
/// This event is sent whenever a server accepts a client.

View File

@ -7,6 +7,8 @@
#pragma once
#include <utility>
namespace deskflow {
/**

View File

@ -9,10 +9,8 @@
#pragma once
#include "base/Event.h"
#include "base/EventTypes.h"
#include <functional>
#include <string>
class IEventQueueBuffer;
@ -78,7 +76,7 @@ public:
/*!
Adds \p event to the end of the queue.
*/
virtual void addEvent(const Event &event) = 0;
virtual void addEvent(Event &&event) = 0;
//! Create a recurring timer
/*!
@ -152,13 +150,6 @@ public:
//! @name accessors
//@{
//! Test if queue is empty
/*!
Returns true iff the queue has no events in it, including timer
events.
*/
virtual bool isEmpty() const = 0;
//! Get the system event type target
/*!
Returns the target to use for dispatching \c EventTypes::System events.

View File

@ -39,15 +39,6 @@ public:
*/
virtual void close() = 0;
//! Show the outputter
/*!
Causes the output to become visible. This generally only makes sense
for a logger in a graphical user interface. Other implementations
will do nothing. Iff \p showIfEmpty is \c false then the implementation
may optionally only show the log if it's not empty.
*/
virtual void show(bool showIfEmpty) = 0;
//! Write a message with level
/*!
Writes \c message, which has the given \c level, to a log.

View File

@ -6,22 +6,21 @@
*/
#include "base/Log.h"
#include "arch/Arch.h"
#include "base/LogLevel.h"
#include "base/LogOutputters.h"
#include "common/Common.h"
#include "common/Constants.h"
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <ctime>
#ifndef __APPLE__
#if HAVE_FORMAT
#include <format>
#endif
#include <QDateTime>
const int kPriorityPrefixLength = 3;
// names of priorities
@ -58,48 +57,16 @@ LogLevel getPriority(const char *&fmt)
return static_cast<LogLevel>(fmt[2] - '0');
}
void makeTimeString(std::vector<char> &buffer)
{
const int yearOffset = 1900;
const int monthOffset = 1;
time_t t;
time(&t);
struct tm tm;
#if WINAPI_MSWINDOWS
localtime_s(&tm, &t);
#else
localtime_r(&t, &tm);
#endif
#ifndef __APPLE__
std::format_to_n(
buffer.data(), buffer.size(), "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}", tm.tm_year + yearOffset,
tm.tm_mon + monthOffset, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec
);
#else
snprintf(
buffer.data(), buffer.size(), "%04i-%02i-%02iT%02i:%02i:%02i", tm.tm_year + yearOffset, tm.tm_mon + monthOffset,
tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec
);
#endif
}
std::vector<char> makeMessage(const char *filename, int lineNumber, const char *message, LogLevel priority)
{
// base size includes null terminator, colon, space, etc.
const int baseSize = 10;
const int timeBufferSize = 50;
const int priorityMaxSize = 10;
const auto currentPriority = static_cast<int>(priority);
std::vector<char> timeBuffer(timeBufferSize);
makeTimeString(timeBuffer);
size_t timestampLength = strnlen(timeBuffer.data(), timeBufferSize);
auto timeStr = QDateTime::currentDateTime().toString(Qt::ISODateWithMs).toStdString();
auto sectionName = "IPC";
if (priority != LogLevel::IPC) {
@ -108,7 +75,7 @@ std::vector<char> makeMessage(const char *filename, int lineNumber, const char *
size_t priorityLength = strnlen(sectionName, priorityMaxSize);
size_t messageLength = strnlen(message, SIZE_MAX);
size_t bufferSize = baseSize + timestampLength + priorityLength + messageLength;
size_t bufferSize = baseSize + timeStr.length() + priorityLength + messageLength;
const auto filenameSet = filename != nullptr && filename[0] != '\0';
if (filenameSet) {
@ -117,22 +84,22 @@ std::vector<char> makeMessage(const char *filename, int lineNumber, const char *
bufferSize += filenameLength + lineNumberLength;
std::vector<char> buffer(bufferSize);
#ifndef __APPLE__
#if HAVE_FORMAT
std::format_to_n(
buffer.data(), bufferSize, "[{}] {}: {}\n\t{}:{}", timeBuffer.data(), sectionName, message, filename, lineNumber
buffer.data(), bufferSize, "[{}] {}: {}\n\t{}:{}", timeStr.c_str(), sectionName, message, filename, lineNumber
);
#else
snprintf(
buffer.data(), bufferSize, "[%s] %s: %s\n\t%s:%d", timeBuffer.data(), sectionName, message, filename, lineNumber
buffer.data(), bufferSize, "[%s] %s: %s\n\t%s:%d", timeStr.c_str(), sectionName, message, filename, lineNumber
);
#endif
return buffer;
} else {
std::vector<char> buffer(bufferSize);
#ifndef __APPLE__
std::format_to_n(buffer.data(), bufferSize, "[{}] {}: {}", timeBuffer.data(), sectionName, message);
#if HAVE_FORMAT
std::format_to_n(buffer.data(), bufferSize, "[{}] {}: {}", timeStr.c_str(), sectionName, message);
#else
snprintf(buffer.data(), bufferSize, "[%s] %s: %s", timeBuffer.data(), sectionName, message);
snprintf(buffer.data(), bufferSize, "[%s] %s: %s", timeStr.c_str(), sectionName, message);
#endif
return buffer;
}
@ -264,18 +231,19 @@ void Log::pop_front(bool alwaysAtHead)
}
}
bool Log::setFilter(const char *maxPriority)
bool Log::setFilter(const QString &maxPriority)
{
if (maxPriority != nullptr) {
for (int i = 0; i < g_numPriority; ++i) {
if (strcmp(maxPriority, g_priority[i]) == 0) {
setFilter(static_cast<LogLevel>(i));
return true;
}
}
if (maxPriority.isEmpty()) {
return false;
}
return true;
for (int i = 0; i < g_numPriority; ++i) {
if (maxPriority == QString(g_priority[i])) {
setFilter(static_cast<LogLevel>(i));
return true;
}
}
return false;
}
void Log::setFilter(LogLevel maxPriority)

View File

@ -7,11 +7,13 @@
#pragma once
#include "arch/Arch.h"
#include "arch/IArchMultithread.h"
#include "LogLevel.h"
#include <list>
#include <mutex>
#include <QString>
#define CLOG (Log::getInstance())
#define BYE "\nTry `%s --help' for more information."
@ -83,7 +85,7 @@ public:
true if the priority \c name was recognized; if \c name is nullptr
then it simply returns true.
*/
bool setFilter(const char *name);
bool setFilter(const QString &name);
//! Set the minimum priority filter (by ordinal).
void setFilter(LogLevel);

View File

@ -31,11 +31,6 @@ void StopLogOutputter::close()
// do nothing
}
void StopLogOutputter::show(bool)
{
// do nothing
}
bool StopLogOutputter::write(LogLevel, const QString &)
{
return false;
@ -55,11 +50,6 @@ void ConsoleLogOutputter::close()
// do nothing
}
void ConsoleLogOutputter::show(bool showIfEmpty)
{
// do nothing
}
bool ConsoleLogOutputter::write(LogLevel level, const QString &msg)
{
if ((level >= LogLevel::Fatal) && (level <= LogLevel::Warning))
@ -89,11 +79,6 @@ void SystemLogOutputter::close()
ARCH->closeLog();
}
void SystemLogOutputter::show(bool showIfEmpty)
{
ARCH->showLog(showIfEmpty);
}
bool SystemLogOutputter::write(LogLevel level, const QString &msg)
{
ARCH->writeLog(level, msg);
@ -141,7 +126,7 @@ void FileLogOutputter::setLogFilename(const QString &logFile)
m_fileName = logFile;
}
bool FileLogOutputter::write(LogLevel level, const QString &message)
bool FileLogOutputter::write(LogLevel, const QString &message)
{
QFile file(m_fileName);
if (!file.open(QFile::WriteOnly | QFile::Append))
@ -153,7 +138,7 @@ bool FileLogOutputter::write(LogLevel level, const QString &message)
if (file.size() > s_logFileSizeLimit) {
const auto oldFile = QStringLiteral("%1.1").arg(m_fileName);
QFile::remove(m_fileName);
file.rename(m_fileName, oldFile);
QFile::rename(m_fileName, oldFile);
}
return true;
@ -168,8 +153,3 @@ void FileLogOutputter::close()
{
// do nothing
}
void FileLogOutputter::show(bool showIfEmpty)
{
// do nothing
}

View File

@ -9,7 +9,6 @@
#pragma once
#include "base/ILogOutputter.h"
#include "mt/Thread.h"
#include <QString>
//! Stop traversing log chain outputter
@ -27,7 +26,6 @@ public:
// ILogOutputter overrides
void open(const QString &title) override;
void close() override;
void show(bool showIfEmpty) override;
bool write(LogLevel level, const QString &message) override;
};
@ -45,7 +43,6 @@ public:
// ILogOutputter overrides
void open(const QString &title) override;
void close() override;
void show(bool showIfEmpty) override;
bool write(LogLevel level, const QString &message) override;
void flush() const;
};
@ -65,7 +62,6 @@ public:
// ILogOutputter overrides
void open(const QString &title) override;
void close() override;
void show(bool showIfEmpty) override;
bool write(LogLevel level, const QString &message) override;
void setLogFilename(const QString &title);
@ -87,7 +83,6 @@ public:
// ILogOutputter overrides
void open(const QString &title) override;
void close() override;
void show(bool showIfEmpty) override;
bool write(LogLevel level, const QString &message) override;
};

View File

@ -6,7 +6,6 @@
#pragma once
#include "common/Common.h"
#include <string>
namespace deskflow::filesystem {

View File

@ -126,5 +126,5 @@ public:
private:
Container c;
Compare comp;
[[no_unique_address]] Compare comp;
};

View File

@ -17,7 +17,7 @@ template <class T> class TMethodJob : public IJob
{
public:
//! run() invokes \c object->method(arg)
TMethodJob(T *object, void (T::*method)(void *), void *arg = nullptr);
TMethodJob(T *object, void (T::*method)(const void *), void *arg = nullptr);
~TMethodJob() override = default;
// IJob overrides
@ -25,12 +25,12 @@ public:
private:
T *m_object;
void (T::*m_method)(void *);
void (T::*m_method)(const void *);
void *m_arg;
};
template <class T>
inline TMethodJob<T>::TMethodJob(T *object, void (T::*method)(void *), void *arg)
inline TMethodJob<T>::TMethodJob(T *object, void (T::*method)(const void *), void *arg)
: m_object(object),
m_method(method),
m_arg(arg)

View File

@ -7,6 +7,8 @@
#include "base/Unicode.h"
#include <assert.h>
//
// local utility functions
//

View File

@ -7,8 +7,7 @@
#pragma once
#include "common/Common.h"
#include <cstdint>
#include <string>
//! Unicode utility functions

View File

@ -11,30 +11,23 @@
#include "arch/Arch.h"
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "base/TMethodJob.h"
#include "client/ServerProxy.h"
#include "deskflow/AppUtil.h"
#include "common/Settings.h"
#include "deskflow/Clipboard.h"
#include "deskflow/DeskflowException.h"
#include "deskflow/IPlatformScreen.h"
#include "deskflow/PacketStreamFilter.h"
#include "deskflow/ProtocolTypes.h"
#include "deskflow/ProtocolUtil.h"
#include "deskflow/Screen.h"
#include "deskflow/StreamChunker.h"
#include "mt/Thread.h"
#include "net/IDataSocket.h"
#include "net/ISocketFactory.h"
#include "net/SecureSocket.h"
#include "net/TCPSocket.h"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iterator>
#include <memory>
#include <sstream>
#include <stdexcept>
using namespace deskflow::client;
@ -44,15 +37,14 @@ using namespace deskflow::client;
Client::Client(
IEventQueue *events, const std::string &name, const NetworkAddress &address, ISocketFactory *socketFactory,
deskflow::Screen *screen, deskflow::ClientArgs const &args
deskflow::Screen *screen
)
: m_name(name),
m_serverAddress(address),
m_socketFactory(socketFactory),
m_screen(screen),
m_events(events),
m_useSecureNetwork(args.m_enableCrypto),
m_args(args)
m_useSecureNetwork(Settings::value(Settings::Security::TlsEnabled).toBool())
{
assert(m_socketFactory != nullptr);
assert(m_screen != nullptr);
@ -145,7 +137,7 @@ void Client::disconnect(const char *msg)
if (msg) {
sendConnectionFailedEvent(msg);
} else {
sendEvent(EventTypes::ClientDisconnected, nullptr);
sendEvent(EventTypes::ClientDisconnected);
}
}
@ -157,7 +149,7 @@ void Client::refuseConnection(const char *msg)
auto info = new FailInfo(msg);
info->m_retry = true;
Event event(EventTypes::ClientConnectionRefused, getEventTarget(), info, Event::EventFlags::DontFreeData);
m_events->addEvent(event);
m_events->addEvent(std::move(event));
}
}
@ -165,7 +157,7 @@ void Client::handshakeComplete()
{
m_ready = true;
m_screen->enable();
sendEvent(EventTypes::ClientConnected, nullptr);
sendEvent(EventTypes::ClientConnected);
}
bool Client::isConnected() const
@ -370,9 +362,9 @@ void Client::sendClipboard(ClipboardID id)
}
}
void Client::sendEvent(EventTypes type, void *data)
void Client::sendEvent(EventTypes type)
{
m_events->addEvent(Event(type, getEventTarget(), data));
m_events->addEvent(Event(type, getEventTarget()));
}
void Client::sendConnectionFailedEvent(const char *msg)
@ -380,14 +372,14 @@ void Client::sendConnectionFailedEvent(const char *msg)
auto *info = new FailInfo(msg);
info->m_retry = true;
Event event(EventTypes::ClientConnectionFailed, getEventTarget(), info, Event::EventFlags::DontFreeData);
m_events->addEvent(event);
m_events->addEvent(std::move(event));
}
void Client::setupConnecting()
{
assert(m_stream != nullptr);
if (m_args.m_enableCrypto) {
if (Settings::value(Settings::Security::TlsEnabled).toBool()) {
m_events->addHandler(EventTypes::DataSocketSecureConnected, m_stream->getEventTarget(), [this](const auto &) {
handleConnected();
});
@ -420,9 +412,6 @@ void Client::setupConnection()
m_events->addHandler(EventTypes::StreamOutputShutdown, m_stream->getEventTarget(), [this](const auto &) {
handleDisconnected();
});
m_events->addHandler(EventTypes::SocketStopRetry, m_stream->getEventTarget(), [this](const auto &) {
m_args.m_restartable = false;
});
}
void Client::setupScreen()
@ -472,7 +461,6 @@ void Client::cleanupConnection()
m_events->removeHandler(StreamInputShutdown, m_stream->getEventTarget());
m_events->removeHandler(StreamOutputShutdown, m_stream->getEventTarget());
m_events->removeHandler(SocketDisconnected, m_stream->getEventTarget());
m_events->removeHandler(SocketStopRetry, m_stream->getEventTarget());
cleanupStream();
}
}
@ -548,7 +536,7 @@ void Client::handleOutputError()
cleanupScreen();
cleanupConnection();
LOG_WARN("error sending to server");
sendEvent(EventTypes::ClientDisconnected, nullptr);
sendEvent(EventTypes::ClientDisconnected);
}
void Client::handleDisconnected()
@ -557,7 +545,7 @@ void Client::handleDisconnected()
cleanupScreen();
cleanupConnection();
LOG_DEBUG1("disconnected");
sendEvent(EventTypes::ClientDisconnected, nullptr);
sendEvent(EventTypes::ClientDisconnected);
}
void Client::handleShapeChanged()
@ -631,10 +619,10 @@ void Client::handleResume()
void Client::bindNetworkInterface(IDataSocket *socket) const
{
try {
if (!m_args.m_deskflowAddress.empty()) {
LOG_DEBUG1("bind to network interface: %s", m_args.m_deskflowAddress.c_str());
if (const auto address = Settings::value(Settings::Core::Interface).toString(); !address.isEmpty()) {
LOG_DEBUG1("bind to network interface: %s", qPrintable(address));
NetworkAddress bindAddress(m_args.m_deskflowAddress);
NetworkAddress bindAddress(address.toStdString());
bindAddress.resolve();
socket->bind(bindAddress);

View File

@ -12,14 +12,13 @@
#include "HelloBack.h"
#include "base/EventTypes.h"
#include "deskflow/ClientArgs.h"
#include "deskflow/Clipboard.h"
#include "mt/CondVar.h"
#include "deskflow/IClipboard.h"
#include "net/NetworkAddress.h"
#include <climits>
#include <memory>
class Event;
class EventQueueTimer;
namespace deskflow {
class Screen;
@ -60,7 +59,7 @@ public:
*/
Client(
IEventQueue *events, const std::string &name, const NetworkAddress &address, ISocketFactory *socketFactory,
deskflow::Screen *screen, deskflow::ClientArgs const &args
deskflow::Screen *screen
);
Client(Client const &) = delete;
Client(Client &&) = delete;
@ -157,7 +156,7 @@ public:
private:
void sendClipboard(ClipboardID);
void sendEvent(EventTypes, void *);
void sendEvent(deskflow::EventTypes);
void sendConnectionFailedEvent(const char *msg);
void setupConnecting();
void setupConnection();
@ -202,7 +201,6 @@ private:
bool m_useSecureNetwork = false;
bool m_enableClipboard = true;
size_t m_maximumClipboardSize = INT_MAX;
deskflow::ClientArgs m_args;
size_t m_resolvedAddressesCount = 0;
std::unique_ptr<deskflow::client::HelloBack> m_pHelloBack;
};

View File

@ -11,6 +11,7 @@
#include <functional>
#include <memory>
#include <string> //IWYU
namespace deskflow::client {

View File

@ -8,11 +8,9 @@
#include "client/ServerProxy.h"
#include "base/BaseException.h"
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "client/Client.h"
#include "deskflow/AppUtil.h"
#include "deskflow/Clipboard.h"
#include "deskflow/ClipboardChunk.h"
#include "deskflow/DeskflowException.h"
@ -22,9 +20,7 @@
#include "deskflow/StreamChunker.h"
#include "io/IStream.h"
#include <algorithm>
#include <cstring>
#include <memory>
//
// ServerProxy

View File

@ -8,7 +8,6 @@
#pragma once
#include "base/Event.h"
#include "deskflow/ClipboardTypes.h"
#include "deskflow/KeyTypes.h"
#include "deskflow/languages/LanguageManager.h"

View File

@ -12,8 +12,10 @@ unset(CLIENT_BINARY)
unset(SERVER_BINARY)
add_library(common STATIC
Common.h
ExitCodes.h
I18N.h
I18N.cpp
PlatformInfo.h
Settings.h
Settings.cpp
QSettingsProxy.cpp

View File

@ -1,34 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2010 - 2018, 2024 - 2025 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 - 2007 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#if defined(_WIN32)
#define SYSAPI_WIN32 1
#define WINAPI_MSWINDOWS 1
#elif HAVE_CONFIG_H
#include "Config.h"
#else
#error "config.h missing"
#endif
// define nullptr
#include <stddef.h>
// make assert available since we use it a lot
#include <assert.h>
#include <stdint.h>
#include <stdlib.h> // IWYU pragma: keep
#include <string.h> // IWYU pragma: keep
// defined in Carbon
#if !defined(__MACTYPES__)
#if defined(__APPLE__)
#include <CoreServices/CoreServices.h>
#else
#endif
#endif

View File

@ -14,6 +14,7 @@ const auto kDaemonBinName = "@CMAKE_PROJECT_NAME@-daemon";
const auto kDaemonIpcName = "@CMAKE_PROJECT_NAME@-daemon";
const auto kDaemonLogFilename = "@CMAKE_PROJECT_NAME@-daemon.log";
const auto kDefaultLogFile = "@CMAKE_PROJECT_NAME@.log";
const auto kRevFqdnName = "@CMAKE_PROJECT_REV_FQDN@";
const auto kCopyright = //
"Copyright @CMAKE_PROJECT_COPYRIGHT@\n"
@ -23,12 +24,6 @@ const auto kCopyright = //
const auto kCoreBinName = "@CORE_BINARY@";
#ifndef NDEBUG
const auto kDebugBuild = true;
#else
const auto kDebugBuild = false;
#endif
const auto kTlsDirName = "tls";
const auto kTlsCertificateFilename = "@CMAKE_PROJECT_NAME@.pem";
const auto kTlsFingerprintLocalFilename = "local-fingerprint";

207
src/lib/common/I18N.cpp Normal file
View File

@ -0,0 +1,207 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "I18N.h"
#include "common/Constants.h"
#include "common/Settings.h"
#include <QCoreApplication>
#include <QDir>
#include <QList>
#include <QMap>
#include <QObject>
#include <QTranslator>
I18N *I18N::instance()
{
static I18N m;
return &m;
}
I18N::I18N(QObject *parent) : QObject{parent}
{
const auto appDir = QCoreApplication::applicationDirPath();
const auto homeDir = QDir::homePath();
const QList<QDir> appTrDirs{
{QStringLiteral("%1/%2").arg(appDir, QStringLiteral("translations"))},
{QStringLiteral("%1/../translations").arg(appDir)},
{QStringLiteral("%1/../Resources/translations").arg(appDir)},
{QStringLiteral("%1/../share/%2/translations").arg(appDir, kAppId)},
{QStringLiteral("%1/.local/share/%2/translations").arg(homeDir, kAppId)},
{QStringLiteral("/usr/local/share/%1/translations").arg(kAppId)},
{QStringLiteral("/usr/share/%1/translations").arg(kAppId)}
};
const QStringList appTrFilter{QStringLiteral("%1*.qm").arg(kAppId)};
for (const auto &dir : appTrDirs) {
if (!dir.entryList(appTrFilter, QDir::Files, QDir::Name).isEmpty()) {
m_appTrPath = dir.absolutePath();
break;
}
}
if (m_appTrPath.isEmpty()) {
qInfo() << "no app translations found";
}
const auto qt = QStringLiteral("qt");
const auto qt6 = QStringLiteral("qt6");
const QList<QDir> qtTrDirs{
{QStringLiteral("%1/%2").arg(appDir, QStringLiteral("translations"))},
{QStringLiteral("%1/../Resources/translations").arg(appDir)},
{QStringLiteral("%1/../qt-depends/translations").arg(appDir)},
{QStringLiteral("%1/../share/%2/translations").arg(appDir, qt6)},
{QStringLiteral("%1/../share/%2/translations").arg(appDir, qt)},
{QStringLiteral("%1/.local/share/%2/translations").arg(homeDir, qt6)},
{QStringLiteral("%1/.local/share/%2/translations").arg(homeDir, qt)},
{QStringLiteral("/usr/local/share/%2/translations").arg(qt6)},
{QStringLiteral("/usr/local/share/%2/translations").arg(qt)},
{QStringLiteral("/usr/share/%2/translations").arg(qt6)},
{QStringLiteral("/usr/share/%2/translations").arg(qt)}
};
const QStringList qtTrFilter{QStringLiteral("qt_*.qm")};
for (const auto &dir : qtTrDirs) {
if (!dir.entryList(qtTrFilter, QDir::Files, QDir::Name).isEmpty()) {
m_qtTrPath = dir.absolutePath();
break;
}
}
if (m_qtTrPath.isEmpty()) {
qInfo() << "no qt translations found";
}
detectLanguages();
if (Settings::value(Settings::Core::Language).isNull()) {
auto appTranslator = new QTranslator(this);
if (appTranslator->load(QLocale(), kAppId, "_", m_appTrPath)) {
m_currentTranslations.append(appTranslator);
QCoreApplication::installTranslator(appTranslator);
}
m_currentLang = appTranslator->translate("i18n", "LocalizedName");
if (m_currentLang.isEmpty())
m_currentLang = QStringLiteral("English");
auto qtTranslator = new QTranslator(this);
if (qtTranslator->load(QLocale(), QStringLiteral("qt"), "_", m_qtTrPath)) {
m_currentTranslations.append(qtTranslator);
QCoreApplication::installTranslator(qtTranslator);
}
} else {
m_currentLang = Settings::value(Settings::Core::Language).toString();
const auto translations = m_translations.value(m_currentLang);
for (const auto &translation : translations) {
auto translator = new QTranslator(this);
if (translator->load(translation)) {
m_currentTranslations.append(translator);
QCoreApplication::installTranslator(translator);
}
}
}
}
QStringList I18N::detectedLanguages()
{
return instance()->m_translations.keys();
}
QString I18N::currentLanguage()
{
return instance()->m_currentLang;
}
void I18N::setLanguage(const QString &langName)
{
if (langName == instance()->m_currentLang) {
return;
}
if (!instance()->m_translations.contains(langName)) {
return;
}
instance()->m_currentLang = langName;
Settings::setValue(Settings::Core::Language, langName);
for (const auto &translation : std::as_const(instance()->m_currentTranslations))
QCoreApplication::removeTranslator(translation);
qDeleteAll(instance()->m_currentTranslations);
instance()->m_currentTranslations.clear();
const auto translations = instance()->m_translations.value(langName);
for (const auto &translation : translations) {
auto translator = new QTranslator(instance());
if (translator->load(translation)) {
instance()->m_currentTranslations.append(translator);
QCoreApplication::installTranslator(translator);
}
}
Q_EMIT instance()->languageChanged(langName);
}
void I18N::reDetectLanguages()
{
instance()->detectLanguages();
}
void I18N::detectLanguages()
{
const auto oldList = m_translations;
m_translations.clear();
QStringList nameFilter = {QStringLiteral("%1_*.qm").arg(kAppId)};
QMap<QString, QString> appTranslations;
QMap<QString, QString> shortToNative;
QStringList detectedLangCodes;
QDir dir(m_appTrPath);
QStringList langList = dir.entryList(nameFilter, QDir::Files, QDir::Name);
for (const QString &translation : std::as_const(langList)) {
QTranslator translator;
std::ignore = translator.load(translation, dir.absolutePath());
const auto longCode = translator.language();
//: Replace with your Language name
//: This is a required string
QString nativeLang = translator.translate("i18n", "LocalizedName");
if (nativeLang.isEmpty())
nativeLang = QStringLiteral("English");
QString shortCode;
if (longCode.startsWith(QStringLiteral("zh")) || longCode.startsWith(QStringLiteral("pt")))
shortCode = longCode;
else
shortCode = longCode.mid(0, 2);
appTranslations.insert(shortCode, translator.filePath());
shortToNative.insert(shortCode, nativeLang);
detectedLangCodes.append(QStringLiteral("qt_%1.qm").arg(shortCode));
}
dir.setPath(m_qtTrPath);
const static auto qtTrNameLen = 3; // length of qt_
langList = dir.entryList(detectedLangCodes, QDir::Files, QDir::Name);
QMap<QString, QString> qtTranslations;
for (const QString &translation : std::as_const(langList)) {
QString lang = translation.mid(qtTrNameLen, translation.lastIndexOf('.') - qtTrNameLen);
qtTranslations.insert(lang, QStringLiteral("%1/%2").arg(m_qtTrPath, translation));
}
const QStringList keys = appTranslations.keys();
for (const QString &lang : keys)
m_translations.insert(shortToNative.value(lang), {appTranslations.value(lang), qtTranslations.value(lang)});
if (oldList != m_translations)
Q_EMIT langaugesChanged(m_translations.keys());
}

70
src/lib/common/I18N.h Normal file
View File

@ -0,0 +1,70 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include <QMap>
#include <QObject>
class QTranslator;
/**
* @brief The I18N singleton class handles detection and loading of translation files
*/
class I18N : public QObject
{
Q_OBJECT
public:
static I18N *instance();
/**
* @brief detectedLanguages
* @return List of detected languages (native names: English, Español etc..)
*/
static QStringList detectedLanguages();
/**
* @brief currentLanguage
* @return The current language string (native name: English, Español etc..)
*/
static QString currentLanguage();
/**
* @brief setLanguage Sets the current language
* @param langName The language name must be an is 639-1 name
*/
static void setLanguage(const QString &langName);
/**
* @brief detectLanguages Detect new language files
*/
static void reDetectLanguages();
Q_SIGNALS:
/**
* @brief languageChanged Emitted when the current language changes
* @param language The current language (native name, i.e English, Español)
*/
void languageChanged(const QString language);
/**
* @brief langaugesChanged Emitted when the detected languages changes
* @param languages The current list of languages (native names i.e English, Español..)
*/
void langaugesChanged(const QStringList languages);
private:
explicit I18N(QObject *parent = nullptr);
I18N *operator=(I18N &other) = delete;
I18N(const I18N &other) = delete;
~I18N() override = default;
void detectLanguages();
QMap<QString, QStringList> m_translations;
QList<QTranslator *> m_currentTranslations;
QString m_currentLang = QStringLiteral("English");
QString m_appTrPath;
QString m_qtTrPath;
};

View File

@ -0,0 +1,65 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include <QFileInfo>
#include <QSysInfo>
namespace deskflow::platform {
inline bool isWayland()
{
return qEnvironmentVariable("XDG_SESSION_TYPE") == QStringLiteral("wayland");
}
/**
* @brief isWindows
* @return Returns true if we are running on windows
*/
inline bool isWindows()
{
return QSysInfo::productType() == QStringLiteral("windows");
}
/**
* @brief isMac
* @return Returns true if we are running on mac os
*/
inline bool isMac()
{
return QSysInfo::productType() == QStringLiteral("macos");
}
/**
* @brief isFlatpak
* @return Returns true if we are running as flatpak
*/
inline bool isFlatpak()
{
return QFileInfo::exists(QStringLiteral("/.flatpak-info"));
}
/**
* @brief isSnap
* @return Returns true if we are running as a snap
*/
inline bool isSnap()
{
return qEnvironmentVariableIsSet("SNAP");
}
/**
* @brief isSandboxed
* @return Returns true if we are running from in a known sandbox.
*/
inline bool isSandboxed()
{
return isFlatpak() || isSnap();
}
} // namespace deskflow::platform

View File

@ -11,6 +11,8 @@
#include <QCoreApplication>
#include <QFile>
#include <QRect>
#include <QRegularExpression>
#include <QStandardPaths>
Settings *Settings::instance()
{
@ -18,47 +20,70 @@ Settings *Settings::instance()
return &m;
}
void Settings::setSettingFile(const QString &settingsFile)
void Settings::setSettingsFile(const QString &settingsFile)
{
if (instance()->m_portableSettingsFile == settingsFile) {
qDebug().noquote() << "settings file already in use";
if (Settings::settingsFile() == settingsFile) {
qDebug("settings file already set, skipping");
return;
}
instance()->m_portableSettingsFile = settingsFile;
if (instance()->m_settings)
instance()->m_settings->deleteLater();
instance()->m_settings = new QSettings(instance()->m_portableSettingsFile, QSettings::IniFormat);
instance()->m_settingsProxy->load(instance()->m_portableSettingsFile);
qInfo().noquote() << "settings file:" << instance()->m_settings->fileName();
instance()->m_settings = new QSettings(settingsFile, QSettings::IniFormat, instance());
instance()->m_settingsProxy->load(settingsFile);
qInfo().noquote() << "settings file changed:" << instance()->m_settings->fileName();
instance()->setupScreenName();
}
void Settings::setStateFile(const QString &stateFile)
{
if (instance()->m_stateSettings->fileName() == stateFile) {
qDebug("settings file already set, skipping");
return;
}
if (instance()->m_stateSettings)
instance()->m_stateSettings->deleteLater();
instance()->m_stateSettings = new QSettings(stateFile, QSettings::IniFormat, instance());
qInfo().noquote() << "settings file changed:" << instance()->m_stateSettings->fileName();
}
Settings::Settings(QObject *parent) : QObject(parent)
{
QString fileToLoad;
#ifdef Q_OS_WIN
m_portableSettingsFile = m_portableSettingsFile.arg(QCoreApplication::applicationDirPath(), kAppName);
if (QFile(m_portableSettingsFile).exists()) {
fileToLoad = m_portableSettingsFile;
m_settings = new QSettings(fileToLoad, QSettings::IniFormat);
} else {
m_settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, kAppName, kAppName);
}
const auto portableFile = portableSettingsFile();
qDebug().noquote() << "checking for portable settings file at:" << portableFile;
if (QFile(portableFile).exists())
fileToLoad = portableFile;
#else
if (!qEnvironmentVariable("XDG_CONFIG_HOME").isEmpty())
fileToLoad = QStringLiteral("%1/%2/%2.conf").arg(qEnvironmentVariable("XDG_CONFIG_HOME"), kAppName);
if (const auto xdgConfigHome = qEnvironmentVariable("XDG_CONFIG_HOME"); !xdgConfigHome.isEmpty())
fileToLoad = QStringLiteral("%1/%2/%2.conf").arg(xdgConfigHome, kAppName);
#endif
else if (QFile(UserSettingFile).exists())
fileToLoad = UserSettingFile;
else if (QFile(SystemSettingFile).exists())
fileToLoad = SystemSettingFile;
else
fileToLoad = UserSettingFile;
m_settings = new QSettings(fileToLoad, QSettings::IniFormat);
#endif
m_settings = new QSettings(fileToLoad, QSettings::IniFormat, this);
m_settingsProxy = std::make_shared<QSettingsProxy>();
m_settingsProxy->load(fileToLoad);
qInfo().noquote() << "settings file:" << m_settings->fileName();
qInfo().noquote() << "initial settings file:" << m_settings->fileName();
const auto xdgStateHome = qEnvironmentVariable("XDG_STATE_HOME");
const auto stateBase = !xdgStateHome.isEmpty()
? xdgStateHome
: QStandardPaths::standardLocations(QStandardPaths::GenericStateLocation).at(0);
const auto stateFile = QStringLiteral("%1/%2.state").arg(stateBase, kAppName);
m_stateSettings = new QSettings(stateFile, QSettings::IniFormat, this);
setupScreenName();
}
void Settings::cleanSettings()
@ -72,22 +97,94 @@ void Settings::cleanSettings()
}
}
void Settings::cleanStateSettings()
{
const QStringList keys = m_stateKeys;
for (const QString &key : keys) {
if (!m_stateKeys.contains(key))
m_stateSettings->remove(key);
if (m_stateSettings->value(key).toString().isEmpty() && !m_stateSettings->value(key).isValid())
m_stateSettings->remove(key);
}
}
void Settings::setupScreenName()
{
if (m_settings->value(Settings::Core::ScreenName).isNull())
m_settings->setValue(Settings::Core::ScreenName, cleanScreenName(QSysInfo::machineHostName()));
}
QString Settings::cleanScreenName(const QString &name)
{
static const auto hyphen = QStringLiteral("-");
static const auto space = QStringLiteral(" ");
static const auto underscore = QStringLiteral("_");
static const auto peroid = QStringLiteral(".");
static const auto nothing = QStringLiteral("");
static const auto nameRegex = QRegularExpression(QStringLiteral("[^\\w\\-\\.]"));
QString cleanName = name.simplified();
cleanName.replace(space, underscore);
cleanName.replace(nameRegex, nothing);
while (cleanName.startsWith(hyphen) || cleanName.startsWith(underscore) || cleanName.startsWith(peroid))
cleanName.removeFirst();
while (cleanName.endsWith(hyphen) || cleanName.endsWith(underscore) || cleanName.endsWith(peroid))
cleanName.removeLast();
if (cleanName.length() > 255) {
cleanName.truncate(255);
cleanName = cleanScreenName(cleanName);
}
return cleanName;
}
int Settings::logLevelToInt(const QString &level)
{
// Can do this better later ?
if (level.toUpper() == "FATAL") {
return 0;
}
if (level.toUpper() == "ERROR") {
return 1;
}
if (level.toUpper() == "WARNING") {
return 2;
}
if (level.toUpper() == "NOTE") {
return 3;
}
if (level.toUpper() == "INFO") {
return 4;
}
if (level.toUpper() == "DEBUG") {
return 5;
}
if (level.toUpper() == "DEBUG1") {
return 6;
}
if (level.toUpper() == "DEBUG2") {
return 7;
}
return 4; // If all else fail return info
}
QVariant Settings::defaultValue(const QString &key)
{
if ((key == Gui::Autohide) || (key == Core::StartedBefore) || (key == Core::PreventSleep) ||
(key == Server::ExternalConfig) || (key == Client::InvertScrollDirection) || (key == Log::ToFile)) {
if (m_defaultFalseValues.contains(key)) {
return false;
}
if ((key == Gui::CloseToTray) || (key == Gui::LogExpanded) || (key == Gui::SymbolicTrayIcon) ||
(key == Gui::CloseReminder) || (key == Security::TlsEnabled) || (key == Security::CheckPeers) ||
(key == Client::LanguageSync) || (key == Gui::ShowGenericClientFailureDialog)) {
if (m_defaultTrueValues.contains(key)) {
return true;
}
if (key == Core::ScreenName)
return QSysInfo::machineHostName();
if (key == Gui::WindowGeometry)
return QRect();
@ -103,14 +200,8 @@ QVariant Settings::defaultValue(const QString &key)
if (key == Log::Level)
return 4; // INFO
if (key == Client::Binary)
return kCoreBinName;
if (key == Server::Binary)
return kCoreBinName;
if (key == Daemon::Elevate)
return Settings::isNativeMode();
return !Settings::isPortableMode();
if (key == Core::UpdateUrl)
return kUrlUpdateCheck;
@ -122,16 +213,22 @@ QVariant Settings::defaultValue(const QString &key)
return 24800;
if (key == Core::ProcessMode) {
if (Settings::isNativeMode())
#ifdef Q_OS_WIN
if (!Settings::isPortableMode())
return Settings::ProcessMode::Service;
else
return Settings::ProcessMode::Desktop;
#endif
return Settings::ProcessMode::Desktop;
}
if (key == Daemon::LogFile) {
return QStringLiteral("%1/%2").arg(Settings::settingsPath(), kDaemonLogFilename);
}
if (key == Client::ScrollSpeed) {
return 120;
}
return QVariant();
}
@ -150,6 +247,7 @@ void Settings::save(bool emitSaving)
if (emitSaving)
Q_EMIT instance()->serverSettingsChanged();
instance()->m_settings->sync();
instance()->m_stateSettings->sync();
}
QStringList Settings::validKeys()
@ -159,14 +257,13 @@ QStringList Settings::validKeys()
bool Settings::isWritable()
{
if (Settings::isNativeMode())
return true;
return instance()->m_settings->isWritable();
}
bool Settings::isNativeMode()
bool Settings::isPortableMode()
{
return instance()->m_settings->format() == QSettings::NativeFormat;
// Enable portable mode only if the portable settings file exists in the expected location.
return QFile(portableSettingsFile()).exists();
}
QString Settings::settingsFile()
@ -176,8 +273,11 @@ QString Settings::settingsFile()
QString Settings::settingsPath()
{
if (instance()->isNativeMode())
#ifdef Q_OS_WIN
if (!isPortableMode())
return SystemDir;
#endif
return QFileInfo(instance()->m_settings->fileName()).absolutePath();
}
@ -203,21 +303,30 @@ QString Settings::tlsTrustedClientsDb()
void Settings::setValue(const QString &key, const QVariant &value)
{
if (instance()->m_settings->value(key) == value)
const bool useState = Settings::m_stateKeys.contains(key) && !instance()->isPortableMode();
auto settings = useState ? instance()->m_stateSettings : instance()->m_settings;
if (settings->value(key) == value)
return;
if (!value.isValid())
instance()->m_settings->remove(key);
else
instance()->m_settings->setValue(key, value);
settings->remove(key);
else {
if (key == Settings::Core::ScreenName)
settings->setValue(key, cleanScreenName(value.toString()));
else
settings->setValue(key, value);
}
instance()->m_settings->sync();
settings->sync();
Q_EMIT instance()->settingsChanged(key);
}
QVariant Settings::value(const QString &key)
{
return instance()->m_settings->value(key, defaultValue(key));
const bool useState = Settings::m_stateKeys.contains(key) && !instance()->isPortableMode();
auto settings = useState ? instance()->m_stateSettings : instance()->m_settings;
return settings->value(key, defaultValue(key));
}
void Settings::restoreDefaultSettings()
@ -226,3 +335,10 @@ void Settings::restoreDefaultSettings()
instance()->setValue(key, defaultValue(key));
}
}
QString Settings::portableSettingsFile()
{
static const auto filename =
QStringLiteral("%1/settings/%2.conf").arg(QCoreApplication::applicationDirPath(), kAppName);
return filename;
}

View File

@ -19,22 +19,23 @@ class Settings : public QObject
Q_OBJECT
public:
#if defined(Q_OS_WIN)
inline const static auto UserDir = QStringLiteral("%1/AppData/Local/%2").arg(QDir::homePath(), kAppName);
inline const static auto UserDir = QStringLiteral("%1/AppData/Roaming/%2").arg(QDir::homePath(), kAppName);
inline const static auto SystemDir = QStringLiteral("%1ProgramData/%2").arg(QDir::rootPath(), kAppName);
#elif defined(Q_OS_MAC)
#elif defined(Q_OS_MACOS)
inline const static auto UserDir = QStringLiteral("%1/Library/%2").arg(QDir::homePath(), kAppName);
inline const static auto SystemDir = QStringLiteral("/Library/%1").arg(kAppName);
#else
inline const static auto UserDir = QStringLiteral("%1/.config/%2").arg(QDir::homePath(), kAppName);
inline const static auto SystemDir = QStringLiteral("/etc/%1").arg(kAppName);
#endif
inline const static auto UserSettingFile = QStringLiteral("%1/%2.conf").arg(UserDir, kAppName);
inline const static auto SystemSettingFile = QStringLiteral("%1/%2.conf").arg(SystemDir, kAppName);
struct Client
{
inline static const auto Binary = QStringLiteral("client/binary");
inline static const auto InvertScrollDirection = QStringLiteral("client/invertScrollDirection");
inline static const auto ScrollSpeed = QStringLiteral("client/yscroll");
inline static const auto LanguageSync = QStringLiteral("client/languageSync");
inline static const auto RemoteHost = QStringLiteral("client/remoteHost");
inline static const auto XdpRestoreToken = QStringLiteral("client/xdpRestoreToken");
@ -50,6 +51,10 @@ public:
inline static const auto ScreenName = QStringLiteral("core/screenName");
inline static const auto StartedBefore = QStringLiteral("core/startedBefore");
inline static const auto UpdateUrl = QStringLiteral("core/updateUrl");
inline static const auto Display = QStringLiteral("core/display");
inline static const auto UseHooks = QStringLiteral("core/useHooks");
inline static const auto Language = QStringLiteral("core/language");
inline static const auto UseWlClipboard = QStringLiteral("core/wlClipboard");
};
struct Daemon
{
@ -74,6 +79,7 @@ public:
inline static const auto File = QStringLiteral("log/file");
inline static const auto Level = QStringLiteral("log/level");
inline static const auto ToFile = QStringLiteral("log/toFile");
inline static const auto GuiDebug = QStringLiteral("log/guiDebug");
};
struct Security
{
@ -84,8 +90,6 @@ public:
};
struct Server
{
inline static const auto Binary = QStringLiteral("server/binary");
inline static const auto ConfigVisible = QStringLiteral("server/configVisible");
inline static const auto ExternalConfig = QStringLiteral("server/externalConfig");
inline static const auto ExternalConfigFile = QStringLiteral("server/externalConfigFile");
};
@ -113,13 +117,14 @@ public:
Q_ENUM(CoreMode)
static Settings *instance();
static void setSettingFile(const QString &settingsFile = QString());
static void setSettingsFile(const QString &settingsFile = QString());
static void setStateFile(const QString &stateFile = QString());
static void setValue(const QString &key = QString(), const QVariant &value = QVariant());
static QVariant value(const QString &key = QString());
static void restoreDefaultSettings();
static QVariant defaultValue(const QString &key);
static bool isWritable();
static bool isNativeMode();
static bool isPortableMode();
static QString settingsFile();
static QString settingsPath();
static QString tlsDir();
@ -130,6 +135,8 @@ public:
static QSettingsProxy &proxy();
static void save(bool emitSaving = true);
static QStringList validKeys();
static int logLevelToInt(const QString &level = "INFO");
static QString portableSettingsFile();
Q_SIGNALS:
void settingsChanged(const QString key);
@ -141,9 +148,22 @@ private:
Settings(const Settings &other) = delete;
~Settings() override = default;
void cleanSettings();
void cleanStateSettings();
/**
* @brief write an initial screen name
*/
void setupScreenName();
/**
* @brief cleanScreenName ensure a valid screenName from the provided one
* @param name any string to be used as the screenName
* @return a valid screeName
*/
static QString cleanScreenName(const QString &name);
QSettings *m_settings = nullptr;
QString m_portableSettingsFile = QStringLiteral("%1/settings/%2.conf");
QSettings *m_stateSettings = nullptr;
std::shared_ptr<QSettingsProxy> m_settingsProxy;
// clang-format off
@ -159,10 +179,10 @@ private:
};
inline static const QStringList m_validKeys = {
Settings::Client::Binary
, Settings::Client::InvertScrollDirection
Settings::Client::InvertScrollDirection
, Settings::Client::LanguageSync
, Settings::Client::RemoteHost
, Settings::Client::ScrollSpeed
, Settings::Client::XdpRestoreToken
, Settings::Core::CoreMode
, Settings::Core::Interface
@ -173,12 +193,17 @@ private:
, Settings::Core::ScreenName
, Settings::Core::StartedBefore
, Settings::Core::UpdateUrl
, Settings::Core::Display
, Settings::Core::UseHooks
, Settings::Core::UseWlClipboard
, Settings::Core::Language
, Settings::Daemon::Command
, Settings::Daemon::Elevate
, Settings::Daemon::LogFile
, Settings::Log::File
, Settings::Log::Level
, Settings::Log::ToFile
, Settings::Log::GuiDebug
, Settings::Gui::Autohide
, Settings::Gui::AutoUpdateCheck
, Settings::Gui::CloseReminder
@ -191,10 +216,36 @@ private:
, Settings::Security::CheckPeers
, Settings::Security::KeySize
, Settings::Security::TlsEnabled
, Settings::Server::Binary
, Settings::Server::ConfigVisible
, Settings::Server::ExternalConfig
, Settings::Server::ExternalConfigFile
};
// When checking the default values this list contains the ones that default to false.
inline static const QStringList m_defaultFalseValues = {
Settings::Gui::Autohide
, Settings::Core::StartedBefore
, Settings::Core::PreventSleep
, Settings::Core::UseWlClipboard
, Settings::Server::ExternalConfig
, Settings::Client::InvertScrollDirection
, Settings::Log::ToFile
, Settings::Log::GuiDebug
};
// When checking the default values this list contains the ones that default to true.
inline static const QStringList m_defaultTrueValues = {
Settings::Core::UseHooks
, Settings::Client::LanguageSync
, Settings::Gui::CloseToTray
, Settings::Gui::CloseReminder
, Settings::Gui::LogExpanded
, Settings::Gui::SymbolicTrayIcon
, Settings::Gui::ShowGenericClientFailureDialog
, Settings::Security::TlsEnabled
, Settings::Security::CheckPeers
};
// Settings saved in our State file
inline static const QStringList m_stateKeys = { Settings::Gui::WindowGeometry };
// clang-format on
};

Some files were not shown because too many files have changed in this diff Show More