Compare commits
409 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d4ff55da13 | |||
| d8bdad4e1d | |||
| fb119f3c56 | |||
| 525123573e | |||
| 858a5d71bd | |||
| f88f79ef8d | |||
| b547493bc5 | |||
| cace2892ba | |||
| 82e23716ac | |||
| 92e3e9ac64 | |||
| 50fd3365e8 | |||
| 4ebf5ff479 | |||
| ab4feaa037 | |||
| 21900ade68 | |||
| 7b0e8e8188 | |||
| c5f7a3792a | |||
| 7a60f0cc9e | |||
| 9ed0b06a42 | |||
| 5ef4f64b11 | |||
| c7e90d3cf5 | |||
| d157713a85 | |||
| 90e7d475f8 | |||
| ecb03297fd | |||
| a09dfad22a | |||
| 9960b657e3 | |||
| bbabb91d42 | |||
| e55c67fa2f | |||
| 56bb41a291 | |||
| 4285361413 | |||
| 37177f8e45 | |||
| 6eadba1ab2 | |||
| aa11dc94ba | |||
| 82c18e26e5 | |||
| e7018bd75a | |||
| 10585e6afd | |||
| 35d26645a7 | |||
| 2376c61292 | |||
| 43a04308f0 | |||
| 0463518baa | |||
| 8e7a950d11 | |||
| ae5dc085d5 | |||
| bc9bc906a5 | |||
| 0890bcac42 | |||
| 22d27f7245 | |||
| 94b7e2ffff | |||
| 1a9e468c86 | |||
| 204a6cca8a | |||
| 4df7c54afb | |||
| 999f174441 | |||
| aca08a5e74 | |||
| ddcd2f0ff1 | |||
| ec9f7efcff | |||
| 8256a3ba43 | |||
| e011351aad | |||
| ad33114e0c | |||
| 4728525ece | |||
| cbc74d99b0 | |||
| f372ccd2b8 | |||
| 4d2597c31b | |||
| 281cda1d14 | |||
| 4a67694676 | |||
| f45df39032 | |||
| 186a20a1ac | |||
| 7eee265010 | |||
| b3cd759a4e | |||
| 93beb491db | |||
| 6d89ee0660 | |||
| b2cdf38fca | |||
| 567766508a | |||
| 1b67293d9c | |||
| 75922cb944 | |||
| e712bf6c8e | |||
| f26ea08469 | |||
| 6a86c2990e | |||
| 859af720c9 | |||
| 5441464a1d | |||
| de63751516 | |||
| 39382bfd8c | |||
| 477c7b07e5 | |||
| 63026752b5 | |||
| 5bcefbe582 | |||
| 8e754b2ce2 | |||
| 1556908ef6 | |||
| 57d36b51af | |||
| c72cdedd4f | |||
| 759457f739 | |||
| 78c90fe7c6 | |||
| 1875c599a4 | |||
| ab1a87ba04 | |||
| d492ddfbbb | |||
| cad92d2ff7 | |||
| 9737f23f5c | |||
| e26c737776 | |||
| d36576a079 | |||
| e48ee3d368 | |||
| 891d5c9bb7 | |||
| cbf3627ae2 | |||
| 44882cfa7a | |||
| c91e051082 | |||
| e01837579d | |||
| 0a5b39be7a | |||
| 19f49e746b | |||
| 8d34bfdcc6 | |||
| 725c2e96a4 | |||
| 113801f967 | |||
| c3e017bd68 | |||
| 71bb8eb750 | |||
| 01cdbf48d4 | |||
| 04bea86254 | |||
| 176b5c2459 | |||
| dad46a5a34 | |||
| 997b0c5f1b | |||
| a13e3f895d | |||
| 6a895f5c45 | |||
| 98a49b7f7a | |||
| 4b3399d951 | |||
| 2743e1dbae | |||
| aff73bbded | |||
| f8b299ff67 | |||
| f294daa077 | |||
| e389b2ed56 | |||
| 9b145c2739 | |||
| bc8dcf76ad | |||
| 93f42df4db | |||
| 00f10bdb14 | |||
| 2ca2500954 | |||
| b20201007e | |||
| 959e6b2d0d | |||
| 65a7fbb90b | |||
| c2ae5eae08 | |||
| 06ac0a747f | |||
| e2a78ac5ac | |||
| 9f213fa1dd | |||
| 6924c4f09d | |||
| 58bb8a0fcf | |||
| e8702cd716 | |||
| fb5e532ff6 | |||
| 1e371ad84a | |||
| 7ce5ce5d41 | |||
| 68a15486ae | |||
| bb42cfb8fd | |||
| af0321ce77 | |||
| fbdee8e10e | |||
| 489d984ab6 | |||
| 1e672bb8a1 | |||
| 6147e9604b | |||
| 97367a20c5 | |||
| 5a029110a3 | |||
| e4557c6b95 | |||
| 685312866b | |||
| a0959e0334 | |||
| a376a714e8 | |||
| 21a7d7db4b | |||
| b4649b4f9c | |||
| 0a407b6726 | |||
| 8b89489d40 | |||
| e8073de989 | |||
| 74e4f1a1e1 | |||
| d9493da173 | |||
| 0e8fdb18d8 | |||
| e07a9f58f7 | |||
| 1f6f6d5dc6 | |||
| 19814c61d7 | |||
| 66efc9c70e | |||
| c4fd8699f9 | |||
| 5dc8263e54 | |||
| 52b23e6609 | |||
| 90fb80872c | |||
| 754210e8f4 | |||
| 5966b8f1b8 | |||
| 72496548de | |||
| 0091cc5e9a | |||
| 241be6abd8 | |||
| f0eaa3efaa | |||
| f56073a090 | |||
| ca2a7d14f0 | |||
| 3ffe8d7c86 | |||
| e130cbb706 | |||
| e6792b00d7 | |||
| f89e857223 | |||
| 5256f05963 | |||
| 8e83b16f5e | |||
| c18e3c9ef2 | |||
| 0a67f63af6 | |||
| 8ef2319b0d | |||
| 0e98702944 | |||
| 080a6e8b54 | |||
| 8fadbecf00 | |||
| eab38dc23e | |||
| 3e76a39326 | |||
| 3af83dd9f2 | |||
| aba38b949f | |||
| 67ceedcad0 | |||
| 6c8c6f5208 | |||
| 6ab1579d38 | |||
| fc6e9f1447 | |||
| 1750dd9149 | |||
| 8134521cca | |||
| b204204920 | |||
| 8066ac8e2f | |||
| 43989dc964 | |||
| c066e394a8 | |||
| 78eae652fd | |||
| e1c0803018 | |||
| 1d82be270a | |||
| b22fa1550a | |||
| dc0a85d34f | |||
| 9f669dbbce | |||
| a5bcc90988 | |||
| acd4b59b4c | |||
| 50240c1fc3 | |||
| 091d309444 | |||
| 90d6fc6f08 | |||
| 6ff8b053af | |||
| 1f950f4c2a | |||
| 4afc20e2c3 | |||
| 550f7c3e06 | |||
| 135fe27007 | |||
| 7e6a674210 | |||
| dc997a80d5 | |||
| 293c321ba5 | |||
| 4816608c50 | |||
| 14fa29505d | |||
| eee4efd59d | |||
| 066e63cc86 | |||
| f4ca17ba3d | |||
| 8c6fa880b4 | |||
| 83e0a6b1ea | |||
| 495a5e6479 | |||
| def479bc7b | |||
| 0bf6e1e9f8 | |||
| a140b3bcca | |||
| 8b513efc95 | |||
| 17392a8e06 | |||
| ddc827e6f1 | |||
| 72792e7d4d | |||
| 1b6a5ced08 | |||
| 9906421460 | |||
| a62613f219 | |||
| 91a82b22b2 | |||
| 73e44916e7 | |||
| 84d2c56b54 | |||
| 18d90aa0bf | |||
| 40c20e3f5e | |||
| 1e0bd5822a | |||
| 7b0d74aace | |||
| becbb01286 | |||
| fc1769ce66 | |||
| 31573bf6e8 | |||
| 2efcc76933 | |||
| 3580b4ead9 | |||
| a82e9a4a74 | |||
| 2d7174c3b2 | |||
| c3f0b18df6 | |||
| 71c1bb87ca | |||
| bf3fd82630 | |||
| bdacd6a994 | |||
| f2a3c708a8 | |||
| 77bf32cb0e | |||
| 0ca87f7576 | |||
| b96e94be14 | |||
| 091dbc0729 | |||
| 29f7a57d97 | |||
| 8d0c1af032 | |||
| 2b83a0ac0f | |||
| 453686655b | |||
| 61f3113d61 | |||
| bf6986bcba | |||
| 6b2c3c1844 | |||
| 023e8dc78e | |||
| b5c188abbc | |||
| 8226c4162b | |||
| 2016ce887b | |||
| 4a0e2c492c | |||
| a34971fee7 | |||
| 5fc4e9dba6 | |||
| 42b90a2b14 | |||
| 6b05c2abc0 | |||
| 697cd57adb | |||
| 88e385d050 | |||
| 91b836a486 | |||
| b8bd6903e4 | |||
| ea7a493c13 | |||
| e108afdb80 | |||
| 4be0dd3d9d | |||
| 55a9596bd5 | |||
| 71fc1a47fb | |||
| d036c5a07c | |||
| e1fbcb379e | |||
| ed8e71072e | |||
| dbc3229b40 | |||
| 8b489b8301 | |||
| d32b98ec34 | |||
| 78e394a210 | |||
| c11a1caf59 | |||
| debfd4dc69 | |||
| 41d5359f4d | |||
| 34f56af6d6 | |||
| 687fd5411a | |||
| bb1394ceeb | |||
| 3ece50e292 | |||
| 497813e198 | |||
| a4aa540bc8 | |||
| 58c750471f | |||
| 7c3ec372df | |||
| 0f335d46bb | |||
| 2b203c8cdd | |||
| ac7bd1ceca | |||
| de6be901b9 | |||
| 0ab65410cc | |||
| a057437e1a | |||
| f9c007cc3a | |||
| d45196304f | |||
| 004089f887 | |||
| 13990438d3 | |||
| a120441a10 | |||
| 26c2f672be | |||
| 48195f9347 | |||
| 5a632bbb6d | |||
| 173638d6f5 | |||
| bd0c5a68f1 | |||
| e3f940c70a | |||
| bb1e2ecc78 | |||
| 8d0c368c8f | |||
| 9e94a4fe0e | |||
| 8d577aaa93 | |||
| 47662c359e | |||
| 4fb76bec41 | |||
| 5945114b7b | |||
| 559b7b5a17 | |||
| a346ff6161 | |||
| eed4b49ea0 | |||
| 88c0c49ba3 | |||
| f64d430f96 | |||
| f4a6d3d43d | |||
| 04b3f5c769 | |||
| aae3067f03 | |||
| 0820c5a188 | |||
| 8ea2671e65 | |||
| 12ea23a3bd | |||
| 0bc82c3a57 | |||
| 3270b40455 | |||
| 9f2327932d | |||
| 1b44a24d75 | |||
| 8db65da345 | |||
| 558833ca05 | |||
| 06263ceaad | |||
| 4a5f173422 | |||
| e30025ea8b | |||
| 59d860874e | |||
| 5978694d95 | |||
| d98f8a524d | |||
| 37827f0540 | |||
| 6fa8ba087a | |||
| a78959e66f | |||
| 5fc00f7af4 | |||
| 2cb36777f9 | |||
| b0fe79d527 | |||
| efebe9ca56 | |||
| bf7a50ab0e | |||
| cfedfc2c1e | |||
| cfd580fb21 | |||
| 1884ab3555 | |||
| eeaf139bd9 | |||
| 295137dbd2 | |||
| c2ee366e23 | |||
| a8348b1ccb | |||
| ff4c9dc421 | |||
| 53b36801e1 | |||
| f3a1bbaf5b | |||
| 05f377e21b | |||
| ead49c4025 | |||
| 5e02adc772 | |||
| 10f34f214f | |||
| 6f574a3076 | |||
| dc86538456 | |||
| 7698980576 | |||
| 9fe445ebd0 | |||
| 087afd22b2 | |||
| 8192d7b2d8 | |||
| d9ce9c4f10 | |||
| 4dfc98c93c | |||
| abbfc91192 | |||
| e3f5e045b7 | |||
| 7f7e7de841 | |||
| bd63d54a9e | |||
| 9dae55fc6f | |||
| e35a2b1c29 | |||
| 4157367aaa | |||
| 3945dc927c | |||
| 801ada88b7 | |||
| c234f2fc66 | |||
| 7b866ec632 | |||
| ff1726677b | |||
| 57e34ded54 | |||
| 03e014c753 | |||
| fd7950bc04 | |||
| 9f8c45b449 | |||
| bf5b0de6ee | |||
| 3aef2f4309 | |||
| 134c8fd1c1 | |||
| 35c7fe7fc4 | |||
| 612ca91f9c | |||
| 4632bec405 | |||
| fcdea215be | |||
| 6f18cf74c5 | |||
| 162cb85811 | |||
| 5ea8d0326a | |||
| 7a2591a1fa |
2
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Apply to all files
|
||||
* @nbolton @sithlord48
|
||||
10
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -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.
|
||||
|
||||
11
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@ -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
|
||||
|
||||
|
||||
21
.github/actions/install-dependencies/action.yml
vendored
@ -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' }}
|
||||
|
||||
45
.github/workflows/codeql-analysis.yml
vendored
@ -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
|
||||
|
||||
132
.github/workflows/continuous-integration.yml
vendored
@ -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')
|
||||
|
||||
47
.github/workflows/sonarcloud-analysis.yml
vendored
@ -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
@ -25,6 +25,9 @@ deskflow-config.toml
|
||||
/*.user
|
||||
*.ui.autosave
|
||||
|
||||
#Qt creator 18 user files
|
||||
/.qtcreator
|
||||
|
||||
# generated vcpkg file
|
||||
vcpkg.json
|
||||
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
15
README.md
@ -22,6 +22,11 @@ TLS encryption is enabled by default. Wayland is supported. Clipboard sharing is
|
||||
|
||||
[](https://github.com/deskflow/deskflow/releases/latest) [](https://github.com/deskflow/deskflow/releases/continuous) [](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
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"dotenv",
|
||||
"Evenson",
|
||||
"Feder",
|
||||
"Flatpaks",
|
||||
"Fourdan",
|
||||
"gdrive",
|
||||
"Hadzhylov",
|
||||
@ -78,6 +79,8 @@
|
||||
"Serhii",
|
||||
"shemp",
|
||||
"SNAPPROCESS",
|
||||
"sonarcloud",
|
||||
"sonarqube",
|
||||
"Sorin",
|
||||
"subproject",
|
||||
"subprojects",
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
|
||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
After Width: | Height: | Size: 2.4 KiB |
@ -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
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||

|
||||
|
||||
**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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
104
src/apps/deskflow-core/CoreArgParser.cpp
Normal 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;
|
||||
}
|
||||
46
src/apps/deskflow-core/CoreArgParser.h
Normal 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;
|
||||
};
|
||||
24
src/apps/deskflow-core/CoreArgs.h
Normal 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};
|
||||
};
|
||||
@ -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;
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
7
src/apps/res/entitlements-dev.plist
Normal 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>
|
||||
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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@
|
||||
|
||||
|
||||
@ -26,11 +26,6 @@ Arch::Arch()
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
Arch::Arch(Arch *arch)
|
||||
{
|
||||
s_instance = arch;
|
||||
}
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
void Arch::init()
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {};
|
||||
}
|
||||
@ -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 {};
|
||||
}
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -23,11 +23,6 @@ void ArchLogUnix::closeLog()
|
||||
closelog();
|
||||
}
|
||||
|
||||
void ArchLogUnix::showLog(bool)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchLogUnix::writeLog(LogLevel level, const QString &msg)
|
||||
{
|
||||
// convert level
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
15
src/lib/base/EventQueueTimer.h
Normal 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;
|
||||
};
|
||||
@ -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.
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace deskflow {
|
||||
|
||||
/**
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Common.h"
|
||||
#include <string>
|
||||
|
||||
namespace deskflow::filesystem {
|
||||
|
||||
@ -126,5 +126,5 @@ public:
|
||||
|
||||
private:
|
||||
Container c;
|
||||
Compare comp;
|
||||
[[no_unique_address]] Compare comp;
|
||||
};
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include "base/Unicode.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// local utility functions
|
||||
//
|
||||
|
||||
@ -7,8 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/Common.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
//! Unicode utility functions
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string> //IWYU
|
||||
|
||||
namespace deskflow::client {
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/Event.h"
|
||||
#include "deskflow/ClipboardTypes.h"
|
||||
#include "deskflow/KeyTypes.h"
|
||||
#include "deskflow/languages/LanguageManager.h"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
@ -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
@ -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;
|
||||
};
|
||||
65
src/lib/common/PlatformInfo.h
Normal 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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||