Compare commits
535 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56f7a0d7b5 | |||
| 3b27fa5771 | |||
| 72ddcdb7f5 | |||
| 4a7d031bc6 | |||
| 45fcdde636 | |||
| 9a799294f7 | |||
| 46db468ede | |||
| 28957a3fa8 | |||
| 2c55f4fe06 | |||
| a4c82869a6 | |||
| f33848f9b8 | |||
| f497b6886a | |||
| 21d38ff444 | |||
| e828563190 | |||
| c7873f2f81 | |||
| 9f72b44d2a | |||
| 8dcd9b0c01 | |||
| e436b41ab0 | |||
| c2eae36650 | |||
| 1079c22736 | |||
| d69febe2dc | |||
| c6a97c24a2 | |||
| d9ec93e7ee | |||
| 9db11a90eb | |||
| c327c80d63 | |||
| 0884a267d1 | |||
| d1b810baa5 | |||
| 6bf25c0089 | |||
| f4b561ac64 | |||
| 28c690074c | |||
| 97c5a17e9d | |||
| f51abad8cc | |||
| ae5ba4e51f | |||
| 9413393057 | |||
| 79b445373b | |||
| 9beea1abfe | |||
| a88a67d47f | |||
| 7fd9070a80 | |||
| e37cea2d6b | |||
| 181d862838 | |||
| 6ca980e3e5 | |||
| 94be5f7498 | |||
| 32e6a84a6f | |||
| b9964b5e90 | |||
| 422b4c3a23 | |||
| 05e32497d1 | |||
| 9905f32738 | |||
| 29e9b8ae3b | |||
| fe3c41f857 | |||
| 3ececbcfd9 | |||
| c12aa4c6e9 | |||
| fc58688bb0 | |||
| 77bdde5434 | |||
| 3472ff6ce5 | |||
| 082d27a88c | |||
| 2d3105206d | |||
| c6778dc9be | |||
| 36e985d8ab | |||
| 865365b2fa | |||
| 8a83f68ff0 | |||
| 245fa815d2 | |||
| f12f312dab | |||
| dded2e3711 | |||
| d71d4eaf07 | |||
| bc9f47c957 | |||
| 0ea2576032 | |||
| 0100cb796e | |||
| 8ec40dd74b | |||
| 4034d15fb3 | |||
| 1a9f8d5cdc | |||
| 66ddaca410 | |||
| 2c2a5c0269 | |||
| e42d3b45b6 | |||
| 5aa9b1bfd4 | |||
| cfbfa88cdc | |||
| 4a92a3295b | |||
| ede27975d7 | |||
| a175c293f6 | |||
| b39b133c9d | |||
| 5976da7e5b | |||
| 7fe50748f3 | |||
| 0474e10b03 | |||
| 2421a8b725 | |||
| ab4fbd1c85 | |||
| 8f6f014bcd | |||
| 47d44db497 | |||
| 879283f46f | |||
| cbbfd495e3 | |||
| 1692bec7e6 | |||
| f9f12a9500 | |||
| 2b38fe6f91 | |||
| 1479a50af5 | |||
| 4c2b9eb9e4 | |||
| 872db8910e | |||
| e06b6b0be4 | |||
| a04572a8c2 | |||
| 5d434d9857 | |||
| 958e14cb13 | |||
| 7116ddac86 | |||
| 5d594dd6be | |||
| f784705a7e | |||
| 22b1e8e543 | |||
| cf9e2ecf49 | |||
| cb508f5c3a | |||
| 26cc85e878 | |||
| 776b02aafc | |||
| 42cd01efd7 | |||
| a7a54ad8a7 | |||
| 20bc88eb1f | |||
| 969b642e0b | |||
| a89f3b6892 | |||
| a973d4277c | |||
| b8124107aa | |||
| 0691d586e7 | |||
| e8c85611a0 | |||
| 0321b2d36e | |||
| f4a49749c7 | |||
| 59ed15628e | |||
| dddb6aadf1 | |||
| 2110411c6d | |||
| b29c5b1bb2 | |||
| 07a217f54f | |||
| aacf922319 | |||
| a80e255bb0 | |||
| 6299f04b59 | |||
| 97006889bd | |||
| f0bb4e5cb3 | |||
| dcb047b6c5 | |||
| 551368a2d2 | |||
| 4d738b4784 | |||
| 06a616ef42 | |||
| 90a651b409 | |||
| dbc7aebfbc | |||
| 024436d82f | |||
| b6bc469744 | |||
| c4e47c0f2c | |||
| 3d712d5621 | |||
| 4e2ad25fc1 | |||
| 66e201c8ef | |||
| 3a35b183d3 | |||
| c15214aee7 | |||
| 5fd4d93f7f | |||
| 84e289bc30 | |||
| 04943fad79 | |||
| a7c1dc4520 | |||
| a73e8df01e | |||
| 0d6837a948 | |||
| ed99306417 | |||
| 782702fe74 | |||
| 5686d24626 | |||
| 818d588b65 | |||
| 86e109baf5 | |||
| fa1ee0690e | |||
| d535593d1d | |||
| f89168d00a | |||
| fb3c8eb965 | |||
| e247f3813a | |||
| f4618ee085 | |||
| 5430625a7e | |||
| eac59768ea | |||
| cb05ece6b6 | |||
| 044c9b24f3 | |||
| a3fe66f213 | |||
| 60a148ce68 | |||
| da5a2088c8 | |||
| 89abcfb238 | |||
| 87704fedb1 | |||
| 3eef21c8df | |||
| 6fbb60a3c2 | |||
| a5522729aa | |||
| 36b4bb09bc | |||
| 8170dd4e99 | |||
| c2a658256e | |||
| bc350852e7 | |||
| 898629f824 | |||
| 7b437b8d35 | |||
| 03b062c96e | |||
| 6c5d08ba7f | |||
| b77d722577 | |||
| bd8e2c5335 | |||
| 13c993c798 | |||
| 7e568121ee | |||
| 115ee2b8af | |||
| 3e4a085bc1 | |||
| 0ce5c2de9a | |||
| ffce22f5a9 | |||
| e40cc11fd5 | |||
| ebec6331cd | |||
| 197b9ed886 | |||
| 8f7fbc0a21 | |||
| 0f5a04825a | |||
| b8835a8a5a | |||
| 8bb6056469 | |||
| 3adcb64f57 | |||
| bc9b1d1178 | |||
| e038dd2b0b | |||
| 1286c18e1d | |||
| 5d94dd0dbf | |||
| b3e03f2856 | |||
| b62e82e792 | |||
| 7837bf416e | |||
| d4d6cbb7fb | |||
| d5910d77ec | |||
| d01a023a8b | |||
| d246d9527a | |||
| a31cff0225 | |||
| da06e97521 | |||
| 7e4d43f1cc | |||
| 0096283779 | |||
| 0751639f93 | |||
| d719df1642 | |||
| 7abaca76af | |||
| 5ee3fc41bd | |||
| cab1eb9cee | |||
| 51db919d8f | |||
| 314899ac7d | |||
| 632c3462ec | |||
| d6f8cdf51d | |||
| 59173d6405 | |||
| d597d023b1 | |||
| 807e60ded2 | |||
| 0b4a2e0e5b | |||
| 9eabbb690c | |||
| 3eb3969a01 | |||
| d2ac36f3ed | |||
| e6a374369b | |||
| 9c56fa5dda | |||
| 6875969255 | |||
| aa1eaf601c | |||
| f77213af04 | |||
| a4f1a382d3 | |||
| 647201cd37 | |||
| e24b1c4b68 | |||
| 475b895755 | |||
| 593e53370a | |||
| be6a4f6b4d | |||
| 929131d10b | |||
| 767919b342 | |||
| 4224215991 | |||
| 45090d6b85 | |||
| 476f3ba243 | |||
| a33574e1bd | |||
| 456afaa13e | |||
| 17f12d8334 | |||
| 723671c2e5 | |||
| 5a90b51e97 | |||
| fe12972e83 | |||
| 9bdb4252e5 | |||
| c73729e72e | |||
| 04805d9a7e | |||
| 293c5e394c | |||
| 3cf0fb89ef | |||
| 52b0c1f061 | |||
| c224cd4661 | |||
| 635962618f | |||
| 0ff72441e6 | |||
| bdffda9a2f | |||
| cac63937ff | |||
| 12fbc2fd57 | |||
| 06324a941a | |||
| 8dd9e17e72 | |||
| a5e5bbc2e4 | |||
| 34f6186941 | |||
| 486b5a491f | |||
| 037fee0e59 | |||
| a9e0c62c24 | |||
| 43a51434ce | |||
| eaa4c71d6e | |||
| 393a006773 | |||
| 332e6c4a4d | |||
| 63e87c16d1 | |||
| 14cb921d4c | |||
| de583145d0 | |||
| 0309d35aef | |||
| c4704649ea | |||
| e4e332c13f | |||
| 33edb6ce01 | |||
| 49173b11b9 | |||
| 81392fe758 | |||
| 3ae2b3a571 | |||
| 2aee6d8812 | |||
| db3b18b36d | |||
| 89c199b630 | |||
| d023c8ef77 | |||
| af383decca | |||
| 8526facbe9 | |||
| 2b3ecd39c6 | |||
| 49589ae4aa | |||
| 8ce6a07d18 | |||
| a8eb772b68 | |||
| 0916009601 | |||
| f5cb636b31 | |||
| 7ced794317 | |||
| 7c279ec72d | |||
| 83a8d1c4ac | |||
| 84a9367124 | |||
| aaeef7905a | |||
| a0d0936433 | |||
| 8f53b88bb6 | |||
| d8b4fab9a3 | |||
| 9d867f6146 | |||
| 632be4b240 | |||
| b8c6d1b58f | |||
| 139dbf61f3 | |||
| e512125595 | |||
| ae37543a37 | |||
| 8a80c52208 | |||
| 38dc845c72 | |||
| b56a282a9c | |||
| d742901775 | |||
| 287d70a138 | |||
| 6c0843d088 | |||
| cbcaafe764 | |||
| 29a86cfbac | |||
| d7506697c8 | |||
| 6c2457d60b | |||
| 30d9034a0f | |||
| a2acfb0678 | |||
| 7fa326234e | |||
| baaa02cda2 | |||
| 231e5c0bc5 | |||
| 78cbe4b775 | |||
| a1a7c8f3ff | |||
| 501726d471 | |||
| b51dec01ad | |||
| 1c907991af | |||
| 0c3c913989 | |||
| 4479b90d37 | |||
| 8a139f0e3d | |||
| 810322623b | |||
| 245f9db5c8 | |||
| 807f32975a | |||
| 5365e34f08 | |||
| 55a7fa3266 | |||
| 118f867fd1 | |||
| 13dc055405 | |||
| 99dae58fd3 | |||
| e9ccd513f7 | |||
| 903401796e | |||
| 97863bda7c | |||
| 0c94adf03c | |||
| d643e4b189 | |||
| c8734755c5 | |||
| edea3b0df1 | |||
| f4264d71a1 | |||
| 6ffd0eb471 | |||
| 7619928d56 | |||
| d70c7fe720 | |||
| b26ca77ff1 | |||
| 68db2c3484 | |||
| 81c19c6ee8 | |||
| 1276a4a29c | |||
| d97f4d87ad | |||
| d5cbd002a9 | |||
| 31a6fd23e7 | |||
| f6c6d4e3a7 | |||
| 5801b92636 | |||
| b427ac91c2 | |||
| e87c596fbe | |||
| 35b4749627 | |||
| 6b2a28600a | |||
| 600378b72d | |||
| 8e24689f72 | |||
| 07f9c4ba47 | |||
| 9d476e143e | |||
| 6bcdd4ebe7 | |||
| 9085468d1c | |||
| 7bace4d36d | |||
| ca0d095283 | |||
| ace5b54912 | |||
| c99767c8b7 | |||
| 297b781578 | |||
| 8c4dc001bf | |||
| 9ca13a65d1 | |||
| 73566c061d | |||
| ed3118b81a | |||
| cc209b9b1f | |||
| ee22c31f32 | |||
| f376b049e4 | |||
| 57e3ff1fab | |||
| c39f69b64d | |||
| 218a506970 | |||
| 57cb7227a4 | |||
| 6aabb1e858 | |||
| 9aec1ad479 | |||
| c8d4a2186a | |||
| f68744a36d | |||
| 183d660186 | |||
| e20ef3ae55 | |||
| 0bfb95996a | |||
| 7d27031723 | |||
| ada495c681 | |||
| e91b9867e0 | |||
| d483dc38bd | |||
| 56dd5492b4 | |||
| 67c7ba6dae | |||
| 1138e3b1fb | |||
| 6523911e4d | |||
| a52552b821 | |||
| bd2188beb1 | |||
| edc0c0d7a0 | |||
| 95586c88fd | |||
| d7174a3a2f | |||
| 2289ed5845 | |||
| 2192636d2b | |||
| 6194868e89 | |||
| 00f53c1aac | |||
| 59df2db7b7 | |||
| 35f0e9e6e4 | |||
| 3b3d9e14fd | |||
| b3f179a08a | |||
| f3fca87e19 | |||
| a3093fa046 | |||
| c00ebbd6c1 | |||
| 09457793b4 | |||
| d436aa3001 | |||
| ec4851d326 | |||
| f338e5e7a8 | |||
| 55b48a5db7 | |||
| 352a871729 | |||
| b103f28202 | |||
| 39b49acb56 | |||
| 5d4fbc8ea6 | |||
| 3dfa3d5069 | |||
| 6d9c66b03f | |||
| 02b2b82c7f | |||
| aac086ad2a | |||
| 0d3f7248ed | |||
| da6e110b69 | |||
| a298cc84ad | |||
| d8348152ec | |||
| 572953d5ba | |||
| a23856ee09 | |||
| 637adc60b2 | |||
| 0ca847334e | |||
| d06d6a5003 | |||
| 6d856b674b | |||
| bcfc121c37 | |||
| b2ec2f1a34 | |||
| df9d5ded1c | |||
| f11dae97d4 | |||
| 3fdc5288eb | |||
| 955f9652fb | |||
| 9dedf25ecc | |||
| ee3dffc4f8 | |||
| 8123166e66 | |||
| 29626d6694 | |||
| 996af86b9b | |||
| 84fbe48d35 | |||
| 11423ef5a1 | |||
| 7cc108d87c | |||
| af70ff98b7 | |||
| b6f7497c44 | |||
| f6602baa36 | |||
| 72091c6ac4 | |||
| fb5f80857b | |||
| 6c7bdf3e07 | |||
| 1df01d3cf1 | |||
| 2bebdbaf4d | |||
| 63d65bb47b | |||
| 60dc4c0cd8 | |||
| 78d1c5222f | |||
| 6e3426855d | |||
| f49f5f790c | |||
| 4eaf6fda62 | |||
| ebc6186fe7 | |||
| 48f824ca31 | |||
| d6ace78a58 | |||
| 41fda741b3 | |||
| 793ea5f81c | |||
| 285abc7bb7 | |||
| d6c9897748 | |||
| ed4ecc2d81 | |||
| 794017402d | |||
| 4034302bfb | |||
| eab87e3869 | |||
| 294348b42b | |||
| 71b6f07220 | |||
| 9addada64c | |||
| b6196f147a | |||
| 2c4a68ebcf | |||
| d8f480b981 | |||
| 80afb92af1 | |||
| f5f3bc0475 | |||
| 17c7b0f8bf | |||
| fcaadc2cd4 | |||
| e3642f0955 | |||
| 07d837a39b | |||
| 1202145bb8 | |||
| abc963f886 | |||
| 4988196c47 | |||
| 8a2b6bd688 | |||
| 901dacf593 | |||
| c62b4ec068 | |||
| c8408c4823 | |||
| 8657532ca2 | |||
| 64b277a5df | |||
| cd7d53d2bd | |||
| d740574bd4 | |||
| 84ae597d69 | |||
| 16ef088c27 | |||
| 7e4b6b82f4 | |||
| daa095461d | |||
| fde880fb6f | |||
| a04568b25f | |||
| 6e6892b6e7 | |||
| 5ad2c9283d | |||
| 487030aade | |||
| 1ace03d4b5 | |||
| 5df333fae9 | |||
| 3b4306183c | |||
| 4203f42363 | |||
| 7e4ac48476 | |||
| 0b05b0e71d | |||
| ebb63d8113 | |||
| c1f1734943 | |||
| 12bcc1a4d6 | |||
| 84283a1b13 | |||
| 0a33e20723 | |||
| d4f916c365 | |||
| 6df96d4a56 | |||
| e617e4b537 | |||
| ca5cc8211b | |||
| f01b592dad | |||
| 46c6275c43 | |||
| b3fb8959a3 | |||
| 8354a81706 | |||
| d9807a2693 | |||
| 1b8067797e | |||
| cf4fe32aab | |||
| 6bbebe75f9 | |||
| 0b9ecbc2f4 | |||
| fc36cf6be8 | |||
| b7b295aeb6 | |||
| 00b5c32fc9 |
37
.github/actions/install-dependencies/action.yml
vendored
37
.github/actions/install-dependencies/action.yml
vendored
@ -13,8 +13,8 @@ inputs:
|
||||
description: "The version of Qt to install (Windows & macOS)"
|
||||
required: false
|
||||
|
||||
qt-install-dir:
|
||||
description: "The path to install Qt into (Windows & macOS)"
|
||||
vcpkg-triplet:
|
||||
description: "vcpkg triplet to use (Windows)"
|
||||
required: false
|
||||
|
||||
outputs:
|
||||
@ -27,11 +27,10 @@ runs:
|
||||
|
||||
steps:
|
||||
- name: Install Depends
|
||||
if: ${{ runner.os != 'Windows' }}
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
echo "Window not supported yet"
|
||||
elif [ "$RUNNER_OS" == "macOS" ]; then
|
||||
brew install cmake googletest ninja openssl --quiet
|
||||
if [ "$RUNNER_OS" == "macOS" ]; then
|
||||
brew install googletest openssl --quiet
|
||||
elif [ "$RUNNER_OS" == "Linux" ]; then
|
||||
if [ ${{inputs.like}} == "debian" ]; then
|
||||
apt update -qqq > /dev/null
|
||||
@ -39,25 +38,26 @@ 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 -y >/dev/null
|
||||
libei-dev libportal-dev libtomlplusplus-dev libcli11-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
|
||||
cli11-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
|
||||
libportal-devel tomlplusplus-devel cli11-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 gtk3 tomlplusplus cli11
|
||||
qt6-base qt6-tools qt6-svg gtk3 tomlplusplus cli11 help2man doxygen graphviz rsync
|
||||
else
|
||||
echo "Unknown like"
|
||||
fi
|
||||
@ -69,29 +69,22 @@ runs:
|
||||
- name: Install Qt
|
||||
if: ${{runner.os != 'Linux' }}
|
||||
uses: jurplel/install-qt-action@v4
|
||||
env:
|
||||
AQT_CONFIG: ${{ github.workspace }}/.github/actions/install-dependencies/aqt.ini
|
||||
with:
|
||||
dir: ${{inputs.qt-install-dir}}
|
||||
version: ${{inputs.qt-version}}
|
||||
cache: true
|
||||
cache-key-prefix: ${{matrix.target.os}}-${{inputs.qt-version}}
|
||||
|
||||
# Install Ninja with an action instead of using Chocolatey, as it's more
|
||||
# reliable and faster. The Ninja install action is pretty good as it
|
||||
# downloads directly from the `ninja-build` GitHub project releases.
|
||||
- name: Install Ninja
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
uses: seanmiddleditch/gha-setup-ninja@master
|
||||
|
||||
- name: Build and cache vcpkg
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
id: vcpkg
|
||||
uses: johnwason/vcpkg-action@v6
|
||||
uses: johnwason/vcpkg-action@v7
|
||||
with:
|
||||
pkgs: gtest openssl
|
||||
extra-args: --classic
|
||||
triplet: x64-windows-release
|
||||
extra-args: --classic --host-triplet=${{inputs.vcpkg-triplet}}
|
||||
triplet: ${{inputs.vcpkg-triplet}}
|
||||
token: ${{ github.token }}
|
||||
github-binarycache: true
|
||||
|
||||
- name: Install Wix
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
|
||||
14
.github/actions/install-dependencies/aqt.ini
vendored
Normal file
14
.github/actions/install-dependencies/aqt.ini
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
[aqt]
|
||||
# Using this mirror instead of download.qt.io because of timeouts in CI
|
||||
baseurl: https://qt.mirror.constant.com
|
||||
|
||||
[requests]
|
||||
hash_algorithm: sha1
|
||||
|
||||
[mirrors]
|
||||
trusted_mirrors:
|
||||
https://qt.mirror.constant.com
|
||||
fallbacks:
|
||||
https://qt.mirror.constant.com
|
||||
https://mirrors.ocf.berkeley.edu
|
||||
https://download.qt.io
|
||||
24
.github/actions/run-tests/action.yml
vendored
24
.github/actions/run-tests/action.yml
vendored
@ -15,12 +15,12 @@ runs:
|
||||
using: "composite"
|
||||
|
||||
steps:
|
||||
- name: Unit tests
|
||||
- name: Unit Tests
|
||||
id: unittests
|
||||
env:
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
run: |
|
||||
./${{ inputs.bin-dir }}/unittests
|
||||
ctest --test-dir "build/src/unittests" --output-on-failure
|
||||
result=$?
|
||||
|
||||
if [ $result -ne 0 ]; then
|
||||
@ -29,16 +29,16 @@ runs:
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
|
||||
- name: Integration tests
|
||||
id: integtests
|
||||
- name: Legacy Tests
|
||||
id: legacytests
|
||||
env:
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
run: |
|
||||
./${{ inputs.bin-dir }}/integtests
|
||||
./${{ inputs.bin-dir }}/legacytests
|
||||
result=$?
|
||||
|
||||
if [ $result -ne 0 ]; then
|
||||
echo "Integration tests failed with code: $result" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Legacy tests failed with code: $result" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
@ -49,11 +49,11 @@ runs:
|
||||
pass="✅ Pass"
|
||||
fail="❌ Fail"
|
||||
unittests_outcome="${{ steps.unittests.outcome }}"
|
||||
integtests_outcome="${{ steps.integtests.outcome }}"
|
||||
legacytests_outcome="${{ steps.legacytests.outcome }}"
|
||||
unittests=$( [ "$unittests_outcome" = "success" ] && echo $pass || echo $fail )
|
||||
integtests=$( [ "$integtests_outcome" = "success" ] && echo $pass || echo $fail )
|
||||
legacytests=$( [ "$legacytests_outcome" = "success" ] && echo $pass || echo $fail )
|
||||
echo "unittests=$unittests" >> $GITHUB_OUTPUT
|
||||
echo "integtests=$integtests" >> $GITHUB_OUTPUT
|
||||
echo "legacytests=$legacytests" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Summary row
|
||||
@ -64,7 +64,7 @@ runs:
|
||||
row=""
|
||||
row+="| ${{ inputs.job }} "
|
||||
row+="| ${{ steps.results.outputs.unittests }} "
|
||||
row+="| ${{ steps.results.outputs.integtests }} |"
|
||||
row+="| ${{ steps.results.outputs.legacytests }} "
|
||||
echo "$row" > $file
|
||||
|
||||
echo "file=$file" > $GITHUB_OUTPUT
|
||||
@ -77,8 +77,8 @@ runs:
|
||||
path: ${{ steps.row.outputs.file }}
|
||||
|
||||
- name: Check test outcome
|
||||
if: steps.unittests.outcome == 'failure'
|
||||
if: (steps.unittests.outcome != 'success' || steps.legacytests.outcome != 'success')
|
||||
run: |
|
||||
echo "Unit tests failed"
|
||||
echo "Tests failed"
|
||||
exit 1
|
||||
shell: bash
|
||||
|
||||
5
.github/actions/winget-publish/action.yaml
vendored
5
.github/actions/winget-publish/action.yaml
vendored
@ -21,12 +21,13 @@ runs:
|
||||
Invoke-WebRequest https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||
|
||||
$packageId = "Deskflow.Deskflow"
|
||||
$installerUrl = "https://github.com/deskflow/deskflow/releases/download/v${{ inputs.release-version }}/deskflow-${{ inputs.release-version }}-win-x64.msi"
|
||||
$x64Url = "https://github.com/deskflow/deskflow/releases/download/v${{ inputs.release-version }}/deskflow-${{ inputs.release-version }}-win-x64.msi"
|
||||
$arm64Url = "https://github.com/deskflow/deskflow/releases/download/v${{ inputs.release-version }}/deskflow-${{ inputs.release-version }}-win-arm64.msi"
|
||||
|
||||
# Submit package update
|
||||
.\wingetcreate.exe update "$packageId" `
|
||||
--version "${{ inputs.release-version }}" `
|
||||
--urls "$installerUrl" `
|
||||
--urls "$x64Url|x64" "$arm64Url|arm64"`
|
||||
--submit `
|
||||
--token "${{ inputs.token }}"
|
||||
shell: pwsh
|
||||
|
||||
78
.github/workflows/continuous-integration.yml
vendored
78
.github/workflows/continuous-integration.yml
vendored
@ -28,7 +28,7 @@ env:
|
||||
GIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
PACKAGE_PREFIX: "deskflow"
|
||||
PACKAGE_PATH: ./dist
|
||||
CMAKE_CONFIGURE: "cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_COMPILE_WARNING_AS_ERROR=ON"
|
||||
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.
|
||||
@ -133,20 +133,29 @@ jobs:
|
||||
runs-on: "windows-2022"
|
||||
timeout: 30
|
||||
config-args: "-G Ninja"
|
||||
qt-install-dir: "C:"
|
||||
qt-version: 6.9.0
|
||||
vcpkg-triplet: x64-windows-release
|
||||
arch: "amd64"
|
||||
|
||||
- name: "windows-2022-arm64"
|
||||
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"
|
||||
timeout: 10
|
||||
arch: arm64
|
||||
config-args: "-DCMAKE_OSX_ARCHITECTURES=\"arm64\""
|
||||
qt-install-dir: "/Users/runner"
|
||||
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
|
||||
|
||||
- name: "macos-13-x64"
|
||||
runs-on: macos-13
|
||||
timeout: 20
|
||||
config-args: "-DCMAKE_OSX_ARCHITECTURES=\"x86_64\""
|
||||
qt-install-dir: "/Users/runner"
|
||||
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
|
||||
|
||||
- name: "debian-13-x86_64"
|
||||
runs-on: ubuntu-latest
|
||||
@ -162,6 +171,20 @@ jobs:
|
||||
timeout: 20
|
||||
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
|
||||
|
||||
- name: "fedora-42-x86_64"
|
||||
runs-on: ubuntu-latest
|
||||
container: fedora:42
|
||||
like: "fedora"
|
||||
timeout: 20
|
||||
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
|
||||
|
||||
- name: "fedora-42-arm64"
|
||||
runs-on: ubuntu-24.04-arm
|
||||
container: fedora:42
|
||||
like: "fedora"
|
||||
timeout: 20
|
||||
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
|
||||
|
||||
- name: "fedora-41-x86_64"
|
||||
runs-on: ubuntu-latest
|
||||
container: fedora:41
|
||||
@ -176,20 +199,6 @@ jobs:
|
||||
timeout: 20
|
||||
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
|
||||
|
||||
- name: "fedora-40-x86_84"
|
||||
runs-on: ubuntu-latest
|
||||
container: fedora:40
|
||||
like: "fedora"
|
||||
timeout: 20
|
||||
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
|
||||
|
||||
- name: "fedora-40-arm64"
|
||||
runs-on: ubuntu-24.04-arm
|
||||
container: fedora:40
|
||||
like: "fedora"
|
||||
timeout: 20
|
||||
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
|
||||
|
||||
- name: "opensuse-x86_84"
|
||||
runs-on: ubuntu-latest
|
||||
container: opensuse/tumbleweed:latest
|
||||
@ -209,7 +218,7 @@ jobs:
|
||||
container: archlinux:latest
|
||||
like: "arch"
|
||||
timeout: 20
|
||||
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr"
|
||||
config-args: "-G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_DEV_DOCS=ON"
|
||||
|
||||
- name: "ubuntu-25.04-x86_64"
|
||||
runs-on: ubuntu-latest
|
||||
@ -253,13 +262,16 @@ jobs:
|
||||
- name: Setup VC++ environment
|
||||
if: ${{ runner.os == 'Windows' }}
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{matrix.target.arch}}
|
||||
|
||||
|
||||
- name: Install dependencies
|
||||
id: get-deps
|
||||
uses: ./.github/actions/install-dependencies
|
||||
with:
|
||||
qt-version: 6.8.2
|
||||
qt-install-dir: ${{matrix.target.qt-install-dir}}
|
||||
qt-version: ${{ matrix.target.qt-version }}
|
||||
vcpkg-triplet: ${{matrix.target.vcpkg-triplet}}
|
||||
like: ${{ matrix.target.like }}
|
||||
|
||||
- name: Get version
|
||||
@ -291,6 +303,13 @@ jobs:
|
||||
with:
|
||||
job: ${{ matrix.target.name }}
|
||||
|
||||
- name: Update Development Documentation
|
||||
if: matrix.target.like == 'arch' && github.ref == 'refs/heads/master'
|
||||
uses: JamesIves/github-pages-deploy-action@v4.7.3
|
||||
with:
|
||||
branch: gh-pages
|
||||
folder: build/doc/dev/html
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@ -316,21 +335,20 @@ jobs:
|
||||
# it also makes sure we can use git --describe correctly
|
||||
- name: Fancy Checkout
|
||||
uses: sithlord48/fancy-checkout@v1
|
||||
|
||||
- name: Build on FreeBSD
|
||||
if: ${{ matrix.distro.name == 'freebsd' }}
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
run: |
|
||||
./scripts/install_deps.sh
|
||||
pkg install -y cmake ninja gmake gcc12 openssl glib \
|
||||
libX11 libXtst libxkbfile qt6-base qt6-tools gtk3 googletest \
|
||||
tomlplusplus cli11 pkgconf libei libportal
|
||||
${{env.CMAKE_CONFIGURE}} -G Ninja
|
||||
cmake --build build -j16
|
||||
|
||||
# Integration tests are flakey by nature, make them optional.
|
||||
export QT_QPA_PLATFORM=offscreen
|
||||
./build/bin/unittests
|
||||
./build/bin/integtests || true
|
||||
./build/bin/unittests || true
|
||||
flatpak:
|
||||
needs: lint-check
|
||||
name: flatpak-${{matrix.flatpak.arch}}
|
||||
@ -363,7 +381,7 @@ jobs:
|
||||
run: flatpak-builder-lint manifest deploy/linux/flatpak/org.deskflow.deskflow.yml
|
||||
|
||||
- name: Build
|
||||
uses: flathub-infra/flatpak-github-actions/flatpak-builder@53987ffa5f687586936d85fdce3f440848bea04d
|
||||
uses: flatpak/flatpak-github-actions/flatpak-builder@v6
|
||||
with:
|
||||
bundle: deskflow-${{env.DESKFLOW_PACKAGE_VERSION}}-linux-${{matrix.flatpak.arch}}.flatpak
|
||||
manifest-path: deploy/linux/flatpak/org.deskflow.deskflow.yml
|
||||
|
||||
19
.github/workflows/sonarcloud-analysis.yml
vendored
19
.github/workflows/sonarcloud-analysis.yml
vendored
@ -44,28 +44,27 @@ jobs:
|
||||
-G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE="Debug" \
|
||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DSKIP_BUILD_TESTS=ON \
|
||||
-DENABLE_COVERAGE=ON
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
build-wrapper-linux-x86-64 --out-dir bw-output cmake --build build -j${CPU_CORE_COUNT}
|
||||
|
||||
- name: Unit tests coverage
|
||||
- name: Test coverage
|
||||
shell: bash
|
||||
env:
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
run: cmake --build build --target coverage-unittests
|
||||
|
||||
- name: Integration tests coverage
|
||||
env:
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
run: cmake --build build --target coverage-integtests
|
||||
run: |
|
||||
tests=(`cmake --build build --target help | grep -o "^coverage-[^:]*"`)
|
||||
for i in "${tests[@]}"; do
|
||||
cmake --build build --target "$i"
|
||||
done
|
||||
|
||||
- name: Get coverage report paths
|
||||
id: coverage-paths
|
||||
run: |
|
||||
unittests=$(find build -name coverage-unittests.xml)
|
||||
integtests=$(find build -name coverage-integtests.xml)
|
||||
paths="${unittests}${integtests:+,$integtests}"
|
||||
paths=$(ls -w 0 -m build/coverage-*.xml | sed 's/ //g')
|
||||
if [ -z "$paths" ]; then
|
||||
echo "Error: No coverage files found"
|
||||
exit 1
|
||||
|
||||
19
.github/workflows/valgrind-analysis.yml
vendored
19
.github/workflows/valgrind-analysis.yml
vendored
@ -31,16 +31,10 @@ jobs:
|
||||
run: cmake --build build -j8
|
||||
|
||||
- name: Valgrind unit tests
|
||||
id: unittests
|
||||
id: legacytests
|
||||
uses: ./.github/actions/run-valgrind
|
||||
with:
|
||||
executable: ./build/bin/unittests
|
||||
|
||||
- name: Valgrind integration tests
|
||||
id: integtests
|
||||
uses: ./.github/actions/run-valgrind
|
||||
with:
|
||||
executable: ./build/bin/integtests
|
||||
executable: ./build/bin/legacytests
|
||||
|
||||
- name: Set job summary
|
||||
run: |
|
||||
@ -48,14 +42,9 @@ jobs:
|
||||
message=$(cat <<EOF
|
||||
## Valgrind summary
|
||||
|
||||
### Unit tests
|
||||
### legacytests Unit tests
|
||||
$backticks
|
||||
${{ steps.unittests.outputs.summary }}
|
||||
$backticks
|
||||
|
||||
### Integration tests
|
||||
$backticks
|
||||
${{ steps.integtests.outputs.summary }}
|
||||
${{ steps.legacytests.outputs.summary }}
|
||||
$backticks
|
||||
EOF
|
||||
)
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -25,6 +25,9 @@ deskflow-config.toml
|
||||
/*.user
|
||||
*.ui.autosave
|
||||
|
||||
# generated vcpkg file
|
||||
vcpkg.json
|
||||
|
||||
#Generic linux files
|
||||
**/*.directory
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2024 Deskflow Developers
|
||||
# SPDX-FileCopyrightText: 2024 - 2025 Deskflow Developers
|
||||
# SPDX-FileCopyrightText: 2012 - 2024 Symless Ltd
|
||||
# SPDX-FileCopyrightText: 2009 - 2012 Nick Bolton
|
||||
# SPDX-License-Identifier: MIT
|
||||
@ -18,8 +18,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Fallback for when git can not be found
|
||||
set(DESKFLOW_VERSION_MAJOR 1)
|
||||
set(DESKFLOW_VERSION_MINOR 21)
|
||||
set(DESKFLOW_VERSION_PATCH 1)
|
||||
set(DESKFLOW_VERSION_MINOR 23)
|
||||
set(DESKFLOW_VERSION_PATCH 0)
|
||||
set(DESKFLOW_VERSION_TWEAK 0)
|
||||
|
||||
# Get the version from git if it's a git repository
|
||||
@ -68,11 +68,20 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#generate vcpkg file if needed
|
||||
if(WIN32)
|
||||
option (VCPKG_QT "Use Qt from VCPKG" OFF)
|
||||
if(VCPKG_QT)
|
||||
set(QT_LIBS ", \"qttranslations\", \"qtsvg\"")
|
||||
endif()
|
||||
configure_file(cmake/vcpkg.json.in ${CMAKE_SOURCE_DIR}/vcpkg.json @ONLY)
|
||||
endif()
|
||||
|
||||
#Define our project
|
||||
project(
|
||||
deskflow
|
||||
VERSION "${DESKFLOW_VERSION_MAJOR}.${DESKFLOW_VERSION_MINOR}.${DESKFLOW_VERSION_PATCH}.${DESKFLOW_VERSION_TWEAK}"
|
||||
DESCRIPTION "Mouse and keyboard sharing utility"
|
||||
DESCRIPTION "Keyboard and mouse sharing utility"
|
||||
LANGUAGES C CXX)
|
||||
|
||||
# Define Additional "PROJECT" vars for packaging and metadata
|
||||
@ -95,6 +104,22 @@ set(REQUIRED_LIBEI_VERSION 1.3)
|
||||
set(REQUIRED_LIBPORTAL_VERSION 0.8)
|
||||
set(REQUIRED_QT_VERSION 6.7.0)
|
||||
|
||||
if (WIN32)
|
||||
# VSCMD_ARG_TGT_ARCH is set on CI
|
||||
if ("$ENV{VSCMD_ARG_TGT_ARCH}" STREQUAL "")
|
||||
# NOT on CI
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "[Aa][Rr][Mm]64")
|
||||
set(BUILD_ARCHITECTURE arm64)
|
||||
else()
|
||||
set(BUILD_ARCHITECTURE x64)
|
||||
endif()
|
||||
else()
|
||||
set (BUILD_ARCHITECTURE $ENV{VSCMD_ARG_TGT_ARCH})
|
||||
endif()
|
||||
else()
|
||||
set (BUILD_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
# On Windows, require that the same MSVC runtime is used as on the host.
|
||||
# Mitigates things like access violations caused by accidental ABI-compatibility breakage.
|
||||
@ -102,7 +127,7 @@ if (MSVC)
|
||||
cmake_host_system_information(
|
||||
RESULT REQUIRED_MSVC_RUNTIME_MINOR
|
||||
QUERY WINDOWS_REGISTRY
|
||||
"HKLM/SOFTWARE/Microsoft/VisualStudio/${REQUIRED_MSVC_RUNTIME_MAJOR}.0/VC/Runtimes/x64"
|
||||
"HKLM/SOFTWARE/Microsoft/VisualStudio/${REQUIRED_MSVC_RUNTIME_MAJOR}.0/VC/Runtimes/${BUILD_ARCHITECTURE}"
|
||||
VALUE "Minor")
|
||||
if (REQUIRED_MSVC_RUNTIME_MINOR)
|
||||
message(STATUS "MSVC runtime: ${REQUIRED_MSVC_RUNTIME_MAJOR}.${REQUIRED_MSVC_RUNTIME_MINOR}")
|
||||
@ -128,6 +153,10 @@ endif()
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
|
||||
|
||||
# disables the use of signals,slots and emit
|
||||
# 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)
|
||||
|
||||
|
||||
13
CONTRIBUTING.md
Normal file
13
CONTRIBUTING.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Contributing to Deskflow
|
||||
|
||||
Thanks for your interest in contributing to Deskflow! We welcome all kinds of contributions — bug reports, feature suggestions, documentation improvements, and code.
|
||||
|
||||
## Read the Full Guidelines
|
||||
|
||||
To keep this repository clean and contribution-friendly, we've outlined our full contributing guidelines on the Deskflow Wiki:
|
||||
|
||||
👉 [How to Contribute to Deskflow](https://github.com/deskflow/deskflow/wiki/Contributing)
|
||||
|
||||
Please take a moment to read through the page before opening an issue or submitting a pull request.
|
||||
|
||||
Thanks again for helping make Deskflow better!
|
||||
105
README.md
105
README.md
@ -1,14 +1,8 @@
|
||||

|
||||
|
||||
> [!TIP]
|
||||
> [Synergy](https://symless.com/synergy) sponsors the Deskflow project by contributing code and providing financial support.
|
||||
>
|
||||
> - [**Bounties**](https://github.com/deskflow/deskflow/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22%F0%9F%92%8E%20bounty%22) - Earn while contributing to open source
|
||||
> - [**Rewarded**](https://github.com/deskflow/deskflow/issues?q=label%3A%22%F0%9F%92%B0%20rewarded%22%20) - Issues with a rewarded bounty
|
||||
>
|
||||
> **Deskflow** is the official upstream project for Synergy.
|
||||
> Purchasing a Synergy license is one way to support Deskflow’s growth and sustainability.
|
||||
> Learn more: [Relationship with Synergy](https://github.com/deskflow/deskflow/wiki/Relationship-with-Synergy)
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/deskflow/deskflow-artwork/blob/main/logo/deskflow-logo-dark-200px.png?raw=true">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://github.com/deskflow/deskflow-artwork/blob/main/logo/deskflow-logo-light-200px.png?raw=true">
|
||||
<img alt="Deskflow" src="https://github.com/user-attachments/assets/f005b958-24df-4f4a-9bfd-4f834dae59d6">
|
||||
</picture>
|
||||
|
||||
**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,
|
||||
@ -16,13 +10,25 @@ and work seamlessly between them.
|
||||
It's like a software KVM (but without the video).
|
||||
TLS encryption is enabled by default. Wayland is supported. Clipboard sharing is supported.
|
||||
|
||||
[](https://github.com/deskflow/deskflow/releases/latest) [](https://github.com/deskflow/deskflow/releases/continuous) [](https://flathub.org/apps/org.deskflow.deskflow)
|
||||
> [!TIP]
|
||||
>
|
||||
> **Chat with us**
|
||||
>
|
||||
> - Main discussion on Matrix: [`#deskflow:matrix.org`](https://matrix.to/#/#deskflow:matrix.org) ([Matrix clients](https://matrix.org/ecosystem/clients/))
|
||||
> - Discussion also happens on IRC: `#deskflow` or `#deskflow-dev` on [Libera Chat](https://libera.chat/)
|
||||
> - Start a [new discussion](https://github.com/deskflow/deskflow/discussions) on our GitHub project.
|
||||
|
||||
To use Deskflow you can use one of our [packages](https://github.com/deskflow/deskflow/releases), install `deskflow` (if available in your package repository), or [build it](#build-quick-start) yourself from source.
|
||||
## Download
|
||||
|
||||
[](https://github.com/deskflow/deskflow/releases/latest) [](https://github.com/deskflow/deskflow/releases/continuous) [](https://flathub.org/apps/org.deskflow.deskflow)
|
||||
|
||||
> [!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).
|
||||
|
||||
To use Deskflow, download one of our [packages](https://github.com/deskflow/deskflow/releases), install `deskflow` (from your package repository), or [build it](https://github.com/deskflow/deskflow/wiki/Building) from source.
|
||||
|
||||
## Stats
|
||||
|
||||
[](https://github.com/deskflow/deskflow/commits/master/)
|
||||
[](https://github.com/deskflow/deskflow/commits/master/)
|
||||
[](LICENSE)
|
||||
@ -36,27 +42,16 @@ To use Deskflow you can use one of our [packages](https://github.com/deskflow/de
|
||||
[](https://github.com/deskflow/deskflow/actions/workflows/continuous-integration.yml)
|
||||
[](https://github.com/deskflow/deskflow/actions/workflows/codeql-analysis.yml)
|
||||
[](https://github.com/deskflow/deskflow/actions/workflows/sonarcloud-analysis.yml)
|
||||
## Project Values
|
||||
|
||||
- Motivated by the community interests (not business-driven)
|
||||
- Privacy by default (e.g. update check is off by default)
|
||||
- Leading edge releases (we don't focus on supporting older systems)
|
||||
- Decisions are discussed and documented publicly with majority rule
|
||||
- Have fun; we don't need to worry about impressing anyone
|
||||
## Contribute
|
||||
|
||||
## Ways to get involved
|
||||
[](https://github.com/deskflow/deskflow/labels/good%20first%20issue) [](https://github.com/deskflow/deskflow/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22%F0%9F%92%8E%20bounty%22) [](https://github.com/deskflow/deskflow/issues?q=label%3A%22%F0%9F%92%B0%20rewarded%22%20sort%3Aupdated-desc)
|
||||
|
||||
> [!TIP]
|
||||
> Join us! Real-time discussion on Matrix: [`#deskflow:matrix.org`](https://matrix.to/#/#deskflow:matrix.org)
|
||||
>
|
||||
> Alternatively, we have [other ways](https://github.com/deskflow/deskflow/wiki/Chat-with-us) to communicate.
|
||||
>
|
||||
Here are a few ways to join in with the project and get involved:
|
||||
* Build the latest `master` version (see below) and [report a bug](https://github.com/deskflow/deskflow/issues)
|
||||
* [Submit a PR](https://github.com/deskflow/deskflow/wiki/Contributing) (pull request) with a bug fix or improvement
|
||||
* [Let us know](https://github.com/deskflow/deskflow/issues) if you have an idea for an improvement
|
||||
There are many ways to contribute to the Deskflow project.
|
||||
|
||||
## Build Quick Start
|
||||
We're a friendly, active, and welcoming community focused on building a great app.
|
||||
|
||||
Read our [Contributing](https://github.com/deskflow/deskflow/wiki/Contributing) page to get started.
|
||||
|
||||
For instructions on building Deskflow, use the wiki page: [Building](https://github.com/deskflow/deskflow/wiki/Building)
|
||||
|
||||
@ -65,11 +60,11 @@ 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
|
||||
> 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)
|
||||
> 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.
|
||||
Windows 10 or higher is required.
|
||||
|
||||
macOS 12 or higher is required.
|
||||
|
||||
@ -102,46 +97,39 @@ macOS users who download directly from releases may need to run `xattr -c /Appli
|
||||
It is recommend to install Deskflow using [Homebrew](https://brew.sh) from our [homebrew-tap](https://github.com/deskflow/homebrew-tap)
|
||||
|
||||
To add our tap, run:
|
||||
|
||||
```
|
||||
brew tap deskflow/homebrew-tap
|
||||
```
|
||||
|
||||
Then install either:
|
||||
|
||||
- Stable: `brew install deskflow`
|
||||
- Continuous: `brew install deskflow-dev`
|
||||
|
||||
|
||||
|
||||
## Collaborative Projects
|
||||
## Similar Projects
|
||||
|
||||
In the open source developer community, similar projects collaborate for the improvement of all
|
||||
mouse and keyboard sharing tools. We aim for idea sharing and interoperability.
|
||||
|
||||
* [**Lan Mouse**](https://github.com/feschber/lan-mouse) -
|
||||
- [**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) -
|
||||
- [**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://github.com/deskflow/deskflow/wiki/Relationship-with-Synergy) -
|
||||
Downstream commercial fork and Deskflow sponsor, geared toward adapting to customer
|
||||
needs, offering business and enterprise licensing.
|
||||
- [**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)).
|
||||
|
||||
## FAQ
|
||||
|
||||
### What is the relationship with Synergy?
|
||||
|
||||
[](https://github.com/deskflow/deskflow/wiki/Relationship-with-Synergy)
|
||||
|
||||
Synergy sponsors the Deskflow project by contributing code and providing financial support while maintaining its customer-oriented code downstream.
|
||||
|
||||
Learn more: [Relationship with Synergy](https://github.com/deskflow/deskflow/wiki/Relationship-with-Synergy)
|
||||
|
||||
### Is Deskflow compatible with Synergy, Input Leap, or Barrier?
|
||||
|
||||
Yes, Deskflow has network compatibility with all forks:
|
||||
|
||||
- Requires Deskflow >= v1.17.0.96
|
||||
- Deskflow will *just work* with Input Leap and Barrier (server or client).
|
||||
- Connecting a Deskflow client to a Synergy 1 server will also *just work*.
|
||||
- Deskflow will _just work_ with Input Leap and Barrier (server or client).
|
||||
- Connecting a Deskflow client to a Synergy 1 server will also _just work_.
|
||||
- To connect a Synergy 1 client, you need to select the Synergy protocol in the Deskflow server settings.
|
||||
|
||||
_Note:_ Only Synergy 1 is compatible with Deskflow (Synergy 3 is not yet compatible).
|
||||
@ -153,20 +141,20 @@ We would love to see compatibility with Lan Mouse. This may be quite an effort a
|
||||
### If I want to solve issues in Deskflow do I need to contribute to a fork?
|
||||
|
||||
We welcome PRs (pull requests) from the community. If you'd like to make a change, please feel
|
||||
free to [start a discussion](https://github.com/deskflow/deskflow/discussions) or
|
||||
free to [start a discussion](https://github.com/deskflow/deskflow/discussions) or
|
||||
[open a PR](https://github.com/deskflow/deskflow/wiki/Contributing).
|
||||
|
||||
### Is clipboard sharing supported?
|
||||
|
||||
Absolutely. The clipboard-sharing feature is a cornerstone feature of the product and we are
|
||||
Absolutely. The clipboard-sharing feature is a cornerstone feature of the product and we are
|
||||
committed to maintaining and improving that feature.
|
||||
|
||||
### Is Wayland for Linux supported?
|
||||
|
||||
Yes! Wayland (the Linux display server protocol aimed to become the successor of the X Window
|
||||
Yes! Wayland (the Linux display server protocol aimed to become the successor of the X Window
|
||||
System) is an important platform for us.
|
||||
The [`libei`](https://gitlab.freedesktop.org/libinput/libei) and
|
||||
[`libportal`](https://github.com/flatpak/libportal) libraries enable
|
||||
The [`libei`](https://gitlab.freedesktop.org/libinput/libei) and
|
||||
[`libportal`](https://github.com/flatpak/libportal) libraries enable
|
||||
Wayland support for Deskflow. We would like to give special thanks to Peter Hutterer,
|
||||
who is the author of `libei`, a major contributor to `libportal`, and the author of the Wayland
|
||||
implementation in Deskflow. Others such as Olivier Fourdan and Povilas Kanapickas helped with the
|
||||
@ -184,9 +172,12 @@ wiki.
|
||||
|
||||

|
||||
|
||||
|
||||
## Deskflow Contributors
|
||||
|
||||
[](https://symless.com/synergy)
|
||||
|
||||
[Synergy](https://symless.com/synergy) sponsors the Deskflow project by contributing code and providing financial support ([learn more](https://github.com/deskflow/deskflow/wiki/Relationship-with-Synergy)).
|
||||
|
||||
Deskflow is made by possible by these contributors.
|
||||
|
||||
<a href = "https://github.com/deskflow/deskflow/graphs/contributors">
|
||||
|
||||
208
REUSE.toml
208
REUSE.toml
@ -7,186 +7,53 @@ SPDX-PackageSupplier = "Deskflow Devs"
|
||||
SPDX-PackageDownloadLocation = "https://github.com/deskflow/deskflow"
|
||||
|
||||
[[annotations]]
|
||||
path = ".github/**"
|
||||
path = [
|
||||
".github/**"
|
||||
, ".clang-format"
|
||||
, ".editorconfig"
|
||||
, ".gitattributes"
|
||||
, ".gitignore"
|
||||
, "cspell.json"
|
||||
, "sonar-project.properties"
|
||||
, "cmake/vcpkg.json.in"
|
||||
, "**/*.md"
|
||||
, "doc/**"
|
||||
, "deploy/linux/flatpak/**"
|
||||
, "deploy/linux/org.deskflow.deskflow.metainfo.xml"
|
||||
, "deploy/windows/wix-patch.xml.in"
|
||||
, "src/apps/deskflow-client/deskflow-client.exe.manifest"
|
||||
, "src/apps/deskflow-core/deskflow-core.exe.manifest"
|
||||
, "src/apps/deskflow-server/deskflow-server.exe.manifest"
|
||||
, "src/apps/res/manpage.txt"
|
||||
, "src/apps/res/deskflow.plist.in"
|
||||
]
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = ".clang-format"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = ".editorconfig"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = ".gitattributes"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = ".gitignore"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "cspell.json"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "README.md"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "SECURITY.md"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "sonar-project.properties"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "vcpkg.json"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "vcpkg-configuration.json"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "doc/**"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "deploy/mac/dmg-background.tiff"
|
||||
path = [
|
||||
"deploy/mac/dmg-background.tiff"
|
||||
, "deploy/mac/dmg-volume.icns"
|
||||
, "deploy/linux/deskflow.png"
|
||||
, "deploy/windows/wix-banner.png"
|
||||
, "deploy/windows/wix-dialog.png"
|
||||
, "src/apps/res/icons/deskflow-**/apps/64/deskflow*.svg"
|
||||
, "src/apps/res/image/welcome.png"
|
||||
, "src/apps/res/Deskflow.icns"
|
||||
, "src/apps/res/deskflow.ico"
|
||||
, "src/apps/res/deskflow.qrc"
|
||||
]
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "deploy/mac/dmg-volume.icns"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "deploy/linux/deskflow.png"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "deploy/linux/flatpak/**"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "deploy/linux/org.deskflow.deskflow.metainfo.xml"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
|
||||
[[annotations]]
|
||||
path = "deploy/windows/wix-banner.png"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "deploy/windows/wix-dialog.png"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "deploy/windows/wix-patch.xml.in"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/deskflow-client/deskflow-client.exe.manifest"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/deskflow-core/deskflow-core.exe.manifest"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/deskflow-server/deskflow-server.exe.manifest"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/res/icons/deskflow-**/**/**/**.svg"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Kde Breeze Icons"
|
||||
SPDX-License-Identifier = "LGPL-2.1-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/res/icons/deskflow-**/apps/64/deskflow*.svg"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/res/image/welcome.png"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/res/Deskflow.icns"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/res/deskflow.ico"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/res/deskflow.plist.in"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/res/deskflow.qrc"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/apps/res/icons/deskflow-**/index.theme"
|
||||
precedence = "override"
|
||||
@ -194,13 +61,10 @@ SPDX-FileCopyrightText = "Chris Rizzitello <sithlord48@gmail.com>"
|
||||
SPDX-License-Identifier = "LGPL-2.1-only"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/lib/gui/MainWindow.ui"
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only WITH LicenseRef-OpenSSL-Exception"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/lib/gui/dialogs/*.ui"
|
||||
path = [
|
||||
"src/lib/gui/MainWindow.ui"
|
||||
, "src/lib/gui/dialogs/*.ui"
|
||||
]
|
||||
precedence = "override"
|
||||
SPDX-FileCopyrightText = "Deskflow Developers"
|
||||
SPDX-License-Identifier = "GPL-2.0-only WITH LicenseRef-OpenSSL-Exception"
|
||||
|
||||
@ -26,13 +26,21 @@ macro(configure_libs)
|
||||
|
||||
# Define the location of Qt deployment tool
|
||||
if(WIN32)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT VCPKG_INSTALL_DIR STREQUAL "")
|
||||
find_program(DEPLOYQT windeployqt.debug.bat)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND VCPKG_QT)
|
||||
set(DEPLOY_TOOL windeployqt.debug.bat)
|
||||
else()
|
||||
find_program(DEPLOYQT windeployqt)
|
||||
set(DEPLOY_TOOL windeployqt)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
find_program(DEPLOYQT macdeployqt)
|
||||
set(DEPLOY_TOOL macdeployqt)
|
||||
endif()
|
||||
|
||||
if (WIN32 OR APPLE)
|
||||
find_program(DEPLOYQT ${DEPLOY_TOOL})
|
||||
if(DEPLOYQT STREQUAL "DEPLOYQT-NOTFOUND")
|
||||
message(FATAL_ERROR "Unable to locate the Qt Deploy Tool: \"${DEPLOY_TOOL}\"")
|
||||
endif()
|
||||
unset(DEPLOY_TOOL)
|
||||
endif()
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
@ -46,7 +54,7 @@ macro(configure_libs)
|
||||
message(STATUS "Enabling code coverage")
|
||||
include(cmake/CodeCoverage.cmake)
|
||||
append_coverage_compiler_flags()
|
||||
set(test_exclude subprojects/* build/* src/test/*)
|
||||
set(test_exclude subprojects/* build/* src/unittests/*)
|
||||
set(test_src ${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
# Apparently solves the bug in gcov where it returns negative counts and confuses gcovr.
|
||||
@ -55,15 +63,8 @@ macro(configure_libs)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-update=atomic")
|
||||
|
||||
setup_target_for_coverage_gcovr_xml(
|
||||
NAME coverage-integtests
|
||||
EXECUTABLE integtests
|
||||
BASE_DIRECTORY ${test_src}
|
||||
EXCLUDE ${test_exclude}
|
||||
)
|
||||
|
||||
setup_target_for_coverage_gcovr_xml(
|
||||
NAME coverage-unittests
|
||||
EXECUTABLE unittests
|
||||
NAME coverage-legacytests
|
||||
EXECUTABLE legacytests
|
||||
BASE_DIRECTORY ${test_src}
|
||||
EXCLUDE ${test_exclude}
|
||||
)
|
||||
@ -89,12 +90,17 @@ macro(configure_unix_libs)
|
||||
include(CheckSymbolExists)
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
||||
check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
|
||||
check_include_files(sys/time.h HAVE_SYS_TIME_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
if (NOT HAVE_SYS_SOCKET_H)
|
||||
message(FATAL_ERROR "Missing header: sys/socket.h")
|
||||
endif()
|
||||
|
||||
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
if (NOT HAVE_UNISTD_H)
|
||||
message(FATAL_ERROR "Missing unistd.h")
|
||||
endif()
|
||||
|
||||
check_function_exists(nanosleep HAVE_NANOSLEEP)
|
||||
check_function_exists(sigwait HAVE_POSIX_SIGWAIT)
|
||||
check_function_exists(inet_aton HAVE_INET_ATON)
|
||||
|
||||
@ -161,15 +167,6 @@ macro(configure_unix_libs)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# For config.h, set some static values; it may be a good idea to make these
|
||||
# values dynamic for non-standard UNIX compilers.
|
||||
set(HAVE_PTHREAD_SIGNAL 1)
|
||||
set(SELECT_TYPE_ARG1 int)
|
||||
set(SELECT_TYPE_ARG234 " (fd_set *)")
|
||||
set(SELECT_TYPE_ARG5 " (struct timeval *)")
|
||||
set(TIME_WITH_SYS_TIME 1)
|
||||
set(HAVE_SOCKLEN_T 1)
|
||||
|
||||
# Unix only: For config.h, save the results based on a template (config.h.in).
|
||||
# Note that this won't work on Windows because filenames are not case sensitive,
|
||||
# and we have header files named "Config.h" (upper case 'C').
|
||||
@ -203,11 +200,6 @@ macro(configure_xorg_libs)
|
||||
check_include_files("${XKBlib}" HAVE_X11_XKBLIB_H)
|
||||
check_include_files("X11/extensions/XInput2.h" HAVE_XI2)
|
||||
|
||||
if(HAVE_X11_EXTENSIONS_DPMS_H)
|
||||
# Assume that function prototypes declared, when include exists.
|
||||
set(HAVE_DPMS_PROTOTYPES 1)
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_X11_XKBLIB_H)
|
||||
message(FATAL_ERROR "Missing header: " ${XKBlib})
|
||||
endif()
|
||||
|
||||
12
cmake/vcpkg.json.in
Normal file
12
cmake/vcpkg.json.in
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"$comment": "Generated file do not hand edit",
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||
"name": "deskflow",
|
||||
"version": "@DESKFLOW_VERSION_MAJOR@.@DESKFLOW_VERSION_MINOR@.@DESKFLOW_VERSION_PATCH@.@DESKFLOW_VERSION_TWEAK@",
|
||||
"builtin-baseline": "d5ec528843d29e3a52d745a64b469f810b2cedbf",
|
||||
"dependencies": [
|
||||
"gtest",
|
||||
"openssl"
|
||||
@QT_LIBS@
|
||||
]
|
||||
}
|
||||
@ -12,24 +12,30 @@ 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}")
|
||||
depends=(
|
||||
'gcc-libs'
|
||||
'glibc'
|
||||
'openssl'
|
||||
'libx11'
|
||||
'libxi'
|
||||
'libxkbfile'
|
||||
'libxext'
|
||||
'libxtst'
|
||||
'libxinerama'
|
||||
'libxkbcommon-x11'
|
||||
'hicolor-icon-theme'
|
||||
'qt6-base'
|
||||
'qt6-tools'
|
||||
'libei'
|
||||
'libportal'
|
||||
'tomlplusplus'
|
||||
'cli11'
|
||||
gcc-libs
|
||||
glib2
|
||||
glibc
|
||||
hicolor-icon-theme
|
||||
libei
|
||||
libglvnd
|
||||
libice
|
||||
libportal
|
||||
libsm
|
||||
libx11
|
||||
libxext
|
||||
libxi
|
||||
libxinerama
|
||||
libxkbcommon
|
||||
libxkbcommon-x11
|
||||
libxkbfile
|
||||
libxrandr
|
||||
libxtst
|
||||
openssl
|
||||
qt6-base
|
||||
qt6-svg
|
||||
tomlplusplus
|
||||
)
|
||||
|
||||
options=('!debug')
|
||||
|
||||
package() {
|
||||
|
||||
@ -100,9 +100,9 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
if("${DISTRO_NAME}" STREQUAL "")
|
||||
set(DISTRO_NAME "linux")
|
||||
endif()
|
||||
set(OS_STRING "${DISTRO_NAME}-${CN_STRING}${CMAKE_SYSTEM_PROCESSOR}")
|
||||
set(OS_STRING "${DISTRO_NAME}-${CN_STRING}${BUILD_ARCHITECTURE}")
|
||||
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "|.*BSD")
|
||||
message(STATUS "BSD packaging not yet supported")
|
||||
set(OS_STRING ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR})
|
||||
set(OS_STRING ${CMAKE_SYSTEM_NAME}-${BUILD_ARCHITECTURE})
|
||||
endif()
|
||||
|
||||
41
deploy/linux/flatpak/libportal-qt69.patch
Normal file
41
deploy/linux/flatpak/libportal-qt69.patch
Normal file
@ -0,0 +1,41 @@
|
||||
From 796053d2eebe4532aad6bd3fd80cdf3b197806ec Mon Sep 17 00:00:00 2001
|
||||
From: Jan Grulich <jgrulich@redhat.com>
|
||||
Date: Thu, 27 Mar 2025 09:38:10 +0100
|
||||
Subject: [PATCH] qt6: fix build against Qt 6.9+
|
||||
|
||||
QGenericUnixServices was renamed to QDesktopUnixServices in Qt 6.9.
|
||||
|
||||
Upstream change: https://codereview.qt-project.org/c/qt/qtbase/+/609639
|
||||
---
|
||||
libportal/portal-qt6.cpp | 8 ++++++++
|
||||
1 file changed, 8 insertions(+)
|
||||
|
||||
diff --git a/libportal/portal-qt6.cpp b/libportal/portal-qt6.cpp
|
||||
index d38a4e30..34f0d72a 100644
|
||||
--- a/libportal/portal-qt6.cpp
|
||||
+++ b/libportal/portal-qt6.cpp
|
||||
@@ -31,8 +31,12 @@
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||
#include <qpa/qplatformintegration.h>
|
||||
#include <private/qguiapplication_p.h>
|
||||
+#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
||||
+#include <private/qdesktopunixservices_p.h>
|
||||
+#else
|
||||
#include <private/qgenericunixservices_p.h>
|
||||
#endif
|
||||
+#endif
|
||||
|
||||
static gboolean
|
||||
_xdp_parent_export_qt (XdpParent *parent,
|
||||
@@ -45,7 +49,11 @@ _xdp_parent_export_qt (XdpParent *parent,
|
||||
}
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
|
||||
+#if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
|
||||
+ if (const auto services = dynamic_cast<QDesktopUnixServices*>(QGuiApplicationPrivate::platformIntegration()->services()))
|
||||
+#else
|
||||
if (const auto services = dynamic_cast<QGenericUnixServices*>(QGuiApplicationPrivate::platformIntegration()->services()))
|
||||
+#endif
|
||||
{
|
||||
g_autofree char *handle = g_strdup(services->portalWindowIdentifier(w).toUtf8().constData());
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
app-id: org.deskflow.deskflow
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: "6.8"
|
||||
runtime-version: "6.9"
|
||||
sdk: org.kde.Sdk
|
||||
command: deskflow
|
||||
finish-args:
|
||||
@ -46,8 +46,8 @@ modules:
|
||||
sources:
|
||||
- type: git
|
||||
url: https://gitlab.freedesktop.org/libinput/libei
|
||||
tag: 1.4.0
|
||||
commit: 5d6d8e6590df210b75559a889baa9459c68d9366
|
||||
tag: 1.4.1
|
||||
commit: 9e0413cbc7d3ae6656266890425f152589ddf74d
|
||||
- name: libportal
|
||||
buildsystem: meson
|
||||
config-opts:
|
||||
@ -61,6 +61,8 @@ modules:
|
||||
url: https://github.com/flatpak/libportal.git
|
||||
tag: 0.9.1
|
||||
commit: 8f5dc8d192f6e31dafe69e35219e3b707bde71ce
|
||||
- type: patch
|
||||
path: libportal-qt69.patch
|
||||
- name: cli11
|
||||
buildsystem: cmake-ninja
|
||||
config-opts:
|
||||
@ -68,8 +70,8 @@ modules:
|
||||
sources:
|
||||
- type: git
|
||||
url: https://github.com/CLIUtils/CLI11
|
||||
tag: v2.4.2
|
||||
commit: 6c7b07a878ad834957b98d0f9ce1dbe0cb204fc9
|
||||
tag: v2.5.0
|
||||
commit: 4160d259d961cd393fd8d67590a8c7d210207348
|
||||
- name: tomlplusplus
|
||||
buildsystem: cmake-ninja
|
||||
sources:
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<component type="desktop-application">
|
||||
<id>org.deskflow.deskflow</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-2.0+</project_license>
|
||||
<project_license>GPL-2.0-only</project_license>
|
||||
<name>Deskflow</name>
|
||||
<developer id="org.deskflow">
|
||||
<name>Deskflow Developers</name>
|
||||
@ -16,6 +16,10 @@
|
||||
<launchable type="desktop-id">org.deskflow.deskflow.desktop</launchable>
|
||||
<url type="homepage">https://deskflow.org</url>
|
||||
<url type="bugtracker">https://github.com/deskflow/deskflow/issues</url>
|
||||
<url type="faq">https://github.com/deskflow/deskflow/wiki/Project-FAQ</url>
|
||||
<url type="help">https://github.com/deskflow/deskflow/wiki#user-guides</url>
|
||||
<url type="vcs-browser">https://github.com/deskflow/deskflow</url>
|
||||
<url type="contribute">https://github.com/deskflow/deskflow/wiki/Contributing</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://deskflow.org/screenshots/deskflow.png</image>
|
||||
@ -42,6 +46,60 @@
|
||||
</branding>
|
||||
<content_rating type="oars-1.0" />
|
||||
<releases>
|
||||
<release version="1.23.0" date="2025-07-23" 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>
|
||||
<ul>
|
||||
<li>Fix: Core app not running when app starts minimized.</li>
|
||||
<li>Fix: Several items in the server configuration dialog being enabled at the wrong time.</li>
|
||||
<li>Fix: Use the correct license in our appstream data (GPL2.0 only).</li>
|
||||
<li>Fix: Apps saved size could grow over time on desktops using client side decorations.</li>
|
||||
<li>Fix: Use the system monospace font in the log area, instead of forcing one that may not be on the system.</li>
|
||||
<li>Fix: Issue with incorrect borders being set for libEI, causing issues on edges without neighbors.</li>
|
||||
<li>Feat: Add Restart action for the core process.</li>
|
||||
<li>Feat: Remove defunct --no-xinitthreads option.</li>
|
||||
<li>Feat: Make 2048 the minimum encryption key size.</li>
|
||||
<li>Feat: Provide additional connection information in the status area.</li>
|
||||
<li>Chore: Continue to update codebase to use C++20 features.</li>
|
||||
<li>Chore: Clean more sonar smells</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url>https://github.com/deskflow/deskflow/releases/tag/v1.23.0</url>
|
||||
</release>
|
||||
<release version="1.22.0" date="2025-05-28" urgency="high">
|
||||
<description>
|
||||
<p>This stable release fixes a issues found in the previous version. For the full changelog see the release page.</p>
|
||||
<ul>
|
||||
<li>Remove: Broken drag and drop file transfer support</li>
|
||||
<li>Remove: DESKFLOW_RESET_ALL and --no-reset. reset settings is now done by passing the --reset option instead</li>
|
||||
<li>Continue migration to Qt by using more Qt classes in more places</li>
|
||||
<li>Retire SHA1 generation and use SHA256 always to compare</li>
|
||||
<li>Add missing accelerators for gui controls</li>
|
||||
<li>unittests binary is now legacytests</li>
|
||||
<li>integtests have been remove and replaced with Qt based tests run during build</li>
|
||||
<li>Fix: Potential XDG-Portal release issue</li>
|
||||
<li>Fix: Issue where the first start dialog could hang in the background</li>
|
||||
<li>Fix: Edge cases that could cause incorrect settings causing client / server process to crash</li>
|
||||
<li>Fix: Default server config file is Deskflow-server.conf</li>
|
||||
<li>Backport: Event Types from downstream</li>
|
||||
<li>Backport: Cleaner error handling from downstream</li>
|
||||
<li>Improve Windows Daemon</li>
|
||||
<li>Better detection of arm on windows, in build and installer</li>
|
||||
<li>Chore: Clean up sonar scan code smells</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url>https://github.com/deskflow/deskflow/releases/tag/v1.22.0</url>
|
||||
</release>
|
||||
<release version="1.21.2" date="2025-04-07" urgency="high">
|
||||
<description>
|
||||
<p>This stable release fixes a few critical bugs in 1.21.1. For the full changelog see the release page.</p>
|
||||
<ul>
|
||||
<li>Fix: Crash with Qt 6.9</li>
|
||||
<li>Fix: Windows settings in wrong locations</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url>https://github.com/deskflow/deskflow/releases/tag/v1.21.2</url>
|
||||
</release>
|
||||
<release version="1.21.1" date="2025-03-31" urgency="high">
|
||||
<description>
|
||||
<p>This stable release fixes a few critical bugs in 1.21.0. For the full changelog see the release page.</p>
|
||||
|
||||
@ -11,7 +11,7 @@ install(CODE "execute_process(COMMAND
|
||||
-timestamp -codesign=-
|
||||
)")
|
||||
|
||||
set(OS_STRING "macos-${CMAKE_SYSTEM_PROCESSOR}")
|
||||
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")
|
||||
|
||||
6
deploy/windows/cpack-options.cmake.in
Normal file
6
deploy/windows/cpack-options.cmake.in
Normal file
@ -0,0 +1,6 @@
|
||||
#SPDX-FileCopyrightText: 2025 Chris Rizzitello <sithlord48@gmail.com>
|
||||
#SPDX-License-Identifier: MIT
|
||||
|
||||
if(CPACK_GENERATOR MATCHES 7Z|ZIP)
|
||||
set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}-portable)
|
||||
endif()
|
||||
@ -5,23 +5,25 @@
|
||||
# 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} --no-compiler-runtime --no-system-d3d-compiler --no-quick-import -network \"\${CMAKE_INSTALL_PREFIX}/deskflow.exe\"
|
||||
)")
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION .)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
|
||||
# Setup OS_STRING
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES AMD64)
|
||||
set(OS_STRING "win-x64")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES ARM64)
|
||||
set(OS_STRING "win-arm64")
|
||||
else()
|
||||
set(OS_STRING "win-${CMAKE_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
configure_file(${MY_DIR}/pre-cpack.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/pre-cpack.cmake @ONLY)
|
||||
set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_CURRENT_BINARY_DIR}/pre-cpack.cmake)
|
||||
|
||||
configure_file(${MY_DIR}/cpack-options.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cpack-options.cmake @ONLY)
|
||||
set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/cpack-options.cmake)
|
||||
|
||||
set(OS_STRING "win-${BUILD_ARCHITECTURE}")
|
||||
|
||||
list(APPEND CPACK_GENERATOR "7Z")
|
||||
|
||||
# If Wix4+ is installed make a package
|
||||
find_program(WIX_APP wix)
|
||||
if (NOT "${WIX_APP}" STREQUAL "")
|
||||
set(CPACK_WIX_VERSION 4)
|
||||
set(CPACK_WIX_ARCHITECTURE ${BUILD_ARCHITECTURE})
|
||||
list(APPEND CPACK_GENERATOR "WIX")
|
||||
endif()
|
||||
|
||||
|
||||
19
deploy/windows/pre-cpack.cmake.in
Normal file
19
deploy/windows/pre-cpack.cmake.in
Normal file
@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2025 Chris Rizzitello <sithlord48@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
if(CPACK_GENERATOR MATCHES 7Z|ZIP)
|
||||
string(REPLACE " " "*" _TEMP_LIST "@CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS@")
|
||||
set(${PORTABLE_LIBS} "")
|
||||
foreach(ITEM ${_TEMP_LIST})
|
||||
string(REPLACE "*" " " _ITEM ${ITEM})
|
||||
file(COPY ${_ITEM} DESTINATION ${CPACK_TEMPORARY_INSTALL_DIRECTORY})
|
||||
endforeach()
|
||||
file(WRITE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/settings/Deskflow.conf " ")
|
||||
file(REMOVE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/deskflow-daemon.exe)
|
||||
file(WRITE ${CPACK_TEMPORARY_INSTALL_DIRECTORY}/README.txt
|
||||
" Portable Deskflow: @CMAKE_PROJECT_VERSION@
|
||||
|
||||
The portable version must have the settings/Deskflow.conf file to save settings, or it will try to use the system settings location.
|
||||
The portable version does not include the daemon, so the client will not work at UAC prompts or the login screen.
|
||||
")
|
||||
endif()
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 3.2 KiB |
@ -34,7 +34,7 @@ static std::string s_logMessageBuffer; // NOSONAR - Must be mutable.
|
||||
|
||||
extern "C" __declspec(dllexport) UINT __stdcall CheckVCRedist(MSIHANDLE hInstall)
|
||||
{
|
||||
const auto kKeyName = TEXT("SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\x64");
|
||||
const auto kKeyName = TEXT(kRegKey);
|
||||
const auto kValueName = TEXT("Minor");
|
||||
const auto kProperty = "VC_REDIST_VERSION_OK";
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
const auto kAppId = "@CMAKE_PROJECT_NAME@";
|
||||
const auto kRegKey = "SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\@BUILD_ARCHITECTURE@";
|
||||
|
||||
// clang-format off
|
||||
const auto kWindowsRuntimeMajor = @REQUIRED_MSVC_RUNTIME_MAJOR@;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 47 KiB |
@ -17,6 +17,7 @@
|
||||
/>
|
||||
</ServiceInstall>
|
||||
<ServiceControl Id="ServiceControl" Name="Deskflow" Remove="uninstall" Start="install" Stop="both"/>
|
||||
<RemoveFile Id="RmOldLog" On="install" Name="deskflow-daemon.log"/>
|
||||
</CPackWiXFragment>
|
||||
|
||||
<CPackWiXFragment Id="CM_CP_deskflow_server.exe">
|
||||
@ -32,7 +33,7 @@
|
||||
<RegistrySearch
|
||||
Id="FindVCRedist"
|
||||
Root="HKLM"
|
||||
Key="SOFTWARE\Microsoft\VisualStudio\@REQUIRED_MSVC_RUNTIME_MAJOR@.0\VC\Runtimes\x64"
|
||||
Key="SOFTWARE\Microsoft\VisualStudio\@REQUIRED_MSVC_RUNTIME_MAJOR@.0\VC\Runtimes\@BUILD_ARCHITECTURE@"
|
||||
Name="Installed"
|
||||
Type="raw" />
|
||||
</Property>
|
||||
@ -44,7 +45,9 @@
|
||||
Control="Finish"
|
||||
Event="DoAction"
|
||||
Value="RunDeskflow"
|
||||
Condition= "NOT Installed" />
|
||||
Condition= "WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed" />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
|
||||
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Run Deskflow when finished" />
|
||||
</UI>
|
||||
<CustomAction
|
||||
Id="CheckVCRedist"
|
||||
|
||||
@ -1,31 +1,24 @@
|
||||
# SPDX-FileCopyrightText: 2019 - 2024 Chris Rizzitello <sithlord48@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2019 - 2025 Chris Rizzitello <sithlord48@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
find_package(Doxygen QUIET)
|
||||
option(BUILD_DOCS "Build and install documents" ${DOXYGEN_FOUND})
|
||||
|
||||
if (BUILD_DOCS AND DOXYGEN_FOUND)
|
||||
option(BUILD_USER_DOCS "Build and install user documentation" ${DOXYGEN_FOUND})
|
||||
option(BUILD_DEV_DOCS "Build and install developer documentation" OFF)
|
||||
|
||||
if (DOXYGEN_FOUND)
|
||||
# Generic Doxygen options
|
||||
set(DOXYGEN_EXTRACT_ALL YES)
|
||||
set(DOXYGEN_EXTRACT_STATIC YES)
|
||||
set(DOXYGEN_STRIP_FROM_PATH ${CMAKE_SOURCE_DIR})
|
||||
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE mainpage.md)
|
||||
set(DOXYGEN_QUIET YES)
|
||||
set(DOXYGEN_PROJECT_NAME ${CMAKE_PROJECT_PROPER_NAME})
|
||||
|
||||
# Files used to make our documents
|
||||
# User facing documents will not include doxy comments in source code
|
||||
doxygen_add_docs(user-docs ${CMAKE_SOURCE_DIR}/doc COMMENT "Generating user documentation" ALL)
|
||||
|
||||
# HACK Only these will show in your IDE
|
||||
target_sources(user-docs PRIVATE
|
||||
mainpage.md
|
||||
configuration.md
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html"
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR}
|
||||
COMPONENT deskflow_docs)
|
||||
|
||||
if (BUILD_USER_DOCS)
|
||||
add_subdirectory(user)
|
||||
endif()
|
||||
if (BUILD_DEV_DOCS)
|
||||
add_subdirectory(dev)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Doxygen not found, skipping docs build")
|
||||
endif()
|
||||
|
||||
22
doc/dev/CMakeLists.txt
Normal file
22
doc/dev/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2025 Chris Rizzitello <sithlord48@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE mainpage.md)
|
||||
set(DOXYGEN_EXCLUDE_PATTERNS "*unittests/*")
|
||||
set(DOXYGEN_DOT_GRAPH_MAX_NODES 100)
|
||||
|
||||
# Files used to make our documents
|
||||
doxygen_add_docs(dev-docs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
COMMENT "Generating developer documentation" ALL)
|
||||
|
||||
# HACK Only these will show in your IDE
|
||||
target_sources(dev-docs PRIVATE
|
||||
mainpage.md
|
||||
contributing.md
|
||||
build.md
|
||||
protocol_reference.md
|
||||
)
|
||||
|
||||
# missing install target is intended generate a local copy
|
||||
84
doc/dev/build.md
Normal file
84
doc/dev/build.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Building Deskflow
|
||||
|
||||
To build Deskflow you will a minimum of:
|
||||
- [cmake] 3.24+
|
||||
- [Qt] 6.7.0+
|
||||
- [openssl] 3.0+
|
||||
- [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 Client application deskflow-client
|
||||
- The Server application deskflow-server
|
||||
- Documentation if [doxygen] was found on your system
|
||||
- Tests that will be run as part of the build process.
|
||||
|
||||
## Configuration
|
||||
Deskflow supports the following build options
|
||||
|
||||
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_UNIFIED | Build unified binary (client+server) | OFF | |
|
||||
| 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 | |
|
||||
|
||||
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`
|
||||
3. Add the path of Qt's binary tools to your system path.
|
||||
- Often `C:\Qt\<version>\<msvcinfo>\bin`
|
||||
|
||||
##### 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.
|
||||
|
||||
## 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>
|
||||
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.
|
||||
|
||||
|
||||
[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
|
||||
13
doc/dev/contributing.md
Normal file
13
doc/dev/contributing.md
Normal file
@ -0,0 +1,13 @@
|
||||
# Contributing to Deskflow {#contributing_guide}
|
||||
|
||||
Thanks for your interest in contributing to Deskflow! We welcome all kinds of contributions — bug reports, feature suggestions, documentation improvements, and code.
|
||||
|
||||
## Read the Full Guidelines
|
||||
|
||||
To keep this repository clean and contribution-friendly, we've outlined our full contributing guidelines on the Deskflow Wiki:
|
||||
|
||||
👉 [How to Contribute to Deskflow](https://github.com/deskflow/deskflow/wiki/Contributing)
|
||||
|
||||
Please take a moment to read through the page before opening an issue or submitting a pull request.
|
||||
|
||||
Thanks again for helping make Deskflow better!
|
||||
55
doc/dev/mainpage.md
Normal file
55
doc/dev/mainpage.md
Normal file
@ -0,0 +1,55 @@
|
||||
**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.
|
||||
|
||||
Deskflow acts as a software KVM (without video) that allows you to:
|
||||
- Share keyboard and mouse input across multiple computers
|
||||
- Synchronize clipboard content between machines
|
||||
- Work seamlessly across different operating systems (Windows, macOS, Linux, BSD)
|
||||
|
||||
Deskflow software consists of a **server** (primary computer) that shares its input devices and **clients** (secondary computers) that receive and execute the input commands over a TCP network connection.
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
Deskflow is built with a modular, cross-platform architecture:
|
||||
|
||||
```
|
||||
┌─────────────────┐ Network Protocol ┌─────────────────┐
|
||||
│ Server App │◄──────────────────────►│ Client App │
|
||||
│ │ (Port 24800) │ (Windows) │
|
||||
│ ┌─────────────┐ │ │ ┌─────────────┐ │
|
||||
│ │ Screen │ │ │ │ Screen │ │
|
||||
│ │ Platform │ │ │ │ Platform │ │
|
||||
│ │ Layer │ │ │ │ Layer │ │
|
||||
│ └─────────────┘ │ │ └─────────────┘ │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
┌───────┐ ┌───────┐
|
||||
│ Keyb. │ │ Mouse │
|
||||
└───────┘ └───────┘
|
||||
|
||||
┌─────────────────┐
|
||||
│ Client App │
|
||||
│ (macOS) │
|
||||
│ ┌─────────────┐ │
|
||||
│ │ Screen │ │
|
||||
│ │ Platform │ │
|
||||
│ │ Layer │ │
|
||||
│ └─────────────┘ │
|
||||
└─────────────────┘
|
||||
|
||||
┌─────────────────┐
|
||||
│ Client App │
|
||||
│ (Custom) │
|
||||
│ ┌─────────────┐ │
|
||||
│ │ Screen │ │
|
||||
│ │ Platform │ │
|
||||
│ │ Layer │ │
|
||||
│ └─────────────┘ │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### More info
|
||||
|
||||
For more info, see our [Wiki](https://github.com/deskflow/deskflow/wiki).
|
||||
|
||||
Check out our [Building guide](build.md) or our general @ref contributing_guide "Contributing section". We also have a detailed [Protocol Reference](protocol_reference.md).
|
||||
532
doc/dev/protocol_reference.md
Normal file
532
doc/dev/protocol_reference.md
Normal file
@ -0,0 +1,532 @@
|
||||
# Protocol Reference {#protocol_reference}
|
||||
|
||||
This document provides a comprehensive reference for the Deskflow network protocol. It is the primary source of information for developers implementing Deskflow clients or extending the protocol.
|
||||
|
||||
## Protocol Overview
|
||||
|
||||
The Deskflow protocol enables keyboard and mouse sharing between multiple computers over a TCP network connection. The protocol uses two distinct sets of terminology to describe the roles of the computers involved:
|
||||
|
||||
- **Network Role (Client/Server)**: This describes the connection architecture.
|
||||
- **Server**: The machine that listens for incoming TCP connections.
|
||||
- **Client**: The machine that initiates the TCP connection to the server.
|
||||
|
||||
- **Input Control Role (Primary/Secondary)**: This describes the flow of keyboard and mouse events.
|
||||
- **Primary**: The computer whose keyboard and mouse are currently being used to control other computers.
|
||||
- **Secondary**: A computer that is being controlled by the Primary's keyboard and mouse.
|
||||
|
||||
In a typical setup, the Primary computer (the one whose keyboard and mouse are shared) also acts as the Server. However, the protocol is flexible and allows these roles to be separate. For example, a Primary machine can act as a Client to connect to a Secondary machine that is configured as a Server. This can be useful for navigating restrictive network environments like firewalls.
|
||||
|
||||
Throughout the documentation, message direction is often described using the Primary/Secondary roles to clarify the input control flow, while Client/Server roles are used when discussing the underlying network connection.
|
||||
|
||||
### Key Implementation Files
|
||||
|
||||
- **[ProtocolTypes.h](@ref ProtocolTypes.h)** – Complete protocol specification
|
||||
- **[ProtocolUtil.h](@ref ProtocolUtil.h)** – Message formatting utilities
|
||||
- **[ClientInfo](@ref ClientInfo)** – Screen information structure
|
||||
|
||||
The protocol is designed to be:
|
||||
- Lightweight and efficient
|
||||
- Cross-platform compatible
|
||||
- Extensible for new features
|
||||
- Backward compatible with older versions
|
||||
|
||||
## Protocol Architecture
|
||||
|
||||
|
||||
```
|
||||
┌─────────────────┐ TCP/IP Network ┌─────────────────┐
|
||||
│ Primary │◄────────────────────►│ Secondary │
|
||||
│ (Server) │ Port 24800 │ (Client) │
|
||||
│ │ │ │
|
||||
│ • Shares input │ │ • Receives input│
|
||||
│ • Manages layout│ │ • Reports info │
|
||||
│ • Coordinates │ │ • Executes cmds │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
The protocol operates over a standard TCP connection on port 24800. In protocol versions 1.4 and later, TLS encryption is supported for secure communications.
|
||||
|
||||
## Protocol State Machine
|
||||
|
||||
The client's connection lifecycle is defined by five primary states:
|
||||
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
│ START │
|
||||
└────────┬─────────┘
|
||||
│
|
||||
▼
|
||||
┌────────┴─────────┐
|
||||
│ DISCONNECTED │
|
||||
│ (Initial & │◄───────────────────┐
|
||||
│ Final State) │ │
|
||||
└────────┬─────────┘ │
|
||||
│ │
|
||||
▼ │
|
||||
┌──────────────────┐ │
|
||||
│ CONNECTING │ TCP Failure │
|
||||
│ (TCP handshake) ├───────────────────►┤
|
||||
└────────┬─────────┘ │
|
||||
│ │
|
||||
TCP Success │ │
|
||||
│ │
|
||||
▼ │
|
||||
┌──────────────────┐ │
|
||||
│ HANDSHAKE │ Version Mismatch │
|
||||
│ (Hello/HelloBk) ├───────────────────►┤
|
||||
└────────┬─────────┘ │
|
||||
│ │
|
||||
OK │ │
|
||||
│ │
|
||||
▼ │
|
||||
┌──────────────────┐ │
|
||||
│ CONNECTED │ CCLOSE (close) │
|
||||
┌───►│ (Authenticated ├───────────────────►┤
|
||||
│ │ but inactive) │ │
|
||||
│ └────────┬─────────┘ │
|
||||
│ │ │
|
||||
COUT │ CINN │ │
|
||||
(Leave) │ (Enter)│ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────┐ │
|
||||
└────┤ ACTIVE │ CCLOSE (close) │
|
||||
│ (Receiving all ├───────────────────►┘
|
||||
┌───►│ input events) │
|
||||
│ └────────┬─────────┘
|
||||
│ │
|
||||
│ ▼
|
||||
│ ┌──────────────────┐
|
||||
└────┤ PROCESS EVENT │
|
||||
└──────────────────┘
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
### State Descriptions
|
||||
|
||||
1. **Disconnected**: Initial and final state. No connection to @ref Server.
|
||||
2. **Connecting**: @ref TCPSocket connection attempt in progress.
|
||||
- Initiating @ref TCPSocket connection.
|
||||
- On successful TCP connection, moves to the `Handshake` state.
|
||||
- If TCP connection fails (timeout, RST packet), returns to `Disconnected`.
|
||||
3. **Handshake**: Protocol version negotiation and authentication.
|
||||
- @ref Server sends @ref kMsgHello with protocol version information.
|
||||
- @ref Client responds with @ref kMsgHelloBack including version and screen name.
|
||||
- @ref Server validates the client's message.
|
||||
- Success transitions to `Connected`, failure sends @ref kMsgEIncompatible error.
|
||||
4. **Connected**: Authenticated but not receiving input events.
|
||||
- @ref Client must respond to @ref kMsgCKeepAlive messages from the @ref Server.
|
||||
- Receiving @ref kMsgCEnter message transitions to `Active`.
|
||||
5. **Active**: Receiving and processing input events from @ref Server.
|
||||
- Receiving @ref kMsgCLeave message transitions back to `Connected`.
|
||||
- Receiving @ref kMsgCClose message transitions to `Disconnected`.
|
||||
|
||||
|
||||
## Message Categories
|
||||
|
||||
The protocol organizes messages into logical categories:
|
||||
|
||||
| Category | Prefix | Purpose | Examples |
|
||||
|----------|---------|----------|-----------|
|
||||
| **[Handshake](@ref protocol_handshake)** | None | Connection setup | @ref kMsgHello, @ref kMsgHelloBack |
|
||||
| **[Commands](@ref protocol_commands)** | `C` | Screen control | @ref kMsgCEnter, @ref kMsgCLeave, @ref kMsgCKeepAlive |
|
||||
| **[Data](@ref protocol_data)** | `D` | Input events | @ref kMsgDKeyDown, @ref kMsgDMouseMove, @ref kMsgCClipboard, @ref kMsgDClipboard |
|
||||
| **[Queries](@ref protocol_queries)** | `Q` | Information requests | @ref kMsgQInfo |
|
||||
| **[Errors](@ref protocol_errors)** | `E` | Error notifications | @ref kMsgEIncompatible, @ref kMsgEBusy |
|
||||
|
||||
## Message Reference Table
|
||||
|
||||
This table lists all protocol messages in alphabetical order. For a typical sequence of messages, see the [Typical Control Flow](#typical-control-flow) section.
|
||||
|
||||
| Message | Constant | Category | Direction | Purpose | Constraints | Protocol Version |
|
||||
|---|---|---|---|---|---|---|
|
||||
| [**CALV**](@ref kMsgCKeepAlive) | @ref kMsgCKeepAlive | Command | Both | Keep-alive | [MsgSize](#constraint-protocol-max-message-length), [KeepAlive](#constraint-keep-alive) | 1.3+ |
|
||||
| [**CBYE**](@ref kMsgCClose) | @ref kMsgCClose | Command | Server→Client | Close connection | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**CCLP**](@ref kMsgCClipboard) | @ref kMsgCClipboard | Command | Both | Clipboard ownership notification | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**CIAK**](@ref kMsgCInfoAck) | @ref kMsgCInfoAck | Command | Server→Client | Acknowledge info message | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**CINN**](@ref kMsgCEnter) | @ref kMsgCEnter | Command | Server→Client | Enter screen | [MsgSize](#constraint-protocol-max-message-length), [ScreenEntrySync](#constraint-screen-entry-sync) | 1.0+ |
|
||||
| [**CNOP**](@ref kMsgCNoop) | @ref kMsgCNoop | Command | Both | No operation | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**COUT**](@ref kMsgCLeave) | @ref kMsgCLeave | Command | Server→Client | Leave screen | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**CROP**](@ref kMsgCResetOptions) | @ref kMsgCResetOptions | Command | Server→Client | Reset options to defaults | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**CSEC**](@ref kMsgCScreenSaver) | @ref kMsgCScreenSaver | Command | Server→Client | Screen saver control | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**DCLP**](@ref kMsgDClipboard) | @ref kMsgDClipboard | Data | Both | Clipboard data | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**DDRG**](@ref kMsgDDragInfo) | @ref kMsgDDragInfo | Data | Server→Client | Drag file info | [MsgSize](#constraint-protocol-max-message-length), [ListSize](#constraint-max-list) | 1.5+ |
|
||||
| [**DFTR**](@ref kMsgDFileTransfer) | @ref kMsgDFileTransfer | Data | Both | File transfer data | [MsgSize](#constraint-protocol-max-message-length) | 1.5+ |
|
||||
| [**DINF**](@ref kMsgDInfo) | @ref kMsgDInfo | Data | Client→Server | Screen information | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**DKDL**](@ref kMsgDKeyDownLang) | @ref kMsgDKeyDownLang | Data | Server→Client | Key down with language | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.8+ |
|
||||
| [**DKDN**](@ref kMsgDKeyDown) | @ref kMsgDKeyDown | Data | Server→Client | Key down | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.1+ |
|
||||
| [**DKDN**](@ref kMsgDKeyDown1_0) | @ref kMsgDKeyDown1_0 | Data | Server→Client | Key down (legacy) | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.0 |
|
||||
| [**DKRP**](@ref kMsgDKeyRepeat) | @ref kMsgDKeyRepeat | Data | Server→Client | Key repeat | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.1+ |
|
||||
| [**DKRP**](@ref kMsgDKeyRepeat1_0) | @ref kMsgDKeyRepeat1_0 | Data | Server→Client | Key repeat (legacy) | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.0 |
|
||||
| [**DKUP**](@ref kMsgDKeyUp) | @ref kMsgDKeyUp | Data | Server→Client | Key up | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.1+ |
|
||||
| [**DKUP**](@ref kMsgDKeyUp1_0) | @ref kMsgDKeyUp1_0 | Data | Server→Client | Key up (legacy) | [MsgSize](#constraint-protocol-max-message-length), [KeyMap](#constraint-keymap) | 1.0 |
|
||||
| [**DMDN**](@ref kMsgDMouseDown) | @ref kMsgDMouseDown | Data | Server→Client | Mouse down | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**DMMV**](@ref kMsgDMouseMove) | @ref kMsgDMouseMove | Data | Server→Client | Mouse move (absolute) | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**DMRM**](@ref kMsgDMouseRelMove) | @ref kMsgDMouseRelMove | Data | Server→Client | Mouse move (relative) | [MsgSize](#constraint-protocol-max-message-length) | 1.2+ |
|
||||
| [**DMUP**](@ref kMsgDMouseUp) | @ref kMsgDMouseUp | Data | Server→Client | Mouse up | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**DMWM**](@ref kMsgDMouseWheel) | @ref kMsgDMouseWheel | Data | Server→Client | Mouse wheel | [MsgSize](#constraint-protocol-max-message-length) | 1.3+ |
|
||||
| [**DMWM**](@ref kMsgDMouseWheel1_0) | @ref kMsgDMouseWheel1_0 | Data | Server→Client | Mouse wheel (legacy) | [MsgSize](#constraint-protocol-max-message-length) | 1.0-1.2 |
|
||||
| [**DSOP**](@ref kMsgDSetOptions) | @ref kMsgDSetOptions | Data | Server→Client | Set options | [MsgSize](#constraint-protocol-max-message-length), [ListSize](#constraint-max-list) | 1.0+ |
|
||||
| [**EBAD**](@ref kMsgEBad) | @ref kMsgEBad | Error | Server→Client | Protocol violation | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**EBSY**](@ref kMsgEBusy) | @ref kMsgEBusy | Error | Server→Client | Server busy | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**EICV**](@ref kMsgEIncompatible) | @ref kMsgEIncompatible | Error | Server→Client | Incompatible version | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**EUNK**](@ref kMsgEUnknown) | @ref kMsgEUnknown | Error | Server→Client | Unknown client | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**Hello**](@ref kMsgHello) | @ref kMsgHello | Handshake | Server→Client | Protocol identification | [HelloSize](#constraint-max-hello), [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**HelloArgs**](@ref kMsgHelloArgs) | @ref kMsgHelloArgs | Handshake | Internal | Hello message construction | [HelloSize](#constraint-max-hello), [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**HelloBack**](@ref kMsgHelloBack) | @ref kMsgHelloBack | Handshake | Client→Server | Client identification | [HelloSize](#constraint-max-hello), [MsgSize](#constraint-protocol-max-message-length), [HandshakeTimeout](#constraint-handshake-timeout) | 1.0+ |
|
||||
| [**HelloBackArgs**](@ref kMsgHelloBackArgs) | @ref kMsgHelloBackArgs | Handshake | Internal | HelloBack message construction | [HelloSize](#constraint-max-hello), [MsgSize](#constraint-protocol-max-message-length), [HandshakeTimeout](#constraint-handshake-timeout) | 1.0+ |
|
||||
| [**LSYN**](@ref kMsgDLanguageSynchronisation) | @ref kMsgDLanguageSynchronisation | Data | Server→Client | Language synchronization | [MsgSize](#constraint-protocol-max-message-length) | 1.8+ |
|
||||
| [**QINF**](@ref kMsgQInfo) | @ref kMsgQInfo | Query | Server→Client | Request screen info | [MsgSize](#constraint-protocol-max-message-length) | 1.0+ |
|
||||
| [**SECN**](@ref kMsgDSecureInputNotification) | @ref kMsgDSecureInputNotification | Data | Server→Client | Secure input notification | [MsgSize](#constraint-protocol-max-message-length) | 1.7+ |
|
||||
|
||||
## Typical Control Flow
|
||||
<a id="typical-control-flow"></a>
|
||||
|
||||
A typical control flow is as follows:
|
||||
1. **Handshake**: The server and client exchange `Hello` and `HelloBack` messages to agree on a protocol version.
|
||||
2. **Information Exchange**: The server requests client information with `QINF`, and the client responds with `DINF`.
|
||||
3. **Options**: The server sends `DSOP` to configure client options.
|
||||
4. **Keep-Alive**: The server and client periodically exchange `CALV` messages to maintain the connection.
|
||||
5. **Screen Entry**: The server sends `CINN` to grant control to the client.
|
||||
6. **Input Events**: The server sends a stream of input event messages (e.g., `DMMV`, `DMDN`, `DKDN`).
|
||||
7. **Screen Leave**: The server sends `COUT` to revoke control from the client.
|
||||
8. **Connection Close**: The server sends `CCLOSE` to terminate the connection.
|
||||
|
||||
## Protocol Constraints
|
||||
|
||||
To ensure security, stability, and compatibility, the protocol enforces strict constraints:
|
||||
|
||||
<a id="constraint-max-msg"></a>
|
||||
|
||||
### Message and Data Size Limits
|
||||
|
||||
**Maximum Message Size:**
|
||||
<a id="constraint-protocol-max-message-length"></a>**4,194,304 bytes (4 MB)** — @ref PROTOCOL_MAX_MESSAGE_LENGTH
|
||||
Maximum total size of any single, length-prefixed data packet
|
||||
Defined in Protocol Limits
|
||||
|
||||
**Maximum List Size:**
|
||||
<a id="constraint-max-list"></a>**1,048,576 elements** — @ref PROTOCOL_MAX_LIST_LENGTH
|
||||
Maximum number of items in a list/vector within a message
|
||||
Defined in Protocol Limits
|
||||
|
||||
**Maximum Hello Size:**
|
||||
<a id="constraint-max-hello"></a>**1,024 bytes** — @ref kMaxHelloLength
|
||||
Maximum size of the initial Connection Handshake message
|
||||
Defined in Protocol Limits
|
||||
|
||||
<a id="constraint-tls"></a>
|
||||
|
||||
### TLS Handshake and Security (Protocol v1.4+)
|
||||
|
||||
When encryption is enabled, the protocol follows this sequence:
|
||||
|
||||
1. Standard TCP connection established
|
||||
2. TLS handshake performed over TCP socket
|
||||
3. Protocol handshake begins only after TLS session is established
|
||||
|
||||
- **Implementation Details**:
|
||||
- The client initiates a standard TCP connection, then the (private) SecureSocket::handleTCPConnected method is called, which begins the TLS handshake
|
||||
|
||||
- **Certificate Validation**:
|
||||
- Client implementations **must** validate the server's certificate
|
||||
- The reference implementation checks that the public key is RSA or DSA and that the key length is at least 2048 bits
|
||||
|
||||
### Key Code and Modifier Mapping
|
||||
|
||||
A modifier (modifier mask) represents the state of modifier keys (like Shift, Control, Alt, and Command) on a keyboard. It is a binary code (like 0000 0110) where each bit corresponds to a specific modifier key.
|
||||
|
||||
**Key-Up/Key-Down Strategy:**
|
||||
- <a id="constraint-keymap"></a>Client must use the @ref KeyButton (physical key) to track pressed keys, as the @ref KeyID (virtual key) can change based on modifier state
|
||||
- This strategy is described in the documentation for @ref kMsgDKeyDown
|
||||
|
||||
**Modifier Remapping:**
|
||||
- The server can command clients to remap modifier keys via the @ref kMsgDSetOptions message
|
||||
- The client processes the @ref kMsgDSetOptions message and updates the modifier translation table accordingly
|
||||
|
||||
## Timing and Synchronization
|
||||
|
||||
<a id="constraint-keep-alive"></a>
|
||||
|
||||
### Keep-Alive Mechanism (Protocol v1.3+)
|
||||
|
||||
**Server-Side Behavior:**
|
||||
- The server sends kMsgCKeepAlive messages every 3.0 seconds (defined by @ref kKeepAliveRate)
|
||||
- This timer is implemented in @ref ClientProxy1_3::addHeartbeatTimer in the @ref ClientProxy1_3 class
|
||||
|
||||
**Client-Side Behavior:**
|
||||
- Upon receiving a kMsgCKeepAlive message, the client must immediately send a kMsgCKeepAlive message back
|
||||
- The client maintains a timeout that is reset each time any message is received
|
||||
- If no message is received for 9.0 seconds (3 × @ref kKeepAliveRate), client must disconnect
|
||||
- This is handled by the (private) ServerProxy::handleKeepAliveAlarm method
|
||||
|
||||
<a id="constraint-screen-entry-sync"></a>
|
||||
### Synchronization on Screen Entry
|
||||
|
||||
- The @ref kMsgCEnter (Enter Screen) message includes the current modifier state
|
||||
- Client must synchronize their local modifier state with this mask
|
||||
|
||||
<a id="constraint-handshake-timeout"></a>
|
||||
### Handshake Timeout
|
||||
|
||||
- Server allows **30 seconds** for handshake completion
|
||||
- If client fails to send valid @ref kMsgHelloBack within this time, connection is closed
|
||||
- When a new client connects, the server creates a temporary @ref ClientProxyUnknown to handle the version handshake
|
||||
- A one-shot timer is started for 30 seconds
|
||||
- If the client fails to respond in time, the @ref protocol_errors function is triggered, the connection is logged as unresponsive, and the socket is closed
|
||||
|
||||
## Version Compatibility
|
||||
|
||||
| Version | Release Date | Project | Features | Compatibility |
|
||||
|---------|----------|---------------|----------|---------------|
|
||||
| **1.0** | May 2001 | Synergy | Basic keyboard/mouse sharing (@ref kMsgDKeyDown, @ref kMsgDMouseMove) | All versions |
|
||||
| **1.1** | Apr 2002 | Synergy | Physical key codes (@ref KeyButton) | 1.1+ |
|
||||
| **1.2** | Jan 2006 | Synergy | Relative mouse movement | 1.2+ |
|
||||
| **1.3** | May 2006 | Synergy | Keep-alive (@ref kMsgCKeepAlive), horizontal scroll (@ref kMsgDMouseWheel) | 1.3+ |
|
||||
| **1.4** | Nov 2012 | Synergy | Encryption support (@ref SecureSocket) | 1.4+ |
|
||||
| **1.5** | Sep 2013 | Synergy | File transfer | 1.5+ |
|
||||
| **1.6** | Jan 2014 | Synergy | Clipboard streaming | 1.6+ |
|
||||
| **1.7** | Nov 2021 | Synergy | Secure input notifications | 1.7+ |
|
||||
| **1.8** | Jun 2025 | Synergy | Language synchronization | 1.8+ |
|
||||
|
||||
### Version Migration Guide
|
||||
|
||||
When implementing a client that supports multiple protocol versions:
|
||||
|
||||
1. **Version Negotiation**: During handshake, client should advertise highest supported version
|
||||
2. **Feature Detection**: Check server's version in `Hello` message before using version-specific features
|
||||
3. **Fallback Mechanism**: Be prepared to operate with only features available in the negotiated version
|
||||
4. **Graceful Degradation**: If server supports a lower version than client's minimum, handle `EIncompatible` error gracefully
|
||||
|
||||
## Implementation Examples
|
||||
|
||||
### Connection Lifecycle
|
||||
|
||||
```cpp
|
||||
// 1. Connect to server
|
||||
std::string server_ip = "192.168.1.100";
|
||||
connect(server_ip, 24800);
|
||||
|
||||
// 2. Receive Hello from server
|
||||
auto hello = receive_message();
|
||||
std::string server_version, server_name;
|
||||
parse_hello(hello, &server_version, &server_name);
|
||||
|
||||
// 3. Send HelloBack to server
|
||||
std::string client_version = "1.8";
|
||||
std::string client_name = "MyClient";
|
||||
send_hello_back(client_version, client_name);
|
||||
|
||||
// 4. Enter main message loop
|
||||
bool connected = true;
|
||||
while (connected) {
|
||||
auto message = receive_message();
|
||||
handle_message(message);
|
||||
}
|
||||
```
|
||||
|
||||
### Message Handling
|
||||
|
||||
```cpp
|
||||
void handle_message(const Message& msg) {
|
||||
switch (msg.type) {
|
||||
case "CINN": // Enter screen
|
||||
handle_enter(msg.x, msg.y, msg.sequence, msg.modifiers);
|
||||
break;
|
||||
case "DKDN": // Key down
|
||||
handle_key_down(msg.key_id, msg.modifiers, msg.key_button);
|
||||
break;
|
||||
case "DMMV": // Mouse move
|
||||
handle_mouse_move(msg.x, msg.y);
|
||||
break;
|
||||
// ... handle other message types
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Complete Message Exchange Sequence
|
||||
|
||||
Below is a typical message exchange sequence for a client connecting to a server:
|
||||
|
||||
|
||||
|
||||
```
|
||||
Client Server
|
||||
| |
|
||||
| | Server starts listening on port 24800
|
||||
| |
|
||||
| TCP SYN |
|
||||
| ───────────────────────────────────► |
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| TCP SYN+ACK |
|
||||
| |
|
||||
| TCP ACK |
|
||||
| ───────────────────────────────────► |
|
||||
| | TCP connection established
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "Deskflow" + version (1.8) | Hello message
|
||||
| |
|
||||
| "Deskflow" + version + name |
|
||||
| ───────────────────────────────────► | HelloBack message
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "QINF" | Query screen info
|
||||
| |
|
||||
| "DINF" + screen dimensions |
|
||||
| ───────────────────────────────────► | Report screen info
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "DSOP" + options | Set options
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "CALV" | Keep-alive
|
||||
| |
|
||||
| "CALV" |
|
||||
| ───────────────────────────────────► | Keep-alive response
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "CINN" + x + y + seq + mask | Enter screen
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "DMMV" + x + y | Mouse move
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "DMDN" + button | Mouse down
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "DMUP" + button | Mouse up
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "DKDN" + key + mask + button | Key down
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "DKUP" + key + mask + button | Key up
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "COUT" | Leave screen
|
||||
| |
|
||||
| ◄─────────────────────────────────── |
|
||||
| "CCLOSE" | Close connection
|
||||
| |
|
||||
| TCP FIN |
|
||||
| ◄─────────────────────────────────── |
|
||||
| |
|
||||
| TCP FIN+ACK |
|
||||
| ───────────────────────────────────► |
|
||||
| | Connection closed
|
||||
```
|
||||
|
||||
**Legend:**
|
||||
|
||||
- Hello message: @ref kMsgHello
|
||||
- HelloBack message: @ref kMsgHelloBack
|
||||
- Query screen info: @ref kMsgQInfo
|
||||
- Report screen info: @ref kMsgDInfo
|
||||
- Set options: @ref kMsgDSetOptions
|
||||
- Keep-alive: @ref kMsgCKeepAlive
|
||||
- Enter screen: @ref kMsgCEnter
|
||||
- Mouse move: @ref kMsgDMouseMove
|
||||
- Mouse down: @ref kMsgDMouseDown
|
||||
- Mouse up: @ref kMsgDMouseUp
|
||||
- Key down: @ref kMsgDKeyDown
|
||||
- Key up: @ref kMsgDKeyUp
|
||||
- Leave screen: @ref kMsgCLeave
|
||||
- Close connection: @ref kMsgCClose
|
||||
|
||||
## Debugging and Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Version Mismatch**: Check protocol version negotiation
|
||||
2. **Message Format**: Validate message structure and parameters
|
||||
3. **Byte Order**: Ensure network byte order for multi-byte integers
|
||||
4. **Keep-Alive**: Implement proper keep-alive response
|
||||
5. **Screen Info**: Send accurate screen dimensions and mouse position
|
||||
|
||||
### Debug Tools
|
||||
|
||||
- **Wireshark**: Capture and analyze network traffic
|
||||
- **Protocol Logs**: Enable verbose logging in Deskflow
|
||||
- **Message Validation**: Check message format against specification
|
||||
|
||||
## Platform-Specific Implementations
|
||||
|
||||
For platform-specific implementation details, refer to:
|
||||
- @ref ProtocolTypes.h – Complete protocol specification
|
||||
- @ref ProtocolUtil.h – Message formatting utilities
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
### Basic Client Implementation
|
||||
|
||||
- **Connection Management**
|
||||
- TCP connection to server port 24800
|
||||
- Protocol handshake (Hello/HelloBack)
|
||||
- Version negotiation
|
||||
- Keep-alive handling
|
||||
|
||||
- **Message Processing**
|
||||
- Message parsing and validation
|
||||
- Command message handling (Enter/Leave)
|
||||
- Input event processing (keyboard/mouse)
|
||||
- Error handling and recovery
|
||||
|
||||
- **Screen Management**
|
||||
- Screen information reporting (DINF)
|
||||
- Resolution change detection
|
||||
- Mouse cursor positioning
|
||||
|
||||
- **Input Synthesis**
|
||||
- Keyboard event injection
|
||||
- Mouse event injection
|
||||
- Modifier key synchronization
|
||||
|
||||
### Advanced Features
|
||||
|
||||
- **Clipboard Synchronization**
|
||||
- Clipboard grab notifications
|
||||
- Data transfer (@ref kMsgDClipboard - text, images, HTML)
|
||||
- Streaming for large data (v1.6+)
|
||||
|
||||
- **File Transfer** (v1.5+)
|
||||
- Drag-and-drop initiation
|
||||
- Chunked file transfer
|
||||
- Progress tracking
|
||||
|
||||
- **Security Features**
|
||||
- TLS/SSL encryption (v1.4+)
|
||||
- Secure input notifications (v1.7+)
|
||||
- Input validation and limits
|
||||
|
||||
## Reference Implementation
|
||||
|
||||
The @ref ServerProxy class provides a reference implementation demonstrating:
|
||||
|
||||
- Basic protocol handling
|
||||
- Message parsing and generation
|
||||
- Connection management
|
||||
- Input event processing
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
When extending the protocol:
|
||||
|
||||
1. **Maintain Compatibility**: New features should be backward compatible
|
||||
2. **Update Documentation**: Document new messages and parameters
|
||||
3. **Version Increment**: Bump minor version for new features
|
||||
4. **Test Thoroughly**: Verify with existing clients and servers
|
||||
|
||||
## Support and Resources
|
||||
|
||||
|
||||
- @ref ProtocolTypes.h – Complete protocol specification
|
||||
- @ref ProtocolUtil.h – Message formatting utilities
|
||||
|
||||
---
|
||||
|
||||
*This documentation is generated from the source code and is always up-to-date with the latest protocol implementation.*
|
||||
20
doc/user/CMakeLists.txt
Normal file
20
doc/user/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2025 Chris Rizzitello <sithlord48@gmail.com>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE mainpage.md)
|
||||
|
||||
# Files used to make our documents
|
||||
# User facing documents will not include doxy comments in source code
|
||||
doxygen_add_docs(user-docs ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating user documentation" ALL)
|
||||
|
||||
# HACK Only these will show in your IDE
|
||||
target_sources(user-docs PRIVATE
|
||||
mainpage.md
|
||||
configuration.md
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html"
|
||||
DESTINATION ${CMAKE_INSTALL_DOCDIR}
|
||||
COMPONENT deskflow_user_docs
|
||||
)
|
||||
@ -1,18 +1,17 @@
|
||||
# GUI Config
|
||||
|
||||
Deskflow will automatically figure out where to save settings and other files.
|
||||
|
||||
|
||||
## Unix Systems
|
||||
The search order for a setting file is:
|
||||
1. `<install-path>/settings/Deskflow.conf`
|
||||
1. `<XDG_CONFIG_HOME>/Deskflow/Deskflow.conf`
|
||||
1. A user settings file
|
||||
1. A system settings file
|
||||
|
||||
A new settings file will be created in the user path if no settings file is found.
|
||||
The path of the settings file will be used as the base for all other config files.
|
||||
|
||||
### Windows
|
||||
- System: `C:\ProgramData\Deskflow\Deskflow.conf`
|
||||
- User: `C:\Users\userName\AppData\Local\Deskflow\Deskflow.conf`
|
||||
|
||||
|
||||
### Linux
|
||||
- System: `/etc/Deskflow/Deskflow.conf`
|
||||
- User: `~/.config/Deskflow/Deskflow.conf`
|
||||
@ -20,6 +19,17 @@
|
||||
### macOS
|
||||
- System: `/Library/Deskflow/Deskflow.conf`
|
||||
- User: `~/Library/Deskflow/Deskflow.conf`
|
||||
|
||||
|
||||
## Windows
|
||||
|
||||
The search order for a setting file is:
|
||||
1. `<install-path>/settings/Deskflow.conf`
|
||||
1. Windows Registry `HKCU\Software\Deskflow\Deskflow`
|
||||
|
||||
Windows will save to the install dir if settings are loaded from there. If not, it saves any other config files in: `C:\ProgramData\Deskflow\`
|
||||
|
||||
When using settings from the install dir, the service mode will not be available.
|
||||
|
||||
# Server Config Examples
|
||||
|
||||
@ -185,7 +195,4 @@ _last = "moe:24800"
|
||||
|
||||
# Enable verbose logging in the GUI (always off by default)
|
||||
# DESKFLOW_GUI_VERBOSE=true
|
||||
|
||||
# Reset all settings and delete all data on startup
|
||||
# DESKFLOW_RESET_ALL=true
|
||||
```
|
||||
@ -1,9 +1,9 @@
|
||||
sonar.organization=deskflow
|
||||
sonar.projectKey=deskflow_deskflow
|
||||
sonar.sources=src/apps,src/lib
|
||||
sonar.tests=src/test
|
||||
sonar.tests=src/unittests
|
||||
sonar.exclusions=subprojects/**,build/**
|
||||
sonar.coverage.exclusions=subprojects/**,src/test/**,src/apps/deskflow-gui/**,src/apps/res/**
|
||||
sonar.coverage.exclusions=subprojects/**,src/unittests/**,src/apps/deskflow-gui/**,src/apps/res/**
|
||||
sonar.cpd.exclusions=**/*Test*.cpp
|
||||
sonar.host.url=https://sonarcloud.io
|
||||
sonar.cfamily.compile-commands=build/compile_commands.json
|
||||
|
||||
@ -11,5 +11,6 @@ add_subdirectory(apps)
|
||||
|
||||
option(BUILD_TESTS "Build tests" ON)
|
||||
if(BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(unittests)
|
||||
endif()
|
||||
|
||||
|
||||
@ -3,6 +3,31 @@
|
||||
# SPDX-FileCopyrightText: 2009 - 2012 Nick Bolton
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_program(HELP2MAN help2man)
|
||||
if(NOT HELP2MAN)
|
||||
message(STATUS "Man page tool (help2man) not found, man pages will not be generated")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
function(generate_app_man TARGET NAME)
|
||||
if(HELP2MAN)
|
||||
add_custom_command(
|
||||
TARGET ${target} POST_BUILD
|
||||
COMMAND QT_QPA_PLATFORM=minimal PATH=$<TARGET_FILE_DIR:${target}>:${PATH} ${HELP2MAN}
|
||||
--name ${NAME}
|
||||
--include ${CMAKE_SOURCE_DIR}/src/apps/res/manpage.txt
|
||||
--no-info
|
||||
${target}
|
||||
-o $<TARGET_FILE_DIR:${target}>/${target}.1
|
||||
)
|
||||
install(
|
||||
FILES $<TARGET_FILE_DIR:${target}>/${target}.1
|
||||
DESTINATION ${CMAKE_INSTALL_MANDIR}/man1
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
option(BUILD_UNIFIED "Build unified binary" OFF)
|
||||
if(BUILD_UNIFIED)
|
||||
add_subdirectory(deskflow-core)
|
||||
|
||||
@ -45,6 +45,7 @@ if(APPLE)
|
||||
)
|
||||
elseif(UNIX)
|
||||
install(TARGETS ${target} DESTINATION bin)
|
||||
generate_app_man(${target} "${CMAKE_PROJECT_DESCRIPTION} \\(Client\\)")
|
||||
elseif(WIN32)
|
||||
install(
|
||||
TARGETS ${target}
|
||||
|
||||
@ -13,15 +13,20 @@
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
#include "arch/win32/ArchMiscWindows.h"
|
||||
#include <QCoreApplication>
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#if SYSAPI_WIN32
|
||||
// HACK to make sure settings gets the correct qApp path
|
||||
QCoreApplication m(argc, argv);
|
||||
m.deleteLater();
|
||||
|
||||
ArchMiscWindows::guardRuntimeVersion();
|
||||
|
||||
// record window instance for tray icon, etc
|
||||
ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
|
||||
ArchMiscWindows::setInstanceWin32(GetModuleHandle(nullptr));
|
||||
#endif
|
||||
|
||||
Arch arch;
|
||||
|
||||
@ -40,6 +40,7 @@ if(APPLE)
|
||||
)
|
||||
elseif(UNIX)
|
||||
install(TARGETS ${target} DESTINATION bin)
|
||||
generate_app_man(${target} "${CMAKE_PROJECT_DESCRIPTION}")
|
||||
elseif(WIN32)
|
||||
install(
|
||||
TARGETS ${target}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
#include "arch/win32/ArchMiscWindows.h"
|
||||
#include <QCoreApplication>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
@ -39,7 +40,11 @@ bool isClient(int argc, char **argv)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#if SYSAPI_WIN32
|
||||
ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
|
||||
// HACK to make sure settings gets the correct qApp path
|
||||
QCoreApplication m(argc, argv);
|
||||
m.deleteLater();
|
||||
|
||||
ArchMiscWindows::setInstanceWin32(GetModuleHandle(nullptr));
|
||||
#endif
|
||||
|
||||
Arch arch;
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "VersionInfo.h"
|
||||
#include "arch/Arch.h"
|
||||
#include "base/EventQueue.h"
|
||||
#include "base/Log.h"
|
||||
@ -97,10 +98,10 @@ int main(int argc, char **argv)
|
||||
|
||||
if (parser.isSet(installOption)) {
|
||||
daemon.install();
|
||||
return kExitSuccess;
|
||||
return s_exitSuccess;
|
||||
} else if (parser.isSet(uninstallOption)) {
|
||||
daemon.uninstall();
|
||||
return kExitSuccess;
|
||||
return s_exitSuccess;
|
||||
}
|
||||
|
||||
const auto ipcServer =
|
||||
@ -120,10 +121,10 @@ int main(int argc, char **argv)
|
||||
|
||||
} catch (std::exception &e) {
|
||||
handleError(e.what());
|
||||
return kExitFailed;
|
||||
return s_exitFailed;
|
||||
} catch (...) {
|
||||
handleError();
|
||||
return kExitFailed;
|
||||
return s_exitFailed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -72,10 +72,29 @@ if(WIN32)
|
||||
RUNTIME DESTINATION .
|
||||
)
|
||||
|
||||
set(QT_DEPENDS_DIR ${CMAKE_BINARY_DIR}/qt-depends)
|
||||
|
||||
add_custom_command(
|
||||
TARGET ${target} POST_BUILD
|
||||
COMMAND ${DEPLOYQT} --no-compiler-runtime --no-system-d3d-compiler --no-quick-import -network $<TARGET_FILE:${target}>
|
||||
COMMAND ${DEPLOYQT}
|
||||
--no-compiler-runtime
|
||||
--no-system-d3d-compiler
|
||||
--no-opengl-sw
|
||||
--no-quick-import
|
||||
--dir "${QT_DEPENDS_DIR}"
|
||||
--plugindir "${QT_DEPENDS_DIR}/plugins"
|
||||
$<TARGET_FILE:${target}>
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different
|
||||
${QT_DEPENDS_DIR}
|
||||
$<TARGET_FILE_DIR:${target}>
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY ${QT_DEPENDS_DIR}/
|
||||
DESTINATION .
|
||||
PATTERN "dx*.dll" EXCLUDE
|
||||
)
|
||||
|
||||
elseif(APPLE)
|
||||
set_target_properties(${target} PROPERTIES
|
||||
INSTALL_RPATH "@loader_path/../Libraries;@loader_path/../Frameworks"
|
||||
@ -84,4 +103,5 @@ elseif(APPLE)
|
||||
install(TARGETS ${target} BUNDLE DESTINATION .)
|
||||
else()
|
||||
install(TARGETS ${target} DESTINATION bin)
|
||||
generate_app_man(${target} "${CMAKE_PROJECT_DESCRIPTION} \\(GUI\\)")
|
||||
endif()
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "VersionInfo.h"
|
||||
#include "common/Constants.h"
|
||||
#include "common/UrlConstants.h"
|
||||
#include "gui/Diagnostic.h"
|
||||
@ -16,13 +17,10 @@
|
||||
#include "gui/StyleUtils.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QLocalSocket>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QSharedMemory>
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#include <Carbon/Carbon.h>
|
||||
@ -35,23 +33,11 @@
|
||||
|
||||
using namespace deskflow::gui;
|
||||
|
||||
class QThreadImpl : public QThread
|
||||
{
|
||||
public:
|
||||
static void msleep(unsigned long msecs)
|
||||
{
|
||||
QThread::msleep(msecs);
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
bool checkMacAssistiveDevices();
|
||||
#endif
|
||||
|
||||
bool hasArg(const QString &arg, const QStringList &args)
|
||||
{
|
||||
return std::ranges::any_of(args, [&arg](const QString &a) { return a == arg; });
|
||||
}
|
||||
const static auto kHeader = QStringLiteral("%1: %2\n").arg(kAppName, kDisplayVersion);
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -60,21 +46,42 @@ int main(int argc, char *argv[])
|
||||
QLoggingCategory::setFilterRules(QStringLiteral("*.debug=true\nqt.*=false"));
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
/* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is
|
||||
* instantiated" */
|
||||
::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1);
|
||||
#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"));
|
||||
|
||||
// used as a prefix for settings paths, and must not be a url.
|
||||
QCoreApplication::setOrganizationDomain(kOrgDomain);
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Add Command Line Options
|
||||
auto helpOption = QCommandLineOption({"h", "help"}, "Display Help on the command line");
|
||||
auto versionOption = QCommandLineOption({"v", "version"}, "Display version information");
|
||||
auto resetOption = QCommandLineOption("reset", "Reset all settings");
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
|
||||
parser.addOption(helpOption);
|
||||
parser.addOption(versionOption);
|
||||
parser.addOption(resetOption);
|
||||
parser.parse(QCoreApplication::arguments());
|
||||
|
||||
if (!parser.errorText().isEmpty()) {
|
||||
qCritical().noquote() << parser.errorText() << "\nUse --help for more information.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parser.isSet(helpOption)) {
|
||||
QTextStream(stdout) << kHeader << QStringLiteral(" %1\n\n").arg(kAppDescription)
|
||||
<< parser.helpText().replace(QApplication::applicationFilePath(), kAppId);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (parser.isSet(versionOption)) {
|
||||
QTextStream(stdout) << kHeader << kCopyright << Qt::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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");
|
||||
@ -105,8 +112,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sets the fallback icon path
|
||||
setIconFallbackPaths();
|
||||
// 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)});
|
||||
|
||||
qInstallMessageHandler(deskflow::gui::messages::messageHandler);
|
||||
qInfo("%s v%s", kAppName, qPrintable(kVersion));
|
||||
@ -121,7 +133,7 @@ int main(int argc, char *argv[])
|
||||
"Please drag %1 to the Applications folder, "
|
||||
"and open it from there."
|
||||
);
|
||||
QMessageBox::information(NULL, kAppName, msgBody.arg(kAppName));
|
||||
QMessageBox::information(nullptr, kAppName, msgBody.arg(kAppName));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -131,10 +143,7 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
|
||||
// --no-reset
|
||||
QStringList arguments = QCoreApplication::arguments();
|
||||
const auto noReset = hasArg("--no-reset", arguments);
|
||||
const auto resetEnvVar = QVariant(qEnvironmentVariable("DESKFLOW_RESET_ALL")).toBool();
|
||||
if (resetEnvVar && !noReset) {
|
||||
if (parser.isSet(resetOption)) {
|
||||
diagnostic::clearSettings(false);
|
||||
}
|
||||
|
||||
@ -147,8 +156,6 @@ int main(int argc, char *argv[])
|
||||
#if defined(Q_OS_MAC)
|
||||
bool checkMacAssistiveDevices()
|
||||
{
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 // mavericks
|
||||
|
||||
// new in mavericks, applications are trusted individually
|
||||
// with use of the accessibility api. this call will show a
|
||||
// prompt which can show the security/privacy/accessibility
|
||||
@ -161,26 +168,10 @@ bool checkMacAssistiveDevices()
|
||||
|
||||
const void *keys[] = {kAXTrustedCheckOptionPrompt};
|
||||
const void *trueValue[] = {kCFBooleanTrue};
|
||||
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, trueValue, 1, NULL, NULL);
|
||||
CFDictionaryRef options = CFDictionaryCreate(nullptr, keys, trueValue, 1, nullptr, nullptr);
|
||||
|
||||
bool result = AXIsProcessTrustedWithOptions(options);
|
||||
CFRelease(options);
|
||||
return result;
|
||||
|
||||
#else
|
||||
|
||||
// now deprecated in mavericks.
|
||||
bool result = AXAPIEnabled();
|
||||
if (!result) {
|
||||
QString msgBody = QString(
|
||||
"Please enable access to assistive devices "
|
||||
"System Preferences -> Security & Privacy -> "
|
||||
"Privacy -> Accessibility, then re-open %1."
|
||||
);
|
||||
QMessageBox::information(NULL, kAppName, msgBody.arg(kAppName));
|
||||
}
|
||||
return result;
|
||||
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -45,6 +45,7 @@ if(APPLE)
|
||||
)
|
||||
elseif(UNIX)
|
||||
install(TARGETS ${target} DESTINATION bin)
|
||||
generate_app_man(${target} "${CMAKE_PROJECT_DESCRIPTION} \\(Server\\)")
|
||||
elseif(WIN32)
|
||||
install(
|
||||
TARGETS ${target}
|
||||
|
||||
@ -13,15 +13,20 @@
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
#include "arch/win32/ArchMiscWindows.h"
|
||||
#include <QCoreApplication>
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#if SYSAPI_WIN32
|
||||
// HACK to make sure settings gets the correct qApp path
|
||||
QCoreApplication m(argc, argv);
|
||||
m.deleteLater();
|
||||
|
||||
ArchMiscWindows::guardRuntimeVersion();
|
||||
|
||||
// record window instance for tray icon, etc
|
||||
ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
|
||||
ArchMiscWindows::setInstanceWin32(GetModuleHandle(nullptr));
|
||||
#endif
|
||||
|
||||
Arch arch;
|
||||
@ -30,30 +35,6 @@ int main(int argc, char **argv)
|
||||
Log log;
|
||||
EventQueue events;
|
||||
|
||||
// HACK: the `--active-desktop` arg actually belongs in the `deskflow-core` binary,
|
||||
// but we are placing it here in the server binary temporarily until we are ready to
|
||||
// ship the `deskflow-core` binary. we are deliberately not integrating `--active-desktop`
|
||||
// into the existing `ServerApp` arg parsing code as that would be a waste of time.
|
||||
#if SYSAPI_WIN32
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg(argv[i]);
|
||||
// This is called by the daemon (running in session 0) when it needs to know the name of the
|
||||
// interactive desktop.
|
||||
// It is necessary to run a utility process because the daemon runs in session 0, which does not
|
||||
// have access to the active desktop, and so cannot query it's name.
|
||||
if (arg == "--active-desktop") {
|
||||
const auto name = ArchMiscWindows::getActiveDesktopName();
|
||||
if (name.empty()) {
|
||||
LOG((CLOG_CRIT "failed to get active desktop name"));
|
||||
return kExitFailed;
|
||||
}
|
||||
|
||||
LOG((CLOG_PRINT "%s", name.c_str()));
|
||||
return kExitSuccess;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ServerApp app(&events);
|
||||
return app.run(argc, argv);
|
||||
}
|
||||
|
||||
5
src/apps/res/manpage.txt
Normal file
5
src/apps/res/manpage.txt
Normal file
@ -0,0 +1,5 @@
|
||||
[SEE ALSO]
|
||||
deskflow(1), deskflow-client(1), deskflow-server(2)
|
||||
|
||||
All documentation is on the web, so please point your browser at
|
||||
<https://github.com/deskflow/deskflow/wiki> and surf away.
|
||||
@ -3,6 +3,8 @@
|
||||
# SPDX-FileCopyrightText: 2009 - 2012 Nick Bolton
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
configure_file(VersionInfo.h.in VersionInfo.h @ONLY)
|
||||
|
||||
add_subdirectory(arch)
|
||||
add_subdirectory(base)
|
||||
add_subdirectory(client)
|
||||
|
||||
@ -7,42 +7,12 @@
|
||||
|
||||
// clang-format off
|
||||
|
||||
/* Define if the <X11/extensions/dpms.h> header file declares function prototypes. */
|
||||
#cmakedefine HAVE_DPMS_PROTOTYPES @HAVE_DPMS_PROTOTYPES@
|
||||
|
||||
/* Define if you have the `inet_aton` function. */
|
||||
#cmakedefine HAVE_INET_ATON @HAVE_INET_ATON@
|
||||
|
||||
/* Define if you have the `nanosleep` function. */
|
||||
#cmakedefine HAVE_NANOSLEEP @HAVE_NANOSLEEP@
|
||||
|
||||
/* Define if you have a POSIX `sigwait` function. */
|
||||
#cmakedefine HAVE_POSIX_SIGWAIT @HAVE_POSIX_SIGWAIT@
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#cmakedefine HAVE_PTHREAD @HAVE_PTHREAD@
|
||||
|
||||
/* Define if you have `pthread_sigmask` and `pthread_kill` functions. */
|
||||
#cmakedefine HAVE_PTHREAD_SIGNAL @HAVE_PTHREAD_SIGNAL@
|
||||
|
||||
/* Define if your compiler defines socklen_t. */
|
||||
#cmakedefine HAVE_SOCKLEN_T @HAVE_SOCKLEN_T@
|
||||
|
||||
/* Define to 1 if you have the <sys/select.h> header file. */
|
||||
#cmakedefine HAVE_SYS_SELECT_H @HAVE_SYS_SELECT_H@
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#cmakedefine HAVE_SYS_SOCKET_H @HAVE_SYS_SOCKET_H@
|
||||
|
||||
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||
#cmakedefine HAVE_SYS_TIME_H @HAVE_SYS_TIME_H@
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#cmakedefine HAVE_SYS_TYPES_H @HAVE_SYS_TYPES_H@
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#cmakedefine HAVE_UNISTD_H @HAVE_UNISTD_H@
|
||||
|
||||
/* Define to 1 if you have the <X11/extensions/Xrandr.h> header file. */
|
||||
#cmakedefine HAVE_X11_EXTENSIONS_XRANDR_H @HAVE_X11_EXTENSIONS_XRANDR_H@
|
||||
|
||||
@ -61,19 +31,4 @@
|
||||
/* Define this if the XKB extension is available. */
|
||||
#cmakedefine HAVE_XKB_EXTENSION @HAVE_XKB_EXTENSION@
|
||||
|
||||
/* Define to the type of arg 1 for `select`. */
|
||||
#cmakedefine SELECT_TYPE_ARG1 @SELECT_TYPE_ARG1@
|
||||
|
||||
/* Define to the type of args 2, 3 and 4 for `select`. */
|
||||
#cmakedefine SELECT_TYPE_ARG234 @SELECT_TYPE_ARG234@
|
||||
|
||||
/* Define to the type of arg 5 for `select`. */
|
||||
#cmakedefine SELECT_TYPE_ARG5 @SELECT_TYPE_ARG5@
|
||||
|
||||
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
|
||||
#cmakedefine TIME_WITH_SYS_TIME @TIME_WITH_SYS_TIME@
|
||||
|
||||
/* Define to 1 if the X Window System is missing or not being used. */
|
||||
#cmakedefine X_DISPLAY_MISSING @X_DISPLAY_MISSING@
|
||||
|
||||
// clang-format on
|
||||
|
||||
14
src/lib/VersionInfo.h.in
Normal file
14
src/lib/VersionInfo.h.in
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2024 - 2025 Chris Rizzitello <sithlord48@gmail.com>
|
||||
* SPDX-FileCopyrightText: (C) 2025 Symless Ltd.
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
const auto kVersion = "@CMAKE_PROJECT_VERSION@";
|
||||
const auto kVersionGitSha = "@GIT_SHA_SHORT@";
|
||||
// clang-format off
|
||||
const auto kDisplayVersion = @CMAKE_PROJECT_VERSION_TWEAK@ ? "@CMAKE_PROJECT_VERSION@ (@GIT_SHA_SHORT@)" : "@CMAKE_PROJECT_VERSION_MAJOR@.@CMAKE_PROJECT_VERSION_MINOR@.@CMAKE_PROJECT_VERSION_PATCH@";
|
||||
// clang-format on
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include "arch/Arch.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
#include "arch/win32/ArchMiscWindows.h"
|
||||
#endif
|
||||
@ -15,11 +17,11 @@
|
||||
// Arch
|
||||
//
|
||||
|
||||
Arch *Arch::s_instance = NULL;
|
||||
Arch *Arch::s_instance = nullptr;
|
||||
|
||||
Arch::Arch()
|
||||
{
|
||||
assert(s_instance == NULL);
|
||||
assert(s_instance == nullptr);
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
@ -28,10 +30,6 @@ Arch::Arch(Arch *arch)
|
||||
s_instance = arch;
|
||||
}
|
||||
|
||||
Arch::~Arch()
|
||||
{
|
||||
}
|
||||
|
||||
void Arch::init()
|
||||
{
|
||||
ARCH_NETWORK::init();
|
||||
@ -42,6 +40,22 @@ void Arch::init()
|
||||
|
||||
Arch *Arch::getInstance()
|
||||
{
|
||||
assert(s_instance != NULL);
|
||||
assert(s_instance != nullptr);
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void Arch::sleep(double timeout)
|
||||
{
|
||||
ARCH->testCancelThread();
|
||||
if (timeout < 0.0)
|
||||
return;
|
||||
const auto msec = static_cast<uint64_t>(timeout * 1000);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(msec));
|
||||
}
|
||||
|
||||
double Arch::time()
|
||||
{
|
||||
auto sinceEpoch = std::chrono::steady_clock::now().time_since_epoch();
|
||||
auto uSecSinceEpoch = std::chrono::duration_cast<std::chrono::microseconds>(sinceEpoch).count();
|
||||
return double(uSecSinceEpoch / 1000000);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
// TODO: consider whether or not to use either encapsulation (as below)
|
||||
// Consider whether or not to use either encapsulation (as below)
|
||||
// or inheritance (as it is now) for the ARCH stuff.
|
||||
//
|
||||
// case for encapsulation:
|
||||
@ -24,32 +24,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "arch/ArchString.h"
|
||||
#include "common/Common.h"
|
||||
|
||||
#if SYSAPI_WIN32
|
||||
|
||||
#include "arch/win32/ArchConsoleWindows.h"
|
||||
#include "arch/win32/ArchDaemonWindows.h"
|
||||
#include "arch/win32/ArchLogWindows.h"
|
||||
#include "arch/win32/ArchMultithreadWindows.h"
|
||||
#include "arch/win32/ArchNetworkWinsock.h"
|
||||
#include "arch/win32/ArchSleepWindows.h"
|
||||
#include "arch/win32/ArchStringWindows.h"
|
||||
#include "arch/win32/ArchTimeWindows.h"
|
||||
|
||||
#elif SYSAPI_UNIX
|
||||
|
||||
#include "arch/unix/ArchConsoleUnix.h"
|
||||
#include "arch/unix/ArchDaemonUnix.h"
|
||||
#include "arch/unix/ArchLogUnix.h"
|
||||
#include "arch/unix/ArchNetworkBSD.h"
|
||||
#include "arch/unix/ArchSleepUnix.h"
|
||||
#include "arch/unix/ArchStringUnix.h"
|
||||
#include "arch/unix/ArchTimeUnix.h"
|
||||
|
||||
#if HAVE_PTHREAD
|
||||
#include "arch/unix/ArchMultithreadPosix.h"
|
||||
#endif
|
||||
#include "arch/unix/ArchNetworkBSD.h"
|
||||
|
||||
#endif
|
||||
|
||||
@ -69,26 +59,19 @@ to each method to those implementations. Clients should use the
|
||||
exactly one of these objects before attempting to call any method,
|
||||
typically at the beginning of \c main().
|
||||
*/
|
||||
class Arch : public ARCH_CONSOLE,
|
||||
public ARCH_DAEMON,
|
||||
public ARCH_LOG,
|
||||
public ARCH_MULTITHREAD,
|
||||
public ARCH_NETWORK,
|
||||
public ARCH_SLEEP,
|
||||
public ARCH_STRING,
|
||||
public ARCH_TIME
|
||||
class Arch : public ARCH_DAEMON, public ARCH_LOG, public ARCH_MULTITHREAD, public ARCH_NETWORK, public ArchString
|
||||
{
|
||||
public:
|
||||
Arch();
|
||||
Arch(Arch *arch);
|
||||
virtual ~Arch();
|
||||
explicit Arch(Arch *arch);
|
||||
~Arch() override = default;
|
||||
|
||||
//! Call init on other arch classes.
|
||||
/*!
|
||||
Some arch classes depend on others to exist first. When init is called
|
||||
these classes will have ARCH available for use.
|
||||
*/
|
||||
virtual void init();
|
||||
void init() override;
|
||||
|
||||
//
|
||||
// accessors
|
||||
@ -106,6 +89,19 @@ public:
|
||||
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
|
||||
*/
|
||||
static void sleep(double timeout);
|
||||
|
||||
/**
|
||||
* @brief time
|
||||
* @return Returns the number of seconds since some arbitrary starting time.
|
||||
* This should return as high a precision as reasonable.
|
||||
*/
|
||||
static double time();
|
||||
|
||||
private:
|
||||
static Arch *s_instance;
|
||||
};
|
||||
@ -114,7 +110,7 @@ private:
|
||||
class ArchMutexLock
|
||||
{
|
||||
public:
|
||||
ArchMutexLock(ArchMutex mutex) : m_mutex(mutex)
|
||||
explicit ArchMutexLock(ArchMutex mutex) : m_mutex(mutex)
|
||||
{
|
||||
ARCH->lockMutex(m_mutex);
|
||||
}
|
||||
|
||||
@ -1,21 +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/ArchConsoleStd.h"
|
||||
#include "base/Log.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void ArchConsoleStd::writeConsole(ELevel level, const char *str)
|
||||
{
|
||||
if ((level >= kFATAL) && (level <= kWARNING))
|
||||
std::cerr << str << std::endl;
|
||||
else
|
||||
std::cout << str << std::endl;
|
||||
|
||||
std::cout.flush();
|
||||
}
|
||||
@ -1,34 +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/IArchConsole.h"
|
||||
|
||||
//! Cross platform implementation of IArchConsole
|
||||
class ArchConsoleStd : public IArchConsole
|
||||
{
|
||||
public:
|
||||
ArchConsoleStd()
|
||||
{
|
||||
}
|
||||
virtual ~ArchConsoleStd()
|
||||
{
|
||||
}
|
||||
|
||||
// IArchConsole overrides
|
||||
virtual void openConsole(const char *title)
|
||||
{
|
||||
}
|
||||
virtual void closeConsole()
|
||||
{
|
||||
}
|
||||
virtual void showConsole(bool)
|
||||
{
|
||||
}
|
||||
virtual void writeConsole(ELevel level, const char *);
|
||||
};
|
||||
@ -11,16 +11,6 @@
|
||||
// ArchDaemonNone
|
||||
//
|
||||
|
||||
ArchDaemonNone::ArchDaemonNone()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ArchDaemonNone::~ArchDaemonNone()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchDaemonNone::installDaemon(const char *, const char *, const char *, const char *, const char *)
|
||||
{
|
||||
// do nothing
|
||||
@ -31,7 +21,7 @@ void ArchDaemonNone::uninstallDaemon(const char *)
|
||||
// do nothing
|
||||
}
|
||||
|
||||
int ArchDaemonNone::daemonize(const char *name, DaemonFunc func)
|
||||
int ArchDaemonNone::daemonize(const char *name, DaemonFunc const &func)
|
||||
{
|
||||
// simply forward the call to func. obviously, this doesn't
|
||||
// do any daemonizing.
|
||||
@ -50,10 +40,12 @@ bool ArchDaemonNone::isDaemonInstalled(const char *)
|
||||
|
||||
void ArchDaemonNone::installDaemon()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchDaemonNone::uninstallDaemon()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
std::string ArchDaemonNone::commandLine() const
|
||||
|
||||
@ -21,18 +21,18 @@ function and returns its result.
|
||||
class ArchDaemonNone : public IArchDaemon
|
||||
{
|
||||
public:
|
||||
ArchDaemonNone();
|
||||
virtual ~ArchDaemonNone();
|
||||
ArchDaemonNone() = default;
|
||||
~ArchDaemonNone() override = default;
|
||||
|
||||
// IArchDaemon overrides
|
||||
virtual void installDaemon(
|
||||
void installDaemon(
|
||||
const char *name, const char *description, const char *pathname, const char *commandLine, const char *dependencies
|
||||
);
|
||||
virtual void uninstallDaemon(const char *name);
|
||||
virtual int daemonize(const char *name, DaemonFunc func);
|
||||
virtual bool canInstallDaemon(const char *name);
|
||||
virtual bool isDaemonInstalled(const char *name);
|
||||
virtual void installDaemon();
|
||||
virtual void uninstallDaemon();
|
||||
virtual std::string commandLine() const;
|
||||
) override;
|
||||
void uninstallDaemon(const char *name) override;
|
||||
int daemonize(const char *name, DaemonFunc const &func) override;
|
||||
bool canInstallDaemon(const char *name) override;
|
||||
bool isDaemonInstalled(const char *name) override;
|
||||
void installDaemon() override;
|
||||
void uninstallDaemon() override;
|
||||
std::string commandLine() const override;
|
||||
};
|
||||
|
||||
@ -5,45 +5,31 @@
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "arch/IArchString.h"
|
||||
#include "arch/Arch.h"
|
||||
#include "common/Common.h"
|
||||
#include "arch/ArchString.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
|
||||
static ArchMutex s_mutex = NULL;
|
||||
std::mutex s_mutex;
|
||||
|
||||
//
|
||||
// use C library non-reentrant multibyte conversion with mutex
|
||||
//
|
||||
|
||||
IArchString::~IArchString()
|
||||
{
|
||||
if (s_mutex != NULL) {
|
||||
ARCH->closeMutex(s_mutex);
|
||||
s_mutex = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int IArchString::convStringWCToMB(char *dst, const wchar_t *src, uint32_t n, bool *errors)
|
||||
int ArchString::convStringWCToMB(char *dst, const wchar_t *src, uint32_t n, bool *errors) const
|
||||
{
|
||||
std::scoped_lock lock{s_mutex};
|
||||
ptrdiff_t len = 0;
|
||||
|
||||
bool dummyErrors;
|
||||
if (errors == NULL) {
|
||||
if (errors == nullptr) {
|
||||
errors = &dummyErrors;
|
||||
}
|
||||
*errors = false;
|
||||
|
||||
if (s_mutex == NULL) {
|
||||
s_mutex = ARCH->newMutex();
|
||||
}
|
||||
|
||||
ARCH->lockMutex(s_mutex);
|
||||
|
||||
if (dst == NULL) {
|
||||
if (dst == nullptr) {
|
||||
char dummy[MB_LEN_MAX];
|
||||
const wchar_t *scan = src;
|
||||
for (; n > 0; --n) {
|
||||
@ -55,16 +41,14 @@ int IArchString::convStringWCToMB(char *dst, const wchar_t *src, uint32_t n, boo
|
||||
len += mblen;
|
||||
++scan;
|
||||
}
|
||||
ptrdiff_t mblen = wctomb(dummy, L'\0');
|
||||
if (mblen != -1) {
|
||||
if (ptrdiff_t mblen = wctomb(dummy, L'\0'); mblen != -1) {
|
||||
len += mblen - 1;
|
||||
}
|
||||
} else {
|
||||
char *dst0 = dst;
|
||||
const char *dst0 = dst;
|
||||
const wchar_t *scan = src;
|
||||
for (; n > 0; --n) {
|
||||
ptrdiff_t mblen = wctomb(dst, *scan);
|
||||
if (mblen == -1) {
|
||||
if (ptrdiff_t mblen = wctomb(dst, *scan); mblen == -1) {
|
||||
*errors = true;
|
||||
*dst++ = '?';
|
||||
} else {
|
||||
@ -72,40 +56,41 @@ int IArchString::convStringWCToMB(char *dst, const wchar_t *src, uint32_t n, boo
|
||||
}
|
||||
++scan;
|
||||
}
|
||||
ptrdiff_t mblen = wctomb(dst, L'\0');
|
||||
if (mblen != -1) {
|
||||
if (ptrdiff_t mblen = wctomb(dst, L'\0'); mblen != -1) {
|
||||
// don't include nul terminator
|
||||
dst += mblen - 1;
|
||||
}
|
||||
len = dst - dst0;
|
||||
}
|
||||
ARCH->unlockMutex(s_mutex);
|
||||
|
||||
return static_cast<int>(len);
|
||||
}
|
||||
|
||||
int IArchString::convStringMBToWC(wchar_t *dst, const char *src, uint32_t n, bool *errors)
|
||||
ArchString::EWideCharEncoding ArchString::getWideCharEncoding() const
|
||||
{
|
||||
#ifdef SYSAPI_WIN32
|
||||
return EWideCharEncoding::kUTF16;
|
||||
#else
|
||||
return EWideCharEncoding::kUCS4;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ArchString::convStringMBToWC(wchar_t *dst, const char *src, uint32_t n, bool *errors) const
|
||||
{
|
||||
std::scoped_lock lock{s_mutex};
|
||||
ptrdiff_t len = 0;
|
||||
wchar_t dummy;
|
||||
|
||||
bool dummyErrors;
|
||||
if (errors == NULL) {
|
||||
if (errors == nullptr) {
|
||||
errors = &dummyErrors;
|
||||
}
|
||||
*errors = false;
|
||||
|
||||
if (s_mutex == NULL) {
|
||||
s_mutex = ARCH->newMutex();
|
||||
}
|
||||
|
||||
ARCH->lockMutex(s_mutex);
|
||||
|
||||
if (dst == NULL) {
|
||||
if (dst == nullptr) {
|
||||
const char *scan = src;
|
||||
while (n > 0) {
|
||||
ptrdiff_t mblen = mbtowc(&dummy, scan, n);
|
||||
switch (mblen) {
|
||||
switch (ptrdiff_t mblen = mbtowc(&dummy, scan, n); mblen) {
|
||||
case -2:
|
||||
// incomplete last character. convert to unknown character.
|
||||
*errors = true;
|
||||
@ -137,11 +122,10 @@ int IArchString::convStringMBToWC(wchar_t *dst, const char *src, uint32_t n, boo
|
||||
}
|
||||
}
|
||||
} else {
|
||||
wchar_t *dst0 = dst;
|
||||
const wchar_t *dst0 = dst;
|
||||
const char *scan = src;
|
||||
while (n > 0) {
|
||||
ptrdiff_t mblen = mbtowc(dst, scan, n);
|
||||
switch (mblen) {
|
||||
switch (ptrdiff_t mblen = mbtowc(dst, scan, n); mblen) {
|
||||
case -2:
|
||||
// incomplete character. convert to unknown character.
|
||||
*errors = true;
|
||||
@ -174,7 +158,6 @@ int IArchString::convStringMBToWC(wchar_t *dst, const char *src, uint32_t n, boo
|
||||
}
|
||||
len = dst - dst0;
|
||||
}
|
||||
ARCH->unlockMutex(s_mutex);
|
||||
|
||||
return static_cast<int>(len);
|
||||
}
|
||||
@ -18,22 +18,22 @@
|
||||
This interface defines the string operations required by
|
||||
deskflow. Each architecture must implement this interface.
|
||||
*/
|
||||
class IArchString : public IInterface
|
||||
class ArchString : public IInterface
|
||||
{
|
||||
public:
|
||||
IArchString() = default;
|
||||
IArchString(const IArchString &) = delete;
|
||||
IArchString(IArchString &&) = delete;
|
||||
virtual ~IArchString();
|
||||
ArchString() = default;
|
||||
ArchString(const ArchString &) = delete;
|
||||
ArchString(ArchString &&) = delete;
|
||||
~ArchString() override = default;
|
||||
|
||||
IArchString &operator=(const IArchString &) = delete;
|
||||
IArchString &operator=(IArchString &&) = delete;
|
||||
ArchString &operator=(const ArchString &) = delete;
|
||||
ArchString &operator=(ArchString &&) = delete;
|
||||
|
||||
//! Wide character encodings
|
||||
/*!
|
||||
The known wide character encodings
|
||||
*/
|
||||
enum EWideCharEncoding
|
||||
enum class EWideCharEncoding : uint8_t
|
||||
{
|
||||
kUCS2, //!< The UCS-2 encoding
|
||||
kUCS4, //!< The UCS-4 encoding
|
||||
@ -46,13 +46,13 @@ public:
|
||||
//@{
|
||||
|
||||
//! Convert multibyte string to wide character string
|
||||
virtual int convStringMBToWC(wchar_t *, const char *, uint32_t n, bool *errors);
|
||||
int convStringMBToWC(wchar_t *, const char *, uint32_t n, bool *errors) const;
|
||||
|
||||
//! Convert wide character string to multibyte string
|
||||
virtual int convStringWCToMB(char *, const wchar_t *, uint32_t n, bool *errors);
|
||||
int convStringWCToMB(char *, const wchar_t *, uint32_t n, bool *errors) const;
|
||||
|
||||
//! Return the architecture's native wide character encoding
|
||||
virtual EWideCharEncoding getWideCharEncoding() = 0;
|
||||
EWideCharEncoding getWideCharEncoding() const;
|
||||
|
||||
//@}
|
||||
};
|
||||
@ -6,8 +6,6 @@
|
||||
# Platform Specific Code
|
||||
if(WIN32)
|
||||
set(PLATFORM_CODE
|
||||
win32/ArchConsoleWindows.cpp
|
||||
win32/ArchConsoleWindows.h
|
||||
win32/ArchDaemonWindows.cpp
|
||||
win32/ArchDaemonWindows.h
|
||||
win32/ArchLogWindows.cpp
|
||||
@ -18,20 +16,12 @@ if(WIN32)
|
||||
win32/ArchMultithreadWindows.h
|
||||
win32/ArchNetworkWinsock.cpp
|
||||
win32/ArchNetworkWinsock.h
|
||||
win32/ArchSleepWindows.cpp
|
||||
win32/ArchSleepWindows.h
|
||||
win32/ArchStringWindows.cpp
|
||||
win32/ArchStringWindows.h
|
||||
win32/ArchTimeWindows.cpp
|
||||
win32/ArchTimeWindows.h
|
||||
win32/XArchWindows.cpp
|
||||
win32/XArchWindows.h
|
||||
)
|
||||
|
||||
elseif(UNIX)
|
||||
set(PLATFORM_CODE
|
||||
unix/ArchConsoleUnix.cpp
|
||||
unix/ArchConsoleUnix.h
|
||||
unix/ArchDaemonUnix.cpp
|
||||
unix/ArchDaemonUnix.h
|
||||
unix/ArchLogUnix.cpp
|
||||
@ -40,12 +30,6 @@ elseif(UNIX)
|
||||
unix/ArchMultithreadPosix.h
|
||||
unix/ArchNetworkBSD.cpp
|
||||
unix/ArchNetworkBSD.h
|
||||
unix/ArchSleepUnix.cpp
|
||||
unix/ArchSleepUnix.h
|
||||
unix/ArchStringUnix.cpp
|
||||
unix/ArchStringUnix.h
|
||||
unix/ArchTimeUnix.cpp
|
||||
unix/ArchTimeUnix.h
|
||||
unix/XArchUnix.cpp
|
||||
unix/XArchUnix.h
|
||||
)
|
||||
@ -54,19 +38,14 @@ endif()
|
||||
add_library(arch STATIC ${PLATFORM_CODE}
|
||||
Arch.cpp
|
||||
Arch.h
|
||||
ArchConsoleStd.cpp
|
||||
ArchConsoleStd.h
|
||||
ArchDaemonNone.cpp
|
||||
ArchDaemonNone.h
|
||||
IArchConsole.h
|
||||
IArchDaemon.h
|
||||
IArchLog.h
|
||||
IArchMultithread.h
|
||||
IArchNetwork.h
|
||||
IArchSleep.h
|
||||
IArchString.cpp
|
||||
IArchString.h
|
||||
IArchTime.h
|
||||
ArchString.cpp
|
||||
ArchString.h
|
||||
XArch.h
|
||||
)
|
||||
|
||||
|
||||
@ -1,56 +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 "base/ELevel.h"
|
||||
#include "common/IInterface.h"
|
||||
|
||||
//! Interface for architecture dependent console output
|
||||
/*!
|
||||
This interface defines the console operations required by
|
||||
deskflow. Each architecture must implement this interface.
|
||||
*/
|
||||
class IArchConsole : public IInterface
|
||||
{
|
||||
public:
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Open the console
|
||||
/*!
|
||||
Opens the console for writing. The console is opened automatically
|
||||
on the first write so calling this method is optional. Uses \c title
|
||||
for the console's title if appropriate for the architecture. Calling
|
||||
this method on an already open console must have no effect.
|
||||
*/
|
||||
virtual void openConsole(const char *title) = 0;
|
||||
|
||||
//! Close the console
|
||||
/*!
|
||||
Close the console. Calling this method on an already closed console
|
||||
must have no effect.
|
||||
*/
|
||||
virtual void closeConsole() = 0;
|
||||
|
||||
//! Show the console
|
||||
/*!
|
||||
Causes the console to become visible. This generally only makes sense
|
||||
for a console in a graphical user interface. Other implementations
|
||||
will do nothing. Iff \p showIfEmpty is \c false then the implementation
|
||||
may optionally only show the console if it's not empty.
|
||||
*/
|
||||
virtual void showConsole(bool showIfEmpty) = 0;
|
||||
|
||||
//! Write to the console
|
||||
/*!
|
||||
Writes the given string to the console, opening it if necessary.
|
||||
*/
|
||||
virtual void writeConsole(ELevel, const char *) = 0;
|
||||
|
||||
//@}
|
||||
};
|
||||
@ -34,7 +34,7 @@ public:
|
||||
\c commandLine should \b not include the name of program as the
|
||||
first argument. If \c allUsers is true then the daemon will be
|
||||
installed to start at boot time, otherwise it will be installed to
|
||||
start when the current user logs in. If \p dependencies is not NULL
|
||||
start when the current user logs in. If \p dependencies is not nullptr
|
||||
then it's a concatenation of NUL terminated other daemon names
|
||||
followed by a NUL; the daemon will be configured to startup after
|
||||
the listed daemons. Throws an \c XArchDaemon exception on failure.
|
||||
@ -88,7 +88,7 @@ public:
|
||||
\c ArchMiscWindows::daemonFailed() to indicate startup failure.
|
||||
</ul>
|
||||
*/
|
||||
virtual int daemonize(const char *name, DaemonFunc func) = 0;
|
||||
virtual int daemonize(const char *name, DaemonFunc const &func) = 0;
|
||||
|
||||
//! Check if user has permission to install the daemon
|
||||
/*!
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base/ELevel.h"
|
||||
#include "base/LogLevel.h"
|
||||
#include "common/IInterface.h"
|
||||
|
||||
//! Interface for architecture dependent logging
|
||||
@ -47,7 +47,7 @@ public:
|
||||
/*!
|
||||
Writes the given string to the log with the given level.
|
||||
*/
|
||||
virtual void writeLog(ELevel, const char *) = 0;
|
||||
virtual void writeLog(LogLevel, const char *) = 0;
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
@ -61,7 +61,7 @@ class IArchMultithread : public IInterface
|
||||
{
|
||||
public:
|
||||
//! Type of thread entry point
|
||||
typedef void *(*ThreadFunc)(void *);
|
||||
using ThreadFunc = void *(*)(void *);
|
||||
//! Type of thread identifier
|
||||
using ThreadID = unsigned int;
|
||||
//! Types of signals
|
||||
@ -69,16 +69,16 @@ public:
|
||||
Not all platforms support all signals. Unsupported signals are
|
||||
ignored.
|
||||
*/
|
||||
enum ESignal
|
||||
enum class ThreadSignal : uint8_t
|
||||
{
|
||||
kINTERRUPT, //!< Interrupt (e.g. Ctrl+C)
|
||||
kTERMINATE, //!< Terminate (e.g. Ctrl+Break)
|
||||
kHANGUP, //!< Hangup (SIGHUP)
|
||||
kUSER, //!< User (SIGUSR2)
|
||||
kNUM_SIGNALS
|
||||
Interrupt, //!< Interrupt (e.g. Ctrl+C)
|
||||
Terminate, //!< Terminate (e.g. Ctrl+Break)
|
||||
Hangup, //!< Hangup (SIGHUP)
|
||||
User, //!< User (SIGUSR2)
|
||||
MaxSignals //!< Number of differnt signals
|
||||
};
|
||||
//! Type of signal handler function
|
||||
typedef void (*SignalFunc)(ESignal, void *userData);
|
||||
using SignalFunc = void (*)(ThreadSignal, void *userData);
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
@ -247,17 +247,17 @@ public:
|
||||
//! Set the interrupt handler
|
||||
/*!
|
||||
Sets the function to call on receipt of an external interrupt.
|
||||
By default and when \p func is NULL, the main thread is cancelled.
|
||||
By default and when \p func is nullptr, the main thread is cancelled.
|
||||
*/
|
||||
virtual void setSignalHandler(ESignal, SignalFunc func, void *userData) = 0;
|
||||
virtual void setSignalHandler(ThreadSignal, SignalFunc func, void *userData) = 0;
|
||||
|
||||
//! Invoke the signal handler
|
||||
/*!
|
||||
Invokes the signal handler for \p signal, if any. If no handler
|
||||
cancels the main thread for \c kINTERRUPT and \c kTERMINATE and
|
||||
cancels the main thread for \c ThreadSignal::Interrupt and \c ThreadSignal::Terminate and
|
||||
ignores the call otherwise.
|
||||
*/
|
||||
virtual void raiseSignal(ESignal signal) = 0;
|
||||
virtual void raiseSignal(ThreadSignal signal) = 0;
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
@ -53,18 +53,18 @@ class IArchNetwork : public IInterface
|
||||
{
|
||||
public:
|
||||
//! Supported address families
|
||||
enum EAddressFamily
|
||||
enum class AddressFamily : uint8_t
|
||||
{
|
||||
kUNKNOWN,
|
||||
kINET,
|
||||
kINET6,
|
||||
Unknown,
|
||||
INet,
|
||||
INet6
|
||||
};
|
||||
|
||||
//! Supported socket types
|
||||
enum ESocketType
|
||||
enum class SocketType : uint8_t
|
||||
{
|
||||
kDGRAM,
|
||||
kSTREAM
|
||||
DataGram,
|
||||
Stream
|
||||
};
|
||||
|
||||
//! Events for \c poll()
|
||||
@ -72,12 +72,12 @@ public:
|
||||
Events for \c poll() are bitmasks and can be combined using the
|
||||
bitwise operators.
|
||||
*/
|
||||
enum
|
||||
struct PollEventMask
|
||||
{
|
||||
kPOLLIN = 1, //!< Socket is readable
|
||||
kPOLLOUT = 2, //!< Socket is writable
|
||||
kPOLLERR = 4, //!< The socket is in an error state
|
||||
kPOLLNVAL = 8 //!< The socket is invalid
|
||||
inline static const int In = 1; //!< Socket is readable
|
||||
inline static const int Out = 2; //!< Socket is writable
|
||||
inline static const int Error = 4; //!< The socket is in an error state
|
||||
inline static const int Invalid = 8; //!< The socket is invalid
|
||||
};
|
||||
|
||||
//! A socket query for \c poll()
|
||||
@ -89,8 +89,7 @@ public:
|
||||
|
||||
//! The events to query for
|
||||
/*!
|
||||
The events to query for can be any combination of kPOLLIN and
|
||||
kPOLLOUT.
|
||||
The events to query for can be any combination of PollEventMask::In and PollEventMask::Out.
|
||||
*/
|
||||
unsigned short m_events;
|
||||
|
||||
@ -105,7 +104,7 @@ public:
|
||||
/*!
|
||||
The socket is an opaque data type.
|
||||
*/
|
||||
virtual ArchSocket newSocket(EAddressFamily, ESocketType) = 0;
|
||||
virtual ArchSocket newSocket(AddressFamily, SocketType) = 0;
|
||||
|
||||
//! Copy a socket object
|
||||
/*!
|
||||
@ -149,10 +148,10 @@ public:
|
||||
/*!
|
||||
Accepts a connection on socket \c s, returning a new socket for the
|
||||
connection and filling in \c addr with the address of the remote
|
||||
end. \c addr may be NULL if the remote address isn't required.
|
||||
end. \c addr may be nullptr if the remote address isn't required.
|
||||
The original socket \c s is unaffected and remains in the listening
|
||||
state. The new socket shares most of the properties of \c s except
|
||||
it's not in the listening state and it's connected. Returns NULL
|
||||
it's not in the listening state and it's connected. Returns nullptr
|
||||
if there are no pending connection requests.
|
||||
*/
|
||||
virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress *addr) = 0;
|
||||
@ -175,9 +174,9 @@ public:
|
||||
and/or writable (or indefinitely if \c timeout < 0). Returns the
|
||||
number of sockets that were readable (if readability was being
|
||||
queried) or writable (if writablility was being queried) and sets
|
||||
the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL
|
||||
the \c m_revents members of the entries. \c PollEventMask::Error and \c PollEventMask::Invalid
|
||||
are set in \c m_revents as appropriate. If a socket indicates
|
||||
\c kPOLLERR then \c throwErrorOnSocket() can be used to determine
|
||||
\c PollEventMask::Error then \c throwErrorOnSocket() can be used to determine
|
||||
the type of error. Returns 0 immediately regardless of the \c timeout
|
||||
if no valid sockets are selected for testing.
|
||||
|
||||
@ -236,7 +235,7 @@ public:
|
||||
virtual std::string getHostName() = 0;
|
||||
|
||||
//! Create an "any" network address
|
||||
virtual ArchNetAddress newAnyAddr(EAddressFamily) = 0;
|
||||
virtual ArchNetAddress newAnyAddr(AddressFamily) = 0;
|
||||
|
||||
//! Copy a network address
|
||||
virtual ArchNetAddress copyAddr(ArchNetAddress) = 0;
|
||||
@ -254,7 +253,7 @@ public:
|
||||
virtual std::string addrToString(ArchNetAddress) = 0;
|
||||
|
||||
//! Get an address's family
|
||||
virtual EAddressFamily getAddrFamily(ArchNetAddress) = 0;
|
||||
virtual AddressFamily getAddrFamily(ArchNetAddress) = 0;
|
||||
|
||||
//! Set the port of an address
|
||||
virtual void setAddrPort(ArchNetAddress, int port) = 0;
|
||||
@ -275,4 +274,15 @@ public:
|
||||
//@}
|
||||
|
||||
virtual void init() = 0;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief throwError, Used to throw network errors
|
||||
*/
|
||||
[[noreturn]] virtual void throwError(int) const = 0;
|
||||
|
||||
/**
|
||||
* @brief throwNameError, Errors related to client names.
|
||||
*/
|
||||
[[noreturn]] virtual void throwNameError(int) const = 0;
|
||||
};
|
||||
|
||||
@ -1,34 +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 "common/IInterface.h"
|
||||
|
||||
//! Interface for architecture dependent sleeping
|
||||
/*!
|
||||
This interface defines the sleep operations required by
|
||||
deskflow. Each architecture must implement this interface.
|
||||
*/
|
||||
class IArchSleep : public IInterface
|
||||
{
|
||||
public:
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Sleep
|
||||
/*!
|
||||
Blocks the calling thread for \c timeout seconds. If
|
||||
\c timeout < 0.0 then the call returns immediately. If \c timeout
|
||||
== 0.0 then the calling thread yields the CPU.
|
||||
|
||||
(cancellation point)
|
||||
*/
|
||||
virtual void sleep(double timeout) = 0;
|
||||
|
||||
//@}
|
||||
};
|
||||
@ -1,31 +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 "common/IInterface.h"
|
||||
|
||||
//! Interface for architecture dependent time operations
|
||||
/*!
|
||||
This interface defines the time operations required by
|
||||
deskflow. Each architecture must implement this interface.
|
||||
*/
|
||||
class IArchTime : public IInterface
|
||||
{
|
||||
public:
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Get the current time
|
||||
/*!
|
||||
Returns the number of seconds since some arbitrary starting time.
|
||||
This should return as high a precision as reasonable.
|
||||
*/
|
||||
virtual double time() = 0;
|
||||
|
||||
//@}
|
||||
};
|
||||
@ -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
|
||||
@ -18,7 +19,7 @@ Exceptions derived from this class are used by the multithreading
|
||||
library to perform stack unwinding when a thread terminates. These
|
||||
exceptions must always be rethrown by clients when caught.
|
||||
*/
|
||||
class XThread
|
||||
class XThread : public std::exception
|
||||
{
|
||||
};
|
||||
|
||||
@ -45,134 +46,160 @@ cleanup but before leaving or returning from the handler.
|
||||
} catch (...) { \
|
||||
}
|
||||
|
||||
//! Lazy error message string evaluation
|
||||
/*!
|
||||
This class encapsulates platform dependent error string lookup.
|
||||
Platforms subclass this type, taking an appropriate error code
|
||||
type in the c'tor and overriding eval() to return the error
|
||||
string for that error code.
|
||||
*/
|
||||
class XArchEval
|
||||
{
|
||||
public:
|
||||
XArchEval()
|
||||
{
|
||||
}
|
||||
virtual ~XArchEval() throw()
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::string eval() const = 0;
|
||||
};
|
||||
|
||||
//! Generic exception architecture dependent library
|
||||
class XArch : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
XArch(XArchEval *adopted) : std::runtime_error(adopted->eval())
|
||||
{
|
||||
delete adopted;
|
||||
}
|
||||
XArch(const std::string &msg) : std::runtime_error(msg)
|
||||
{
|
||||
}
|
||||
virtual ~XArch() throw()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Macro to declare XArch derived types
|
||||
#define XARCH_SUBCLASS(name_, super_) \
|
||||
class name_ : public super_ \
|
||||
{ \
|
||||
public: \
|
||||
name_(XArchEval *adoptedEvaluator) : super_(adoptedEvaluator) \
|
||||
{ \
|
||||
} \
|
||||
name_(const std::string &msg) : super_(msg) \
|
||||
{ \
|
||||
} \
|
||||
}
|
||||
|
||||
//! Generic network exception
|
||||
/*!
|
||||
Exceptions derived from this class are used by the networking
|
||||
library to indicate various errors.
|
||||
*/
|
||||
XARCH_SUBCLASS(XArchNetwork, XArch);
|
||||
class XArchNetwork : public std::runtime_error
|
||||
{
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
//! Operation was interrupted
|
||||
XARCH_SUBCLASS(XArchNetworkInterrupted, XArchNetwork);
|
||||
class XArchNetworkInterrupted : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Network insufficient permission
|
||||
XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork);
|
||||
class XArchNetworkAccess : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Network insufficient resources
|
||||
XARCH_SUBCLASS(XArchNetworkResource, XArchNetwork);
|
||||
class XArchNetworkResource : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! No support for requested network resource/service
|
||||
XARCH_SUBCLASS(XArchNetworkSupport, XArchNetwork);
|
||||
class XArchNetworkSupport : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Network I/O error
|
||||
XARCH_SUBCLASS(XArchNetworkIO, XArchNetwork);
|
||||
class XArchNetworkIO : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Network address is unavailable or not local
|
||||
XARCH_SUBCLASS(XArchNetworkNoAddress, XArchNetwork);
|
||||
class XArchNetworkNoAddress : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Network address in use
|
||||
XARCH_SUBCLASS(XArchNetworkAddressInUse, XArchNetwork);
|
||||
class XArchNetworkAddressInUse : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! No route to address
|
||||
XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork);
|
||||
class XArchNetworkNoRoute : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Socket not connected
|
||||
XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork);
|
||||
class XArchNetworkNotConnected : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Remote read end of socket has closed
|
||||
XARCH_SUBCLASS(XArchNetworkShutdown, XArchNetwork);
|
||||
class XArchNetworkShutdown : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Remote end of socket has disconnected
|
||||
XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork);
|
||||
class XArchNetworkDisconnected : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Remote end of socket refused connection
|
||||
XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork);
|
||||
class XArchNetworkConnectionRefused : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Remote end of socket is not responding
|
||||
XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork);
|
||||
class XArchNetworkTimedOut : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! Generic network name lookup erros
|
||||
XARCH_SUBCLASS(XArchNetworkName, XArchNetwork);
|
||||
class XArchNetworkName : public XArchNetwork
|
||||
{
|
||||
using XArchNetwork::XArchNetwork;
|
||||
};
|
||||
|
||||
//! The named host is unknown
|
||||
XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName);
|
||||
class XArchNetworkNameUnknown : public XArchNetworkName
|
||||
{
|
||||
using XArchNetworkName::XArchNetworkName;
|
||||
};
|
||||
|
||||
//! The named host is known but has no address
|
||||
XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName);
|
||||
class XArchNetworkNameNoAddress : public XArchNetworkName
|
||||
{
|
||||
using XArchNetworkName::XArchNetworkName;
|
||||
};
|
||||
|
||||
//! Non-recoverable name server error
|
||||
XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName);
|
||||
class XArchNetworkNameFailure : public XArchNetworkName
|
||||
{
|
||||
using XArchNetworkName::XArchNetworkName;
|
||||
};
|
||||
|
||||
//! Temporary name server error
|
||||
XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName);
|
||||
class XArchNetworkNameUnavailable : public XArchNetworkName
|
||||
{
|
||||
using XArchNetworkName::XArchNetworkName;
|
||||
};
|
||||
|
||||
//! The named host is known but no supported address
|
||||
XARCH_SUBCLASS(XArchNetworkNameUnsupported, XArchNetworkName);
|
||||
class XArchNetworkNameUnsupported : public XArchNetworkName
|
||||
{
|
||||
using XArchNetworkName::XArchNetworkName;
|
||||
};
|
||||
|
||||
//! Generic daemon exception
|
||||
/*!
|
||||
Exceptions derived from this class are used by the daemon
|
||||
library to indicate various errors.
|
||||
*/
|
||||
XARCH_SUBCLASS(XArchDaemon, XArch);
|
||||
class XArchDaemon : public std::runtime_error
|
||||
{
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
//! Could not daemonize
|
||||
XARCH_SUBCLASS(XArchDaemonFailed, XArchDaemon);
|
||||
class XArchDaemonFailed : public XArchDaemon
|
||||
{
|
||||
using XArchDaemon::XArchDaemon;
|
||||
};
|
||||
|
||||
//! Could not install daemon
|
||||
XARCH_SUBCLASS(XArchDaemonInstallFailed, XArchDaemon);
|
||||
class XArchDaemonInstallFailed : public XArchDaemon
|
||||
{
|
||||
using XArchDaemon::XArchDaemon;
|
||||
};
|
||||
|
||||
//! Could not uninstall daemon
|
||||
XARCH_SUBCLASS(XArchDaemonUninstallFailed, XArchDaemon);
|
||||
class XArchDaemonUninstallFailed : public XArchDaemon
|
||||
{
|
||||
using XArchDaemon::XArchDaemon;
|
||||
};
|
||||
|
||||
//! Attempted to uninstall a daemon that was not installed
|
||||
XARCH_SUBCLASS(XArchDaemonUninstallNotInstalled, XArchDaemonUninstallFailed);
|
||||
class XArchDaemonUninstallNotInstalled : public XArchDaemonFailed
|
||||
{
|
||||
using XArchDaemonFailed::XArchDaemonFailed;
|
||||
};
|
||||
|
||||
@ -1,16 +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/unix/ArchConsoleUnix.h"
|
||||
|
||||
ArchConsoleUnix::ArchConsoleUnix()
|
||||
{
|
||||
}
|
||||
|
||||
ArchConsoleUnix::~ArchConsoleUnix()
|
||||
{
|
||||
}
|
||||
@ -1,19 +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/ArchConsoleStd.h"
|
||||
|
||||
#define ARCH_CONSOLE ArchConsoleUnix
|
||||
|
||||
class ArchConsoleUnix : public ArchConsoleStd
|
||||
{
|
||||
public:
|
||||
ArchConsoleUnix();
|
||||
virtual ~ArchConsoleUnix();
|
||||
};
|
||||
@ -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
|
||||
@ -7,6 +8,7 @@
|
||||
|
||||
#include "arch/unix/ArchDaemonUnix.h"
|
||||
|
||||
#include "arch/XArch.h"
|
||||
#include "arch/unix/XArchUnix.h"
|
||||
#include "base/Log.h"
|
||||
|
||||
@ -21,16 +23,6 @@
|
||||
// ArchDaemonUnix
|
||||
//
|
||||
|
||||
ArchDaemonUnix::ArchDaemonUnix()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ArchDaemonUnix::~ArchDaemonUnix()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
// In Mac OS X, fork()'d child processes can't use most APIs (the frameworks
|
||||
@ -49,12 +41,12 @@ int execSelfNonDaemonized()
|
||||
|
||||
bool alreadyDaemonized()
|
||||
{
|
||||
return getenv("_DESKFLOW_DAEMONIZED") != NULL;
|
||||
return std::getenv("_DESKFLOW_DAEMONIZED") != nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int ArchDaemonUnix::daemonize(const char *name, DaemonFunc func)
|
||||
int ArchDaemonUnix::daemonize(const char *name, DaemonFunc const &func)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
if (alreadyDaemonized())
|
||||
@ -66,7 +58,7 @@ int ArchDaemonUnix::daemonize(const char *name, DaemonFunc func)
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
// failed
|
||||
throw XArchDaemonFailed(new XArchEvalUnix(errno));
|
||||
throw XArchDaemonFailed(errorToString(errno));
|
||||
|
||||
case 0:
|
||||
// child
|
||||
@ -84,8 +76,7 @@ int ArchDaemonUnix::daemonize(const char *name, DaemonFunc func)
|
||||
// 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?
|
||||
int chdirErr = chdir("/");
|
||||
if (chdirErr)
|
||||
if (int chdirErr = chdir("/"); chdirErr)
|
||||
// NB: file logging actually isn't working at this point!
|
||||
LOG((CLOG_ERR "chdir error: %i", chdirErr));
|
||||
#endif
|
||||
@ -103,9 +94,7 @@ int ArchDaemonUnix::daemonize(const char *name, DaemonFunc func)
|
||||
open("/dev/null", O_RDONLY);
|
||||
open("/dev/null", O_RDWR);
|
||||
|
||||
int dupErr = dup(1);
|
||||
|
||||
if (dupErr < 0) {
|
||||
if (int dupErr = dup(1); dupErr < 0) {
|
||||
// NB: file logging actually isn't working at this point!
|
||||
LOG((CLOG_ERR "dup error: %i", dupErr));
|
||||
}
|
||||
|
||||
@ -16,9 +16,9 @@
|
||||
class ArchDaemonUnix : public ArchDaemonNone
|
||||
{
|
||||
public:
|
||||
ArchDaemonUnix();
|
||||
virtual ~ArchDaemonUnix();
|
||||
ArchDaemonUnix() = default;
|
||||
~ArchDaemonUnix() override = default;
|
||||
|
||||
// IArchDaemon overrides
|
||||
virtual int daemonize(const char *name, DaemonFunc func);
|
||||
int daemonize(const char *name, DaemonFunc const &func) override;
|
||||
};
|
||||
|
||||
@ -13,16 +13,6 @@
|
||||
// ArchLogUnix
|
||||
//
|
||||
|
||||
ArchLogUnix::ArchLogUnix()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ArchLogUnix::~ArchLogUnix()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchLogUnix::openLog(const char *name)
|
||||
{
|
||||
openlog(name, 0, LOG_DAEMON);
|
||||
@ -38,24 +28,25 @@ void ArchLogUnix::showLog(bool)
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchLogUnix::writeLog(ELevel level, const char *msg)
|
||||
void ArchLogUnix::writeLog(LogLevel level, const char *msg)
|
||||
{
|
||||
// convert level
|
||||
int priority;
|
||||
switch (level) {
|
||||
case kERROR:
|
||||
using enum LogLevel;
|
||||
case Error:
|
||||
priority = LOG_ERR;
|
||||
break;
|
||||
|
||||
case kWARNING:
|
||||
case Warning:
|
||||
priority = LOG_WARNING;
|
||||
break;
|
||||
|
||||
case kNOTE:
|
||||
case Note:
|
||||
priority = LOG_NOTICE;
|
||||
break;
|
||||
|
||||
case kINFO:
|
||||
case Info:
|
||||
priority = LOG_INFO;
|
||||
break;
|
||||
|
||||
|
||||
@ -15,12 +15,12 @@
|
||||
class ArchLogUnix : public IArchLog
|
||||
{
|
||||
public:
|
||||
ArchLogUnix();
|
||||
virtual ~ArchLogUnix();
|
||||
ArchLogUnix() = default;
|
||||
~ArchLogUnix() override = default;
|
||||
|
||||
// IArchLog overrides
|
||||
virtual void openLog(const char *name);
|
||||
virtual void closeLog();
|
||||
virtual void showLog(bool);
|
||||
virtual void writeLog(ELevel, const char *);
|
||||
void openLog(const char *name) override;
|
||||
void closeLog() override;
|
||||
void showLog(bool) override;
|
||||
void writeLog(LogLevel, const char *) override;
|
||||
};
|
||||
|
||||
@ -10,32 +10,13 @@
|
||||
#include "arch/Arch.h"
|
||||
#include "arch/XArch.h"
|
||||
|
||||
#include <signal.h>
|
||||
#if TIME_WITH_SYS_TIME
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#else
|
||||
#if HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <cerrno>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#define SIGWAKEUP SIGUSR1
|
||||
|
||||
#if !HAVE_PTHREAD_SIGNAL
|
||||
// boy, is this platform broken. forget about pthread signal
|
||||
// handling and let signals through to every process. deskflow
|
||||
// will not terminate cleanly when it gets SIGTERM or SIGINT.
|
||||
#define pthread_sigmask sigprocmask
|
||||
#define pthread_kill(tid_, sig_) kill(0, (sig_))
|
||||
#define sigwait(set_, sig_)
|
||||
#undef HAVE_POSIX_SIGWAIT
|
||||
#define HAVE_POSIX_SIGWAIT 1
|
||||
#endif
|
||||
|
||||
static void setSignalSet(sigset_t *sigset)
|
||||
{
|
||||
sigemptyset(sigset);
|
||||
@ -52,56 +33,39 @@ static void setSignalSet(sigset_t *sigset)
|
||||
class ArchThreadImpl
|
||||
{
|
||||
public:
|
||||
ArchThreadImpl();
|
||||
ArchThreadImpl() = default;
|
||||
|
||||
public:
|
||||
int m_refCount;
|
||||
IArchMultithread::ThreadID m_id;
|
||||
int m_refCount = 1;
|
||||
IArchMultithread::ThreadID m_id = 0;
|
||||
pthread_t m_thread;
|
||||
IArchMultithread::ThreadFunc m_func;
|
||||
void *m_userData;
|
||||
bool m_cancel;
|
||||
bool m_cancelling;
|
||||
bool m_exited;
|
||||
void *m_result;
|
||||
void *m_networkData;
|
||||
IArchMultithread::ThreadFunc m_func = nullptr;
|
||||
void *m_userData = nullptr;
|
||||
bool m_cancel = false;
|
||||
bool m_cancelling = false;
|
||||
bool m_exited = false;
|
||||
void *m_result = nullptr;
|
||||
void *m_networkData = nullptr;
|
||||
};
|
||||
|
||||
ArchThreadImpl::ArchThreadImpl()
|
||||
: m_refCount(1),
|
||||
m_id(0),
|
||||
m_func(NULL),
|
||||
m_userData(NULL),
|
||||
m_cancel(false),
|
||||
m_cancelling(false),
|
||||
m_exited(false),
|
||||
m_result(NULL),
|
||||
m_networkData(NULL)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
//
|
||||
// ArchMultithreadPosix
|
||||
//
|
||||
|
||||
ArchMultithreadPosix *ArchMultithreadPosix::s_instance = NULL;
|
||||
ArchMultithreadPosix *ArchMultithreadPosix::s_instance = nullptr;
|
||||
|
||||
ArchMultithreadPosix::ArchMultithreadPosix() : m_newThreadCalled(false), m_nextID(0)
|
||||
ArchMultithreadPosix::ArchMultithreadPosix()
|
||||
{
|
||||
assert(s_instance == NULL);
|
||||
assert(s_instance == nullptr);
|
||||
|
||||
s_instance = this;
|
||||
|
||||
// no signal handlers
|
||||
for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
|
||||
m_signalFunc[i] = NULL;
|
||||
m_signalUserData[i] = NULL;
|
||||
for (size_t i = 0; i < static_cast<size_t>(ThreadSignal::MaxSignals); ++i) {
|
||||
m_signalFunc[i] = nullptr;
|
||||
m_signalUserData[i] = nullptr;
|
||||
}
|
||||
|
||||
// create mutex for thread list
|
||||
m_threadMutex = newMutex();
|
||||
|
||||
// create thread for calling (main) thread and add it to our
|
||||
// list. no need to lock the mutex since we're the only thread.
|
||||
m_mainThread = new ArchThreadImpl;
|
||||
@ -121,41 +85,36 @@ ArchMultithreadPosix::ArchMultithreadPosix() : m_newThreadCalled(false), m_nextI
|
||||
act.sa_flags = 0;
|
||||
#endif
|
||||
act.sa_handler = &threadCancel;
|
||||
sigaction(SIGWAKEUP, &act, NULL);
|
||||
sigaction(SIGWAKEUP, &act, nullptr);
|
||||
|
||||
// set desired signal dispositions. let SIGWAKEUP through but
|
||||
// ignore SIGPIPE (we'll handle EPIPE).
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGWAKEUP);
|
||||
pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
|
||||
pthread_sigmask(SIG_UNBLOCK, &sigset, nullptr);
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGPIPE);
|
||||
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
|
||||
pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
|
||||
}
|
||||
|
||||
ArchMultithreadPosix::~ArchMultithreadPosix()
|
||||
{
|
||||
assert(s_instance != NULL);
|
||||
|
||||
closeMutex(m_threadMutex);
|
||||
s_instance = NULL;
|
||||
assert(s_instance != nullptr);
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::setNetworkDataForCurrentThread(void *data)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
ArchThreadImpl *thread = find(pthread_self());
|
||||
thread->m_networkData = data;
|
||||
unlockMutex(m_threadMutex);
|
||||
}
|
||||
|
||||
void *ArchMultithreadPosix::getNetworkDataForThread(ArchThread thread)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
void *data = thread->m_networkData;
|
||||
unlockMutex(m_threadMutex);
|
||||
return data;
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
return thread->m_networkData;
|
||||
}
|
||||
|
||||
ArchMultithreadPosix *ArchMultithreadPosix::getInstance()
|
||||
@ -165,8 +124,8 @@ ArchMultithreadPosix *ArchMultithreadPosix::getInstance()
|
||||
|
||||
ArchCond ArchMultithreadPosix::newCondVar()
|
||||
{
|
||||
ArchCondImpl *cond = new ArchCondImpl;
|
||||
int status = pthread_cond_init(&cond->m_cond, NULL);
|
||||
auto *cond = new ArchCondImpl;
|
||||
int status = pthread_cond_init(&cond->m_cond, nullptr);
|
||||
(void)status;
|
||||
assert(status == 0);
|
||||
return cond;
|
||||
@ -204,8 +163,7 @@ bool ArchMultithreadPosix::waitCondVar(ArchCond cond, ArchMutex mutex, double ti
|
||||
// so we have to return to the caller. since the caller will
|
||||
// always check for spurious wakeups the only drawback here is
|
||||
// performance: we're waking up a lot more than desired.
|
||||
static const double maxCancellationLatency = 0.1;
|
||||
if (timeout < 0.0 || timeout > maxCancellationLatency) {
|
||||
if (static const double maxCancellationLatency = 0.1; timeout < 0.0 || timeout > maxCancellationLatency) {
|
||||
timeout = maxCancellationLatency;
|
||||
}
|
||||
|
||||
@ -214,12 +172,12 @@ bool ArchMultithreadPosix::waitCondVar(ArchCond cond, ArchMutex mutex, double ti
|
||||
|
||||
// get final time
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
gettimeofday(&now, nullptr);
|
||||
struct timespec finalTime;
|
||||
finalTime.tv_sec = now.tv_sec;
|
||||
finalTime.tv_nsec = now.tv_usec * 1000;
|
||||
long timeout_sec = (long)timeout;
|
||||
long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec));
|
||||
auto timeout_sec = (long)timeout;
|
||||
auto timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec));
|
||||
finalTime.tv_sec += timeout_sec;
|
||||
finalTime.tv_nsec += timeout_nsec;
|
||||
if (finalTime.tv_nsec >= 1000000000) {
|
||||
@ -252,7 +210,7 @@ ArchMutex ArchMultithreadPosix::newMutex()
|
||||
pthread_mutexattr_t attr;
|
||||
int status = pthread_mutexattr_init(&attr);
|
||||
assert(status == 0);
|
||||
ArchMutexImpl *mutex = new ArchMutexImpl;
|
||||
auto *mutex = new ArchMutexImpl;
|
||||
status = pthread_mutex_init(&mutex->m_mutex, &attr);
|
||||
assert(status == 0);
|
||||
return mutex;
|
||||
@ -312,7 +270,7 @@ void ArchMultithreadPosix::unlockMutex(ArchMutex mutex)
|
||||
|
||||
ArchThread ArchMultithreadPosix::newThread(ThreadFunc func, void *data)
|
||||
{
|
||||
assert(func != NULL);
|
||||
assert(func != nullptr);
|
||||
|
||||
// initialize signal handler. we do this here instead of the
|
||||
// constructor so we can avoid daemonizing (using fork())
|
||||
@ -323,20 +281,19 @@ ArchThread ArchMultithreadPosix::newThread(ThreadFunc func, void *data)
|
||||
// can't tell the difference.
|
||||
if (!m_newThreadCalled) {
|
||||
m_newThreadCalled = true;
|
||||
#if HAVE_PTHREAD_SIGNAL
|
||||
startSignalHandler();
|
||||
#endif
|
||||
}
|
||||
|
||||
lockMutex(m_threadMutex);
|
||||
// note that the child thread will wait until we release this mutex
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
|
||||
// create thread impl for new thread
|
||||
ArchThreadImpl *thread = new ArchThreadImpl;
|
||||
auto *thread = new ArchThreadImpl;
|
||||
thread->m_func = func;
|
||||
thread->m_userData = data;
|
||||
|
||||
// create the thread. pthread_create() on RedHat 7.2 smp fails
|
||||
// if passed a NULL attr so use a default attr.
|
||||
// if passed a nullptr attr so use a default attr.
|
||||
pthread_attr_t attr;
|
||||
int status = pthread_attr_init(&attr);
|
||||
if (status == 0) {
|
||||
@ -348,7 +305,7 @@ ArchThread ArchMultithreadPosix::newThread(ThreadFunc func, void *data)
|
||||
if (status != 0) {
|
||||
// failed to start thread so clean up
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
thread = nullptr;
|
||||
} else {
|
||||
// add thread to list
|
||||
insert(thread);
|
||||
@ -357,37 +314,34 @@ ArchThread ArchMultithreadPosix::newThread(ThreadFunc func, void *data)
|
||||
refThread(thread);
|
||||
}
|
||||
|
||||
// note that the child thread will wait until we release this mutex
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
ArchThread ArchMultithreadPosix::newCurrentThread()
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
ArchThreadImpl *thread = find(pthread_self());
|
||||
unlockMutex(m_threadMutex);
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
return thread;
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::closeThread(ArchThread thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// decrement ref count and clean up thread if no more references
|
||||
if (--thread->m_refCount == 0) {
|
||||
// detach from thread (unless it's the main thread)
|
||||
if (thread->m_func != NULL) {
|
||||
if (thread->m_func != nullptr) {
|
||||
pthread_detach(thread->m_thread);
|
||||
}
|
||||
|
||||
// remove thread from list
|
||||
lockMutex(m_threadMutex);
|
||||
assert(findNoRef(thread->m_thread) == thread);
|
||||
erase(thread);
|
||||
unlockMutex(m_threadMutex);
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
assert(findNoRef(thread->m_thread) == thread);
|
||||
erase(thread);
|
||||
}
|
||||
|
||||
// done with thread
|
||||
delete thread;
|
||||
@ -402,16 +356,18 @@ ArchThread ArchMultithreadPosix::copyThread(ArchThread thread)
|
||||
|
||||
void ArchMultithreadPosix::cancelThread(ArchThread thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// set cancel and wakeup flags if thread can be cancelled
|
||||
bool wakeup = false;
|
||||
lockMutex(m_threadMutex);
|
||||
if (!thread->m_exited && !thread->m_cancelling) {
|
||||
thread->m_cancel = true;
|
||||
wakeup = true;
|
||||
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
if (!thread->m_exited && !thread->m_cancelling) {
|
||||
thread->m_cancel = true;
|
||||
wakeup = true;
|
||||
}
|
||||
}
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
// force thread to exit system calls if wakeup is true
|
||||
if (wakeup) {
|
||||
@ -421,7 +377,7 @@ void ArchMultithreadPosix::cancelThread(ArchThread thread)
|
||||
|
||||
void ArchMultithreadPosix::setPriorityOfThread(ArchThread thread, int /*n*/)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
@ -429,34 +385,32 @@ void ArchMultithreadPosix::setPriorityOfThread(ArchThread thread, int /*n*/)
|
||||
void ArchMultithreadPosix::testCancelThread()
|
||||
{
|
||||
// find current thread
|
||||
lockMutex(m_threadMutex);
|
||||
ArchThreadImpl *thread = findNoRef(pthread_self());
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
ArchThreadImpl *thread = nullptr;
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
thread = findNoRef(pthread_self());
|
||||
}
|
||||
// test cancel on thread
|
||||
testCancelThreadImpl(thread);
|
||||
}
|
||||
|
||||
bool ArchMultithreadPosix::wait(ArchThread target, double timeout)
|
||||
{
|
||||
assert(target != NULL);
|
||||
assert(target != nullptr);
|
||||
|
||||
lockMutex(m_threadMutex);
|
||||
|
||||
// find current thread
|
||||
ArchThreadImpl *self = findNoRef(pthread_self());
|
||||
|
||||
// ignore wait if trying to wait on ourself
|
||||
if (target == self) {
|
||||
unlockMutex(m_threadMutex);
|
||||
return false;
|
||||
ArchThreadImpl *self = nullptr;
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
// find current thread
|
||||
self = findNoRef(pthread_self());
|
||||
// ignore wait if trying to wait on ourself
|
||||
if (target == self) {
|
||||
return false;
|
||||
}
|
||||
// ref the target so it can't go away while we're watching it
|
||||
refThread(target);
|
||||
}
|
||||
|
||||
// ref the target so it can't go away while we're watching it
|
||||
refThread(target);
|
||||
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
try {
|
||||
// do first test regardless of timeout
|
||||
testCancelThreadImpl(self);
|
||||
@ -467,10 +421,10 @@ bool ArchMultithreadPosix::wait(ArchThread target, double timeout)
|
||||
|
||||
// wait and repeat test if there's a timeout
|
||||
if (timeout != 0.0) {
|
||||
const double start = ARCH->time();
|
||||
const double start = Arch::time();
|
||||
do {
|
||||
// wait a little
|
||||
ARCH->sleep(0.05);
|
||||
Arch::sleep(0.05);
|
||||
|
||||
// repeat test
|
||||
testCancelThreadImpl(self);
|
||||
@ -480,7 +434,7 @@ bool ArchMultithreadPosix::wait(ArchThread target, double timeout)
|
||||
}
|
||||
|
||||
// repeat wait and test until timed out
|
||||
} while (timeout < 0.0 || (ARCH->time() - start) <= timeout);
|
||||
} while (timeout < 0.0 || (Arch::time() - start) <= timeout);
|
||||
}
|
||||
|
||||
closeThread(target);
|
||||
@ -498,18 +452,14 @@ bool ArchMultithreadPosix::isSameThread(ArchThread thread1, ArchThread thread2)
|
||||
|
||||
bool ArchMultithreadPosix::isExitedThread(ArchThread thread)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
bool exited = thread->m_exited;
|
||||
unlockMutex(m_threadMutex);
|
||||
return exited;
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
return thread->m_exited;
|
||||
}
|
||||
|
||||
void *ArchMultithreadPosix::getResultOfThread(ArchThread thread)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
void *result = thread->m_result;
|
||||
unlockMutex(m_threadMutex);
|
||||
return result;
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
return thread->m_result;
|
||||
}
|
||||
|
||||
IArchMultithread::ThreadID ArchMultithreadPosix::getIDOfThread(ArchThread thread)
|
||||
@ -517,31 +467,34 @@ IArchMultithread::ThreadID ArchMultithreadPosix::getIDOfThread(ArchThread thread
|
||||
return thread->m_id;
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::setSignalHandler(ESignal signal, SignalFunc func, void *userData)
|
||||
void ArchMultithreadPosix::setSignalHandler(ThreadSignal signal, SignalFunc func, void *userData)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
m_signalFunc[signal] = func;
|
||||
m_signalUserData[signal] = userData;
|
||||
unlockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
const auto index = static_cast<int>(signal);
|
||||
m_signalFunc[index] = func;
|
||||
m_signalUserData[index] = userData;
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::raiseSignal(ESignal signal)
|
||||
void ArchMultithreadPosix::raiseSignal(ThreadSignal signal)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
if (m_signalFunc[signal] != NULL) {
|
||||
m_signalFunc[signal](signal, m_signalUserData[signal]);
|
||||
using enum ThreadSignal;
|
||||
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
const auto index = static_cast<int>(signal);
|
||||
if (m_signalFunc[index] != nullptr) {
|
||||
m_signalFunc[index](signal, m_signalUserData[index]);
|
||||
pthread_kill(m_mainThread->m_thread, SIGWAKEUP);
|
||||
} else if (signal == kINTERRUPT || signal == kTERMINATE) {
|
||||
} else if (signal == Interrupt || signal == Terminate) {
|
||||
ARCH->cancelThread(m_mainThread);
|
||||
}
|
||||
unlockMutex(m_threadMutex);
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::startSignalHandler()
|
||||
{
|
||||
// set signal mask. the main thread blocks these signals and
|
||||
// the signal handler thread will listen for them.
|
||||
sigset_t sigset, oldsigset;
|
||||
sigset_t sigset;
|
||||
sigset_t oldsigset;
|
||||
setSignalSet(&sigset);
|
||||
pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset);
|
||||
|
||||
@ -552,20 +505,20 @@ void ArchMultithreadPosix::startSignalHandler()
|
||||
pthread_attr_t attr;
|
||||
int status = pthread_attr_init(&attr);
|
||||
if (status == 0) {
|
||||
status = pthread_create(&m_signalThread, &attr, &ArchMultithreadPosix::threadSignalHandler, NULL);
|
||||
status = pthread_create(&m_signalThread, &attr, &ArchMultithreadPosix::threadSignalHandler, nullptr);
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
if (status != 0) {
|
||||
// can't create thread to wait for signal so don't block
|
||||
// the signals.
|
||||
pthread_sigmask(SIG_UNBLOCK, &oldsigset, NULL);
|
||||
pthread_sigmask(SIG_UNBLOCK, &oldsigset, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
ArchThreadImpl *ArchMultithreadPosix::find(pthread_t thread)
|
||||
{
|
||||
ArchThreadImpl *impl = findNoRef(thread);
|
||||
if (impl != NULL) {
|
||||
if (impl != nullptr) {
|
||||
refThread(impl);
|
||||
}
|
||||
return impl;
|
||||
@ -579,15 +532,15 @@ ArchThreadImpl *ArchMultithreadPosix::findNoRef(pthread_t thread)
|
||||
return *index;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::insert(ArchThreadImpl *thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// thread shouldn't already be on the list
|
||||
assert(findNoRef(thread->m_thread) == NULL);
|
||||
assert(findNoRef(thread->m_thread) == nullptr);
|
||||
|
||||
// set thread id. note that we don't worry about m_nextID
|
||||
// wrapping back to 0 and duplicating thread ID's since the
|
||||
@ -599,9 +552,9 @@ void ArchMultithreadPosix::insert(ArchThreadImpl *thread)
|
||||
m_threadList.push_back(thread);
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::erase(ArchThreadImpl *thread)
|
||||
void ArchMultithreadPosix::erase(const ArchThreadImpl *thread)
|
||||
{
|
||||
for (ThreadList::iterator index = m_threadList.begin(); index != m_threadList.end(); ++index) {
|
||||
for (auto index = m_threadList.begin(); index != m_threadList.end(); ++index) {
|
||||
if (*index == thread) {
|
||||
m_threadList.erase(index);
|
||||
break;
|
||||
@ -611,24 +564,23 @@ void ArchMultithreadPosix::erase(ArchThreadImpl *thread)
|
||||
|
||||
void ArchMultithreadPosix::refThread(ArchThreadImpl *thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(findNoRef(thread->m_thread) != NULL);
|
||||
assert(thread != nullptr);
|
||||
assert(findNoRef(thread->m_thread) != nullptr);
|
||||
++thread->m_refCount;
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::testCancelThreadImpl(ArchThreadImpl *thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// update cancel state
|
||||
lockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
bool cancel = false;
|
||||
if (thread->m_cancel && !thread->m_cancelling) {
|
||||
thread->m_cancelling = true;
|
||||
thread->m_cancel = false;
|
||||
cancel = true;
|
||||
}
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
// unwind thread's stack if cancelling
|
||||
if (cancel) {
|
||||
@ -639,17 +591,17 @@ void ArchMultithreadPosix::testCancelThreadImpl(ArchThreadImpl *thread)
|
||||
void *ArchMultithreadPosix::threadFunc(void *vrep)
|
||||
{
|
||||
// get the thread
|
||||
ArchThreadImpl *thread = static_cast<ArchThreadImpl *>(vrep);
|
||||
auto *thread = static_cast<ArchThreadImpl *>(vrep);
|
||||
|
||||
// setup pthreads
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, nullptr);
|
||||
|
||||
// run thread
|
||||
s_instance->doThreadFunc(thread);
|
||||
|
||||
// terminate the thread
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ArchMultithreadPosix::doThreadFunc(ArchThread thread)
|
||||
@ -658,8 +610,9 @@ void ArchMultithreadPosix::doThreadFunc(ArchThread thread)
|
||||
setPriorityOfThread(thread, 1);
|
||||
|
||||
// wait for parent to initialize this object
|
||||
lockMutex(m_threadMutex);
|
||||
unlockMutex(m_threadMutex);
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
}
|
||||
|
||||
void *result = nullptr;
|
||||
try {
|
||||
@ -673,18 +626,20 @@ void ArchMultithreadPosix::doThreadFunc(ArchThread thread)
|
||||
result = nullptr;
|
||||
} catch (...) {
|
||||
// note -- don't catch (...) to avoid masking bugs
|
||||
lockMutex(m_threadMutex);
|
||||
thread->m_exited = true;
|
||||
unlockMutex(m_threadMutex);
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
thread->m_exited = true;
|
||||
}
|
||||
closeThread(thread);
|
||||
throw;
|
||||
}
|
||||
|
||||
// thread has exited
|
||||
lockMutex(m_threadMutex);
|
||||
thread->m_result = result;
|
||||
thread->m_exited = true;
|
||||
unlockMutex(m_threadMutex);
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
thread->m_result = result;
|
||||
thread->m_exited = true;
|
||||
}
|
||||
|
||||
// done with thread
|
||||
closeThread(thread);
|
||||
@ -726,20 +681,21 @@ void *ArchMultithreadPosix::threadSignalHandler(void *)
|
||||
|
||||
// if we get here then the signal was raised
|
||||
switch (signal) {
|
||||
using enum ThreadSignal;
|
||||
case SIGINT:
|
||||
ARCH->raiseSignal(kINTERRUPT);
|
||||
ARCH->raiseSignal(Interrupt);
|
||||
break;
|
||||
|
||||
case SIGTERM:
|
||||
ARCH->raiseSignal(kTERMINATE);
|
||||
ARCH->raiseSignal(Terminate);
|
||||
break;
|
||||
|
||||
case SIGHUP:
|
||||
ARCH->raiseSignal(kHANGUP);
|
||||
ARCH->raiseSignal(Hangup);
|
||||
break;
|
||||
|
||||
case SIGUSR2:
|
||||
ARCH->raiseSignal(kUSER);
|
||||
ARCH->raiseSignal(User);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -748,5 +704,5 @@ void *ArchMultithreadPosix::threadSignalHandler(void *)
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "arch/IArchMultithread.h"
|
||||
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <pthread.h>
|
||||
|
||||
#define ARCH_MULTITHREAD ArchMultithreadPosix
|
||||
@ -33,7 +34,7 @@ public:
|
||||
ArchMultithreadPosix();
|
||||
ArchMultithreadPosix(ArchMultithreadPosix const &) = delete;
|
||||
ArchMultithreadPosix(ArchMultithreadPosix &&) = delete;
|
||||
virtual ~ArchMultithreadPosix();
|
||||
~ArchMultithreadPosix() override;
|
||||
|
||||
ArchMultithreadPosix &operator=(ArchMultithreadPosix const &) = delete;
|
||||
ArchMultithreadPosix &operator=(ArchMultithreadPosix &&) = delete;
|
||||
@ -54,29 +55,29 @@ public:
|
||||
//@}
|
||||
|
||||
// IArchMultithread overrides
|
||||
virtual ArchCond newCondVar();
|
||||
virtual void closeCondVar(ArchCond);
|
||||
virtual void signalCondVar(ArchCond);
|
||||
virtual void broadcastCondVar(ArchCond);
|
||||
virtual bool waitCondVar(ArchCond, ArchMutex, double timeout);
|
||||
virtual ArchMutex newMutex();
|
||||
virtual void closeMutex(ArchMutex);
|
||||
virtual void lockMutex(ArchMutex);
|
||||
virtual void unlockMutex(ArchMutex);
|
||||
virtual ArchThread newThread(ThreadFunc, void *);
|
||||
virtual ArchThread newCurrentThread();
|
||||
virtual ArchThread copyThread(ArchThread);
|
||||
virtual void closeThread(ArchThread);
|
||||
virtual void cancelThread(ArchThread);
|
||||
virtual void setPriorityOfThread(ArchThread, int n);
|
||||
virtual void testCancelThread();
|
||||
virtual bool wait(ArchThread, double timeout);
|
||||
virtual bool isSameThread(ArchThread, ArchThread);
|
||||
virtual bool isExitedThread(ArchThread);
|
||||
virtual void *getResultOfThread(ArchThread);
|
||||
virtual ThreadID getIDOfThread(ArchThread);
|
||||
virtual void setSignalHandler(ESignal, SignalFunc, void *);
|
||||
virtual void raiseSignal(ESignal);
|
||||
ArchCond newCondVar() override;
|
||||
void closeCondVar(ArchCond) override;
|
||||
void signalCondVar(ArchCond) override;
|
||||
void broadcastCondVar(ArchCond) override;
|
||||
bool waitCondVar(ArchCond, ArchMutex, double timeout) override;
|
||||
ArchMutex newMutex() override;
|
||||
void closeMutex(ArchMutex) override;
|
||||
void lockMutex(ArchMutex) override;
|
||||
void unlockMutex(ArchMutex) override;
|
||||
ArchThread newThread(ThreadFunc, void *) final;
|
||||
ArchThread newCurrentThread() override;
|
||||
ArchThread copyThread(ArchThread) override;
|
||||
void closeThread(ArchThread) final;
|
||||
void cancelThread(ArchThread) override;
|
||||
void setPriorityOfThread(ArchThread, int n) override;
|
||||
void testCancelThread() override;
|
||||
bool wait(ArchThread, double timeout) override;
|
||||
bool isSameThread(ArchThread, ArchThread) override;
|
||||
bool isExitedThread(ArchThread) override;
|
||||
void *getResultOfThread(ArchThread) override;
|
||||
ThreadID getIDOfThread(ArchThread) override;
|
||||
void setSignalHandler(ThreadSignal, SignalFunc, void *) override;
|
||||
void raiseSignal(ThreadSignal) override;
|
||||
|
||||
private:
|
||||
void startSignalHandler();
|
||||
@ -84,7 +85,7 @@ private:
|
||||
ArchThreadImpl *find(pthread_t thread);
|
||||
ArchThreadImpl *findNoRef(pthread_t thread);
|
||||
void insert(ArchThreadImpl *thread);
|
||||
void erase(ArchThreadImpl *thread);
|
||||
void erase(const ArchThreadImpl *thread);
|
||||
|
||||
void refThread(ArchThreadImpl *rep);
|
||||
void testCancelThreadImpl(ArchThreadImpl *rep);
|
||||
@ -94,19 +95,18 @@ private:
|
||||
static void threadCancel(int);
|
||||
static void *threadSignalHandler(void *vrep);
|
||||
|
||||
private:
|
||||
using ThreadList = std::list<ArchThread>;
|
||||
|
||||
static ArchMultithreadPosix *s_instance;
|
||||
|
||||
bool m_newThreadCalled;
|
||||
bool m_newThreadCalled = false;
|
||||
|
||||
ArchMutex m_threadMutex;
|
||||
std::mutex m_threadMutex;
|
||||
ArchThread m_mainThread;
|
||||
ThreadList m_threadList;
|
||||
ThreadID m_nextID;
|
||||
ThreadID m_nextID = 0;
|
||||
|
||||
pthread_t m_signalThread;
|
||||
SignalFunc m_signalFunc[kNUM_SIGNALS];
|
||||
void *m_signalUserData[kNUM_SIGNALS];
|
||||
SignalFunc m_signalFunc[static_cast<int>(ThreadSignal::MaxSignals)];
|
||||
void *m_signalUserData[static_cast<int>(ThreadSignal::MaxSignals)];
|
||||
};
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "arch/unix/ArchNetworkBSD.h"
|
||||
|
||||
#include "arch/Arch.h"
|
||||
#include "arch/XArch.h"
|
||||
#include "arch/unix/ArchMultithreadPosix.h"
|
||||
#include "arch/unix/XArchUnix.h"
|
||||
|
||||
@ -17,10 +18,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if !defined(TCP_NODELAY)
|
||||
#include <netinet/tcp.h>
|
||||
@ -65,8 +63,7 @@ static in_addr_t inet_aton(const char *cp, struct in_addr *inp)
|
||||
|
||||
void ArchNetworkBSD::Deps::sleep(double seconds)
|
||||
{
|
||||
//
|
||||
ARCH->sleep(seconds);
|
||||
Arch::sleep(seconds);
|
||||
}
|
||||
|
||||
int ArchNetworkBSD::Deps::poll(struct pollfd *fds, nfds_t nfds, int timeout)
|
||||
@ -76,10 +73,7 @@ int ArchNetworkBSD::Deps::poll(struct pollfd *fds, nfds_t nfds, int timeout)
|
||||
|
||||
std::shared_ptr<struct pollfd[]> ArchNetworkBSD::Deps::makePollFD(nfds_t n)
|
||||
{
|
||||
// C++20 supports std::make_shared<struct pollfd[]>(n) but this is not
|
||||
// implemented on the compiler that comes with Ubuntu 22 and a few other
|
||||
// distros, so use the manual new and delete until we drop those distros.
|
||||
return std::shared_ptr<struct pollfd[]>(new struct pollfd[n], std::default_delete<struct pollfd[]>());
|
||||
return std::make_shared<struct pollfd[]>(n);
|
||||
}
|
||||
|
||||
ssize_t ArchNetworkBSD::Deps::read(int fd, void *buf, size_t len)
|
||||
@ -96,22 +90,15 @@ void ArchNetworkBSD::Deps::testCancelThread()
|
||||
// ArchNetworkBSD
|
||||
//
|
||||
|
||||
ArchNetworkBSD::~ArchNetworkBSD()
|
||||
{
|
||||
if (m_mutex)
|
||||
ARCH->closeMutex(m_mutex);
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::init()
|
||||
{
|
||||
// create mutex to make some calls thread safe
|
||||
m_mutex = ARCH->newMutex();
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ArchSocket ArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type)
|
||||
ArchSocket ArchNetworkBSD::newSocket(AddressFamily family, SocketType type)
|
||||
{
|
||||
// create socket
|
||||
int fd = socket(s_family[family], s_type[type], 0);
|
||||
int fd = socket(s_family[static_cast<int>(family)], s_type[static_cast<int>(type)], 0);
|
||||
if (fd == -1) {
|
||||
throwError(errno);
|
||||
}
|
||||
@ -131,32 +118,34 @@ ArchSocket ArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type)
|
||||
|
||||
ArchSocket ArchNetworkBSD::copySocket(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// ref the socket and return it
|
||||
ARCH->lockMutex(m_mutex);
|
||||
std::scoped_lock lock{m_mutex};
|
||||
++s->m_refCount;
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
return s;
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::closeSocket(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
bool doClose = false;
|
||||
// unref the socket and note if it should be released
|
||||
ARCH->lockMutex(m_mutex);
|
||||
const bool doClose = (--s->m_refCount == 0);
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
doClose = (--s->m_refCount == 0);
|
||||
}
|
||||
|
||||
// close the socket if necessary
|
||||
if (doClose) {
|
||||
if (close(s->m_fd) == -1) {
|
||||
// close failed. restore the last ref and throw.
|
||||
int err = errno;
|
||||
ARCH->lockMutex(m_mutex);
|
||||
++s->m_refCount;
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
++s->m_refCount;
|
||||
}
|
||||
throwError(err);
|
||||
}
|
||||
delete s;
|
||||
@ -165,30 +154,26 @@ void ArchNetworkBSD::closeSocket(ArchSocket s)
|
||||
|
||||
void ArchNetworkBSD::closeSocketForRead(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
if (shutdown(s->m_fd, 0) == -1) {
|
||||
if (errno != ENOTCONN) {
|
||||
throwError(errno);
|
||||
}
|
||||
if ((shutdown(s->m_fd, 0) == -1) && (errno != ENOTCONN)) {
|
||||
throwError(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::closeSocketForWrite(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
if (shutdown(s->m_fd, 1) == -1) {
|
||||
if (errno != ENOTCONN) {
|
||||
throwError(errno);
|
||||
}
|
||||
if ((shutdown(s->m_fd, 1) == -1) && (errno != ENOTCONN)) {
|
||||
throwError(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::bindSocket(ArchSocket s, ArchNetAddress addr)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(addr != NULL);
|
||||
assert(s != nullptr);
|
||||
assert(addr != nullptr);
|
||||
|
||||
if (bind(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) {
|
||||
throwError(errno);
|
||||
@ -197,7 +182,7 @@ void ArchNetworkBSD::bindSocket(ArchSocket s, ArchNetAddress addr)
|
||||
|
||||
void ArchNetworkBSD::listenOnSocket(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// hardcoding backlog
|
||||
if (listen(s->m_fd, 3) == -1) {
|
||||
@ -207,9 +192,9 @@ void ArchNetworkBSD::listenOnSocket(ArchSocket s)
|
||||
|
||||
ArchSocket ArchNetworkBSD::acceptSocket(ArchSocket s, ArchNetAddress *addr)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// if user passed NULL in addr then use scratch space
|
||||
// if user passed nullptr in addr then use scratch space
|
||||
ArchNetAddress dummy;
|
||||
if (addr == nullptr) {
|
||||
addr = &dummy;
|
||||
@ -258,8 +243,8 @@ ArchSocket ArchNetworkBSD::acceptSocket(ArchSocket s, ArchNetAddress *addr)
|
||||
|
||||
bool ArchNetworkBSD::connectSocket(ArchSocket s, ArchNetAddress addr)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(addr != NULL);
|
||||
assert(s != nullptr);
|
||||
assert(addr != nullptr);
|
||||
|
||||
if (connect(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) {
|
||||
if (errno == EISCONN) {
|
||||
@ -293,10 +278,10 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout)
|
||||
for (int i = 0; i < num; ++i) {
|
||||
pfd[i].fd = (pe[i].m_socket == nullptr) ? -1 : pe[i].m_socket->m_fd;
|
||||
pfd[i].events = 0;
|
||||
if ((pe[i].m_events & kPOLLIN) != 0) {
|
||||
if ((pe[i].m_events & PollEventMask::In) != 0) {
|
||||
pfd[i].events |= POLLIN;
|
||||
}
|
||||
if ((pe[i].m_events & kPOLLOUT) != 0) {
|
||||
if ((pe[i].m_events & PollEventMask::Out) != 0) {
|
||||
pfd[i].events |= POLLOUT;
|
||||
}
|
||||
}
|
||||
@ -343,16 +328,16 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout)
|
||||
for (int i = 0; i < num; ++i) {
|
||||
pe[i].m_revents = 0;
|
||||
if ((pfd[i].revents & POLLIN) != 0) {
|
||||
pe[i].m_revents |= kPOLLIN;
|
||||
pe[i].m_revents |= PollEventMask::In;
|
||||
}
|
||||
if ((pfd[i].revents & POLLOUT) != 0) {
|
||||
pe[i].m_revents |= kPOLLOUT;
|
||||
pe[i].m_revents |= PollEventMask::Out;
|
||||
}
|
||||
if ((pfd[i].revents & POLLERR) != 0) {
|
||||
pe[i].m_revents |= kPOLLERR;
|
||||
pe[i].m_revents |= PollEventMask::Error;
|
||||
}
|
||||
if ((pfd[i].revents & POLLNVAL) != 0) {
|
||||
pe[i].m_revents |= kPOLLNVAL;
|
||||
pe[i].m_revents |= PollEventMask::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,15 +349,13 @@ void ArchNetworkBSD::unblockPollSocket(ArchThread thread)
|
||||
const int *unblockPipe = getUnblockPipeForThread(thread);
|
||||
if (unblockPipe != nullptr) {
|
||||
char dummy = 0;
|
||||
int ignore;
|
||||
|
||||
ignore = write(unblockPipe[1], &dummy, 1);
|
||||
std::ignore = write(unblockPipe[1], &dummy, 1);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ArchNetworkBSD::readSocket(ArchSocket s, void *buf, size_t len)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
ssize_t n = read(s->m_fd, buf, len);
|
||||
if (n == -1) {
|
||||
@ -386,7 +369,7 @@ size_t ArchNetworkBSD::readSocket(ArchSocket s, void *buf, size_t len)
|
||||
|
||||
size_t ArchNetworkBSD::writeSocket(ArchSocket s, const void *buf, size_t len)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
ssize_t n = write(s->m_fd, buf, len);
|
||||
if (n == -1) {
|
||||
@ -400,12 +383,12 @@ size_t ArchNetworkBSD::writeSocket(ArchSocket s, const void *buf, size_t len)
|
||||
|
||||
void ArchNetworkBSD::throwErrorOnSocket(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// get the error from the socket layer
|
||||
int err = 0;
|
||||
auto size = static_cast<socklen_t>(sizeof(err));
|
||||
if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<optval_t *>(&err), &size) == -1) {
|
||||
if (auto size = static_cast<socklen_t>(sizeof(err));
|
||||
getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<optval_t *>(&err), &size) == -1) {
|
||||
err = errno;
|
||||
}
|
||||
|
||||
@ -415,7 +398,7 @@ void ArchNetworkBSD::throwErrorOnSocket(ArchSocket s)
|
||||
}
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking)
|
||||
void ArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking) const
|
||||
{
|
||||
assert(fd != -1);
|
||||
|
||||
@ -435,7 +418,7 @@ void ArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking)
|
||||
|
||||
bool ArchNetworkBSD::setNoDelayOnSocket(ArchSocket s, bool noDelay)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// get old state
|
||||
int oflag;
|
||||
@ -455,7 +438,7 @@ bool ArchNetworkBSD::setNoDelayOnSocket(ArchSocket s, bool noDelay)
|
||||
|
||||
bool ArchNetworkBSD::setReuseAddrOnSocket(ArchSocket s, bool reuse)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// get old state
|
||||
int oflag;
|
||||
@ -484,14 +467,16 @@ std::string ArchNetworkBSD::getHostName()
|
||||
return name;
|
||||
}
|
||||
|
||||
ArchNetAddress ArchNetworkBSD::newAnyAddr(EAddressFamily family)
|
||||
ArchNetAddress ArchNetworkBSD::newAnyAddr(AddressFamily family)
|
||||
{
|
||||
using enum AddressFamily;
|
||||
|
||||
// allocate address
|
||||
auto *addr = new ArchNetAddressImpl;
|
||||
|
||||
// fill it in
|
||||
switch (family) {
|
||||
case kINET: {
|
||||
case INet: {
|
||||
auto *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
ipAddr->sin_family = AF_INET;
|
||||
ipAddr->sin_port = 0;
|
||||
@ -500,8 +485,8 @@ ArchNetAddress ArchNetworkBSD::newAnyAddr(EAddressFamily family)
|
||||
break;
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
case INet6: {
|
||||
auto *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
ipAddr->sin6_family = AF_INET6;
|
||||
ipAddr->sin6_port = 0;
|
||||
memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
|
||||
@ -518,7 +503,7 @@ ArchNetAddress ArchNetworkBSD::newAnyAddr(EAddressFamily family)
|
||||
|
||||
ArchNetAddress ArchNetworkBSD::copyAddr(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
// allocate and copy address
|
||||
return new ArchNetAddressImpl(*addr);
|
||||
@ -543,11 +528,10 @@ std::vector<ArchNetAddress> ArchNetworkBSD::nameToAddr(const std::string &name)
|
||||
}
|
||||
|
||||
// done with static buffer
|
||||
ARCH->lockMutex(m_mutex);
|
||||
std::scoped_lock lock{m_mutex};
|
||||
struct addrinfo *pResult = nullptr;
|
||||
int ret = getaddrinfo(name.c_str(), nullptr, &hints, &pResult);
|
||||
if (ret != 0) {
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
|
||||
if (int ret = getaddrinfo(name.c_str(), nullptr, &hints, &pResult); ret != 0) {
|
||||
throwNameError(ret);
|
||||
}
|
||||
|
||||
@ -565,61 +549,59 @@ std::vector<ArchNetAddress> ArchNetworkBSD::nameToAddr(const std::string &name)
|
||||
}
|
||||
|
||||
freeaddrinfo(pResult);
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::closeAddr(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
delete addr;
|
||||
}
|
||||
|
||||
std::string ArchNetworkBSD::addrToName(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
// mutexed name lookup (ugh)
|
||||
ARCH->lockMutex(m_mutex);
|
||||
std::scoped_lock lock{m_mutex};
|
||||
char host[1024];
|
||||
char service[20];
|
||||
int ret =
|
||||
getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host, sizeof(host), service, sizeof(service), 0);
|
||||
if (ret != 0) {
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
|
||||
if (int ret =
|
||||
getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host, sizeof(host), service, sizeof(service), 0);
|
||||
ret != 0) {
|
||||
throwNameError(ret);
|
||||
}
|
||||
|
||||
// save (primary) name
|
||||
std::string name = host;
|
||||
|
||||
// done with static buffer
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string ArchNetworkBSD::addrToString(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
using enum AddressFamily;
|
||||
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (getAddrFamily(addr)) {
|
||||
case kINET: {
|
||||
auto *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
ARCH->lockMutex(m_mutex);
|
||||
case INet: {
|
||||
const auto *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
std::scoped_lock lock{m_mutex};
|
||||
std::string s = inet_ntoa(ipAddr->sin_addr);
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
return s;
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
case INet6: {
|
||||
char strAddr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
ARCH->lockMutex(m_mutex);
|
||||
inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
const auto *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
|
||||
}
|
||||
return strAddr;
|
||||
}
|
||||
|
||||
@ -629,34 +611,38 @@ std::string ArchNetworkBSD::addrToString(ArchNetAddress addr)
|
||||
}
|
||||
}
|
||||
|
||||
IArchNetwork::EAddressFamily ArchNetworkBSD::getAddrFamily(ArchNetAddress addr)
|
||||
IArchNetwork::AddressFamily ArchNetworkBSD::getAddrFamily(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
using enum AddressFamily;
|
||||
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (addr->m_addr.ss_family) {
|
||||
case AF_INET:
|
||||
return kINET;
|
||||
return INet;
|
||||
case AF_INET6:
|
||||
return kINET6;
|
||||
return INet6;
|
||||
|
||||
default:
|
||||
return kUNKNOWN;
|
||||
return Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::setAddrPort(ArchNetAddress addr, int port)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
using enum AddressFamily;
|
||||
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (getAddrFamily(addr)) {
|
||||
case kINET: {
|
||||
case INet: {
|
||||
auto *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
ipAddr->sin_port = htons(port);
|
||||
break;
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
case INet6: {
|
||||
auto *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
ipAddr->sin6_port = htons(port);
|
||||
break;
|
||||
}
|
||||
@ -669,16 +655,18 @@ void ArchNetworkBSD::setAddrPort(ArchNetAddress addr, int port)
|
||||
|
||||
int ArchNetworkBSD::getAddrPort(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
using enum AddressFamily;
|
||||
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (getAddrFamily(addr)) {
|
||||
case kINET: {
|
||||
auto *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
case INet: {
|
||||
const auto *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
return ntohs(ipAddr->sin_port);
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
case INet6: {
|
||||
const auto *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
return ntohs(ipAddr->sin6_port);
|
||||
}
|
||||
|
||||
@ -690,16 +678,18 @@ int ArchNetworkBSD::getAddrPort(ArchNetAddress addr)
|
||||
|
||||
bool ArchNetworkBSD::isAnyAddr(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
using enum AddressFamily;
|
||||
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (getAddrFamily(addr)) {
|
||||
case kINET: {
|
||||
auto *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
case INet: {
|
||||
const auto *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
return (ipAddr->sin_addr.s_addr == INADDR_ANY && addr->m_len == static_cast<socklen_t>(sizeof(struct sockaddr_in)));
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
case INet6: {
|
||||
const auto *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
return (
|
||||
addr->m_len == (socklen_t)sizeof(struct sockaddr_in6) &&
|
||||
memcmp(
|
||||
@ -750,16 +740,16 @@ const int *ArchNetworkBSD::getUnblockPipeForThread(ArchThread thread)
|
||||
return unblockPipe;
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::throwError(int err)
|
||||
[[noreturn]] void ArchNetworkBSD::throwError(int err) const
|
||||
{
|
||||
switch (err) {
|
||||
case EINTR:
|
||||
ARCH->testCancelThread();
|
||||
throw XArchNetworkInterrupted(new XArchEvalUnix(err));
|
||||
throw XArchNetworkInterrupted(errorToString(err));
|
||||
|
||||
case EACCES:
|
||||
case EPERM:
|
||||
throw XArchNetworkAccess(new XArchEvalUnix(err));
|
||||
throw XArchNetworkAccess(errorToString(err));
|
||||
|
||||
case ENFILE:
|
||||
case EMFILE:
|
||||
@ -770,7 +760,7 @@ void ArchNetworkBSD::throwError(int err)
|
||||
#if defined(ENOSR)
|
||||
case ENOSR:
|
||||
#endif
|
||||
throw XArchNetworkResource(new XArchEvalUnix(err));
|
||||
throw XArchNetworkResource(errorToString(err));
|
||||
|
||||
case EPROTOTYPE:
|
||||
case EPROTONOSUPPORT:
|
||||
@ -784,44 +774,44 @@ void ArchNetworkBSD::throwError(int err)
|
||||
#if defined(ENOPKG)
|
||||
case ENOPKG:
|
||||
#endif
|
||||
throw XArchNetworkSupport(new XArchEvalUnix(err));
|
||||
throw XArchNetworkSupport(errorToString(err));
|
||||
|
||||
case EIO:
|
||||
throw XArchNetworkIO(new XArchEvalUnix(err));
|
||||
throw XArchNetworkIO(errorToString(err));
|
||||
|
||||
case EADDRNOTAVAIL:
|
||||
throw XArchNetworkNoAddress(new XArchEvalUnix(err));
|
||||
throw XArchNetworkNoAddress(errorToString(err));
|
||||
|
||||
case EADDRINUSE:
|
||||
throw XArchNetworkAddressInUse(new XArchEvalUnix(err));
|
||||
throw XArchNetworkAddressInUse(errorToString(err));
|
||||
|
||||
case EHOSTUNREACH:
|
||||
case ENETUNREACH:
|
||||
throw XArchNetworkNoRoute(new XArchEvalUnix(err));
|
||||
throw XArchNetworkNoRoute(errorToString(err));
|
||||
|
||||
case ENOTCONN:
|
||||
throw XArchNetworkNotConnected(new XArchEvalUnix(err));
|
||||
throw XArchNetworkNotConnected(errorToString(err));
|
||||
|
||||
case EPIPE:
|
||||
throw XArchNetworkShutdown(new XArchEvalUnix(err));
|
||||
throw XArchNetworkShutdown(errorToString(err));
|
||||
|
||||
case ECONNABORTED:
|
||||
case ECONNRESET:
|
||||
throw XArchNetworkDisconnected(new XArchEvalUnix(err));
|
||||
throw XArchNetworkDisconnected(errorToString(err));
|
||||
|
||||
case ECONNREFUSED:
|
||||
throw XArchNetworkConnectionRefused(new XArchEvalUnix(err));
|
||||
throw XArchNetworkConnectionRefused(errorToString(err));
|
||||
|
||||
case EHOSTDOWN:
|
||||
case ETIMEDOUT:
|
||||
throw XArchNetworkTimedOut(new XArchEvalUnix(err));
|
||||
throw XArchNetworkTimedOut(errorToString(err));
|
||||
|
||||
default:
|
||||
throw XArchNetwork(new XArchEvalUnix(err));
|
||||
throw XArchNetwork(errorToString(err));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchNetworkBSD::throwNameError(int err)
|
||||
[[noreturn]] void ArchNetworkBSD::throwNameError(int err) const
|
||||
{
|
||||
static const char *s_msg[] = {
|
||||
"The specified host is unknown", "The requested name is valid but does not have an IP address",
|
||||
|
||||
@ -11,29 +11,9 @@
|
||||
#include "arch/IArchNetwork.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#if HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
struct sockaddr_storage
|
||||
{
|
||||
unsigned char ss_len; /* address length */
|
||||
unsigned char ss_family; /* [XSI] address family */
|
||||
char __ss_pad1[_SS_PAD1SIZE];
|
||||
long long __ss_align; /* force structure storage alignment */
|
||||
char __ss_pad2[_SS_PAD2SIZE];
|
||||
};
|
||||
#endif
|
||||
|
||||
#if !HAVE_SOCKLEN_T
|
||||
using socklen_t = int;
|
||||
#endif
|
||||
|
||||
#include <mutex>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define ARCH_NETWORK ArchNetworkBSD
|
||||
#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_ *>(&addr_->m_addr))
|
||||
@ -55,6 +35,7 @@ class ArchNetAddressImpl
|
||||
public:
|
||||
ArchNetAddressImpl() : m_len(sizeof(m_addr))
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public:
|
||||
@ -82,7 +63,7 @@ public:
|
||||
}
|
||||
ArchNetworkBSD(ArchNetworkBSD const &) = delete;
|
||||
ArchNetworkBSD(ArchNetworkBSD &&) = delete;
|
||||
~ArchNetworkBSD() override;
|
||||
~ArchNetworkBSD() override = default;
|
||||
|
||||
ArchNetworkBSD &operator=(ArchNetworkBSD const &) = delete;
|
||||
ArchNetworkBSD &operator=(ArchNetworkBSD &&) = delete;
|
||||
@ -90,7 +71,7 @@ public:
|
||||
void init() override;
|
||||
|
||||
// IArchNetwork overrides
|
||||
ArchSocket newSocket(EAddressFamily, ESocketType) override;
|
||||
ArchSocket newSocket(AddressFamily, SocketType) override;
|
||||
ArchSocket copySocket(ArchSocket s) override;
|
||||
void closeSocket(ArchSocket s) override;
|
||||
void closeSocketForRead(ArchSocket s) override;
|
||||
@ -107,13 +88,13 @@ public:
|
||||
bool setNoDelayOnSocket(ArchSocket, bool noDelay) override;
|
||||
bool setReuseAddrOnSocket(ArchSocket, bool reuse) override;
|
||||
std::string getHostName() override;
|
||||
ArchNetAddress newAnyAddr(EAddressFamily) override;
|
||||
ArchNetAddress newAnyAddr(AddressFamily) override;
|
||||
ArchNetAddress copyAddr(ArchNetAddress) override;
|
||||
std::vector<ArchNetAddress> nameToAddr(const std::string &) override;
|
||||
void closeAddr(ArchNetAddress) override;
|
||||
std::string addrToName(ArchNetAddress) override;
|
||||
std::string addrToString(ArchNetAddress) override;
|
||||
EAddressFamily getAddrFamily(ArchNetAddress) override;
|
||||
AddressFamily getAddrFamily(ArchNetAddress) override;
|
||||
void setAddrPort(ArchNetAddress, int port) override;
|
||||
int getAddrPort(ArchNetAddress) override;
|
||||
bool isAnyAddr(ArchNetAddress) override;
|
||||
@ -122,11 +103,10 @@ public:
|
||||
private:
|
||||
const int *getUnblockPipe();
|
||||
const int *getUnblockPipeForThread(ArchThread);
|
||||
void setBlockingOnSocket(int fd, bool blocking);
|
||||
void throwError(int);
|
||||
void throwNameError(int);
|
||||
void setBlockingOnSocket(int fd, bool blocking) const;
|
||||
[[noreturn]] void throwError(int) const override;
|
||||
[[noreturn]] void throwNameError(int) const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Deps> m_pDeps;
|
||||
ArchMutex m_mutex{};
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
@ -1,80 +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/unix/ArchSleepUnix.h"
|
||||
|
||||
#include "arch/Arch.h"
|
||||
|
||||
#if TIME_WITH_SYS_TIME
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#else
|
||||
#if HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
#endif
|
||||
#if !HAVE_NANOSLEEP
|
||||
#if HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#if HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// ArchSleepUnix
|
||||
//
|
||||
|
||||
ArchSleepUnix::ArchSleepUnix()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ArchSleepUnix::~ArchSleepUnix()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchSleepUnix::sleep(double timeout)
|
||||
{
|
||||
ARCH->testCancelThread();
|
||||
if (timeout < 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if HAVE_NANOSLEEP
|
||||
// prep timeout
|
||||
struct timespec t;
|
||||
t.tv_sec = (long)timeout;
|
||||
t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec));
|
||||
|
||||
// wait
|
||||
while (nanosleep(&t, &t) < 0)
|
||||
ARCH->testCancelThread();
|
||||
#else
|
||||
/* emulate nanosleep() with select() */
|
||||
double startTime = ARCH->time();
|
||||
double timeLeft = timeout;
|
||||
while (timeLeft > 0.0) {
|
||||
struct timeval timeout2;
|
||||
timeout2.tv_sec = static_cast<int>(timeLeft);
|
||||
timeout2.tv_usec = static_cast<int>(1.0e+6 * (timeLeft - timeout2.tv_sec));
|
||||
select(
|
||||
(SELECT_TYPE_ARG1)0, SELECT_TYPE_ARG234 NULL, SELECT_TYPE_ARG234 NULL, SELECT_TYPE_ARG234 NULL,
|
||||
SELECT_TYPE_ARG5 & timeout2
|
||||
);
|
||||
ARCH->testCancelThread();
|
||||
timeLeft = timeout - (ARCH->time() - startTime);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -1,23 +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/IArchSleep.h"
|
||||
|
||||
#define ARCH_SLEEP ArchSleepUnix
|
||||
|
||||
//! Unix implementation of IArchSleep
|
||||
class ArchSleepUnix : public IArchSleep
|
||||
{
|
||||
public:
|
||||
ArchSleepUnix();
|
||||
virtual ~ArchSleepUnix();
|
||||
|
||||
// IArchSleep overrides
|
||||
virtual void sleep(double timeout);
|
||||
};
|
||||
@ -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/unix/ArchStringUnix.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//
|
||||
// ArchStringUnix
|
||||
//
|
||||
|
||||
ArchStringUnix::ArchStringUnix()
|
||||
{
|
||||
}
|
||||
|
||||
ArchStringUnix::~ArchStringUnix()
|
||||
{
|
||||
}
|
||||
|
||||
IArchString::EWideCharEncoding ArchStringUnix::getWideCharEncoding()
|
||||
{
|
||||
return kUCS4;
|
||||
}
|
||||
@ -1,23 +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/IArchString.h"
|
||||
|
||||
#define ARCH_STRING ArchStringUnix
|
||||
|
||||
//! Unix implementation of IArchString
|
||||
class ArchStringUnix : public IArchString
|
||||
{
|
||||
public:
|
||||
ArchStringUnix();
|
||||
virtual ~ArchStringUnix();
|
||||
|
||||
// IArchString overrides
|
||||
virtual EWideCharEncoding getWideCharEncoding();
|
||||
};
|
||||
@ -1,40 +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/unix/ArchTimeUnix.h"
|
||||
|
||||
#if TIME_WITH_SYS_TIME
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#else
|
||||
#if HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// ArchTimeUnix
|
||||
//
|
||||
|
||||
ArchTimeUnix::ArchTimeUnix()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ArchTimeUnix::~ArchTimeUnix()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
double ArchTimeUnix::time()
|
||||
{
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec;
|
||||
}
|
||||
@ -1,23 +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/IArchTime.h"
|
||||
|
||||
#define ARCH_TIME ArchTimeUnix
|
||||
|
||||
//! Generic Unix implementation of IArchTime
|
||||
class ArchTimeUnix : public IArchTime
|
||||
{
|
||||
public:
|
||||
ArchTimeUnix();
|
||||
virtual ~ArchTimeUnix();
|
||||
|
||||
// IArchTime overrides
|
||||
virtual double time();
|
||||
};
|
||||
@ -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
|
||||
@ -9,12 +10,8 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
//
|
||||
// XArchEvalUnix
|
||||
//
|
||||
|
||||
std::string XArchEvalUnix::eval() const
|
||||
std::string errorToString(int error)
|
||||
{
|
||||
// FIXME -- not thread safe
|
||||
return strerror(m_error);
|
||||
return std::strerror(error);
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -7,21 +8,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "arch/XArch.h"
|
||||
#include <string>
|
||||
|
||||
//! Lazy error message string evaluation for unix
|
||||
class XArchEvalUnix : public XArchEval
|
||||
{
|
||||
public:
|
||||
XArchEvalUnix(int error) : m_error(error)
|
||||
{
|
||||
}
|
||||
virtual ~XArchEvalUnix() throw()
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::string eval() const;
|
||||
|
||||
private:
|
||||
int m_error;
|
||||
};
|
||||
std::string errorToString(int error);
|
||||
|
||||
@ -1,16 +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/win32/ArchConsoleWindows.h"
|
||||
|
||||
ArchConsoleWindows::ArchConsoleWindows()
|
||||
{
|
||||
}
|
||||
|
||||
ArchConsoleWindows::~ArchConsoleWindows()
|
||||
{
|
||||
}
|
||||
@ -1,19 +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/ArchConsoleStd.h"
|
||||
|
||||
#define ARCH_CONSOLE ArchConsoleWindows
|
||||
|
||||
class ArchConsoleWindows : public ArchConsoleStd
|
||||
{
|
||||
public:
|
||||
ArchConsoleWindows();
|
||||
virtual ~ArchConsoleWindows();
|
||||
};
|
||||
@ -8,42 +8,40 @@
|
||||
#include "arch/win32/ArchDaemonWindows.h"
|
||||
|
||||
#include "arch/Arch.h"
|
||||
#include "arch/XArch.h"
|
||||
#include "arch/win32/ArchMiscWindows.h"
|
||||
#include "arch/win32/XArchWindows.h"
|
||||
#include "base/Log.h"
|
||||
#include "common/Constants.h"
|
||||
|
||||
inline static const auto kDefaultDaemonName = _T(kAppName);
|
||||
//
|
||||
// ArchDaemonWindows
|
||||
//
|
||||
|
||||
ArchDaemonWindows *ArchDaemonWindows::s_daemon = NULL;
|
||||
ArchDaemonWindows *ArchDaemonWindows::s_daemon = nullptr;
|
||||
|
||||
ArchDaemonWindows::ArchDaemonWindows() : m_daemonThreadID(0)
|
||||
{
|
||||
m_quitMessage = RegisterWindowMessage("DeskflowDaemonExit");
|
||||
}
|
||||
|
||||
ArchDaemonWindows::~ArchDaemonWindows()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
int ArchDaemonWindows::runDaemon(RunFunc runFunc)
|
||||
{
|
||||
assert(s_daemon != NULL);
|
||||
assert(s_daemon != nullptr);
|
||||
return s_daemon->doRunDaemon(runFunc);
|
||||
}
|
||||
|
||||
void ArchDaemonWindows::daemonRunning(bool running)
|
||||
{
|
||||
if (s_daemon != NULL) {
|
||||
if (s_daemon != nullptr) {
|
||||
s_daemon->doDaemonRunning(running);
|
||||
}
|
||||
}
|
||||
|
||||
UINT ArchDaemonWindows::getDaemonQuitMessage()
|
||||
{
|
||||
if (s_daemon != NULL) {
|
||||
if (s_daemon != nullptr) {
|
||||
return s_daemon->doGetDaemonQuitMessage();
|
||||
} else {
|
||||
return 0;
|
||||
@ -52,7 +50,7 @@ UINT ArchDaemonWindows::getDaemonQuitMessage()
|
||||
|
||||
void ArchDaemonWindows::daemonFailed(int result)
|
||||
{
|
||||
assert(s_daemon != NULL);
|
||||
assert(s_daemon != nullptr);
|
||||
throw XArchDaemonRunFailed(result);
|
||||
}
|
||||
|
||||
@ -63,24 +61,24 @@ void ArchDaemonWindows::installDaemon(
|
||||
LOG_DEBUG("installing windows service: %s", name);
|
||||
|
||||
// open service manager
|
||||
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
|
||||
if (mgr == NULL) {
|
||||
SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_WRITE);
|
||||
if (mgr == nullptr) {
|
||||
// can't open service manager
|
||||
throw XArchDaemonInstallFailed(new XArchEvalWindows);
|
||||
throw XArchDaemonInstallFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
// create the service
|
||||
SC_HANDLE service = CreateService(
|
||||
mgr, name, name, 0, SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START,
|
||||
SERVICE_ERROR_NORMAL, pathname, NULL, NULL, dependencies, NULL, NULL
|
||||
SERVICE_ERROR_NORMAL, pathname, nullptr, nullptr, dependencies, nullptr, nullptr
|
||||
);
|
||||
|
||||
if (service == NULL) {
|
||||
if (service == nullptr) {
|
||||
// can't create service
|
||||
DWORD err = GetLastError();
|
||||
if (err != ERROR_SERVICE_EXISTS) {
|
||||
CloseServiceHandle(mgr);
|
||||
throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
|
||||
throw XArchDaemonInstallFailed(windowsErrorToString(err));
|
||||
}
|
||||
} else {
|
||||
// done with service (but only try to close if not null)
|
||||
@ -93,7 +91,7 @@ void ArchDaemonWindows::installDaemon(
|
||||
// open the registry key for this service
|
||||
HKEY key = openNTServicesKey();
|
||||
key = ArchMiscWindows::addKey(key, name);
|
||||
if (key == NULL) {
|
||||
if (key == nullptr) {
|
||||
// can't open key
|
||||
DWORD err = GetLastError();
|
||||
try {
|
||||
@ -101,7 +99,7 @@ void ArchDaemonWindows::installDaemon(
|
||||
} catch (...) {
|
||||
// ignore
|
||||
}
|
||||
throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
|
||||
throw XArchDaemonInstallFailed(windowsErrorToString(err));
|
||||
}
|
||||
|
||||
// set the description
|
||||
@ -109,7 +107,7 @@ void ArchDaemonWindows::installDaemon(
|
||||
|
||||
// set command line
|
||||
key = ArchMiscWindows::addKey(key, _T("Parameters"));
|
||||
if (key == NULL) {
|
||||
if (key == nullptr) {
|
||||
// can't open key
|
||||
DWORD err = GetLastError();
|
||||
ArchMiscWindows::closeKey(key);
|
||||
@ -118,7 +116,7 @@ void ArchDaemonWindows::installDaemon(
|
||||
} catch (...) {
|
||||
// ignore
|
||||
}
|
||||
throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
|
||||
throw XArchDaemonInstallFailed(windowsErrorToString(err));
|
||||
}
|
||||
ArchMiscWindows::setValue(key, _T("CommandLine"), commandLine);
|
||||
|
||||
@ -133,27 +131,27 @@ void ArchDaemonWindows::uninstallDaemon(const char *name)
|
||||
// remove parameters for this service. ignore failures.
|
||||
HKEY key = openNTServicesKey();
|
||||
key = ArchMiscWindows::openKey(key, name);
|
||||
if (key != NULL) {
|
||||
if (key != nullptr) {
|
||||
ArchMiscWindows::deleteKey(key, _T("Parameters"));
|
||||
ArchMiscWindows::closeKey(key);
|
||||
}
|
||||
|
||||
// open service manager
|
||||
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
|
||||
if (mgr == NULL) {
|
||||
SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_WRITE);
|
||||
if (mgr == nullptr) {
|
||||
// can't open service manager
|
||||
throw XArchDaemonUninstallFailed(new XArchEvalWindows);
|
||||
throw XArchDaemonUninstallFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
// open the service. oddly, you must open a service to delete it.
|
||||
SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP);
|
||||
if (service == NULL) {
|
||||
if (service == nullptr) {
|
||||
DWORD err = GetLastError();
|
||||
CloseServiceHandle(mgr);
|
||||
if (err != ERROR_SERVICE_DOES_NOT_EXIST) {
|
||||
throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
|
||||
throw XArchDaemonUninstallFailed(windowsErrorToString(err));
|
||||
}
|
||||
throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
|
||||
throw XArchDaemonUninstallNotInstalled(windowsErrorToString(err));
|
||||
}
|
||||
|
||||
// stop the service. we don't care if we fail.
|
||||
@ -171,7 +169,7 @@ void ArchDaemonWindows::uninstallDaemon(const char *name)
|
||||
// give windows a chance to remove the service before we check if it still exists.
|
||||
// 100ms should be plenty of time.
|
||||
LOG_DEBUG("waiting for service to be removed");
|
||||
ARCH->sleep(0.1);
|
||||
Arch::sleep(0.1);
|
||||
|
||||
// handle failure. ignore error if service isn't installed anymore.
|
||||
if (!okay && isDaemonInstalled(name)) {
|
||||
@ -186,16 +184,16 @@ void ArchDaemonWindows::uninstallDaemon(const char *name)
|
||||
return;
|
||||
}
|
||||
if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
|
||||
throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
|
||||
throw XArchDaemonUninstallFailed(windowsErrorToString(err));
|
||||
}
|
||||
throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
|
||||
throw XArchDaemonUninstallNotInstalled(windowsErrorToString(err));
|
||||
}
|
||||
}
|
||||
|
||||
int ArchDaemonWindows::daemonize(const char *name, DaemonFunc func)
|
||||
int ArchDaemonWindows::daemonize(const char *name, DaemonFunc const &func)
|
||||
{
|
||||
assert(name != NULL);
|
||||
assert(func != NULL);
|
||||
assert(name != nullptr);
|
||||
assert(func != nullptr);
|
||||
|
||||
// save daemon function
|
||||
m_daemonFunc = func;
|
||||
@ -204,27 +202,27 @@ int ArchDaemonWindows::daemonize(const char *name, DaemonFunc func)
|
||||
SERVICE_TABLE_ENTRY entry[2];
|
||||
entry[0].lpServiceName = const_cast<char *>(name);
|
||||
entry[0].lpServiceProc = &ArchDaemonWindows::serviceMainEntry;
|
||||
entry[1].lpServiceName = NULL;
|
||||
entry[1].lpServiceProc = NULL;
|
||||
entry[1].lpServiceName = nullptr;
|
||||
entry[1].lpServiceProc = nullptr;
|
||||
|
||||
// hook us up to the service control manager. this won't return
|
||||
// (if successful) until the processes have terminated.
|
||||
s_daemon = this;
|
||||
if (StartServiceCtrlDispatcher(entry) == 0) {
|
||||
// StartServiceCtrlDispatcher failed
|
||||
s_daemon = NULL;
|
||||
throw XArchDaemonFailed(new XArchEvalWindows);
|
||||
s_daemon = nullptr;
|
||||
throw XArchDaemonFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
s_daemon = NULL;
|
||||
s_daemon = nullptr;
|
||||
return m_daemonResult;
|
||||
}
|
||||
|
||||
bool ArchDaemonWindows::canInstallDaemon(const char * /*name*/)
|
||||
{
|
||||
// check if we can open service manager for write
|
||||
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
|
||||
if (mgr == NULL) {
|
||||
SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_WRITE);
|
||||
if (mgr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
CloseServiceHandle(mgr);
|
||||
@ -233,14 +231,14 @@ bool ArchDaemonWindows::canInstallDaemon(const char * /*name*/)
|
||||
HKEY key = openNTServicesKey();
|
||||
ArchMiscWindows::closeKey(key);
|
||||
|
||||
return (key != NULL);
|
||||
return (key != nullptr);
|
||||
}
|
||||
|
||||
bool ArchDaemonWindows::isDaemonInstalled(const char *name)
|
||||
{
|
||||
// open service manager
|
||||
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
|
||||
if (mgr == NULL) {
|
||||
SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_READ);
|
||||
if (mgr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -248,17 +246,17 @@ bool ArchDaemonWindows::isDaemonInstalled(const char *name)
|
||||
SC_HANDLE service = OpenService(mgr, name, GENERIC_READ);
|
||||
|
||||
// clean up
|
||||
if (service != NULL) {
|
||||
if (service != nullptr) {
|
||||
CloseServiceHandle(service);
|
||||
}
|
||||
CloseServiceHandle(mgr);
|
||||
|
||||
return (service != NULL);
|
||||
return (service != nullptr);
|
||||
}
|
||||
|
||||
HKEY ArchDaemonWindows::openNTServicesKey()
|
||||
{
|
||||
static const char *s_keyNames[] = {_T("SYSTEM"), _T("CurrentControlSet"), _T("Services"), NULL};
|
||||
static const char *s_keyNames[] = {_T("SYSTEM"), _T("CurrentControlSet"), _T("Services"), nullptr};
|
||||
|
||||
return ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
|
||||
}
|
||||
@ -279,12 +277,12 @@ bool ArchDaemonWindows::isRunState(DWORD state)
|
||||
int ArchDaemonWindows::doRunDaemon(RunFunc run)
|
||||
{
|
||||
// should only be called from DaemonFunc
|
||||
assert(m_serviceMutex != NULL);
|
||||
assert(run != NULL);
|
||||
assert(m_serviceMutex != nullptr);
|
||||
assert(run != nullptr);
|
||||
|
||||
// create message queue for this thread
|
||||
MSG dummy;
|
||||
PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE);
|
||||
PeekMessage(&dummy, nullptr, 0, 0, PM_NOREMOVE);
|
||||
|
||||
int result = 0;
|
||||
ARCH->lockMutex(m_serviceMutex);
|
||||
@ -348,7 +346,7 @@ void ArchDaemonWindows::setStatus(DWORD state)
|
||||
|
||||
void ArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
|
||||
{
|
||||
assert(s_daemon != NULL);
|
||||
assert(s_daemon != nullptr);
|
||||
|
||||
LOG_DEBUG("setting service status: state=%d, step=%d, waitHint=%d", state, step, waitHint);
|
||||
|
||||
@ -365,7 +363,7 @@ void ArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
|
||||
|
||||
void ArchDaemonWindows::setStatusError(DWORD error)
|
||||
{
|
||||
assert(s_daemon != NULL);
|
||||
assert(s_daemon != nullptr);
|
||||
|
||||
SERVICE_STATUS status;
|
||||
status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
|
||||
@ -413,7 +411,7 @@ void ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR *argvIn)
|
||||
HKEY key = openNTServicesKey();
|
||||
key = ArchMiscWindows::openKey(key, argvIn[0]);
|
||||
key = ArchMiscWindows::openKey(key, _T("Parameters"));
|
||||
if (key != NULL) {
|
||||
if (key != nullptr) {
|
||||
commandLine = ArchMiscWindows::readValueString(key, _T("CommandLine"));
|
||||
}
|
||||
|
||||
@ -498,14 +496,14 @@ void WINAPI ArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR *argv)
|
||||
|
||||
void ArchDaemonWindows::serviceHandler(DWORD ctrl)
|
||||
{
|
||||
assert(m_serviceMutex != NULL);
|
||||
assert(m_serviceCondVar != NULL);
|
||||
assert(m_serviceMutex != nullptr);
|
||||
assert(m_serviceCondVar != nullptr);
|
||||
|
||||
ARCH->lockMutex(m_serviceMutex);
|
||||
|
||||
// ignore request if service is already stopped
|
||||
if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) {
|
||||
if (s_daemon != NULL) {
|
||||
if (s_daemon == nullptr || m_serviceState == SERVICE_STOPPED) {
|
||||
if (s_daemon != nullptr) {
|
||||
setStatus(m_serviceState);
|
||||
}
|
||||
ARCH->unlockMutex(m_serviceMutex);
|
||||
@ -560,39 +558,39 @@ void WINAPI ArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
|
||||
void ArchDaemonWindows::start(const char *name)
|
||||
{
|
||||
// open service manager
|
||||
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
|
||||
if (mgr == NULL) {
|
||||
throw XArchDaemonFailed(new XArchEvalWindows());
|
||||
SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_READ);
|
||||
if (mgr == nullptr) {
|
||||
throw XArchDaemonFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
// open the service
|
||||
SC_HANDLE service = OpenService(mgr, name, SERVICE_START);
|
||||
|
||||
if (service == NULL) {
|
||||
if (service == nullptr) {
|
||||
CloseServiceHandle(mgr);
|
||||
throw XArchDaemonFailed(new XArchEvalWindows());
|
||||
throw XArchDaemonFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
// start the service
|
||||
if (!StartService(service, 0, NULL)) {
|
||||
throw XArchDaemonFailed(new XArchEvalWindows());
|
||||
if (!StartService(service, 0, nullptr)) {
|
||||
throw XArchDaemonFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchDaemonWindows::stop(const char *name)
|
||||
{
|
||||
// open service manager
|
||||
SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
|
||||
if (mgr == NULL) {
|
||||
throw XArchDaemonFailed(new XArchEvalWindows());
|
||||
SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_READ);
|
||||
if (mgr == nullptr) {
|
||||
throw XArchDaemonFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
// open the service
|
||||
SC_HANDLE service = OpenService(mgr, name, SERVICE_STOP | SERVICE_QUERY_STATUS);
|
||||
|
||||
if (service == NULL) {
|
||||
if (service == nullptr) {
|
||||
CloseServiceHandle(mgr);
|
||||
throw XArchDaemonFailed(new XArchEvalWindows());
|
||||
throw XArchDaemonFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
// ask the service to stop, asynchronously
|
||||
@ -600,7 +598,7 @@ void ArchDaemonWindows::stop(const char *name)
|
||||
if (!ControlService(service, SERVICE_CONTROL_STOP, &ss)) {
|
||||
DWORD dwErrCode = GetLastError();
|
||||
if (dwErrCode != ERROR_SERVICE_NOT_ACTIVE) {
|
||||
throw XArchDaemonFailed(new XArchEvalWindows());
|
||||
throw XArchDaemonFailed(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -608,17 +606,17 @@ void ArchDaemonWindows::stop(const char *name)
|
||||
void ArchDaemonWindows::installDaemon()
|
||||
{
|
||||
// install default daemon if not already installed.
|
||||
if (!isDaemonInstalled(DEFAULT_DAEMON_NAME)) {
|
||||
if (!isDaemonInstalled(kDefaultDaemonName)) {
|
||||
char binPath[MAX_PATH];
|
||||
GetModuleFileName(ArchMiscWindows::instanceWin32(), binPath, MAX_PATH);
|
||||
|
||||
// wrap in quotes so a malicious user can't start \Program.exe as admin.
|
||||
const auto command = "\"" + std::string(binPath) + "\"";
|
||||
|
||||
installDaemon(DEFAULT_DAEMON_NAME, DEFAULT_DAEMON_INFO, command.c_str(), "", "");
|
||||
installDaemon(kDefaultDaemonName, DEFAULT_DAEMON_INFO, command.c_str(), "", "");
|
||||
}
|
||||
|
||||
start(DEFAULT_DAEMON_NAME);
|
||||
start(kDefaultDaemonName);
|
||||
}
|
||||
|
||||
void ArchDaemonWindows::uninstallDaemon()
|
||||
@ -632,7 +630,7 @@ void ArchDaemonWindows::uninstallDaemon()
|
||||
}
|
||||
|
||||
// remove new service if installed.
|
||||
if (isDaemonInstalled(DEFAULT_DAEMON_NAME)) {
|
||||
uninstallDaemon(DEFAULT_DAEMON_NAME);
|
||||
if (isDaemonInstalled(kDefaultDaemonName)) {
|
||||
uninstallDaemon(kDefaultDaemonName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
|
||||
#include "arch/IArchDaemon.h"
|
||||
#include "arch/IArchMultithread.h"
|
||||
#include "common/Constants.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
@ -27,7 +26,7 @@ public:
|
||||
using RunFunc = std::function<int()>;
|
||||
|
||||
ArchDaemonWindows();
|
||||
virtual ~ArchDaemonWindows();
|
||||
~ArchDaemonWindows() override = default;
|
||||
|
||||
//! Run the daemon
|
||||
/*!
|
||||
@ -68,16 +67,16 @@ public:
|
||||
static UINT getDaemonQuitMessage();
|
||||
|
||||
// IArchDaemon overrides
|
||||
virtual void installDaemon(
|
||||
void installDaemon(
|
||||
const char *name, const char *description, const char *pathname, const char *commandLine, const char *dependencies
|
||||
);
|
||||
virtual void uninstallDaemon(const char *name);
|
||||
virtual void installDaemon();
|
||||
virtual void uninstallDaemon();
|
||||
virtual int daemonize(const char *name, DaemonFunc func);
|
||||
virtual bool canInstallDaemon(const char *name);
|
||||
virtual bool isDaemonInstalled(const char *name);
|
||||
std::string commandLine() const
|
||||
) override;
|
||||
void uninstallDaemon(const char *name) override;
|
||||
void installDaemon() override;
|
||||
void uninstallDaemon() override;
|
||||
int daemonize(const char *name, DaemonFunc const &func) override;
|
||||
bool canInstallDaemon(const char *name) override;
|
||||
bool isDaemonInstalled(const char *name) override;
|
||||
std::string commandLine() const override
|
||||
{
|
||||
return m_commandLine;
|
||||
}
|
||||
@ -136,7 +135,6 @@ private:
|
||||
std::string m_commandLine;
|
||||
};
|
||||
|
||||
#define DEFAULT_DAEMON_NAME _T(kAppName)
|
||||
#define DEFAULT_DAEMON_INFO _T("Runs the Core process on secure desktops (UAC prompts, login screen, etc).")
|
||||
|
||||
#define LEGACY_SERVER_DAEMON_NAME _T("Deskflow Server")
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#include "arch/win32/ArchLogWindows.h"
|
||||
#include "arch/win32/ArchMiscWindows.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@ -14,28 +13,23 @@
|
||||
// ArchLogWindows
|
||||
//
|
||||
|
||||
ArchLogWindows::ArchLogWindows() : m_eventLog(NULL)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ArchLogWindows::~ArchLogWindows()
|
||||
ArchLogWindows::ArchLogWindows() : m_eventLog(nullptr)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchLogWindows::openLog(const char *name)
|
||||
{
|
||||
if (m_eventLog == NULL) {
|
||||
m_eventLog = RegisterEventSource(NULL, name);
|
||||
if (m_eventLog == nullptr) {
|
||||
m_eventLog = RegisterEventSource(nullptr, name);
|
||||
}
|
||||
}
|
||||
|
||||
void ArchLogWindows::closeLog()
|
||||
{
|
||||
if (m_eventLog != NULL) {
|
||||
if (m_eventLog != nullptr) {
|
||||
DeregisterEventSource(m_eventLog);
|
||||
m_eventLog = NULL;
|
||||
m_eventLog = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,17 +38,17 @@ void ArchLogWindows::showLog(bool)
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchLogWindows::writeLog(ELevel level, const char *msg)
|
||||
void ArchLogWindows::writeLog(LogLevel level, const char *msg)
|
||||
{
|
||||
if (m_eventLog != NULL) {
|
||||
if (m_eventLog != nullptr) {
|
||||
// convert priority
|
||||
WORD type;
|
||||
switch (level) {
|
||||
case kERROR:
|
||||
case LogLevel::Error:
|
||||
type = EVENTLOG_ERROR_TYPE;
|
||||
break;
|
||||
|
||||
case kWARNING:
|
||||
case LogLevel::Warning:
|
||||
type = EVENTLOG_WARNING_TYPE;
|
||||
break;
|
||||
|
||||
@ -72,9 +66,9 @@ void ArchLogWindows::writeLog(ELevel level, const char *msg)
|
||||
ReportEvent(
|
||||
m_eventLog, type, static_cast<WORD>(level),
|
||||
0, // event ID
|
||||
NULL, 0,
|
||||
nullptr, 0,
|
||||
(DWORD)strlen(msg) + 1, // raw data size
|
||||
NULL,
|
||||
nullptr,
|
||||
const_cast<char *>(msg)
|
||||
); // raw data
|
||||
}
|
||||
|
||||
@ -19,13 +19,13 @@ class ArchLogWindows : public IArchLog
|
||||
{
|
||||
public:
|
||||
ArchLogWindows();
|
||||
virtual ~ArchLogWindows();
|
||||
~ArchLogWindows() override = default;
|
||||
|
||||
// IArchLog overrides
|
||||
virtual void openLog(const char *name);
|
||||
virtual void closeLog();
|
||||
virtual void showLog(bool showIfEmpty);
|
||||
virtual void writeLog(ELevel, const char *);
|
||||
void openLog(const char *name) override;
|
||||
void closeLog() override;
|
||||
void showLog(bool showIfEmpty) override;
|
||||
void writeLog(LogLevel, const char *) override;
|
||||
|
||||
private:
|
||||
HANDLE m_eventLog;
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "arch/win32/XArchWindows.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/String.h"
|
||||
#include "common/Constants.h"
|
||||
|
||||
#include <Psapi.h>
|
||||
|
||||
@ -31,7 +32,7 @@ void errorMessageBox(const char *message, const char *title = "Fatal Error");
|
||||
std::string getBinaryName()
|
||||
{
|
||||
std::array<char, MAX_PATH> buffer;
|
||||
if (!GetModuleFileNameA(NULL, buffer.data(), MAX_PATH)) {
|
||||
if (!GetModuleFileNameA(nullptr, buffer.data(), MAX_PATH)) {
|
||||
errorMessageBox("Failed to get binary name.");
|
||||
abort();
|
||||
}
|
||||
@ -52,10 +53,10 @@ const std::string s_binaryName = getBinaryName();
|
||||
//
|
||||
|
||||
DWORD ArchMiscWindows::s_busyState = 0;
|
||||
ArchMiscWindows::STES_t ArchMiscWindows::s_stes = NULL;
|
||||
HICON ArchMiscWindows::s_largeIcon = NULL;
|
||||
HICON ArchMiscWindows::s_smallIcon = NULL;
|
||||
HINSTANCE ArchMiscWindows::s_instanceWin32 = NULL;
|
||||
ArchMiscWindows::STES_t ArchMiscWindows::s_stes = nullptr;
|
||||
HICON ArchMiscWindows::s_largeIcon = nullptr;
|
||||
HICON ArchMiscWindows::s_smallIcon = nullptr;
|
||||
HINSTANCE ArchMiscWindows::s_instanceWin32 = nullptr;
|
||||
|
||||
void ArchMiscWindows::init()
|
||||
{
|
||||
@ -105,9 +106,9 @@ HKEY ArchMiscWindows::addKey(HKEY key, const TCHAR *const *keyNames)
|
||||
|
||||
HKEY ArchMiscWindows::openKey(HKEY key, const TCHAR *keyName, bool create)
|
||||
{
|
||||
// ignore if parent is NULL
|
||||
if (key == NULL) {
|
||||
return NULL;
|
||||
// ignore if parent is nullptr
|
||||
if (key == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// open next key
|
||||
@ -115,11 +116,11 @@ HKEY ArchMiscWindows::openKey(HKEY key, const TCHAR *keyName, bool create)
|
||||
LSTATUS result = RegOpenKeyEx(key, keyName, 0, KEY_WRITE | KEY_QUERY_VALUE, &newKey);
|
||||
if (result != ERROR_SUCCESS && create) {
|
||||
DWORD disp;
|
||||
result = RegCreateKeyEx(key, keyName, 0, NULL, 0, KEY_WRITE | KEY_QUERY_VALUE, NULL, &newKey, &disp);
|
||||
result = RegCreateKeyEx(key, keyName, 0, nullptr, 0, KEY_WRITE | KEY_QUERY_VALUE, nullptr, &newKey, &disp);
|
||||
}
|
||||
if (result != ERROR_SUCCESS) {
|
||||
RegCloseKey(key);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// switch to new key
|
||||
@ -129,7 +130,7 @@ HKEY ArchMiscWindows::openKey(HKEY key, const TCHAR *keyName, bool create)
|
||||
|
||||
HKEY ArchMiscWindows::openKey(HKEY key, const TCHAR *const *keyNames, bool create)
|
||||
{
|
||||
for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) {
|
||||
for (size_t i = 0; key != nullptr && keyNames[i] != nullptr; ++i) {
|
||||
// open next key
|
||||
key = openKey(key, keyNames[i], create);
|
||||
}
|
||||
@ -138,17 +139,17 @@ HKEY ArchMiscWindows::openKey(HKEY key, const TCHAR *const *keyNames, bool creat
|
||||
|
||||
void ArchMiscWindows::closeKey(HKEY key)
|
||||
{
|
||||
assert(key != NULL);
|
||||
if (key == NULL)
|
||||
assert(key != nullptr);
|
||||
if (key == nullptr)
|
||||
return;
|
||||
RegCloseKey(key);
|
||||
}
|
||||
|
||||
void ArchMiscWindows::deleteKey(HKEY key, const TCHAR *name)
|
||||
{
|
||||
assert(key != NULL);
|
||||
assert(name != NULL);
|
||||
if (key == NULL || name == NULL)
|
||||
assert(key != nullptr);
|
||||
assert(name != nullptr);
|
||||
if (key == nullptr || name == nullptr)
|
||||
return;
|
||||
RegDeleteKey(key, name);
|
||||
}
|
||||
@ -156,7 +157,7 @@ void ArchMiscWindows::deleteKey(HKEY key, const TCHAR *name)
|
||||
ArchMiscWindows::EValueType ArchMiscWindows::typeOfValue(HKEY key, const TCHAR *name)
|
||||
{
|
||||
DWORD type;
|
||||
LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
|
||||
LONG result = RegQueryValueEx(key, name, 0, &type, nullptr, nullptr);
|
||||
if (result != ERROR_SUCCESS) {
|
||||
return kNO_VALUE;
|
||||
}
|
||||
@ -177,8 +178,8 @@ ArchMiscWindows::EValueType ArchMiscWindows::typeOfValue(HKEY key, const TCHAR *
|
||||
|
||||
void ArchMiscWindows::setValue(HKEY key, const TCHAR *name, const std::string &value)
|
||||
{
|
||||
assert(key != NULL);
|
||||
if (key == NULL) {
|
||||
assert(key != nullptr);
|
||||
if (key == nullptr) {
|
||||
// TODO: throw exception
|
||||
return;
|
||||
}
|
||||
@ -187,8 +188,8 @@ void ArchMiscWindows::setValue(HKEY key, const TCHAR *name, const std::string &v
|
||||
|
||||
void ArchMiscWindows::setValue(HKEY key, const TCHAR *name, DWORD value)
|
||||
{
|
||||
assert(key != NULL);
|
||||
if (key == NULL) {
|
||||
assert(key != nullptr);
|
||||
if (key == nullptr) {
|
||||
// TODO: throw exception
|
||||
return;
|
||||
}
|
||||
@ -200,7 +201,7 @@ std::string ArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR *name, DWO
|
||||
// get the size of the string
|
||||
DWORD actualType;
|
||||
DWORD size = 0;
|
||||
LONG result = RegQueryValueEx(key, name, 0, &actualType, NULL, &size);
|
||||
LONG result = RegQueryValueEx(key, name, 0, &actualType, nullptr, &size);
|
||||
if (result != ERROR_SUCCESS || actualType != type) {
|
||||
return std::string();
|
||||
}
|
||||
@ -263,12 +264,12 @@ void ArchMiscWindows::removeBusyState(DWORD busyModes)
|
||||
void ArchMiscWindows::setThreadExecutionState(DWORD busyModes)
|
||||
{
|
||||
// look up function dynamically so we work on older systems
|
||||
if (s_stes == NULL) {
|
||||
if (s_stes == nullptr) {
|
||||
HINSTANCE kernel = LoadLibrary("kernel32.dll");
|
||||
if (kernel != NULL) {
|
||||
if (kernel != nullptr) {
|
||||
s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel, "SetThreadExecutionState"));
|
||||
}
|
||||
if (s_stes == NULL) {
|
||||
if (s_stes == nullptr) {
|
||||
s_stes = &ArchMiscWindows::dummySetThreadExecutionState;
|
||||
}
|
||||
}
|
||||
@ -301,12 +302,12 @@ void ArchMiscWindows::wakeupDisplay()
|
||||
// We can't use ::setThreadExecutionState here because it sets
|
||||
// ES_CONTINUOUS, which we don't want.
|
||||
|
||||
if (s_stes == NULL) {
|
||||
if (s_stes == nullptr) {
|
||||
HINSTANCE kernel = LoadLibrary("kernel32.dll");
|
||||
if (kernel != NULL) {
|
||||
if (kernel != nullptr) {
|
||||
s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel, "SetThreadExecutionState"));
|
||||
}
|
||||
if (s_stes == NULL) {
|
||||
if (s_stes == nullptr) {
|
||||
s_stes = &ArchMiscWindows::dummySetThreadExecutionState;
|
||||
}
|
||||
}
|
||||
@ -394,13 +395,13 @@ BOOL WINAPI ArchMiscWindows::getProcessEntry(PROCESSENTRY32 &entry, DWORD proces
|
||||
HINSTANCE
|
||||
ArchMiscWindows::instanceWin32()
|
||||
{
|
||||
assert(s_instanceWin32 != NULL);
|
||||
assert(s_instanceWin32 != nullptr);
|
||||
return s_instanceWin32;
|
||||
}
|
||||
|
||||
void ArchMiscWindows::setInstanceWin32(HINSTANCE instance)
|
||||
{
|
||||
assert(instance != NULL);
|
||||
assert(instance != nullptr);
|
||||
s_instanceWin32 = instance;
|
||||
}
|
||||
|
||||
@ -409,7 +410,7 @@ std::string ArchMiscWindows::getActiveDesktopName()
|
||||
HDESK desk = OpenInputDesktop(0, TRUE, GENERIC_READ);
|
||||
if (desk == nullptr) {
|
||||
LOG((CLOG_ERR "could not open input desktop"));
|
||||
throw XArch(new XArchEvalWindows());
|
||||
throw std::runtime_error(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
DWORD size;
|
||||
@ -530,7 +531,7 @@ bool ArchMiscWindows::isProcessElevated()
|
||||
|
||||
HANDLE hToken = nullptr;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
|
||||
throw XArch(new XArchEvalWindows());
|
||||
throw std::runtime_error(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
|
||||
TOKEN_ELEVATION elevation;
|
||||
@ -538,7 +539,7 @@ bool ArchMiscWindows::isProcessElevated()
|
||||
try {
|
||||
DWORD dwSize = sizeof(TOKEN_ELEVATION);
|
||||
if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) {
|
||||
throw XArch(new XArchEvalWindows());
|
||||
throw std::runtime_error(windowsErrorToString(GetLastError()));
|
||||
}
|
||||
} catch (...) {
|
||||
CloseHandle(hToken);
|
||||
|
||||
@ -50,16 +50,16 @@ public:
|
||||
|
||||
ArchThreadImpl::ArchThreadImpl()
|
||||
: m_refCount(1),
|
||||
m_thread(NULL),
|
||||
m_thread(nullptr),
|
||||
m_id(0),
|
||||
m_func(NULL),
|
||||
m_userData(NULL),
|
||||
m_func(nullptr),
|
||||
m_userData(nullptr),
|
||||
m_cancelling(false),
|
||||
m_result(NULL),
|
||||
m_networkData(NULL)
|
||||
m_result(nullptr),
|
||||
m_networkData(nullptr)
|
||||
{
|
||||
m_exit = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
m_exit = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
m_cancel = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
}
|
||||
|
||||
ArchThreadImpl::~ArchThreadImpl()
|
||||
@ -72,65 +72,56 @@ ArchThreadImpl::~ArchThreadImpl()
|
||||
// ArchMultithreadWindows
|
||||
//
|
||||
|
||||
ArchMultithreadWindows *ArchMultithreadWindows::s_instance = NULL;
|
||||
ArchMultithreadWindows *ArchMultithreadWindows::s_instance = nullptr;
|
||||
|
||||
ArchMultithreadWindows::ArchMultithreadWindows()
|
||||
{
|
||||
assert(s_instance == NULL);
|
||||
assert(s_instance == nullptr);
|
||||
s_instance = this;
|
||||
|
||||
// no signal handlers
|
||||
for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
|
||||
m_signalFunc[i] = NULL;
|
||||
m_signalUserData[i] = NULL;
|
||||
for (size_t i = 0; i < static_cast<size_t>(ThreadSignal::MaxSignals); ++i) {
|
||||
m_signalFunc[i] = nullptr;
|
||||
m_signalUserData[i] = nullptr;
|
||||
}
|
||||
|
||||
// create mutex for thread list
|
||||
m_threadMutex = newMutex();
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
|
||||
// create thread for calling (main) thread and add it to our
|
||||
// list. no need to lock the mutex since we're the only thread.
|
||||
m_mainThread = new ArchThreadImpl;
|
||||
m_mainThread->m_thread = NULL;
|
||||
m_mainThread->m_thread = nullptr;
|
||||
m_mainThread->m_id = GetCurrentThreadId();
|
||||
insert(m_mainThread);
|
||||
}
|
||||
|
||||
ArchMultithreadWindows::~ArchMultithreadWindows()
|
||||
{
|
||||
s_instance = NULL;
|
||||
s_instance = nullptr;
|
||||
|
||||
// clean up thread list
|
||||
for (ThreadList::iterator index = m_threadList.begin(); index != m_threadList.end(); ++index) {
|
||||
delete *index;
|
||||
}
|
||||
|
||||
// done with mutex
|
||||
delete m_threadMutex;
|
||||
}
|
||||
|
||||
void ArchMultithreadWindows::setNetworkDataForCurrentThread(void *data)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
ArchThreadImpl *thread = findNoRef(GetCurrentThreadId());
|
||||
thread->m_networkData = data;
|
||||
unlockMutex(m_threadMutex);
|
||||
}
|
||||
|
||||
void *ArchMultithreadWindows::getNetworkDataForThread(ArchThread thread)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
void *data = thread->m_networkData;
|
||||
unlockMutex(m_threadMutex);
|
||||
return data;
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
return thread->m_networkData;
|
||||
}
|
||||
|
||||
HANDLE
|
||||
ArchMultithreadWindows::getCancelEventForCurrentThread()
|
||||
HANDLE ArchMultithreadWindows::getCancelEventForCurrentThread()
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
ArchThreadImpl *thread = findNoRef(GetCurrentThreadId());
|
||||
unlockMutex(m_threadMutex);
|
||||
return thread->m_cancel;
|
||||
}
|
||||
|
||||
@ -142,8 +133,8 @@ ArchMultithreadWindows *ArchMultithreadWindows::getInstance()
|
||||
ArchCond ArchMultithreadWindows::newCondVar()
|
||||
{
|
||||
ArchCondImpl *cond = new ArchCondImpl;
|
||||
cond->m_events[ArchCondImpl::kSignal] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
cond->m_events[ArchCondImpl::kBroadcast] = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
cond->m_events[ArchCondImpl::kSignal] = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
cond->m_events[ArchCondImpl::kBroadcast] = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
cond->m_waitCountMutex = newMutex();
|
||||
cond->m_waitCount = 0;
|
||||
return cond;
|
||||
@ -263,7 +254,8 @@ void ArchMultithreadWindows::unlockMutex(ArchMutex mutex)
|
||||
|
||||
ArchThread ArchMultithreadWindows::newThread(ThreadFunc func, void *data)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
// note that the child thread will wait until we release this mutex
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
|
||||
// create thread impl for new thread
|
||||
ArchThreadImpl *thread = new ArchThreadImpl;
|
||||
@ -272,14 +264,14 @@ ArchThread ArchMultithreadWindows::newThread(ThreadFunc func, void *data)
|
||||
|
||||
// create thread
|
||||
unsigned int id = 0;
|
||||
thread->m_thread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0, threadFunc, (void *)thread, 0, &id));
|
||||
thread->m_thread = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, threadFunc, (void *)thread, 0, &id));
|
||||
thread->m_id = static_cast<DWORD>(id);
|
||||
|
||||
// check if thread was started
|
||||
if (thread->m_thread == 0) {
|
||||
// failed to start thread so clean up
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
thread = nullptr;
|
||||
} else {
|
||||
// add thread to list
|
||||
insert(thread);
|
||||
@ -288,37 +280,34 @@ ArchThread ArchMultithreadWindows::newThread(ThreadFunc func, void *data)
|
||||
refThread(thread);
|
||||
}
|
||||
|
||||
// note that the child thread will wait until we release this mutex
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
ArchThread ArchMultithreadWindows::newCurrentThread()
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
ArchThreadImpl *thread = find(GetCurrentThreadId());
|
||||
unlockMutex(m_threadMutex);
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
return thread;
|
||||
}
|
||||
|
||||
void ArchMultithreadWindows::closeThread(ArchThread thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// decrement ref count and clean up thread if no more references
|
||||
if (--thread->m_refCount == 0) {
|
||||
// close the handle (main thread has a NULL handle)
|
||||
if (thread->m_thread != NULL) {
|
||||
// close the handle (main thread has a nullptr handle)
|
||||
if (thread->m_thread != nullptr) {
|
||||
CloseHandle(thread->m_thread);
|
||||
}
|
||||
|
||||
// remove thread from list
|
||||
lockMutex(m_threadMutex);
|
||||
assert(findNoRefOrCreate(thread->m_id) == thread);
|
||||
erase(thread);
|
||||
unlockMutex(m_threadMutex);
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
assert(findNoRefOrCreate(thread->m_id) == thread);
|
||||
erase(thread);
|
||||
}
|
||||
|
||||
// done with thread
|
||||
delete thread;
|
||||
@ -333,7 +322,7 @@ ArchThread ArchMultithreadWindows::copyThread(ArchThread thread)
|
||||
|
||||
void ArchMultithreadWindows::cancelThread(ArchThread thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// set cancel flag
|
||||
SetEvent(thread->m_cancel);
|
||||
@ -380,7 +369,7 @@ void ArchMultithreadWindows::setPriorityOfThread(ArchThread thread, int n)
|
||||
#endif
|
||||
static const size_t s_pBase = 8; // index of normal priority
|
||||
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
size_t index;
|
||||
if (n > 0 && s_pBase < (size_t)n) {
|
||||
@ -400,9 +389,8 @@ void ArchMultithreadWindows::setPriorityOfThread(ArchThread thread, int n)
|
||||
void ArchMultithreadWindows::testCancelThread()
|
||||
{
|
||||
// find current thread
|
||||
lockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
ArchThreadImpl *thread = findNoRef(GetCurrentThreadId());
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
// test cancel on thread
|
||||
testCancelThreadImpl(thread);
|
||||
@ -410,24 +398,21 @@ void ArchMultithreadWindows::testCancelThread()
|
||||
|
||||
bool ArchMultithreadWindows::wait(ArchThread target, double timeout)
|
||||
{
|
||||
assert(target != NULL);
|
||||
assert(target != nullptr);
|
||||
|
||||
lockMutex(m_threadMutex);
|
||||
|
||||
// find current thread
|
||||
ArchThreadImpl *self = findNoRef(GetCurrentThreadId());
|
||||
|
||||
// ignore wait if trying to wait on ourself
|
||||
if (target == self) {
|
||||
unlockMutex(m_threadMutex);
|
||||
return false;
|
||||
ArchThreadImpl *self = nullptr;
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
// find current thread
|
||||
self = findNoRef(GetCurrentThreadId());
|
||||
// ignore wait if trying to wait on ourself
|
||||
if (target == self) {
|
||||
return false;
|
||||
}
|
||||
// ref the target so it can't go away while we're watching it
|
||||
refThread(target);
|
||||
}
|
||||
|
||||
// ref the target so it can't go away while we're watching it
|
||||
refThread(target);
|
||||
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
// convert timeout
|
||||
DWORD t;
|
||||
if (timeout < 0.0) {
|
||||
@ -480,10 +465,8 @@ bool ArchMultithreadWindows::isExitedThread(ArchThread thread)
|
||||
|
||||
void *ArchMultithreadWindows::getResultOfThread(ArchThread thread)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
void *result = thread->m_result;
|
||||
unlockMutex(m_threadMutex);
|
||||
return result;
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
return thread->m_result;
|
||||
}
|
||||
|
||||
IArchMultithread::ThreadID ArchMultithreadWindows::getIDOfThread(ArchThread thread)
|
||||
@ -491,30 +474,31 @@ IArchMultithread::ThreadID ArchMultithreadWindows::getIDOfThread(ArchThread thre
|
||||
return static_cast<ThreadID>(thread->m_id);
|
||||
}
|
||||
|
||||
void ArchMultithreadWindows::setSignalHandler(ESignal signal, SignalFunc func, void *userData)
|
||||
void ArchMultithreadWindows::setSignalHandler(ThreadSignal signal, SignalFunc func, void *userData)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
m_signalFunc[signal] = func;
|
||||
m_signalUserData[signal] = userData;
|
||||
unlockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
const auto index = static_cast<int>(signal);
|
||||
m_signalFunc[index] = func;
|
||||
m_signalUserData[index] = userData;
|
||||
}
|
||||
|
||||
void ArchMultithreadWindows::raiseSignal(ESignal signal)
|
||||
void ArchMultithreadWindows::raiseSignal(ThreadSignal signal)
|
||||
{
|
||||
lockMutex(m_threadMutex);
|
||||
if (m_signalFunc[signal] != NULL) {
|
||||
m_signalFunc[signal](signal, m_signalUserData[signal]);
|
||||
using enum IArchMultithread::ThreadSignal;
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
const auto index = static_cast<int>(signal);
|
||||
if (m_signalFunc[index] != nullptr) {
|
||||
m_signalFunc[index](signal, m_signalUserData[index]);
|
||||
ARCH->unblockPollSocket(m_mainThread);
|
||||
} else if (signal == kINTERRUPT || signal == kTERMINATE) {
|
||||
} else if (signal == Interrupt || signal == Terminate) {
|
||||
ARCH->cancelThread(m_mainThread);
|
||||
}
|
||||
unlockMutex(m_threadMutex);
|
||||
}
|
||||
|
||||
ArchThreadImpl *ArchMultithreadWindows::find(DWORD id)
|
||||
{
|
||||
ArchThreadImpl *impl = findNoRef(id);
|
||||
if (impl != NULL) {
|
||||
if (impl != nullptr) {
|
||||
refThread(impl);
|
||||
}
|
||||
return impl;
|
||||
@ -523,13 +507,13 @@ ArchThreadImpl *ArchMultithreadWindows::find(DWORD id)
|
||||
ArchThreadImpl *ArchMultithreadWindows::findNoRef(DWORD id)
|
||||
{
|
||||
ArchThreadImpl *impl = findNoRefOrCreate(id);
|
||||
if (impl == NULL) {
|
||||
if (impl == nullptr) {
|
||||
// create thread for calling thread which isn't in our list and
|
||||
// add it to the list. this won't normally happen but it can if
|
||||
// the system calls us under a new thread, like it does when we
|
||||
// run as a service.
|
||||
impl = new ArchThreadImpl;
|
||||
impl->m_thread = NULL;
|
||||
impl->m_thread = nullptr;
|
||||
impl->m_id = GetCurrentThreadId();
|
||||
insert(impl);
|
||||
}
|
||||
@ -544,15 +528,15 @@ ArchThreadImpl *ArchMultithreadWindows::findNoRefOrCreate(DWORD id)
|
||||
return *index;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ArchMultithreadWindows::insert(ArchThreadImpl *thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// thread shouldn't already be on the list
|
||||
assert(findNoRefOrCreate(thread->m_id) == NULL);
|
||||
assert(findNoRefOrCreate(thread->m_id) == nullptr);
|
||||
|
||||
// append to list
|
||||
m_threadList.push_back(thread);
|
||||
@ -570,14 +554,14 @@ void ArchMultithreadWindows::erase(ArchThreadImpl *thread)
|
||||
|
||||
void ArchMultithreadWindows::refThread(ArchThreadImpl *thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(findNoRefOrCreate(thread->m_id) != NULL);
|
||||
assert(thread != nullptr);
|
||||
assert(findNoRefOrCreate(thread->m_id) != nullptr);
|
||||
++thread->m_refCount;
|
||||
}
|
||||
|
||||
void ArchMultithreadWindows::testCancelThreadImpl(ArchThreadImpl *thread)
|
||||
{
|
||||
assert(thread != NULL);
|
||||
assert(thread != nullptr);
|
||||
|
||||
// poll cancel event. return if not set.
|
||||
const DWORD result = WaitForSingleObject(thread->m_cancel, 0);
|
||||
@ -586,11 +570,10 @@ void ArchMultithreadWindows::testCancelThreadImpl(ArchThreadImpl *thread)
|
||||
}
|
||||
|
||||
// update cancel state
|
||||
lockMutex(m_threadMutex);
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
bool cancel = !thread->m_cancelling;
|
||||
thread->m_cancelling = true;
|
||||
ResetEvent(thread->m_cancel);
|
||||
unlockMutex(m_threadMutex);
|
||||
|
||||
// unwind thread's stack if cancelling
|
||||
if (cancel) {
|
||||
@ -613,10 +596,11 @@ unsigned int __stdcall ArchMultithreadWindows::threadFunc(void *vrep)
|
||||
void ArchMultithreadWindows::doThreadFunc(ArchThread thread)
|
||||
{
|
||||
// wait for parent to initialize this object
|
||||
lockMutex(m_threadMutex);
|
||||
unlockMutex(m_threadMutex);
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
}
|
||||
|
||||
void *result = NULL;
|
||||
void *result = nullptr;
|
||||
try {
|
||||
// go
|
||||
result = (*thread->m_func)(thread->m_userData);
|
||||
@ -626,16 +610,20 @@ void ArchMultithreadWindows::doThreadFunc(ArchThread thread)
|
||||
// client called cancel()
|
||||
} catch (...) {
|
||||
// note -- don't catch (...) to avoid masking bugs
|
||||
SetEvent(thread->m_exit);
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
SetEvent(thread->m_exit);
|
||||
}
|
||||
closeThread(thread);
|
||||
throw;
|
||||
}
|
||||
|
||||
// thread has exited
|
||||
lockMutex(m_threadMutex);
|
||||
thread->m_result = result;
|
||||
unlockMutex(m_threadMutex);
|
||||
SetEvent(thread->m_exit);
|
||||
{
|
||||
std::scoped_lock lock{m_threadMutex};
|
||||
thread->m_result = result;
|
||||
SetEvent(thread->m_exit);
|
||||
}
|
||||
|
||||
// done with thread
|
||||
closeThread(thread);
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "arch/IArchMultithread.h"
|
||||
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
@ -41,7 +42,7 @@ class ArchMultithreadWindows : public IArchMultithread
|
||||
{
|
||||
public:
|
||||
ArchMultithreadWindows();
|
||||
virtual ~ArchMultithreadWindows();
|
||||
~ArchMultithreadWindows() override;
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
@ -61,29 +62,29 @@ public:
|
||||
//@}
|
||||
|
||||
// IArchMultithread overrides
|
||||
virtual ArchCond newCondVar();
|
||||
virtual void closeCondVar(ArchCond);
|
||||
virtual void signalCondVar(ArchCond);
|
||||
virtual void broadcastCondVar(ArchCond);
|
||||
virtual bool waitCondVar(ArchCond, ArchMutex, double timeout);
|
||||
virtual ArchMutex newMutex();
|
||||
virtual void closeMutex(ArchMutex);
|
||||
virtual void lockMutex(ArchMutex);
|
||||
virtual void unlockMutex(ArchMutex);
|
||||
virtual ArchThread newThread(ThreadFunc, void *);
|
||||
virtual ArchThread newCurrentThread();
|
||||
virtual ArchThread copyThread(ArchThread);
|
||||
virtual void closeThread(ArchThread);
|
||||
virtual void cancelThread(ArchThread);
|
||||
virtual void setPriorityOfThread(ArchThread, int n);
|
||||
virtual void testCancelThread();
|
||||
virtual bool wait(ArchThread, double timeout);
|
||||
virtual bool isSameThread(ArchThread, ArchThread);
|
||||
virtual bool isExitedThread(ArchThread);
|
||||
virtual void *getResultOfThread(ArchThread);
|
||||
virtual ThreadID getIDOfThread(ArchThread);
|
||||
virtual void setSignalHandler(ESignal, SignalFunc, void *);
|
||||
virtual void raiseSignal(ESignal);
|
||||
ArchCond newCondVar() override;
|
||||
void closeCondVar(ArchCond) override;
|
||||
void signalCondVar(ArchCond) override;
|
||||
void broadcastCondVar(ArchCond) override;
|
||||
bool waitCondVar(ArchCond, ArchMutex, double timeout) override;
|
||||
ArchMutex newMutex() override;
|
||||
void closeMutex(ArchMutex) override;
|
||||
void lockMutex(ArchMutex) override;
|
||||
void unlockMutex(ArchMutex) override;
|
||||
ArchThread newThread(ThreadFunc, void *) override;
|
||||
ArchThread newCurrentThread() override;
|
||||
ArchThread copyThread(ArchThread) override;
|
||||
void closeThread(ArchThread) override;
|
||||
void cancelThread(ArchThread) override;
|
||||
void setPriorityOfThread(ArchThread, int n) override;
|
||||
void testCancelThread() override;
|
||||
bool wait(ArchThread, double timeout) override;
|
||||
bool isSameThread(ArchThread, ArchThread) override;
|
||||
bool isExitedThread(ArchThread) override;
|
||||
void *getResultOfThread(ArchThread) override;
|
||||
ThreadID getIDOfThread(ArchThread) override;
|
||||
void setSignalHandler(ThreadSignal, SignalFunc, void *) override;
|
||||
void raiseSignal(ThreadSignal) override;
|
||||
|
||||
private:
|
||||
ArchThreadImpl *find(DWORD id);
|
||||
@ -103,11 +104,11 @@ private:
|
||||
|
||||
static ArchMultithreadWindows *s_instance;
|
||||
|
||||
ArchMutex m_threadMutex;
|
||||
std::mutex m_threadMutex;
|
||||
|
||||
ThreadList m_threadList;
|
||||
ArchThread m_mainThread;
|
||||
|
||||
SignalFunc m_signalFunc[kNUM_SIGNALS];
|
||||
void *m_signalUserData[kNUM_SIGNALS];
|
||||
SignalFunc m_signalFunc[static_cast<int>(ThreadSignal::MaxSignals)];
|
||||
void *m_signalUserData[static_cast<int>(ThreadSignal::MaxSignals)];
|
||||
};
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "arch/win32/ArchNetworkWinsock.h"
|
||||
#include "arch/Arch.h"
|
||||
#include "arch/IArchMultithread.h"
|
||||
#include "arch/XArch.h"
|
||||
#include "arch/win32/ArchMultithreadWindows.h"
|
||||
#include "arch/win32/XArchWindows.h"
|
||||
|
||||
@ -58,7 +59,7 @@ static int(PASCAL FAR *WSAEnumNetworkEvents_winsock)(SOCKET, WSAEVENT, LPWSANETW
|
||||
|
||||
#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name)
|
||||
|
||||
static HMODULE s_networkModule = NULL;
|
||||
static HMODULE s_networkModule = nullptr;
|
||||
|
||||
static FARPROC netGetProcAddress(HMODULE module, LPCSTR name)
|
||||
{
|
||||
@ -81,21 +82,14 @@ ArchNetAddressImpl *ArchNetAddressImpl::alloc(size_t size)
|
||||
// ArchNetworkWinsock
|
||||
//
|
||||
|
||||
ArchNetworkWinsock::ArchNetworkWinsock() : m_mutex(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
ArchNetworkWinsock::~ArchNetworkWinsock()
|
||||
{
|
||||
if (s_networkModule != NULL) {
|
||||
if (s_networkModule != nullptr) {
|
||||
WSACleanup_winsock();
|
||||
::FreeLibrary(s_networkModule);
|
||||
|
||||
WSACleanup_winsock = NULL;
|
||||
s_networkModule = NULL;
|
||||
}
|
||||
if (m_mutex != NULL) {
|
||||
ARCH->closeMutex(m_mutex);
|
||||
WSACleanup_winsock = nullptr;
|
||||
s_networkModule = nullptr;
|
||||
}
|
||||
|
||||
EventList::iterator it;
|
||||
@ -108,14 +102,13 @@ void ArchNetworkWinsock::init()
|
||||
{
|
||||
static const char *s_library[] = {"ws2_32.dll"};
|
||||
|
||||
assert(WSACleanup_winsock == NULL);
|
||||
assert(s_networkModule == NULL);
|
||||
assert(WSACleanup_winsock == nullptr);
|
||||
assert(s_networkModule == nullptr);
|
||||
|
||||
// try each winsock library
|
||||
for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) {
|
||||
try {
|
||||
initModule((HMODULE)::LoadLibrary(s_library[i]));
|
||||
m_mutex = ARCH->newMutex();
|
||||
return;
|
||||
} catch (XArchNetwork &) {
|
||||
// ignore
|
||||
@ -128,7 +121,7 @@ void ArchNetworkWinsock::init()
|
||||
|
||||
void ArchNetworkWinsock::initModule(HMODULE module)
|
||||
{
|
||||
if (module == NULL) {
|
||||
if (module == nullptr) {
|
||||
throw XArchNetworkSupport("");
|
||||
}
|
||||
|
||||
@ -141,7 +134,7 @@ void ArchNetworkWinsock::initModule(HMODULE module)
|
||||
WSADATA data;
|
||||
int err = startup(version, &data);
|
||||
if (data.wVersion != version) {
|
||||
throw XArchNetworkSupport(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkSupport(winsockErrorToString(err));
|
||||
}
|
||||
if (err != 0) {
|
||||
// some other initialization error
|
||||
@ -199,10 +192,10 @@ void ArchNetworkWinsock::initModule(HMODULE module)
|
||||
s_networkModule = module;
|
||||
}
|
||||
|
||||
ArchSocket ArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type)
|
||||
ArchSocket ArchNetworkWinsock::newSocket(AddressFamily family, SocketType type)
|
||||
{
|
||||
// create socket
|
||||
SOCKET fd = socket_winsock(s_family[family], s_type[type], 0);
|
||||
SOCKET fd = socket_winsock(s_family[static_cast<int>(family)], s_type[static_cast<int>(type)], 0);
|
||||
if (fd == INVALID_SOCKET) {
|
||||
throwError(getsockerror_winsock());
|
||||
}
|
||||
@ -229,32 +222,34 @@ ArchSocket ArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type
|
||||
|
||||
ArchSocket ArchNetworkWinsock::copySocket(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// ref the socket and return it
|
||||
ARCH->lockMutex(m_mutex);
|
||||
std::scoped_lock lock{m_mutex};
|
||||
++s->m_refCount;
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
return s;
|
||||
}
|
||||
|
||||
void ArchNetworkWinsock::closeSocket(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// unref the socket and note if it should be released
|
||||
ARCH->lockMutex(m_mutex);
|
||||
const bool doClose = (--s->m_refCount == 0);
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
bool doClose = false;
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
doClose = (--s->m_refCount == 0);
|
||||
}
|
||||
|
||||
// close the socket if necessary
|
||||
if (doClose) {
|
||||
if (close_winsock(s->m_socket) == SOCKET_ERROR) {
|
||||
// close failed. restore the last ref and throw.
|
||||
int err = getsockerror_winsock();
|
||||
ARCH->lockMutex(m_mutex);
|
||||
++s->m_refCount;
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
++s->m_refCount;
|
||||
}
|
||||
throwError(err);
|
||||
}
|
||||
WSACloseEvent_winsock(s->m_event);
|
||||
@ -264,7 +259,7 @@ void ArchNetworkWinsock::closeSocket(ArchSocket s)
|
||||
|
||||
void ArchNetworkWinsock::closeSocketForRead(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) {
|
||||
if (getsockerror_winsock() != WSAENOTCONN) {
|
||||
@ -275,7 +270,7 @@ void ArchNetworkWinsock::closeSocketForRead(ArchSocket s)
|
||||
|
||||
void ArchNetworkWinsock::closeSocketForWrite(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) {
|
||||
if (getsockerror_winsock() != WSAENOTCONN) {
|
||||
@ -286,8 +281,8 @@ void ArchNetworkWinsock::closeSocketForWrite(ArchSocket s)
|
||||
|
||||
void ArchNetworkWinsock::bindSocket(ArchSocket s, ArchNetAddress addr)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(addr != NULL);
|
||||
assert(s != nullptr);
|
||||
assert(addr != nullptr);
|
||||
|
||||
if (bind_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == SOCKET_ERROR) {
|
||||
throwError(getsockerror_winsock());
|
||||
@ -296,7 +291,7 @@ void ArchNetworkWinsock::bindSocket(ArchSocket s, ArchNetAddress addr)
|
||||
|
||||
void ArchNetworkWinsock::listenOnSocket(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// hardcoding backlog
|
||||
if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) {
|
||||
@ -306,7 +301,7 @@ void ArchNetworkWinsock::listenOnSocket(ArchSocket s)
|
||||
|
||||
ArchSocket ArchNetworkWinsock::acceptSocket(ArchSocket s, ArchNetAddress *const addr)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// create new socket and temporary address
|
||||
ArchSocketImpl *socket = new ArchSocketImpl;
|
||||
@ -319,10 +314,10 @@ ArchSocket ArchNetworkWinsock::acceptSocket(ArchSocket s, ArchNetAddress *const
|
||||
delete socket;
|
||||
free(tmp);
|
||||
if (addr) {
|
||||
*addr = NULL;
|
||||
*addr = nullptr;
|
||||
}
|
||||
if (err == WSAEWOULDBLOCK) {
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
throwError(err);
|
||||
}
|
||||
@ -334,7 +329,7 @@ ArchSocket ArchNetworkWinsock::acceptSocket(ArchSocket s, ArchNetAddress *const
|
||||
delete socket;
|
||||
free(tmp);
|
||||
if (addr) {
|
||||
*addr = NULL;
|
||||
*addr = nullptr;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
@ -346,7 +341,7 @@ ArchSocket ArchNetworkWinsock::acceptSocket(ArchSocket s, ArchNetAddress *const
|
||||
socket->m_pollWrite = true;
|
||||
|
||||
// copy address if requested
|
||||
if (addr != NULL) {
|
||||
if (addr != nullptr) {
|
||||
*addr = ARCH->copyAddr(tmp);
|
||||
}
|
||||
|
||||
@ -356,8 +351,8 @@ ArchSocket ArchNetworkWinsock::acceptSocket(ArchSocket s, ArchNetAddress *const
|
||||
|
||||
bool ArchNetworkWinsock::connectSocket(ArchSocket s, ArchNetAddress addr)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(addr != NULL);
|
||||
assert(s != nullptr);
|
||||
assert(addr != nullptr);
|
||||
|
||||
if (connect_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == SOCKET_ERROR) {
|
||||
if (getsockerror_winsock() == WSAEISCONN) {
|
||||
@ -384,17 +379,17 @@ int ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
|
||||
pe[i].m_revents = 0;
|
||||
|
||||
// set invalid flag if socket is bogus then go to next socket
|
||||
if (pe[i].m_socket == NULL) {
|
||||
pe[i].m_revents |= kPOLLNVAL;
|
||||
if (pe[i].m_socket == nullptr) {
|
||||
pe[i].m_revents |= PollEventMask::Invalid;
|
||||
continue;
|
||||
}
|
||||
|
||||
// select desired events
|
||||
long socketEvents = 0;
|
||||
if ((pe[i].m_events & kPOLLIN) != 0) {
|
||||
if ((pe[i].m_events & PollEventMask::In) != 0) {
|
||||
socketEvents |= FD_READ | FD_ACCEPT | FD_CLOSE;
|
||||
}
|
||||
if ((pe[i].m_events & kPOLLOUT) != 0) {
|
||||
if ((pe[i].m_events & PollEventMask::Out) != 0) {
|
||||
socketEvents |= FD_WRITE | FD_CONNECT | FD_CLOSE;
|
||||
|
||||
// if m_pollWrite is false then we assume the socket is
|
||||
@ -402,7 +397,7 @@ int ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
|
||||
// when the state changes from unwritable.
|
||||
if (!pe[i].m_socket->m_pollWrite) {
|
||||
canWrite = true;
|
||||
pe[i].m_revents |= kPOLLOUT;
|
||||
pe[i].m_revents |= PollEventMask::Out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -428,7 +423,7 @@ int ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
|
||||
ArchThread thread = mt->newCurrentThread();
|
||||
WSAEVENT *unblockEvent = (WSAEVENT *)mt->getNetworkDataForThread(thread);
|
||||
ARCH->closeThread(thread);
|
||||
if (unblockEvent == NULL) {
|
||||
if (unblockEvent == nullptr) {
|
||||
unblockEvent = new WSAEVENT;
|
||||
m_unblockEvents.push_back(unblockEvent);
|
||||
*unblockEvent = WSACreateEvent_winsock();
|
||||
@ -467,7 +462,7 @@ int ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
|
||||
}
|
||||
for (i = 0, n = 0; i < num; ++i) {
|
||||
// skip events we didn't check
|
||||
if (pe[i].m_socket == NULL || (pe[i].m_events & (kPOLLIN | kPOLLOUT)) == 0) {
|
||||
if (pe[i].m_socket == nullptr || (pe[i].m_events & (PollEventMask::In | PollEventMask::Out)) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -477,13 +472,13 @@ int ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
|
||||
continue;
|
||||
}
|
||||
if ((info.lNetworkEvents & FD_READ) != 0) {
|
||||
pe[i].m_revents |= kPOLLIN;
|
||||
pe[i].m_revents |= PollEventMask::In;
|
||||
}
|
||||
if ((info.lNetworkEvents & FD_ACCEPT) != 0) {
|
||||
pe[i].m_revents |= kPOLLIN;
|
||||
pe[i].m_revents |= PollEventMask::In;
|
||||
}
|
||||
if ((info.lNetworkEvents & FD_WRITE) != 0) {
|
||||
pe[i].m_revents |= kPOLLOUT;
|
||||
pe[i].m_revents |= PollEventMask::Out;
|
||||
|
||||
// socket is now writable so don't bothing polling for
|
||||
// writable until it becomes unwritable.
|
||||
@ -491,21 +486,21 @@ int ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
|
||||
}
|
||||
if ((info.lNetworkEvents & FD_CONNECT) != 0) {
|
||||
if (info.iErrorCode[FD_CONNECT_BIT] != 0) {
|
||||
pe[i].m_revents |= kPOLLERR;
|
||||
pe[i].m_revents |= PollEventMask::Error;
|
||||
} else {
|
||||
pe[i].m_revents |= kPOLLOUT;
|
||||
pe[i].m_revents |= PollEventMask::Out;
|
||||
pe[i].m_socket->m_pollWrite = false;
|
||||
}
|
||||
}
|
||||
if ((info.lNetworkEvents & FD_CLOSE) != 0) {
|
||||
if (info.iErrorCode[FD_CLOSE_BIT] != 0) {
|
||||
pe[i].m_revents |= kPOLLERR;
|
||||
pe[i].m_revents |= PollEventMask::Error;
|
||||
} else {
|
||||
if ((pe[i].m_events & kPOLLIN) != 0) {
|
||||
pe[i].m_revents |= kPOLLIN;
|
||||
if ((pe[i].m_events & PollEventMask::In) != 0) {
|
||||
pe[i].m_revents |= PollEventMask::In;
|
||||
}
|
||||
if ((pe[i].m_events & kPOLLOUT) != 0) {
|
||||
pe[i].m_revents |= kPOLLOUT;
|
||||
if ((pe[i].m_events & PollEventMask::Out) != 0) {
|
||||
pe[i].m_revents |= PollEventMask::Out;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -522,14 +517,14 @@ void ArchNetworkWinsock::unblockPollSocket(ArchThread thread)
|
||||
// set the unblock event
|
||||
ArchMultithreadWindows *mt = ArchMultithreadWindows::getInstance();
|
||||
WSAEVENT *unblockEvent = (WSAEVENT *)mt->getNetworkDataForThread(thread);
|
||||
if (unblockEvent != NULL) {
|
||||
if (unblockEvent != nullptr) {
|
||||
WSASetEvent_winsock(*unblockEvent);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ArchNetworkWinsock::readSocket(ArchSocket s, void *buf, size_t len)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
int n = recv_winsock(s->m_socket, buf, (int)len, 0);
|
||||
if (n == SOCKET_ERROR) {
|
||||
@ -544,7 +539,7 @@ size_t ArchNetworkWinsock::readSocket(ArchSocket s, void *buf, size_t len)
|
||||
|
||||
size_t ArchNetworkWinsock::writeSocket(ArchSocket s, const void *buf, size_t len)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
int n = send_winsock(s->m_socket, buf, (int)len, 0);
|
||||
if (n == SOCKET_ERROR) {
|
||||
@ -563,7 +558,7 @@ size_t ArchNetworkWinsock::writeSocket(ArchSocket s, const void *buf, size_t len
|
||||
|
||||
void ArchNetworkWinsock::throwErrorOnSocket(ArchSocket s)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// get the error from the socket layer
|
||||
int err = 0;
|
||||
@ -590,7 +585,7 @@ void ArchNetworkWinsock::setBlockingOnSocket(SOCKET s, bool blocking)
|
||||
|
||||
bool ArchNetworkWinsock::setNoDelayOnSocket(ArchSocket s, bool noDelay)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// get old state
|
||||
BOOL oflag;
|
||||
@ -611,7 +606,7 @@ bool ArchNetworkWinsock::setNoDelayOnSocket(ArchSocket s, bool noDelay)
|
||||
|
||||
bool ArchNetworkWinsock::setReuseAddrOnSocket(ArchSocket s, bool reuse)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(s != nullptr);
|
||||
|
||||
// get old state
|
||||
BOOL oflag;
|
||||
@ -641,11 +636,11 @@ std::string ArchNetworkWinsock::getHostName()
|
||||
return name;
|
||||
}
|
||||
|
||||
ArchNetAddress ArchNetworkWinsock::newAnyAddr(EAddressFamily family)
|
||||
ArchNetAddress ArchNetworkWinsock::newAnyAddr(AddressFamily family)
|
||||
{
|
||||
ArchNetAddressImpl *addr = NULL;
|
||||
ArchNetAddressImpl *addr = nullptr;
|
||||
switch (family) {
|
||||
case kINET: {
|
||||
case AddressFamily::INet: {
|
||||
addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
|
||||
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
ipAddr->sin_family = AF_INET;
|
||||
@ -654,7 +649,7 @@ ArchNetAddress ArchNetworkWinsock::newAnyAddr(EAddressFamily family)
|
||||
break;
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
case AddressFamily::INet6: {
|
||||
addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6));
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
ipAddr->sin6_family = AF_INET6;
|
||||
@ -671,7 +666,7 @@ ArchNetAddress ArchNetworkWinsock::newAnyAddr(EAddressFamily family)
|
||||
|
||||
ArchNetAddress ArchNetworkWinsock::copyAddr(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
ArchNetAddressImpl *copy = ArchNetAddressImpl::alloc(addr->m_len);
|
||||
memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len);
|
||||
@ -689,9 +684,8 @@ std::vector<ArchNetAddress> ArchNetworkWinsock::nameToAddr(const std::string &na
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
int ret = -1;
|
||||
|
||||
ARCH->lockMutex(m_mutex);
|
||||
if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &pResult)) != 0) {
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
std::scoped_lock lock{m_mutex};
|
||||
if ((ret = getaddrinfo(name.c_str(), nullptr, &hints, &pResult)) != 0) {
|
||||
throwNameError(ret);
|
||||
}
|
||||
|
||||
@ -707,27 +701,26 @@ std::vector<ArchNetAddress> ArchNetworkWinsock::nameToAddr(const std::string &na
|
||||
}
|
||||
|
||||
freeaddrinfo(pResult);
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
return addresses;
|
||||
}
|
||||
|
||||
void ArchNetworkWinsock::closeAddr(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
free(addr);
|
||||
}
|
||||
|
||||
std::string ArchNetworkWinsock::addrToName(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
char host[1024];
|
||||
char service[20];
|
||||
int ret =
|
||||
getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host, sizeof(host), service, sizeof(service), 0);
|
||||
|
||||
if (ret != NULL) {
|
||||
if (ret != 0) {
|
||||
throwNameError(ret);
|
||||
}
|
||||
|
||||
@ -738,15 +731,15 @@ std::string ArchNetworkWinsock::addrToName(ArchNetAddress addr)
|
||||
|
||||
std::string ArchNetworkWinsock::addrToString(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (getAddrFamily(addr)) {
|
||||
case kINET: {
|
||||
case AddressFamily::INet: {
|
||||
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
return inet_ntoa_winsock(ipAddr->sin_addr);
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
case AddressFamily::INet6: {
|
||||
char strAddr[INET6_ADDRSTRLEN];
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
|
||||
@ -759,34 +752,34 @@ std::string ArchNetworkWinsock::addrToString(ArchNetAddress addr)
|
||||
}
|
||||
}
|
||||
|
||||
IArchNetwork::EAddressFamily ArchNetworkWinsock::getAddrFamily(ArchNetAddress addr)
|
||||
IArchNetwork::AddressFamily ArchNetworkWinsock::getAddrFamily(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (addr->m_addr.ss_family) {
|
||||
case AF_INET:
|
||||
return kINET;
|
||||
return AddressFamily::INet;
|
||||
|
||||
case AF_INET6:
|
||||
return kINET6;
|
||||
return AddressFamily::INet6;
|
||||
|
||||
default:
|
||||
return kUNKNOWN;
|
||||
return AddressFamily::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void ArchNetworkWinsock::setAddrPort(ArchNetAddress addr, int port)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (getAddrFamily(addr)) {
|
||||
case kINET: {
|
||||
case AddressFamily::INet: {
|
||||
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
ipAddr->sin_port = htons_winsock(static_cast<u_short>(port));
|
||||
break;
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
case AddressFamily::INet6: {
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
ipAddr->sin6_port = htons_winsock(static_cast<u_short>(port));
|
||||
break;
|
||||
@ -800,15 +793,15 @@ void ArchNetworkWinsock::setAddrPort(ArchNetAddress addr, int port)
|
||||
|
||||
int ArchNetworkWinsock::getAddrPort(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (getAddrFamily(addr)) {
|
||||
case kINET: {
|
||||
case AddressFamily::INet: {
|
||||
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
return ntohs_winsock(ipAddr->sin_port);
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
case AddressFamily::INet6: {
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
return ntohs_winsock(ipAddr->sin6_port);
|
||||
}
|
||||
@ -821,15 +814,15 @@ int ArchNetworkWinsock::getAddrPort(ArchNetAddress addr)
|
||||
|
||||
bool ArchNetworkWinsock::isAnyAddr(ArchNetAddress addr)
|
||||
{
|
||||
assert(addr != NULL);
|
||||
assert(addr != nullptr);
|
||||
|
||||
switch (getAddrFamily(addr)) {
|
||||
case kINET: {
|
||||
case AddressFamily::INet: {
|
||||
struct sockaddr_in *ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
|
||||
return (addr->m_len == sizeof(struct sockaddr_in) && ipAddr->sin_addr.s_addr == INADDR_ANY);
|
||||
}
|
||||
|
||||
case kINET6: {
|
||||
case AddressFamily::INet6: {
|
||||
struct sockaddr_in6 *ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
|
||||
return (
|
||||
addr->m_len == sizeof(struct sockaddr_in) && memcmp(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0
|
||||
@ -847,16 +840,16 @@ bool ArchNetworkWinsock::isEqualAddr(ArchNetAddress a, ArchNetAddress b)
|
||||
return (a == b || (a->m_len == b->m_len && memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0));
|
||||
}
|
||||
|
||||
void ArchNetworkWinsock::throwError(int err)
|
||||
[[noreturn]] void ArchNetworkWinsock::throwError(int err) const
|
||||
{
|
||||
switch (err) {
|
||||
case WSAEACCES:
|
||||
throw XArchNetworkAccess(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkAccess(winsockErrorToString(err));
|
||||
|
||||
case WSAEMFILE:
|
||||
case WSAENOBUFS:
|
||||
case WSAENETDOWN:
|
||||
throw XArchNetworkResource(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkResource(winsockErrorToString(err));
|
||||
|
||||
case WSAEPROTOTYPE:
|
||||
case WSAEPROTONOSUPPORT:
|
||||
@ -870,69 +863,69 @@ void ArchNetworkWinsock::throwError(int err)
|
||||
case WSANOTINITIALISED:
|
||||
case WSAVERNOTSUPPORTED:
|
||||
case WSASYSNOTREADY:
|
||||
throw XArchNetworkSupport(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkSupport(winsockErrorToString(err));
|
||||
|
||||
case WSAEADDRNOTAVAIL:
|
||||
throw XArchNetworkNoAddress(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNoAddress(winsockErrorToString(err));
|
||||
|
||||
case WSAEADDRINUSE:
|
||||
throw XArchNetworkAddressInUse(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkAddressInUse(winsockErrorToString(err));
|
||||
|
||||
case WSAEHOSTUNREACH:
|
||||
case WSAENETUNREACH:
|
||||
throw XArchNetworkNoRoute(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNoRoute(winsockErrorToString(err));
|
||||
|
||||
case WSAENOTCONN:
|
||||
throw XArchNetworkNotConnected(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNotConnected(winsockErrorToString(err));
|
||||
|
||||
case WSAEDISCON:
|
||||
throw XArchNetworkShutdown(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkShutdown(winsockErrorToString(err));
|
||||
|
||||
case WSAENETRESET:
|
||||
case WSAECONNABORTED:
|
||||
case WSAECONNRESET:
|
||||
throw XArchNetworkDisconnected(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkDisconnected(winsockErrorToString(err));
|
||||
|
||||
case WSAECONNREFUSED:
|
||||
throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkConnectionRefused(winsockErrorToString(err));
|
||||
|
||||
case WSAEHOSTDOWN:
|
||||
case WSAETIMEDOUT:
|
||||
throw XArchNetworkTimedOut(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkTimedOut(winsockErrorToString(err));
|
||||
|
||||
case WSAHOST_NOT_FOUND:
|
||||
throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNameUnknown(winsockErrorToString(err));
|
||||
|
||||
case WSANO_DATA:
|
||||
throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNameNoAddress(winsockErrorToString(err));
|
||||
|
||||
case WSANO_RECOVERY:
|
||||
throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNameFailure(winsockErrorToString(err));
|
||||
|
||||
case WSATRY_AGAIN:
|
||||
throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNameUnavailable(winsockErrorToString(err));
|
||||
|
||||
default:
|
||||
throw XArchNetwork(new XArchEvalWinsock(err));
|
||||
throw XArchNetwork(winsockErrorToString(err));
|
||||
}
|
||||
}
|
||||
|
||||
void ArchNetworkWinsock::throwNameError(int err)
|
||||
[[noreturn]] void ArchNetworkWinsock::throwNameError(int err) const
|
||||
{
|
||||
switch (err) {
|
||||
case WSAHOST_NOT_FOUND:
|
||||
throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNameUnknown(winsockErrorToString(err));
|
||||
|
||||
case WSANO_DATA:
|
||||
throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNameNoAddress(winsockErrorToString(err));
|
||||
|
||||
case WSANO_RECOVERY:
|
||||
throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNameFailure(winsockErrorToString(err));
|
||||
|
||||
case WSATRY_AGAIN:
|
||||
throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkNameUnavailable(winsockErrorToString(err));
|
||||
|
||||
default:
|
||||
throw XArchNetworkName(new XArchEvalWinsock(err));
|
||||
throw XArchNetworkName(winsockErrorToString(err));
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
|
||||
@ -51,52 +52,51 @@ public:
|
||||
class ArchNetworkWinsock : public IArchNetwork
|
||||
{
|
||||
public:
|
||||
ArchNetworkWinsock();
|
||||
virtual ~ArchNetworkWinsock();
|
||||
ArchNetworkWinsock() = default;
|
||||
~ArchNetworkWinsock() override;
|
||||
|
||||
virtual void init();
|
||||
void init() override;
|
||||
|
||||
// IArchNetwork overrides
|
||||
virtual ArchSocket newSocket(EAddressFamily, ESocketType);
|
||||
virtual ArchSocket copySocket(ArchSocket s);
|
||||
virtual void closeSocket(ArchSocket s);
|
||||
virtual void closeSocketForRead(ArchSocket s);
|
||||
virtual void closeSocketForWrite(ArchSocket s);
|
||||
virtual void bindSocket(ArchSocket s, ArchNetAddress addr);
|
||||
virtual void listenOnSocket(ArchSocket s);
|
||||
virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress *addr);
|
||||
virtual bool connectSocket(ArchSocket s, ArchNetAddress name);
|
||||
virtual int pollSocket(PollEntry[], int num, double timeout);
|
||||
virtual void unblockPollSocket(ArchThread thread);
|
||||
virtual size_t readSocket(ArchSocket s, void *buf, size_t len);
|
||||
virtual size_t writeSocket(ArchSocket s, const void *buf, size_t len);
|
||||
virtual void throwErrorOnSocket(ArchSocket);
|
||||
virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay);
|
||||
virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse);
|
||||
virtual std::string getHostName();
|
||||
virtual ArchNetAddress newAnyAddr(EAddressFamily);
|
||||
virtual ArchNetAddress copyAddr(ArchNetAddress);
|
||||
virtual std::vector<ArchNetAddress> nameToAddr(const std::string &);
|
||||
virtual void closeAddr(ArchNetAddress);
|
||||
virtual std::string addrToName(ArchNetAddress);
|
||||
virtual std::string addrToString(ArchNetAddress);
|
||||
virtual EAddressFamily getAddrFamily(ArchNetAddress);
|
||||
virtual void setAddrPort(ArchNetAddress, int port);
|
||||
virtual int getAddrPort(ArchNetAddress);
|
||||
virtual bool isAnyAddr(ArchNetAddress);
|
||||
virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress);
|
||||
ArchSocket newSocket(AddressFamily, SocketType) override;
|
||||
ArchSocket copySocket(ArchSocket s) override;
|
||||
void closeSocket(ArchSocket s) override;
|
||||
void closeSocketForRead(ArchSocket s) override;
|
||||
void closeSocketForWrite(ArchSocket s) override;
|
||||
void bindSocket(ArchSocket s, ArchNetAddress addr) override;
|
||||
void listenOnSocket(ArchSocket s) override;
|
||||
ArchSocket acceptSocket(ArchSocket s, ArchNetAddress *addr) override;
|
||||
bool connectSocket(ArchSocket s, ArchNetAddress name) override;
|
||||
int pollSocket(PollEntry[], int num, double timeout) override;
|
||||
void unblockPollSocket(ArchThread thread) override;
|
||||
size_t readSocket(ArchSocket s, void *buf, size_t len) override;
|
||||
size_t writeSocket(ArchSocket s, const void *buf, size_t len) override;
|
||||
void throwErrorOnSocket(ArchSocket) override;
|
||||
bool setNoDelayOnSocket(ArchSocket, bool noDelay) override;
|
||||
bool setReuseAddrOnSocket(ArchSocket, bool reuse) override;
|
||||
std::string getHostName() override;
|
||||
ArchNetAddress newAnyAddr(AddressFamily) override;
|
||||
ArchNetAddress copyAddr(ArchNetAddress) override;
|
||||
std::vector<ArchNetAddress> nameToAddr(const std::string &) override;
|
||||
void closeAddr(ArchNetAddress) override;
|
||||
std::string addrToName(ArchNetAddress) override;
|
||||
std::string addrToString(ArchNetAddress) override;
|
||||
AddressFamily getAddrFamily(ArchNetAddress) override;
|
||||
void setAddrPort(ArchNetAddress, int port) override;
|
||||
int getAddrPort(ArchNetAddress) override;
|
||||
bool isAnyAddr(ArchNetAddress) override;
|
||||
bool isEqualAddr(ArchNetAddress, ArchNetAddress) override;
|
||||
|
||||
private:
|
||||
void initModule(HMODULE);
|
||||
|
||||
void setBlockingOnSocket(SOCKET, bool blocking);
|
||||
|
||||
void throwError(int);
|
||||
void throwNameError(int);
|
||||
[[noreturn]] void throwError(int) const override;
|
||||
[[noreturn]] void throwNameError(int) const override;
|
||||
|
||||
private:
|
||||
using EventList = std::list<WSAEVENT>;
|
||||
|
||||
ArchMutex m_mutex;
|
||||
std::mutex m_mutex;
|
||||
EventList m_unblockEvents;
|
||||
};
|
||||
|
||||
@ -1,48 +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/win32/ArchSleepWindows.h"
|
||||
#include "arch/Arch.h"
|
||||
#include "arch/win32/ArchMultithreadWindows.h"
|
||||
|
||||
//
|
||||
// ArchSleepWindows
|
||||
//
|
||||
|
||||
ArchSleepWindows::ArchSleepWindows()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
ArchSleepWindows::~ArchSleepWindows()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void ArchSleepWindows::sleep(double timeout)
|
||||
{
|
||||
ARCH->testCancelThread();
|
||||
if (timeout < 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get the cancel event from the current thread. this only
|
||||
// works if we're using the windows multithread object but
|
||||
// this is windows so that's pretty certain; we'll get a
|
||||
// link error if we're not, though.
|
||||
ArchMultithreadWindows *mt = ArchMultithreadWindows::getInstance();
|
||||
if (mt != NULL) {
|
||||
HANDLE cancelEvent = mt->getCancelEventForCurrentThread();
|
||||
WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout));
|
||||
if (timeout == 0.0) {
|
||||
Sleep(0);
|
||||
}
|
||||
} else {
|
||||
Sleep((DWORD)(1000.0 * timeout));
|
||||
}
|
||||
ARCH->testCancelThread();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user