diff --git a/.env.example b/.env.example index ffce9a2df..372078633 100644 --- a/.env.example +++ b/.env.example @@ -6,9 +6,6 @@ # App # -# Serial key value to pre-fill the activation dialog -# DESKFLOW_TEST_SERIAL_KEY="DEADBEEF" - # Shows the test menu in the GUI (on by default in debug mode) # DESKFLOW_TEST_MENU=true @@ -24,12 +21,6 @@ # Reset all settings and delete all data on startup # DESKFLOW_RESET_ALL=true -# Enable the activation dialog (used for testing) -# DESKFLOW_ENABLE_ACTIVATION=true - -# Show licensed product menu items, etc (used for testing) -# DESKFLOW_LICENSED_PRODUCT=true - # # Build # diff --git a/.github/actions/dist-upload/action.yml b/.github/actions/dist-upload/action.yml deleted file mode 100644 index 3365d3569..000000000 --- a/.github/actions/dist-upload/action.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: "Distribute upload" -description: "Uploads the package from the dist dir to GitHub artifacts or Google Drive" -inputs: - use-github: - description: "Whether to upload to GitHub artifacts" - required: true - - use_gdrive: - description: "Whether to upload to Google Drive" - required: true - - github-target-filename: - description: "Filename to upload (GitHub artifacts)" - required: true - - gdrive-target-base-dir: - description: "Base directory to upload (Google Drive)" - required: true - - gdrive-secret-key: - description: "Google Drive secret key" - required: true - - gdrive-parent-folder-id: - description: "Google Drive parent folder ID" - required: true - - package-version: - description: "Package version appended to the Google Drive dir" - required: true - -runs: - using: "composite" - - steps: - - if: ${{ inputs.use_gdrive == inputs.use-github }} - run: | - echo "Either 'use-github' or 'use_gdrive' must be true (and not both)" - exit 1 - shell: bash - - - if: ${{ inputs.use_gdrive == 'true' && !inputs.package-version }} - run: | - echo "Input 'package-version' is required when uploading to Google Drive" - exit 1 - shell: bash - - - if: ${{ inputs.use_gdrive == 'true' }} - run: | - SHORT_VERSION=$(echo "${{ inputs.package-version }}" | cut -d'-' -f1 | cut -d'+' -f1) - echo "SHORT_VERSION=$SHORT_VERSION" >> $GITHUB_ENV - shell: bash - - - name: Upload to GitHub - if: ${{ inputs.use-github == 'true' }} - uses: actions/upload-artifact@v4 - with: - name: ${{ inputs.github-target-filename }} - path: ./dist - retention-days: 3 - - - name: Upload to Google Drive - if: ${{ inputs.use_gdrive == 'true' }} - uses: symless/gdrive-upload@target-glob - with: - credentials: ${{ inputs.gdrive-secret-key }} - target: "./dist/*" - parent_folder_id: ${{ inputs.gdrive-parent-folder-id }} - child_folder: Packages/${{ inputs.gdrive-target-base-dir }}/${{ env.SHORT_VERSION }}/${{ inputs.package-version }} diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml index 60086df21..b1a4546fc 100644 --- a/.github/actions/run-tests/action.yml +++ b/.github/actions/run-tests/action.yml @@ -4,8 +4,13 @@ description: "Runs both unit tests and integration tests and appends to a PR com inputs: job: description: "The job name to append to the PR comment" + required: true default: "unknown" + bin-dir: + description: "The directory containing the test binaries" + default: "build/bin" + runs: using: "composite" @@ -15,7 +20,7 @@ runs: env: QT_QPA_PLATFORM: offscreen run: | - ./build/bin/unittests + ./${{ inputs.bin-dir }}/unittests result=$? if [ $result -ne 0 ]; then @@ -29,7 +34,7 @@ runs: env: QT_QPA_PLATFORM: offscreen run: | - ./build/bin/integtests + ./${{ inputs.bin-dir }}/integtests result=$? if [ $result -ne 0 ]; then diff --git a/.github/workflows/ci-linux.json b/.github/workflows/ci-linux.json index a2ae4f938..99b814c8a 100644 --- a/.github/workflows/ci-linux.json +++ b/.github/workflows/ci-linux.json @@ -9,7 +9,7 @@ { "name": "debian-12-arm64", "container": "deskflow/deskflow:debian-12-arm64", - "runs-on": "ubuntu-24.04-2-core-arm64", + "runs-on": "ubuntu-24.04-4-core-arm64", "extra-packages": true }, { @@ -33,7 +33,7 @@ { "name": "fedora-40-arm64", "container": "deskflow/deskflow:fedora-40-arm64", - "runs-on": "ubuntu-24.04-2-core-arm64" + "runs-on": "ubuntu-24.04-4-core-arm64" }, { "name": "fedora-40-amd64", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e6f575cf..bb97ef960 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,20 +19,42 @@ on: - cron: "0 5 * * *" # 5am UTC env: - GIT_SHA: ${{ github.sha }} - DESKFLOW_PRODUCT_NAME: ${{ vars.DESKFLOW_PRODUCT_NAME }} - DESKFLOW_PACKAGE_PREFIX: ${{ vars.DESKFLOW_PACKAGE_PREFIX || 'deskflow' }} - PACKAGE_BUILD: ${{ !github.event.pull_request.draft }} - PACKAGE_UPLOAD: ${{ !github.event.pull_request.draft }} + GIT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + PACKAGE_PREFIX: "deskflow" + PACKAGE_PATH: ./dist jobs: + pr-comment-flags: + runs-on: ubuntu-latest + if: ${{ github.event_name == 'pull_request' }} + + outputs: + no-sonar: ${{ steps.check.outputs.no-sonar }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Check PR comment for flags + id: check + run: | + pr_body="${{ github.event.pull_request.body }}" + no_sonar="{no-sonar}" + + if echo $pr_body | grep -q "$no_sonar"; then + echo "Flag $no_sonar found in PR body." + echo "no-sonar=true" >> $GITHUB_OUTPUT + else + echo "No $no_sonar flag found in PR body." + fi + # Quality gate to allow PR merge, used in the branch protection rules. ci-passed: runs-on: ubuntu-latest needs: [lint-cmake, lint-clang, windows, macos, linux, unix] steps: - - run: echo "CI passed" > $GITHUB_STEP_SUMMARY + - run: echo "✅ CI passed" > $GITHUB_STEP_SUMMARY # Summary of test results, combined from test result artifacts. # Runs even if the tests fail to provide a summary of the failures. @@ -67,7 +89,8 @@ jobs: uses: ./.github/workflows/codeql-analysis.yml analyse-sonarcloud: - if: ${{ github.event_name == 'pull_request' }} + needs: pr-comment-flags + if: ${{ github.event_name == 'pull_request' && needs.pr-comment-flags.outputs.no-sonar != 'true' }} uses: ./.github/workflows/sonarcloud-analysis.yml secrets: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} @@ -98,9 +121,12 @@ jobs: - name: Cache vcpkg dir uses: actions/cache@v4 with: + # We also need to cache the `LOCALAPPDATA` dir; without doing so, openssl + # rebuilds (after it is detected in cache) every time, which takes around 10 mins. path: | vcpkg vcpkg_installed + ${{ env.LOCALAPPDATA }}/vcpkg key: vcpkg-${{ runner.os }}-${{ hashFiles('vcpkg.json', 'vcpkg-configuration.json') }} # Should only restore the .venv directory from cache. @@ -145,19 +171,16 @@ jobs: job: ${{ matrix.target.name }} - name: Package - if: ${{ env.PACKAGE_BUILD == 'true' }} run: python ./scripts/package.py env: WINDOWS_PFX_CERTIFICATE: ${{ secrets.WINDOWS_PFX }} WINDOWS_PFX_PASSWORD: ${{ secrets.WINDOWS_PFX_PASS }} - name: Upload - if: ${{ env.PACKAGE_UPLOAD == 'true' }} - uses: ./.github/actions/dist-upload + uses: actions/upload-artifact@v4 with: - use-github: true - github-target-filename: "${{ env.DESKFLOW_PACKAGE_PREFIX }}-${{ matrix.target.name }}" - package-version: ${{ env.DESKFLOW_VERSION }} + name: ${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} + path: ${{ env.PACKAGE_PATH }} macos: name: ${{ matrix.target.name }} @@ -223,7 +246,6 @@ jobs: job: ${{ matrix.target.name }} - name: Package - if: ${{ env.PACKAGE_BUILD == 'true' }} run: ./scripts/package.py env: APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} @@ -234,12 +256,10 @@ jobs: APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - name: Upload - if: ${{ env.PACKAGE_UPLOAD == 'true' }} - uses: ./.github/actions/dist-upload + uses: actions/upload-artifact@v4 with: - use-github: true - github-target-filename: "${{ env.DESKFLOW_PACKAGE_PREFIX }}-${{ matrix.target.name }}" - package-version: ${{ env.DESKFLOW_VERSION }} + name: ${{ env.PACKAGE_PREFIX }}-${{ matrix.target.name }} + path: ${{ env.PACKAGE_PATH }} linux-matrix: runs-on: ubuntu-latest @@ -308,19 +328,16 @@ jobs: job: linux-${{ matrix.distro.name }} - name: Package - if: ${{ env.PACKAGE_BUILD == 'true' }} env: LINUX_EXTRA_PACKAGES: ${{ matrix.distro.extra-packages }} LINUX_PACKAGE_USER: ${{ matrix.distro.package-user }} run: ./scripts/package.py - name: Upload - if: ${{ env.PACKAGE_UPLOAD == 'true' }} - uses: ./.github/actions/dist-upload + uses: actions/upload-artifact@v4 with: - use-github: true - github-target-filename: "${{ env.DESKFLOW_PACKAGE_PREFIX }}-${{ matrix.distro.name }}" - package-version: ${{ env.DESKFLOW_VERSION }} + name: ${{ env.PACKAGE_PREFIX }}-${{ matrix.distro.name }} + path: ${{ env.PACKAGE_PATH }} # Technically, "unix" is a misnomer, but we use it here to mean "Unix-like BSD-derived". unix: diff --git a/.vscode/launch.json b/.vscode/launch.json index 1f207a1d4..c8a78f27d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -39,7 +39,7 @@ "type": "lldb", "cwd": "${workspaceRoot}", "request": "launch", - "program": "${workspaceFolder}/build/bin/deskflowc", + "program": "${workspaceFolder}/build/bin/deskflow-client", "args": ["--config-toml", "deskflow-config.toml"], "preLaunchTask": "kill-build" }, @@ -81,7 +81,7 @@ "type": "cppvsdbg", "cwd": "${workspaceRoot}", "request": "launch", - "program": "${workspaceFolder}/build/bin/deskflows", + "program": "${workspaceFolder}/build/bin/deskflow-server", "args": ["--config-toml", "deskflow-config.toml"], "internalConsoleOptions": "openOnSessionStart", "preLaunchTask": "kill-build" @@ -91,7 +91,7 @@ "type": "cppvsdbg", "cwd": "${workspaceRoot}", "request": "launch", - "program": "${workspaceFolder}/build/bin/deskflowc", + "program": "${workspaceFolder}/build/bin/deskflow-client", "args": ["--config-toml", "deskflow-config.toml"], "internalConsoleOptions": "openOnSessionStart", "preLaunchTask": "kill-build" @@ -101,7 +101,7 @@ "type": "cppvsdbg", "cwd": "${workspaceRoot}", "request": "launch", - "program": "${workspaceFolder}/build/bin/deskflowd", + "program": "${workspaceFolder}/build/bin/deskflow-daemon", "args": ["-f"], "internalConsoleOptions": "openOnSessionStart", "preLaunchTask": "build" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0ebcf63bd..55639351e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -67,9 +67,9 @@ { "label": "kill", "type": "shell", - "command": "killall deskflow; killall deskflowc; killall deskflows || true", + "command": "killall deskflow; killall deskflow-client; killall deskflow-server || true", "windows": { - "command": "taskkill /F /IM deskflow.exe /IM deskflowc.exe /IM deskflows.exe; $true" + "command": "taskkill /F /IM deskflow.exe /IM deskflow-client.exe /IM deskflow-client.exe; $true" }, "presentation": { "reveal": "silent" diff --git a/Doxyfile b/Doxyfile index e319e8c57..d24bcca6e 100644 --- a/Doxyfile +++ b/Doxyfile @@ -51,7 +51,7 @@ PROJECT_BRIEF = # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = res/deskflow.svg +PROJECT_LOGO = res/app.svg # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is diff --git a/cmake/Build.cmake b/cmake/Build.cmake index e0947df7f..0b1340cbc 100644 --- a/cmake/Build.cmake +++ b/cmake/Build.cmake @@ -14,8 +14,22 @@ # along with this program. If not, see . macro(configure_build) + + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_EXTENSIONS OFF) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") + + if(APPLE) + message(STATUS "Configuring for Apple") + set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0") + endif() + warnings_as_errors() set_build_date() + configure_file_shared() + endmacro() macro(warnings_as_errors) @@ -37,6 +51,11 @@ macro(set_build_date) add_definitions(-DBUILD_DATE="${BUILD_DATE}") endmacro() +macro(configure_file_shared) + configure_file(${PROJECT_SOURCE_DIR}/src/lib/gui/gui_config.h.in + ${PROJECT_BINARY_DIR}/config/gui_config.h) +endmacro() + macro(post_config) # Build to a temp bin dir on Windows and then copy to the final bin dir @@ -58,28 +77,39 @@ endmacro() macro(post_config_all) - # Always try to copy the files to the bin directory after every build, even if - # there was nothing to do. This is because the copy may have failed last time - # due to the file being in use, and we'll usually want to try again. if(WIN32) - - if(NOT EXISTS ${PYTHON_BIN}) - message(FATAL_ERROR "Python not found at: ${PYTHON_BIN}") - endif() - + # Always try to copy the files to the bin directory after every build and deliberatly ignore + # copy errors, usually the error is because a running process has locked the file. + # + # It is useful to copy every time because the copy may have failed last time due to the file + # being in use, and we'll usually want to try again (after killing the process). + # + # Yes, this looks like a ridiculous thing to do, but it really is necessary to + # use a Python script to copy files on Windows. Why? Two reasons: + # + # 1. Windows file locks (on running processes) creates a very painful development + # experience; you can't overwrite the binary you're running it. + # Why not just stop the process? Windows services are an abject PITA to manage, + # and we don't always care about overwriting all binaries. + # + # 2. The Windows copy command is limited and gives vague/misleading errors. + # The CMake copy command also has similar shortfalls. + # + # Patches welcome! :) add_custom_target( run_post_build ALL - COMMAND ${PYTHON_BIN} ${CMAKE_SOURCE_DIR}/scripts/fancy_copy.py - ${BIN_TEMP_DIR} ${CMAKE_BINARY_DIR}/bin --ignore-errors + COMMAND ${Python_EXECUTABLE} ${PROJECT_SOURCE_DIR}/scripts/fancy_copy.py + ${BIN_TEMP_DIR} ${PROJECT_BINARY_DIR}/bin --ignore-errors + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} VERBATIM COMMENT "Copying files to bin dir") add_dependencies( run_post_build - deskflow - deskflowc - deskflows - deskflowd) + ${GUI_BINARY_NAME} + ${CLIENT_BINARY_NAME} + ${SERVER_BINARY_NAME} + ${DAEMON_BINARY_NAME}) endif() endmacro() diff --git a/cmake/Definitions.cmake b/cmake/Definitions.cmake index fe95d05a5..b9e918efc 100644 --- a/cmake/Definitions.cmake +++ b/cmake/Definitions.cmake @@ -16,28 +16,13 @@ macro(configure_definitions) - set(CMAKE_CXX_STANDARD 20) - set(CMAKE_CXX_EXTENSIONS OFF) - set(CMAKE_CXX_STANDARD_REQUIRED ON) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") - - if(APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0") - endif() - + configure_meta() configure_ninja() configure_options() - configure_python() set(INTEG_TESTS_BIN integtests) set(UNIT_TESTS_BIN unittests) - if("${VERSION_URL}" STREQUAL "") - set(VERSION_URL "https://api.deskflow.org/version") - endif() - add_definitions(-DDESKFLOW_VERSION_URL="${VERSION_URL}") - if(NOT "$ENV{GIT_SHA}" STREQUAL "") # Shorten the Git SHA to 8 chars for readability string(SUBSTRING "$ENV{GIT_SHA}" 0 8 GIT_SHA_SHORT) @@ -45,17 +30,6 @@ macro(configure_definitions) add_definitions(-DGIT_SHA_SHORT="${GIT_SHA_SHORT}") endif() - if(NOT "$ENV{DESKFLOW_PRODUCT_NAME}" STREQUAL "") - set(PRODUCT_NAME $ENV{DESKFLOW_PRODUCT_NAME}) - endif() - - if("${PRODUCT_NAME}" STREQUAL "") - set(PRODUCT_NAME "Deskflow") - endif() - - message(STATUS "Product name: ${PRODUCT_NAME}") - add_definitions(-DDESKFLOW_PRODUCT_NAME="${PRODUCT_NAME}") - if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") message(STATUS "Disabling debug build") add_definitions(-DNDEBUG) @@ -74,7 +48,130 @@ macro(configure_definitions) set(ADD_HEADERS_TO_SOURCES TRUE) endif() - set(BIN_TEMP_DIR ${CMAKE_BINARY_DIR}/temp/bin) + set(BIN_TEMP_DIR ${PROJECT_BINARY_DIR}/temp/bin) + +endmacro() + +macro(configure_meta) + + set(DESKFLOW_APP_ID + "deskflow" + CACHE STRING "ID of the app for filenames, etc") + + set(DESKFLOW_DOMAIN + "deskflow.org" + CACHE STRING "Domain of the app maintainer (not a URL)") + + set(DESKFLOW_APP_NAME + "Deskflow" + CACHE STRING "App name (used in GUI title bar, etc)") + + set(DESKFLOW_AUTHOR_NAME + "Deskflow" + CACHE STRING "Author name (also used as organization name)") + + set(DESKFLOW_MAINTAINER + "Deskflow " + CACHE STRING "Maintainer email address in RFC 5322 mailbox format") + + set(DESKFLOW_WEBSITE_URL + "https://deskflow.org" + CACHE STRING "URL of the app website") + + set(DESKFLOW_VERSION_URL + "https://api.deskflow.org/version" + CACHE STRING "URL to get the latest version") + + set(DESKFLOW_HELP_TEXT + "Report a bug" + CACHE STRING "Text label for the help menu item") + + set(DESKFLOW_RES_DIR + "${PROJECT_SOURCE_DIR}/res" + CACHE STRING "Resource directory for images, etc") + + set(DESKFLOW_MAC_BUNDLE_CODE + "DFLW" + CACHE STRING "Mac bundle code (4 characters)") + + set(DESKFLOW_SHOW_DEV_THANKS + true + CACHE BOOL "Show developer thanks message") + + message(VERBOSE "App ID: ${DESKFLOW_APP_ID}") + message(VERBOSE "App domain: ${DESKFLOW_DOMAIN}") + message(VERBOSE "App name: ${DESKFLOW_APP_NAME}") + message(VERBOSE "Author name: ${DESKFLOW_AUTHOR_NAME}") + message(VERBOSE "Maintainer: ${DESKFLOW_MAINTAINER}") + message(VERBOSE "Website URL: ${DESKFLOW_WEBSITE_URL}") + message(VERBOSE "Version URL: ${DESKFLOW_VERSION_URL}") + message(VERBOSE "Help text: ${DESKFLOW_HELP_TEXT}") + message(VERBOSE "Res dir: ${DESKFLOW_RES_DIR}") + message(VERBOSE "Mac bundle code: ${DESKFLOW_MAC_BUNDLE_CODE}") + message(VERBOSE "Show dev thanks: ${DESKFLOW_SHOW_DEV_THANKS}") + + # TODO: We need to move this to configure_file() in the future, which is much cleaner. + add_definitions(-DDESKFLOW_APP_ID="${DESKFLOW_APP_ID}") + add_definitions(-DDESKFLOW_DOMAIN="${DESKFLOW_DOMAIN}") + add_definitions(-DDESKFLOW_APP_NAME="${DESKFLOW_APP_NAME}") + add_definitions(-DDESKFLOW_AUTHOR_NAME="${DESKFLOW_AUTHOR_NAME}") + add_definitions(-DDESKFLOW_MAINTAINER="${DESKFLOW_MAINTAINER}") + add_definitions(-DDESKFLOW_WEBSITE_URL="${DESKFLOW_WEBSITE_URL}") + add_definitions(-DDESKFLOW_VERSION_URL="${DESKFLOW_VERSION_URL}") + add_definitions(-DDESKFLOW_HELP_TEXT="${DESKFLOW_HELP_TEXT}") + add_definitions(-DDESKFLOW_RES_DIR="${DESKFLOW_RES_DIR}") + + if(DESKFLOW_SHOW_DEV_THANKS) + message(VERBOSE "Showing developer thanks message") + add_definitions(-DDESKFLOW_SHOW_DEV_THANKS) + else() + message(VERBOSE "Not showing developer thanks message") + endif() + + configure_bin_names() + +endmacro() + +macro(configure_bin_names) + + set(GUI_BINARY_NAME + "deskflow" + CACHE STRING "Filename of the GUI binary") + + set(SERVER_BINARY_NAME + "deskflow-server" + CACHE STRING "Filename of the server binary") + + set(CLIENT_BINARY_NAME + "deskflow-client" + CACHE STRING "Filename of the client binary") + + set(CORE_BINARY_NAME + "deskflow-core" + CACHE STRING "Filename of the core binary") + + set(DAEMON_BINARY_NAME + "deskflow-daemon" + CACHE STRING "Filename of the daemon binary") + + set(LEGACY_BINARY_NAME + "deskflow-legacy" + CACHE STRING "Filename of the legacy binary") + + message(VERBOSE "GUI binary: ${GUI_BINARY_NAME}") + message(VERBOSE "Server binary: ${SERVER_BINARY_NAME}") + message(VERBOSE "Client binary: ${CLIENT_BINARY_NAME}") + message(VERBOSE "Core binary: ${CORE_BINARY_NAME}") + message(VERBOSE "Daemon binary: ${DAEMON_BINARY_NAME}") + message(VERBOSE "Legacy binary: ${LEGACY_BINARY_NAME}") + + add_definitions(-DGUI_BINARY_NAME="${GUI_BINARY_NAME}") + add_definitions(-DSERVER_BINARY_NAME="${SERVER_BINARY_NAME}") + add_definitions(-DCLIENT_BINARY_NAME="${CLIENT_BINARY_NAME}") + add_definitions(-DCORE_BINARY_NAME="${CORE_BINARY_NAME}") + add_definitions(-DDAEMON_BINARY_NAME="${DAEMON_BINARY_NAME}") + add_definitions(-DLEGACY_BINARY_NAME="${LEGACY_BINARY_NAME}") + endmacro() macro(configure_ninja) @@ -101,15 +198,6 @@ macro(configure_options) # coverage is off by default because it's GCC only and a developer preference. set(DEFAULT_ENABLE_COVERAGE OFF) - # licensed product is off by default to show links to github, etc. - set(DEFAULT_LICENSED_PRODUCT OFF) - - # activation is off by default to make life easier for contributors. - set(DEFAULT_ENABLE_ACTIVATION OFF) - - # by default, show the dev thanks message, guides contributions, etc. - set(DEFAULT_SHOW_DEV_THANKS ON) - if("$ENV{DESKFLOW_BUILD_MINIMAL}" STREQUAL "true") set(DEFAULT_BUILD_GUI OFF) set(DEFAULT_BUILD_INSTALLER OFF) @@ -123,14 +211,6 @@ macro(configure_options) set(DEFAULT_BUILD_UNIFIED ON) endif() - if("$ENV{DESKFLOW_ENABLE_ACTIVATION}" STREQUAL "true") - set(DEFAULT_ENABLE_ACTIVATION ON) - endif() - - if("$ENV{DESKFLOW_LICENSED_PRODUCT}" STREQUAL "true") - set(DEFAULT_LICENSED_PRODUCT ON) - endif() - if("$ENV{DESKFLOW_ENABLE_COVERAGE}" STREQUAL "true") set(DEFAULT_ENABLE_COVERAGE ON) endif() @@ -139,8 +219,6 @@ macro(configure_options) option(BUILD_INSTALLER "Build installer" ${DEFAULT_BUILD_INSTALLER}) option(BUILD_TESTS "Build tests" ${DEFAULT_BUILD_TESTS}) option(BUILD_UNIFIED "Build unified binary" ${DEFAULT_BUILD_UNIFIED}) - option(ENABLE_ACTIVATION "Enable activation" ${DEFAULT_ENABLE_ACTIVATION}) - option(LICENSED_PRODUCT "Show licensing info" ${DEFAULT_LICENSED_PRODUCT}) option(ENABLE_COVERAGE "Enable test coverage" ${DEFAULT_ENABLE_COVERAGE}) endmacro() diff --git a/cmake/Libraries.cmake b/cmake/Libraries.cmake index b05cc05d6..545b49e0b 100644 --- a/cmake/Libraries.cmake +++ b/cmake/Libraries.cmake @@ -1,3 +1,18 @@ +# Deskflow -- mouse and keyboard sharing utility +# Copyright (C) 2024 Symless Ltd. +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# found in the file LICENSE that should have accompanied this file. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + set(LIBEI_MIN_VERSION 1.2.1) set(LIBPORTAL_MIN_VERSION 0.6) @@ -10,6 +25,7 @@ macro(configure_libs) configure_windows_libs() endif() + configure_python() configure_qt() configure_openssl() configure_coverage() @@ -215,8 +231,8 @@ macro(configure_libei) message(WARNING "libei >= ${LIBEI_MIN_VERSION} not found") endif() else() - set(libei_bin_dir ${CMAKE_BINARY_DIR}/meson/subprojects/libei/src) - set(libei_src_dir ${CMAKE_SOURCE_DIR}/subprojects/libei) + set(libei_bin_dir ${PROJECT_BINARY_DIR}/meson/subprojects/libei/src) + set(libei_src_dir ${PROJECT_SOURCE_DIR}/subprojects/libei) find_library( LIBEI_LINK_LIBRARIES NAMES ei @@ -246,8 +262,8 @@ macro(configure_libportal) endif() else() set(libportal_bin_dir - ${CMAKE_BINARY_DIR}/meson/subprojects/libportal/libportal) - set(libportal_src_dir ${CMAKE_SOURCE_DIR}/subprojects/libportal) + ${PROJECT_BINARY_DIR}/meson/subprojects/libportal/libportal) + set(libportal_src_dir ${PROJECT_SOURCE_DIR}/subprojects/libportal) option(LIBPORTAL_STATIC "Use the static libportal binary" OFF) if(LIBPORTAL_STATIC) @@ -466,31 +482,39 @@ macro(configure_windows_libs) /DDESKFLOW_VERSION=\"${DESKFLOW_VERSION}\" /D_XKEYCHECK_H) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/res/win/version.rc.in - ${CMAKE_BINARY_DIR}/src/version.rc @ONLY) + configure_file(${PROJECT_SOURCE_DIR}/res/win/version.rc.in + ${PROJECT_BINARY_DIR}/src/version.rc @ONLY) configure_windows_openssl() endmacro() macro(configure_windows_openssl) - set(OPENSSL_ROOT_DIR ${CMAKE_SOURCE_DIR}/vcpkg_installed/x64-windows) + set(OPENSSL_ROOT_DIR ${PROJECT_SOURCE_DIR}/vcpkg_installed/x64-windows) set(OPENSSL_EXE_DIR ${OPENSSL_ROOT_DIR}/tools/openssl) if(EXISTS ${OPENSSL_EXE_DIR}) - message(STATUS "OpenSSL exe dir: ${OPENSSL_EXE_DIR}") + message(VERBOSE "OpenSSL exe dir: ${OPENSSL_EXE_DIR}") add_definitions(-DOPENSSL_EXE_DIR="${OPENSSL_EXE_DIR}") else() message(FATAL_ERROR "OpenSSL exe dir not found: ${OPENSSL_EXE_DIR}") endif() if(EXISTS ${OPENSSL_ROOT_DIR}) - message(STATUS "OpenSSL root dir: ${OPENSSL_ROOT_DIR}") + message(VERBOSE "OpenSSL root dir: ${OPENSSL_ROOT_DIR}") else() message(FATAL_ERROR "OpenSSL root dir not found: ${OPENSSL_ROOT_DIR}") endif() endmacro() +macro(configure_python) + if(WIN32) + find_package(Python REQUIRED QUIET) + else() + find_package(Python3 REQUIRED QUIET) + endif() +endmacro() + macro(configure_qt) find_package( @@ -500,6 +524,9 @@ macro(configure_qt) message(STATUS "Qt version: ${Qt6_VERSION}") + set(GUI_RES_DIR ${DESKFLOW_RES_DIR}/gui) + set(GUI_QRC_FILE ${GUI_RES_DIR}/app.qrc) + endmacro() macro(configure_openssl) @@ -553,7 +580,7 @@ macro(configure_gtest) ) endif() - message(STATUS "Building GoogleTest") + message(VERBOSE "Using local GoogleTest") set(gtest_dir ${gtest_base_dir}/googletest) set(gmock_dir ${gtest_base_dir}/googlemock) include_directories(${gtest_dir} ${gmock_dir} ${gtest_dir}/include @@ -613,19 +640,10 @@ macro(configure_coverage) endif() endmacro() -macro(configure_python) - set(python_venv_dir ${CMAKE_SOURCE_DIR}/.venv) - if(WIN32) - set(PYTHON_BIN ${python_venv_dir}/Scripts/python.exe) - else() - set(PYTHON_BIN ${python_venv_dir}/bin/python) - endif() -endmacro() - macro(configure_wintoast) # WinToast is a pretty niche library, and there doesn't seem to be an installable package, # so we rely on building from source. - file(GLOB WINTOAST_DIR ${CMAKE_SOURCE_DIR}/subprojects/WinToast-*) + file(GLOB WINTOAST_DIR ${PROJECT_SOURCE_DIR}/subprojects/WinToast-*) if(WINTOAST_DIR) set(HAVE_WINTOAST true) add_definitions(-DHAVE_WINTOAST=1) @@ -637,7 +655,7 @@ macro(configure_wintoast) endmacro() macro(configure_tomlplusplus) - file(GLOB tomlplusplus_dir ${CMAKE_SOURCE_DIR}/subprojects/tomlplusplus-*) + file(GLOB tomlplusplus_dir ${PROJECT_SOURCE_DIR}/subprojects/tomlplusplus-*) if(tomlplusplus_dir) set(DEFAULT_SYSTEM_TOMLPLUSPLUS OFF) @@ -657,6 +675,7 @@ macro(configure_tomlplusplus) endif() else() if(EXISTS ${tomlplusplus_dir}) + message(VERBOSE "Using local tomlplusplus") set(HAVE_TOMLPLUSPLUS true) add_definitions(-DHAVE_TOMLPLUSPLUS=1) include_directories(${tomlplusplus_dir}/include) @@ -667,7 +686,7 @@ macro(configure_tomlplusplus) endmacro() macro(configure_cli11) - file(GLOB cli11_dir ${CMAKE_SOURCE_DIR}/subprojects/CLI11-*) + file(GLOB cli11_dir ${PROJECT_SOURCE_DIR}/subprojects/CLI11-*) if(cli11_dir) set(DEFAULT_SYSTEM_CLI11 OFF) @@ -685,12 +704,13 @@ macro(configure_cli11) message(WARNING "System CLI11 not found") endif() else() - if(EXISTS ${CLI11_dir}) + if(EXISTS ${cli11_dir}) + message(VERBOSE "Using local CLI11") set(HAVE_CLI11 true) add_definitions(-DHAVE_CLI11=1) include_directories(${cli11_dir}/include) else() - message(WARNING "Local CLI11 subproject not found") + message(WARNING "Local CLI11 subproject not found at: ${cli11_dir}") endif() endif() diff --git a/cmake/Packaging.cmake b/cmake/Packaging.cmake index cd50b1937..57706204f 100644 --- a/cmake/Packaging.cmake +++ b/cmake/Packaging.cmake @@ -19,17 +19,19 @@ # macro(configure_packaging) + set(DESKFLOW_PROJECT_RES_DIR ${PROJECT_SOURCE_DIR}/res) + if(${BUILD_INSTALLER}) - set(CPACK_PACKAGE_NAME "deskflow") - set(CPACK_PACKAGE_CONTACT "Deskflow ") + set(CPACK_PACKAGE_NAME ${DESKFLOW_APP_ID}) + set(CPACK_PACKAGE_CONTACT ${DESKFLOW_MAINTAINER}) set(CPACK_PACKAGE_DESCRIPTION "Mouse and keyboard sharing utility") - set(CPACK_PACKAGE_VENDOR "Symless") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") + set(CPACK_PACKAGE_VENDOR ${DESKFLOW_AUTHOR_NAME}) + set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE) if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") configure_windows_packaging() elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - configure_macos_packaging() + configure_mac_packaging() elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") configure_linux_packaging() elseif(${CMAKE_SYSTEM_NAME} MATCHES "|.*BSD") @@ -48,35 +50,46 @@ endmacro() # macro(configure_windows_packaging) - message(STATUS "Configuring Windows installer") + message(VERBOSE "Configuring Windows installer") set(CPACK_PACKAGE_VERSION ${DESKFLOW_VERSION_MS}) set(QT_PATH $ENV{CMAKE_PREFIX_PATH}) - configure_files(${CMAKE_CURRENT_SOURCE_DIR}/res/dist/wix - ${CMAKE_BINARY_DIR}/installer) + set(DESKFLOW_MSI_64_GUID + "027D1C8A-E7A5-4754-BB93-B2D45BFDBDC8" + CACHE STRING "GUID for 64-bit MSI installer") + + set(DESKFLOW_MSI_32_GUID + "8F57C657-BC87-45E6-840E-41242A93511C" + CACHE STRING "GUID for 32-bit MSI installer") + + configure_files(${PROJECT_SOURCE_DIR}/res/dist/wix + ${PROJECT_BINARY_DIR}/installer) endmacro() # # macOS app bundle # -macro(configure_macos_packaging) +macro(configure_mac_packaging) - message(STATUS "Configuring macOS app bundle") + message(VERBOSE "Configuring macOS app bundle") set(CPACK_PACKAGE_VERSION ${DESKFLOW_VERSION}) set(CMAKE_INSTALL_RPATH "@loader_path/../Libraries;@loader_path/../Frameworks") set(DESKFLOW_BUNDLE_SOURCE_DIR - ${CMAKE_CURRENT_SOURCE_DIR}/res/dist/macos/bundle) - set(DESKFLOW_BUNDLE_DIR ${CMAKE_BINARY_DIR}/bundle) - set(DESKFLOW_BUNDLE_APP_DIR ${DESKFLOW_BUNDLE_DIR}/Deskflow.app) - set(DESKFLOW_BUNDLE_BINARY_DIR ${DESKFLOW_BUNDLE_APP_DIR}/Contents/MacOS) + ${PROJECT_SOURCE_DIR}/res/dist/mac/bundle + CACHE PATH "Path to the macOS app bundle") + set(DESKFLOW_BUNDLE_DIR ${PROJECT_BINARY_DIR}/bundle/${DESKFLOW_APP_NAME}.app) + set(DESKFLOW_BUNDLE_BINARY_DIR ${DESKFLOW_BUNDLE_DIR}/Contents/MacOS) configure_files(${DESKFLOW_BUNDLE_SOURCE_DIR} ${DESKFLOW_BUNDLE_DIR}) + file(RENAME ${DESKFLOW_BUNDLE_DIR}/Contents/Resources/App.icns + ${DESKFLOW_BUNDLE_DIR}/Contents/Resources/${DESKFLOW_APP_NAME}.icns) + endmacro() # @@ -84,12 +97,12 @@ endmacro() # macro(configure_linux_packaging) - message(STATUS "Configuring Linux packaging") + message(VERBOSE "Configuring Linux packaging") set(CPACK_PACKAGE_VERSION ${DESKFLOW_VERSION_LINUX}) set(CPACK_GENERATOR "DEB;RPM;TGZ") - set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Deskflow ") + set(CPACK_DEBIAN_PACKAGE_MAINTAINER ${DESKFLOW_MAINTAINER}) set(CPACK_DEBIAN_PACKAGE_SECTION "utils") set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) @@ -108,11 +121,25 @@ macro(configure_linux_packaging) # apps install to. set(CMAKE_INSTALL_PREFIX /usr) - install(FILES res/dist/linux/deskflow.desktop DESTINATION share/applications) - install(FILES res/deskflow.png DESTINATION share/pixmaps) + set(source_desktop_file ${DESKFLOW_PROJECT_RES_DIR}/dist/linux/app.desktop.in) + set(configured_desktop_file ${PROJECT_BINARY_DIR}/app.desktop) + set(install_desktop_file ${DESKFLOW_APP_ID}.desktop) + + configure_file(${source_desktop_file} ${configured_desktop_file} @ONLY) + + install( + FILES ${configured_desktop_file} + DESTINATION share/applications + RENAME ${install_desktop_file}) + + install( + FILES ${DESKFLOW_RES_DIR}/app.png + DESTINATION share/pixmaps + RENAME ${DESKFLOW_APP_ID}.png) # Prepare PKGBUILD for Arch Linux - configure_file(res/dist/arch/PKGBUILD.in ${CMAKE_BINARY_DIR}/PKGBUILD @ONLY) + configure_file(${DESKFLOW_PROJECT_RES_DIR}/dist/arch/PKGBUILD.in + ${CMAKE_BINARY_DIR}/PKGBUILD @ONLY) endmacro() diff --git a/cmake/Version.cmake b/cmake/Version.cmake index bd5f678be..5d477d0c9 100644 --- a/cmake/Version.cmake +++ b/cmake/Version.cmake @@ -22,7 +22,7 @@ macro(set_version) string(STRIP "${DESKFLOW_VERSION}" DESKFLOW_VERSION) if(NOT DESKFLOW_VERSION) - file(READ "${CMAKE_SOURCE_DIR}/VERSION" DESKFLOW_VERSION) + file(READ "${PROJECT_SOURCE_DIR}/VERSION" DESKFLOW_VERSION) string(STRIP "${DESKFLOW_VERSION}" DESKFLOW_VERSION) endif() @@ -67,14 +67,14 @@ macro(set_windows_version) # Dot-separated version number for MSI and Windows version .rc file. set(DESKFLOW_VERSION_MS ${DESKFLOW_VERSION_FOUR_PART}) - message(STATUS "Version number for (Microsoft 4-part): " - ${DESKFLOW_VERSION_MS}) + message(VERBOSE "Version number for (Microsoft 4-part): " + ${DESKFLOW_VERSION_MS}) # CSV version number for Windows version .rc file. set(DESKFLOW_VERSION_MS_CSV "${VERSION_MAJOR},${VERSION_MINOR},${VERSION_PATCH},${VERSION_REVISION}") - message(STATUS "Version number for (Microsoft CSV): " - ${DESKFLOW_VERSION_MS_CSV}) + message(VERBOSE "Version number for (Microsoft CSV): " + ${DESKFLOW_VERSION_MS_CSV}) endmacro() macro(set_linux_version) diff --git a/cspell.json b/cspell.json index 71e72cf7e..5ba583724 100644 --- a/cspell.json +++ b/cspell.json @@ -52,6 +52,7 @@ "Poschta", "Povilas", "Priddy", + "psutil", "pyproject", "qputenv", "Regen", diff --git a/res/deskflow.ico b/res/app.ico similarity index 100% rename from res/deskflow.ico rename to res/app.ico diff --git a/res/deskflow.png b/res/app.png similarity index 100% rename from res/deskflow.png rename to res/app.png diff --git a/res/deskflow.svg b/res/app.svg similarity index 100% rename from res/deskflow.svg rename to res/app.svg diff --git a/res/config.h.in b/res/config.h.in index 9e5420a8f..3d03273ef 100644 --- a/res/config.h.in +++ b/res/config.h.in @@ -1,3 +1,22 @@ +/* + * Deskflow -- mouse and keyboard sharing utility + * Copyright (C) 2009 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// clang-format off + /* Define version here for Unix, but using /D for Windows. */ #cmakedefine DESKFLOW_VERSION "${DESKFLOW_VERSION}" @@ -177,3 +196,5 @@ /* Define if libportal has input capture support */ #cmakedefine HAVE_LIBPORTAL_INPUTCAPTURE ${HAVE_LIBPORTAL_INPUTCAPTURE} + +// clang-format on diff --git a/res/dist/arch/PKGBUILD.in b/res/dist/arch/PKGBUILD.in index 6bd3814e0..b3a413d3d 100644 --- a/res/dist/arch/PKGBUILD.in +++ b/res/dist/arch/PKGBUILD.in @@ -1,10 +1,10 @@ -# Maintainer: Deskflow +# Maintainer: @DESKFLOW_MAINTAINER@ -pkgname=deskflow +pkgname=@DESKFLOW_APP_ID@ pkgver=@DESKFLOW_VERSION_FOUR_PART@ pkgrel=1 pkgdesc="Mouse and keyboard sharing utility" -url='https://deskflow.org/' +url='@DESKFLOW_WEBSITE_URL@' arch=('x86_64') license=('GPL-2.0-only') depends=( diff --git a/res/dist/flatpak/deskflow.yml b/res/dist/flatpak/deskflow.yml index 0f5271ca9..b3686f290 100644 --- a/res/dist/flatpak/deskflow.yml +++ b/res/dist/flatpak/deskflow.yml @@ -1,4 +1,4 @@ -app-id: com.symless.Deskflow +app-id: org.deskflow.Deskflow runtime: org.kde.Platform runtime-version: "5.15-21.08" sdk: org.kde.Sdk diff --git a/res/dist/linux/deskflow.desktop b/res/dist/linux/app.desktop.in similarity index 69% rename from res/dist/linux/deskflow.desktop rename to res/dist/linux/app.desktop.in index 3142bc974..15b9a7ecf 100644 --- a/res/dist/linux/deskflow.desktop +++ b/res/dist/linux/app.desktop.in @@ -1,12 +1,11 @@ [Desktop Entry] Type=Application Version=1.0 -Name=Deskflow +Name=@DESKFLOW_APP_NAME@ Comment=Mouse and keyboard sharing utility Path=/usr/bin -Exec=/usr/bin/deskflow -Icon=deskflow +Exec=/usr/bin/@GUI_BINARY_NAME@ +Icon=@DESKFLOW_APP_ID@ Terminal=false Categories=Utility; Keywords=keyboard;mouse;sharing;network;share; - diff --git a/res/dist/macos/bundle/Deskflow.app/Contents/Info.plist.in b/res/dist/mac/bundle/Contents/Info.plist.in similarity index 77% rename from res/dist/macos/bundle/Deskflow.app/Contents/Info.plist.in rename to res/dist/mac/bundle/Contents/Info.plist.in index 7f7298a59..1c9dedb12 100644 --- a/res/dist/macos/bundle/Deskflow.app/Contents/Info.plist.in +++ b/res/dist/mac/bundle/Contents/Info.plist.in @@ -4,21 +4,21 @@ CFBundleDevelopmentRegion English CFBundleDisplayName - Deskflow + @DESKFLOW_APP_NAME@ CFBundleExecutable - deskflow + @DESKFLOW_APP_ID@ CFBundleIconFile - Deskflow.icns + @DESKFLOW_APP_NAME@.icns CFBundleIdentifier - deskflow + @DESKFLOW_APP_ID@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - Deskflow + @DESKFLOW_APP_NAME@ CFBundlePackageType APPL CFBundleSignature - SYN1 + @DESKFLOW_MAC_BUNDLE_CODE@ CFBundleShortVersionString @DESKFLOW_VERSION@ CFBundleVersion diff --git a/res/dist/mac/bundle/Contents/PkgInfo.in b/res/dist/mac/bundle/Contents/PkgInfo.in new file mode 100644 index 000000000..c50e7b961 --- /dev/null +++ b/res/dist/mac/bundle/Contents/PkgInfo.in @@ -0,0 +1 @@ +APPL@DESKFLOW_MAC_BUNDLE_CODE@ \ No newline at end of file diff --git a/res/dist/mac/bundle/Contents/Resources/App.icns b/res/dist/mac/bundle/Contents/Resources/App.icns new file mode 100644 index 000000000..72bfe6246 Binary files /dev/null and b/res/dist/mac/bundle/Contents/Resources/App.icns differ diff --git a/res/dist/mac/bundle/Contents/Resources/Background.tiff b/res/dist/mac/bundle/Contents/Resources/Background.tiff new file mode 100644 index 000000000..4a1be13e8 Binary files /dev/null and b/res/dist/mac/bundle/Contents/Resources/Background.tiff differ diff --git a/res/dist/mac/bundle/Contents/Resources/Volume.icns b/res/dist/mac/bundle/Contents/Resources/Volume.icns new file mode 100644 index 000000000..8273301fd Binary files /dev/null and b/res/dist/mac/bundle/Contents/Resources/Volume.icns differ diff --git a/res/dist/mac/dmgbuild/settings.py b/res/dist/mac/dmgbuild/settings.py new file mode 100644 index 000000000..16ff4b8ad --- /dev/null +++ b/res/dist/mac/dmgbuild/settings.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +# Example: https://dmgbuild.readthedocs.io/en/latest/example.html + +from __future__ import unicode_literals + +import os.path + +app = defines.get("app") +app_basename = os.path.basename(app) +format = defines.get("format", "UDBZ") +size = defines.get("size", None) +files = [app] +symlinks = {"Applications": "/Applications"} +icon = os.path.join(app, "Contents/Resources/Volume.icns") +icon_locations = { + app_basename: (144, 190), + "Applications": (455, 190), +} +background = os.path.join(app, "Contents/Resources/Background.tiff") +show_status_bar = False +show_tab_view = False +show_toolbar = False +show_pathbar = False +show_sidebar = False +sidebar_width = 180 +window_rect = ((200, 120), (620, 420)) +default_view = "icon-view" +show_icon_preview = False +include_icon_view_settings = "auto" +include_list_view_settings = "auto" +arrange_by = None +grid_offset = (0, 0) +grid_spacing = 100 +scroll_position = (0, 0) +label_pos = "bottom" +text_size = 16 +icon_size = 100 +list_icon_size = 16 +list_text_size = 12 +list_scroll_position = (0, 0) +list_sort_by = "name" +list_use_relative_dates = True +list_calculate_all_sizes = (False,) +list_columns = ("name", "date-modified", "size", "kind", "date-added") +list_column_widths = { + "name": 300, + "date-modified": 181, + "date-created": 181, + "date-added": 181, + "date-last-opened": 181, + "size": 97, + "kind": 115, + "label": 100, + "version": 75, + "comments": 300, +} +list_column_sort_directions = { + "name": "ascending", + "date-modified": "descending", + "date-created": "descending", + "date-added": "descending", + "date-last-opened": "descending", + "size": "descending", + "kind": "ascending", + "label": "ascending", + "version": "ascending", + "comments": "ascending", +} diff --git a/res/dist/macos/bundle/Deskflow.app/Contents/PkgInfo b/res/dist/macos/bundle/Deskflow.app/Contents/PkgInfo deleted file mode 100644 index 8a95e2b34..000000000 --- a/res/dist/macos/bundle/Deskflow.app/Contents/PkgInfo +++ /dev/null @@ -1 +0,0 @@ -APPLDFLW \ No newline at end of file diff --git a/res/dist/macos/bundle/Deskflow.app/Contents/Resources/.background.tiff b/res/dist/macos/bundle/Deskflow.app/Contents/Resources/.background.tiff deleted file mode 100644 index e13ab35b3..000000000 Binary files a/res/dist/macos/bundle/Deskflow.app/Contents/Resources/.background.tiff and /dev/null differ diff --git a/res/dist/macos/bundle/Deskflow.app/Contents/Resources/Deskflow.icns b/res/dist/macos/bundle/Deskflow.app/Contents/Resources/Deskflow.icns deleted file mode 100644 index 8fc714750..000000000 Binary files a/res/dist/macos/bundle/Deskflow.app/Contents/Resources/Deskflow.icns and /dev/null differ diff --git a/res/dist/macos/bundle/Deskflow.app/Contents/Resources/VolumeIcon.icns b/res/dist/macos/bundle/Deskflow.app/Contents/Resources/VolumeIcon.icns deleted file mode 100644 index 0e165618f..000000000 Binary files a/res/dist/macos/bundle/Deskflow.app/Contents/Resources/VolumeIcon.icns and /dev/null differ diff --git a/res/dist/macos/dmgbuild/settings.py b/res/dist/macos/dmgbuild/settings.py deleted file mode 100644 index 3b282dfa3..000000000 --- a/res/dist/macos/dmgbuild/settings.py +++ /dev/null @@ -1,150 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -import os.path - -# Use like this: dmgbuild -s settings.py "Test Volume" test.dmg -# dmgbuild -s settings.py -D app=/path/to/My.app "My Application" MyApp.dmg - -# .. Useful stuff .............................................................. - -app = defines.get("app") -app_basename = os.path.basename(app) - -# .. Basics .................................................................... - -# Volume format (see hdiutil create -help) -format = defines.get("format", "UDBZ") - -# Volume size -size = defines.get("size", None) - -# Files to include -files = [app] - -# Symlinks to create -symlinks = {"Applications": "/Applications"} - -# Volume icon -# -# You can either define icon, in which case that icon file will be copied to the -# image, *or* you can define badge_icon, in which case the icon file you specify -# will be used to badge the system's Removable Disk icon -# -icon = os.path.join(app, "Contents/Resources/VolumeIcon.icns") - -# Where to put the icons -icon_locations = { - app_basename: (144, 190), - "Applications": (455, 190), - ".background.tiff": (150, 450), - ".VolumeIcon.icns": (455, 450), -} - -# .. Window configuration ...................................................... - -# Background -# -# This is a STRING containing any of the following: -# -# #3344ff - web-style RGB color -# #34f - web-style RGB color, short form (#34f == #3344ff) -# rgb(1,0,0) - RGB color, each value is between 0 and 1 -# hsl(120,1,.5) - HSL (hue saturation lightness) color -# hwb(300,0,0) - HWB (hue whiteness blackness) color -# cmyk(0,1,0,0) - CMYK color -# goldenrod - X11/SVG named color -# builtin-arrow - A simple built-in background with a blue arrow -# /foo/bar/baz.png - The path to an image file -# -# The hue component in hsl() and hwb() may include a unit; it defaults to -# degrees ('deg'), but also supports radians ('rad') and gradians ('grad' -# or 'gon'). -# -# Other color components may be expressed either in the range 0 to 1, or -# as percentages (e.g. 60% is equivalent to 0.6). -background = os.path.join(app, "Contents/Resources/.background.tiff") - -show_status_bar = False -show_tab_view = False -show_toolbar = False -show_pathbar = False -show_sidebar = False -sidebar_width = 180 - -# Window position in ((x, y), (w, h)) format -window_rect = ((200, 120), (620, 420)) - -# Select the default view; must be one of -# -# 'icon-view' -# 'list-view' -# 'column-view' -# 'coverflow' -# -default_view = "icon-view" - -# General view configuration -show_icon_preview = False - -# Set these to True to force inclusion of icon/list view settings (otherwise -# we only include settings for the default view) -include_icon_view_settings = "auto" -include_list_view_settings = "auto" - -# .. Icon view configuration ................................................... - -arrange_by = None -grid_offset = (0, 0) -grid_spacing = 100 -scroll_position = (0, 0) -label_pos = "bottom" # or 'right' -text_size = 16 -icon_size = 100 - -# .. List view configuration ................................................... - -# Column names are as follows: -# -# name -# date-modified -# date-created -# date-added -# date-last-opened -# size -# kind -# label -# version -# comments -# -list_icon_size = 16 -list_text_size = 12 -list_scroll_position = (0, 0) -list_sort_by = "name" -list_use_relative_dates = True -list_calculate_all_sizes = (False,) -list_columns = ("name", "date-modified", "size", "kind", "date-added") -list_column_widths = { - "name": 300, - "date-modified": 181, - "date-created": 181, - "date-added": 181, - "date-last-opened": 181, - "size": 97, - "kind": 115, - "label": 100, - "version": 75, - "comments": 300, -} -list_column_sort_directions = { - "name": "ascending", - "date-modified": "descending", - "date-created": "descending", - "date-added": "descending", - "date-last-opened": "descending", - "size": "descending", - "kind": "ascending", - "label": "ascending", - "version": "ascending", - "comments": "ascending", -} diff --git a/res/dist/wix/DeskflowBrowseDlg.wxs b/res/dist/wix/AppBrowseDlg.wxs similarity index 96% rename from res/dist/wix/DeskflowBrowseDlg.wxs rename to res/dist/wix/AppBrowseDlg.wxs index 1b3664b7d..9497816e6 100644 --- a/res/dist/wix/DeskflowBrowseDlg.wxs +++ b/res/dist/wix/AppBrowseDlg.wxs @@ -2,7 +2,7 @@ - + 1 diff --git a/res/dist/wix/AppDlgSequence.wxs b/res/dist/wix/AppDlgSequence.wxs new file mode 100644 index 000000000..2acb0099d --- /dev/null +++ b/res/dist/wix/AppDlgSequence.wxs @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 1 + "1"]]> + + 1 + NOT Installed + + 1 + Installed AND PATCH + + 1 + NOT WIXUI_DONTVALIDATEPATH + "1"]]> + WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" + 1 + 1 + 1 + + NOT Installed + Installed AND NOT PATCH + Installed AND PATCH + + 1 + + 1 + 1 + 1 + + + + + + + diff --git a/res/dist/wix/DeskflowInstallDlg.wxs b/res/dist/wix/AppInstallDirDlg.wxs similarity index 85% rename from res/dist/wix/DeskflowInstallDlg.wxs rename to res/dist/wix/AppInstallDirDlg.wxs index e39f557ba..6f1afefd0 100644 --- a/res/dist/wix/DeskflowInstallDlg.wxs +++ b/res/dist/wix/AppInstallDirDlg.wxs @@ -2,8 +2,8 @@ - - + + @@ -12,7 +12,7 @@ - + diff --git a/res/dist/wix/DeskflowMaintenanceTypeDlg.wxs b/res/dist/wix/AppMaintenanceTypeDlg.wxs similarity index 95% rename from res/dist/wix/DeskflowMaintenanceTypeDlg.wxs rename to res/dist/wix/AppMaintenanceTypeDlg.wxs index 5fb21c172..d93473d0f 100644 --- a/res/dist/wix/DeskflowMaintenanceTypeDlg.wxs +++ b/res/dist/wix/AppMaintenanceTypeDlg.wxs @@ -13,8 +13,8 @@ - - + + 1 ARPNOMODIFY diff --git a/res/dist/wix/DeskflowVerifyReadyDlg.wxs b/res/dist/wix/AppVerifyReadyDlg.wxs similarity index 98% rename from res/dist/wix/DeskflowVerifyReadyDlg.wxs rename to res/dist/wix/AppVerifyReadyDlg.wxs index 699716bc4..950246d99 100644 --- a/res/dist/wix/DeskflowVerifyReadyDlg.wxs +++ b/res/dist/wix/AppVerifyReadyDlg.wxs @@ -3,7 +3,7 @@ - + diff --git a/res/dist/wix/DeskflowWelcome.wxs b/res/dist/wix/AppWelcome.wxs similarity index 70% rename from res/dist/wix/DeskflowWelcome.wxs rename to res/dist/wix/AppWelcome.wxs index 4e6711046..77d2b2645 100644 --- a/res/dist/wix/DeskflowWelcome.wxs +++ b/res/dist/wix/AppWelcome.wxs @@ -5,24 +5,24 @@ - - welcome_background - + + welcome_background + - + Installed AND PATCH 1 - + - NOT Installed OR PATCH + NOT Installed OR PATCH diff --git a/res/dist/wix/Include.wxi.in b/res/dist/wix/Include.wxi.in index 1787e346a..9c6c39630 100644 --- a/res/dist/wix/Include.wxi.in +++ b/res/dist/wix/Include.wxi.in @@ -1,23 +1,30 @@ - + + - - - - + + + + + + - + - + - - - + + + + + + + \ No newline at end of file diff --git a/res/dist/wix/Deskflow.sln b/res/dist/wix/Installer.sln similarity index 88% rename from res/dist/wix/Deskflow.sln rename to res/dist/wix/Installer.sln index 3149f2081..8a188479b 100644 --- a/res/dist/wix/Deskflow.sln +++ b/res/dist/wix/Installer.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29411.108 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Deskflow", "Deskflow.wixproj", "{D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}" +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Installer", "Installer.wixproj", "{D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/res/dist/wix/Deskflow.wixproj b/res/dist/wix/Installer.wixproj similarity index 64% rename from res/dist/wix/Deskflow.wixproj rename to res/dist/wix/Installer.wixproj index fc3b058d2..9a2f339fa 100644 --- a/res/dist/wix/Deskflow.wixproj +++ b/res/dist/wix/Installer.wixproj @@ -1,21 +1,22 @@  - + 3.11 {d4ba9f39-6a35-4c8f-9cb2-67fcbe5cab17} 2.0 - Deskflow + Installer Package bin\$(Configuration)\ wix\obj\$(Configuration)\ - - - - - - + + + + + + @@ -35,5 +36,6 @@ - - + + \ No newline at end of file diff --git a/res/dist/wix/Product.wxs b/res/dist/wix/Product.wxs index ac51ef3fc..5c364f3d6 100644 --- a/res/dist/wix/Product.wxs +++ b/res/dist/wix/Product.wxs @@ -19,39 +19,39 @@ - - + + - - + - - + = 602)]]> - + - + - common_background - - - - - + CommonBackground + + + + + - + @@ -59,7 +59,7 @@ - + @@ -77,18 +77,18 @@ - - + + - - - + + + - - + + - + @@ -98,42 +98,42 @@ - - - + + + - - - - - - + + + + + + - - - - - - + + + + + + - + - + - + diff --git a/res/dist/wix/images/banner.png b/res/dist/wix/images/banner.png index cbf9b4675..90f52a114 100644 Binary files a/res/dist/wix/images/banner.png and b/res/dist/wix/images/banner.png differ diff --git a/res/dist/wix/images/common_background.png b/res/dist/wix/images/common_background.png index 647e8b40c..78dd8045d 100644 Binary files a/res/dist/wix/images/common_background.png and b/res/dist/wix/images/common_background.png differ diff --git a/res/dist/wix/images/dialog.png b/res/dist/wix/images/dialog.png index c43c94470..45be2dae6 100644 Binary files a/res/dist/wix/images/dialog.png and b/res/dist/wix/images/dialog.png differ diff --git a/res/dist/wix/images/welcome_background.png b/res/dist/wix/images/welcome_background.png index cbc306b21..ef39002cb 100644 Binary files a/res/dist/wix/images/welcome_background.png and b/res/dist/wix/images/welcome_background.png differ diff --git a/res/gui/Deskflow.qrc b/res/gui/app.qrc similarity index 93% rename from res/gui/Deskflow.qrc rename to res/gui/app.qrc index 9e6c03b58..25db31155 100644 --- a/res/gui/Deskflow.qrc +++ b/res/gui/app.qrc @@ -12,5 +12,6 @@ icons/64x64/tray-light.png image/welcome.png icons/64x64/folder.png + image/placeholder.png diff --git a/res/gui/image/placeholder.png b/res/gui/image/placeholder.png new file mode 100644 index 000000000..984d40b28 Binary files /dev/null and b/res/gui/image/placeholder.png differ diff --git a/res/gui/mac/Deskflow.icns b/res/gui/mac/Deskflow.icns deleted file mode 100644 index 3f53bed99..000000000 Binary files a/res/gui/mac/Deskflow.icns and /dev/null differ diff --git a/res/gui/mac/Info.plist b/res/gui/mac/Info.plist deleted file mode 100644 index 4dd49d0fc..000000000 --- a/res/gui/mac/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - CFBundleDevelopmentRegion - English - CFBundleDisplayName - Deskflow - CFBundleExecutable - Deskflow - CFBundleIconFile - Deskflow.icns - CFBundleIdentifier - deskflow - - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Deskflow - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.8.8 - CFBundleVersion - 1.8.8 - NSHumanReadableCopyright - © 2012-2016, Symless Ltd - NSPrincipalClass - NSApplication - NSHighResolutionCapable - True - - diff --git a/res/gui/win/Deskflow.rc b/res/gui/win/app.rc similarity index 100% rename from res/gui/win/Deskflow.rc rename to res/gui/win/app.rc diff --git a/res/win/version.rc.in b/res/win/version.rc.in index e770247ce..83c78fb88 100644 --- a/res/win/version.rc.in +++ b/res/win/version.rc.in @@ -12,14 +12,14 @@ https://learn.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource?redi #define VER_PRODUCTVERSION @DESKFLOW_VERSION_MS_CSV@ #define VER_PRODUCTVERSION_STR "@DESKFLOW_VERSION_MS@\0" -#define VER_COMPANYNAME_STR "Symless\0" -#define VER_FILEDESCRIPTION_STR "Deskflow\0" -#define VER_INTERNALNAME_STR "Deskflow\0" +#define VER_COMPANYNAME_STR "@DESKFLOW_AUTHOR_NAME@\0" +#define VER_FILEDESCRIPTION_STR "@DESKFLOW_APP_NAME@\0" +#define VER_INTERNALNAME_STR "@DESKFLOW_APP_NAME@\0" #define VER_LEGALCOPYRIGHT_STR "Copyright (C) Symless Ltd. @DESKFLOW_BUILD_YEAR@\0" #define VER_LEGALTRADEMARKS1_STR "All Rights Reserved\0" #define VER_LEGALTRADEMARKS2_STR "\0" #define VER_ORIGINALFILENAME_STR "\0" -#define VER_PRODUCTNAME_STR "Deskflow\0" +#define VER_PRODUCTNAME_STR "@DESKFLOW_APP_NAME@\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION diff --git a/scripts/daemon.py b/scripts/daemon.py index 17873a9fa..ec35b3204 100644 --- a/scripts/daemon.py +++ b/scripts/daemon.py @@ -19,25 +19,17 @@ import lib.env as env env.ensure_in_venv(__file__) -import os, sys, time, subprocess, argparse +import os, sys, argparse import lib.windows as windows -import psutil # type: ignore import lib.colors as colors -import lib.file_utils as file_utils -DEFAULT_BIN_NAME = "deskflowd" +DEFAULT_SERVICE_ID = "deskflow" +DEFAULT_BIN_NAME = "deskflow-daemon" DEFAULT_SOURCE_DIR = os.path.join("build", "temp", "bin") DEFAULT_TARGET_DIR = os.path.join("build", "bin") -SERVICE_NOT_RUNNING_ERROR = 2 -ERROR_ACCESS_VIOLATION = 0xC0000005 IGNORE_PROCESSES = ["deskflow.exe"] -class Context: - def __init__(self, verbose): - self.verbose = verbose - - def main(): """Entry point for the script.""" @@ -49,6 +41,8 @@ def main(): parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR) parser.add_argument("--target-dir", default=DEFAULT_TARGET_DIR) parser.add_argument("--bin-name", default=DEFAULT_BIN_NAME) + parser.add_argument("--ignore-processes", nargs="+", default=IGNORE_PROCESSES) + parser.add_argument("--service-id", default=DEFAULT_SERVICE_ID) parser.add_argument("--verbose", action="store_true") args = parser.parse_args() @@ -59,15 +53,15 @@ def main(): ) sys.exit(1) - context = Context(args.verbose) + service = windows.WindowsService(__file__, args) try: if args.reinstall: - reinstall(context, args.source_dir, args.target_dir, args.bin_name) + service.reinstall() elif args.stop: - stop(context, args.target_dir) + service.stop() elif args.restart: - restart(context, args.source_dir, args.target_dir) + service.restart() else: print("No action specified", file=sys.stderr) exit(1) @@ -78,135 +72,5 @@ def main(): input("Press enter to continue...") -def print_verbose(context, message): - if context.verbose: - print(message) - - -def ensure_admin(): - if not windows.is_admin(): - windows.run_elevated(__file__) - sys.exit() - - -def restart(context, source_dir, target_dir): - """Stops the daemon service, copies files, and restarts the daemon service.""" - - ensure_admin() - stop(context, target_dir) - copy_files(source_dir, target_dir) - start() - - -def reinstall(context, source_dir, target_dir, bin_name): - """Stops and uninstalls daemon service, copies files, and reinstalls the daemon service.""" - - ensure_admin() - stop(context, target_dir) - - source_bin_path = f"{os.path.join(source_dir, bin_name)}.exe" - - copy_files(source_dir, target_dir) - - print("Removing old daemon service") - try: - subprocess.run([source_bin_path, "/uninstall"], shell=True, check=True) - except subprocess.CalledProcessError as e: - check_access_violation(e.returncode, source_bin_path) - if e.returncode != 0: - print( - f"{colors.WARNING_TEXT} Uninstall failed, return code: {e.returncode}", - file=sys.stderr, - ) - - target_bin_path = os.path.join(target_dir, bin_name + ".exe") - - try: - print("Installing daemon service") - subprocess.run([target_bin_path, "/install"], shell=True, check=True) - except subprocess.CalledProcessError as e: - check_access_violation(e.returncode, target_bin_path) - if e.returncode != 0: - print(f"{colors.WARNING_TEXT} Install failed, return code: {e.returncode}") - - -def copy_files(source_dir, target_dir): - options = file_utils.CopyOptions(ignore_errors=True, verbose=False) - print(f"Copying files from {source_dir} to {target_dir}") - file_utils.copy(f"{source_dir}/*", target_dir, options) - - -def stop(context, target_dir): - ensure_admin() - print("Stopping daemon service") - try: - subprocess.run(["net", "stop", "deskflow"], shell=True, check=True) - except subprocess.CalledProcessError as e: - if e.returncode == SERVICE_NOT_RUNNING_ERROR: - print_verbose(context, "Daemon service not running") - else: - raise e - - # Wait for Windows to release the file handles after process termination. - wait_for_stop(context, target_dir) - - -def start(): - ensure_admin() - print("Starting daemon service") - subprocess.run(["net", "start", "deskflow"], shell=True, check=True) - - -def wait_for_stop(context, target_dir): - if is_any_process_running(context, target_dir): - print("Waiting for file handles to release...", end="", flush=True) - while is_any_process_running(context, target_dir): - if not context.verbose: - print(".", end="", flush=True) - time.sleep(1) - if not context.verbose: - print() - - -def check_access_violation(return_code, bin_path): - if return_code == ERROR_ACCESS_VIOLATION: - print( - f"{colors.WARNING_TEXT} Process crashed with memory access violation: {bin_path}", - file=sys.stderr, - ) - - -def is_ignored_process(exe): - for ignore_process in IGNORE_PROCESSES: - if exe.endswith(ignore_process): - return True - return False - - -def is_any_process_running(context, dir): - """Check if there is any running process that contains the given directory.""" - - print_verbose(context, f"Checking if any process is running in: {dir}") - - for proc in psutil.process_iter(attrs=["name", "exe"]): - exe = proc.info["exe"] - - if not exe: - print_verbose(context, f"Skipping process with no exe: {proc}") - continue - - if is_ignored_process(exe): - print_verbose(context, f"Ignoring process: {exe}") - continue - - try: - if dir.lower() in exe.lower(): - print_verbose(context, f"Process found: {exe}") - return True - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - - return False - - -main() +if __name__ == "__main__": + main() diff --git a/scripts/install_deps.py b/scripts/install_deps.py index 81f0517d1..8e34f1a35 100755 --- a/scripts/install_deps.py +++ b/scripts/install_deps.py @@ -131,7 +131,7 @@ def parse_args(is_ci): def run(args): env.ensure_dependencies() - env.ensure_in_venv(__file__, auto_create=True) + env.ensure_in_venv(__file__, create_venv=True) if not args.skip_python: env.install_requirements() @@ -280,7 +280,7 @@ class Dependencies: cmd_utils.run(command, shell=True, print_cmd=True) if env_vars_set: - print(f"To load env vars, run: source {mac.shell_rc}") + print(f"To load env vars, run: source {mac.SHELL_RC}") def linux(self): """Installs dependencies on Linux.""" diff --git a/scripts/lib/env.py b/scripts/lib/env.py index fe44f31b6..464be46c7 100644 --- a/scripts/lib/env.py +++ b/scripts/lib/env.py @@ -107,7 +107,7 @@ def get_env_bool(name, default=False): return value.lower() in ["true", "1", "yes"] -def get_python_executable(binary="python"): +def get_venv_executable(binary="python"): if sys.platform == "win32": return os.path.join(VENV_DIR, "Scripts", binary) else: @@ -119,7 +119,7 @@ def in_venv(): return sys.prefix != sys.base_prefix -def ensure_in_venv(script_file, auto_create=False): +def ensure_in_venv(script_file, create_venv=False): """ Ensures the script is running in a Python virtual environment (venv). If the script is not running in a venv, it will create one and re-run the script in the venv. @@ -128,24 +128,27 @@ def ensure_in_venv(script_file, auto_create=False): check_dependencies(raise_error=True) import venv - if not in_venv(): - if not os.path.exists(VENV_DIR): - if not auto_create: - print( - "The Python virtual environment (.venv) needs to be created before you can " - "run this script.\n" - "Please run: scripts/setup_venv.py" - ) - sys.exit(1) + if in_venv(): + print(f"Running in venv, executable: {sys.executable}", flush=True) + return - print(f"Creating virtual environment at {VENV_DIR}") - venv.create(VENV_DIR, with_pip=True) + if create_venv and not os.path.exists(VENV_DIR): + print(f"Creating virtual environment at {VENV_DIR}") + venv.create(VENV_DIR, with_pip=True) + if os.path.exists(VENV_DIR): script_file_abs = os.path.abspath(script_file) print(f"Using virtual environment for: {script_file_abs}", flush=True) - python_executable = get_python_executable() + python_executable = get_venv_executable() result = subprocess.run([python_executable, script_file_abs] + sys.argv[1:]) sys.exit(result.returncode) + else: + print( + "The Python virtual environment (.venv) needs to be created before you can " + "run this script.\n" + "Please run: scripts/setup_venv.py" + ) + sys.exit(1) def install_requirements(): @@ -240,18 +243,6 @@ def ensure_dependencies(): cmd_utils.run(f"{sudo} {install_cmd}".strip(), shell=True, print_cmd=True) -def get_app_version(): - """ - Returns the version either from the env var, or from the version file. - """ - version = get_env("DESKFLOW_VERSION", required=False) - if version: - return version - - with open("VERSION", "r") as f: - return f.read().strip() - - def import_colors(): import lib.colors as colors diff --git a/scripts/lib/linux.py b/scripts/lib/linux.py index 50c20ab4d..45402177b 100644 --- a/scripts/lib/linux.py +++ b/scripts/lib/linux.py @@ -18,18 +18,14 @@ import lib.cmd_utils as cmd_utils import lib.env as env from enum import Enum, auto +BUILD_ROOT_DIR = "build" + class PackageType(Enum): DISTRO = auto() TGZ = auto() -dist_dir = "dist" -build_dir = "build" -package_name = "deskflow" -test_cmd = "deskflows --version" - - def run_command(command, check=True): has_sudo = cmd_utils.has_command("sudo") @@ -44,19 +40,31 @@ def run_command(command, check=True): cmd_utils.run(command, check, shell=True, print_cmd=True) -def package(filename_base, package_type: PackageType, leave_test_installed=False): +def package( + filename_base, + dist_dir, + test_cmd, + package_name, + package_type: PackageType, + leave_test_installed=False, +): + working_dir = BUILD_ROOT_DIR + extension, cmd = get_package_build_info( + package_type, + ) + run_package_cmd(cmd, working_dir) - extension, cmd = get_package_info(package_type) - run_package_cmd(cmd) - package_filename = get_package_filename(extension) + package_filename = get_package_filename(extension, working_dir) target_file = f"{filename_base}.{extension}" - target_path = copy_to_dist_dir(package_filename, target_file) + target_path = copy_to_dist_dir(package_filename, dist_dir, target_file) if package_type == PackageType.DISTRO: - test_install(target_path, remove_test=not leave_test_installed) + test_install( + target_path, package_name, test_cmd, remove_test=not leave_test_installed + ) -def get_package_info(package_type: PackageType): +def get_package_build_info(package_type: PackageType): command = None cpack_generator = None @@ -93,32 +101,36 @@ def get_package_info(package_type: PackageType): return file_extension, command -def run_package_cmd(command): +def run_package_cmd(command, working_dir): package_user = env.get_env("LINUX_PACKAGE_USER", required=False) if package_user: cmd_utils.run( - ["sudo", "chown", "-R", package_user, "build"], check=True, print_cmd=True + ["sudo", "chown", "-R", package_user, working_dir], + check=True, + print_cmd=True, ) command = ["sudo", "-u", package_user] + command cwd = os.getcwd() try: - os.chdir("build") + os.chdir(working_dir) cmd_utils.run(command, check=True, print_cmd=True) finally: os.chdir(cwd) -def get_package_filename(extension): - files = glob.glob(f"build/*.{extension}") +def get_package_filename(extension, working_dir): + files = glob.glob(f"{working_dir}/*.{extension}") if not files: - raise ValueError(f"No .{extension} file found in build directory") + raise ValueError( + f"No .{extension} file found in build directory: {working_dir}" + ) return files[0] -def copy_to_dist_dir(source_file, target_file): +def copy_to_dist_dir(source_file, dist_dir, target_file): os.makedirs(dist_dir, exist_ok=True) target_path = f"{dist_dir}/{target_file}" @@ -128,7 +140,7 @@ def copy_to_dist_dir(source_file, target_file): return target_path -def test_install(package_file, remove_test=True): +def test_install(package_file, package_name, test_cmd, remove_test=True): distro, distro_like, _distro_version = env.get_linux_distro() if not distro_like: @@ -140,19 +152,19 @@ def test_install(package_file, remove_test=True): if "debian" in distro_like: install_base = ["apt", "install", "-f", "-y"] remove_base = ["apt", "remove", "-y"] - list_cmd = ["dpkg", "-L", "deskflow"] + list_cmd = ["dpkg", "-L", package_name] elif "fedora" in distro_like: install_base = ["dnf", "install", "-y"] remove_base = ["dnf", "remove", "-y"] - list_cmd = ["rpm", "-ql", "deskflow"] + list_cmd = ["rpm", "-ql", package_name] elif "opensuse" in distro_like: install_base = ["zypper", "--no-gpg-checks", "install", "-y"] remove_base = ["zypper", "remove", "-y"] - list_cmd = ["rpm", "-ql", "deskflow"] + list_cmd = ["rpm", "-ql", package_name] elif "arch" in distro_like: install_base = ["pacman", "-U", "--noconfirm"] remove_base = ["pacman", "-R", "--noconfirm"] - list_cmd = ["pacman", "-Ql", "deskflow"] + list_cmd = ["pacman", "-Ql", package_name] else: raise RuntimeError(f"Linux distro not yet supported: {distro}") diff --git a/scripts/lib/mac.py b/scripts/lib/mac.py index ac6cd8624..7e26c1e4a 100644 --- a/scripts/lib/mac.py +++ b/scripts/lib/mac.py @@ -19,20 +19,17 @@ import lib.cmd_utils as cmd_utils import lib.env as env from lib.certificate import Certificate -cert_p12_env = "APPLE_P12_CERTIFICATE" -notary_user_env = "APPLE_NOTARY_USER" -codesign_env = "APPLE_CODESIGN_ID" -shell_rc = "~/.zshrc" -dist_dir = "dist" -product_name = "Deskflow" -settings_file = "res/dist/macos/dmgbuild/settings.py" -app_path = "build/bundle/Deskflow.app" -security_path = "/usr/bin/security" -sudo_path = "/usr/bin/sudo" -notarytool_path = "/usr/bin/notarytool" -codesign_path = "/usr/bin/codesign" -xcode_select_path = "/usr/bin/xcode-select" -keychain_path = "/Library/Keychains/System.keychain" +CERT_P12_ENV = "APPLE_P12_CERTIFICATE" +NOTARY_USER_ENV = "APPLE_NOTARY_USER" +CODESIGN_ENV = "APPLE_CODESIGN_ID" +SHELL_RC = "~/.zshrc" +SETTINGS_FILE = "res/dist/mac/dmgbuild/settings.py" +SECURITY_PATH = "/usr/bin/security" +SUDO_PATH = "/usr/bin/sudo" +NOTARYTOOL_PATH = "/usr/bin/notarytool" +CODESIGN_PATH = "/usr/bin/codesign" +XCODE_SELECT_PATH = "/usr/bin/xcode-select" +KEYCHAIN_PATH = "/Library/Keychains/System.keychain" def set_env_var(name, value): @@ -42,7 +39,7 @@ def set_env_var(name, value): Returns True if the variable was added, False if it already exists. """ text = f'export {name}="{value}:${name}"' - file = os.path.expanduser(shell_rc) + file = os.path.expanduser(SHELL_RC) if os.path.exists(file): with open(file, "r") as f: if text in f.read(): @@ -51,11 +48,11 @@ def set_env_var(name, value): print(f"Setting environment variable: {name}={name}") with open(file, "a") as f: f.write(f"\n{text}\n") - print(f"Appended to {shell_rc}: {text}") + print(f"Appended to {SHELL_RC}: {text}") return True -def package(filename_base): +def package(filename_base, source_dir, build_dir, dist_dir, product_name): """ Package the application for macOS. The app bundle must be signed, or an error will occur: @@ -76,35 +73,40 @@ def package(filename_base): install_certificate(cert_base64, cert_password) else: print( - f"Warning: Skipped certificate installation, env var {cert_p12_env} not set", + f"Warning: Skipped certificate installation, env var {CERT_P12_ENV} not set", file=sys.stderr, ) - build_bundle() + bundle_source_dir = os.path.join( + build_dir, os.path.join("bundle", product_name + ".app") + ) + build_bundle(bundle_source_dir) if codesign_id: - sign_bundle(codesign_id) + sign_bundle(bundle_source_dir, codesign_id) else: print( - f"Warning: Skipped code signing, env var {codesign_env} not set", + f"Warning: Skipped code signing, env var {CODESIGN_ENV} not set", file=sys.stderr, ) - dmg_path = build_dmg(filename_base) + dmg_path = build_dmg( + bundle_source_dir, filename_base, source_dir, dist_dir, product_name + ) if notary_user: notarize_package(dmg_path, notary_user, notary_password, notary_team_id) else: print( - f"Warning: Skipped notarization, env var {notary_user_env} not set", + f"Warning: Skipped notarization, env var {NOTARY_USER_ENV} not set", file=sys.stderr, ) def package_env_vars(): - codesign_id = env.get_env(codesign_env, required=False) - cert_base64 = env.get_env(cert_p12_env, required=False) - notary_user = env.get_env(notary_user_env, required=False) + codesign_id = env.get_env(CODESIGN_ENV, required=False) + cert_base64 = env.get_env(CERT_P12_ENV, required=False) + notary_user = env.get_env(NOTARY_USER_ENV, required=False) if notary_user: notary_password = env.get_env("APPLE_NOTARY_PASSWORD") @@ -128,11 +130,12 @@ def package_env_vars(): ) -def build_bundle(): +def build_bundle(bundle_source_dir): + # it's important to build a new bundle every time, so that we catch bugs with fresh builds. - if os.path.exists(app_path): - print(f"Bundle already exists, deleting: {app_path}") - shutil.rmtree(app_path) + if os.path.exists(bundle_source_dir): + print(f"Bundle already exists, deleting: {bundle_source_dir}") + shutil.rmtree(bundle_source_dir) print("Building bundle...") @@ -140,20 +143,20 @@ def build_bundle(): cmd_utils.run("cmake --build build --target install", shell=True, print_cmd=True) -def sign_bundle(codesign_id): - print(f"Signing bundle {app_path}...") +def sign_bundle(bundle_source_dir, codesign_id): + print(f"Signing bundle {bundle_source_dir}...") assert_certificate_installed(codesign_id) cmd_utils.run( [ - codesign_path, + CODESIGN_PATH, "-f", "--options", "runtime", "--deep", "-s", codesign_id, - app_path, + bundle_source_dir, ] ) @@ -172,9 +175,12 @@ def assert_certificate_installed(codesign_id): raise RuntimeError("Code signing certificate not installed or has expired") -def build_dmg(filename_base): - settings_file_abs = os.path.abspath(settings_file) - app_path_abs = os.path.abspath(app_path) +def build_dmg(bundle_source_dir, filename_base, source_dir, dist_dir, product_name): + settings_path = ( + SETTINGS_FILE if source_dir is None else os.path.join(source_dir, SETTINGS_FILE) + ) + settings_path_abs = os.path.abspath(settings_path) + app_path_abs = os.path.abspath(bundle_source_dir) # cwd for dmgbuild, since setting the dmg filename to a path (include the dist dir) seems to # make the dmg disappear and never writes to the specified path. the dmgbuild module also @@ -191,7 +197,7 @@ def build_dmg(filename_base): dmgbuild.build_dmg( dmg_filename, product_name, - settings_file=settings_file_abs, + settings_file=settings_path_abs, defines={ "app": app_path_abs, }, @@ -216,18 +222,18 @@ def install_certificate(cert_base64, cert_password): # WARNING: contains private key password, never print this command cmd_utils.run( [ - sudo_path, - security_path, + SUDO_PATH, + SECURITY_PATH, "import", cert_path, "-k", - keychain_path, + KEYCHAIN_PATH, "-P", cert_password, "-T", - codesign_path, + CODESIGN_PATH, "-T", - security_path, + SECURITY_PATH, ], ) @@ -241,7 +247,7 @@ def notarize_package(dmg_path, user, password, team_id): def get_xcode_path(): result = cmd_utils.run( - [xcode_select_path, "-p"], get_output=True, shell=False, print_cmd=True + [XCODE_SELECT_PATH, "-p"], get_output=True, shell=False, print_cmd=True ) return result.stdout.strip() @@ -255,7 +261,7 @@ class NotaryTool: self.xcode_path = get_xcode_path() def get_path(self): - return f"{self.xcode_path}{notarytool_path}" + return f"{self.xcode_path}{NOTARYTOOL_PATH}" def store_credentials(self, user, password, team_id): print("Storing credentials for notary tool...") diff --git a/scripts/lib/meson.py b/scripts/lib/meson.py index 101b41a65..7c6f68b45 100644 --- a/scripts/lib/meson.py +++ b/scripts/lib/meson.py @@ -13,16 +13,22 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os, sys import lib.cmd_utils as cmd_utils import lib.env as env -import os build_dir = "build/meson" -meson_bin = env.get_python_executable("meson") + + +def meson_venv_bin(): + if not env.in_venv(): + raise RuntimeError("Not in a virtual environment") + + return os.path.join(os.path.dirname(sys.executable), "meson") def setup(no_system_list, static_list): - cmd = [meson_bin, "setup", build_dir] + cmd = [meson_venv_bin(), "setup", build_dir] # TODO: These special Windows exceptions should probably be in Meson # or somewhere other than this script, as it's a bit hacky. @@ -72,11 +78,11 @@ def static_subproject(subproject): def compile(): - cmd_utils.run([meson_bin, "compile", "-C", build_dir], print_cmd=True) + cmd_utils.run([meson_venv_bin(), "compile", "-C", build_dir], print_cmd=True) def install(): - cmd = [meson_bin, "install", "-C", build_dir] + cmd = [meson_venv_bin(), "install", "-C", build_dir] has_sudo = cmd_utils.has_command("sudo") if has_sudo: diff --git a/scripts/lib/qt_utils.py b/scripts/lib/qt_utils.py index ca830f638..61e577470 100644 --- a/scripts/lib/qt_utils.py +++ b/scripts/lib/qt_utils.py @@ -13,9 +13,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import os +import os, sys import lib.cmd_utils as cmd_utils -import lib.env as env import glob @@ -43,7 +42,7 @@ class Qt: print(f"Skipping Qt, already installed at: {self.dir_pattern}") return - args = [env.get_python_executable(), "-m", "aqt", "install-qt"] + args = [sys.executable, "-m", "aqt", "install-qt"] args.extend(["--outputdir", self.base_dir]) args.extend(["--base", self.mirror_url]) args.extend([self.os_name, "desktop", str(self.version), self.compiler]) diff --git a/scripts/lib/windows.py b/scripts/lib/windows.py index 163f9af48..d93a4aa1b 100644 --- a/scripts/lib/windows.py +++ b/scripts/lib/windows.py @@ -13,21 +13,22 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import ctypes, sys, os, shutil +import ctypes, sys, os, shutil, time, subprocess import xml.etree.ElementTree as ET import lib.cmd_utils as cmd_utils import lib.env as env +import psutil # type: ignore from lib.certificate import Certificate +import lib.colors as colors +import lib.file_utils as file_utils LOCK_FILE = "tmp/elevated.lock" MSBUILD_CMD = "msbuild" SIGNTOOL_CMD = "signtool" CERTUTIL_CMD = "certutil" RUNNER_TEMP_ENV = "RUNNER_TEMP" -DIST_DIR = "dist" -BUILD_DIR = "build" -WIX_FILE = f"{BUILD_DIR}/installer/Deskflow.sln" -MSI_FILE = f"{BUILD_DIR}/installer/bin/Release/Deskflow.msi" +SERVICE_NOT_RUNNING_ERROR = 2 +ERROR_ACCESS_VIOLATION = 0xC0000005 def run_elevated(script, args=None, use_sys_argv=True, wait_for_exit=False): @@ -100,17 +101,19 @@ def set_env_var(name, value): cmd_utils.run(["setx", name, new_value], check=True, shell=True, print_cmd=True) -def package(filename_base): +def package(filename_base, build_dir, dist_dir): cert_env_key = "WINDOWS_PFX_CERTIFICATE" cert_base64 = env.get_env(cert_env_key, required=False) + packager = WindowsPackager(filename_base, build_dir, dist_dir) + if cert_base64: cert_password = env.get_env("WINDOWS_PFX_PASSWORD") - sign_binaries(cert_base64, cert_password) + packager.sign_binaries(cert_base64, cert_password) - build_msi(filename_base) + packager.build_msi() if cert_base64: - sign_msi(filename_base, cert_base64, cert_password) + packager.sign_msi(cert_base64, cert_password) else: print(f"Skipped code signing, env var not set: {cert_env_key}") @@ -124,43 +127,6 @@ def assert_vs_cmd(cmd): ) -def build_msi(filename_base): - print("Building MSI installer...") - configuration = "Release" - platform = "x64" - - assert_vs_cmd(MSBUILD_CMD) - cmd_utils.run( - [ - MSBUILD_CMD, - WIX_FILE, - f"/p:Configuration={configuration}", - f"/p:Platform={platform}", - ], - shell=True, - print_cmd=True, - ) - - path = get_package_path(filename_base) - print(f"Copying MSI installer to {DIST_DIR}") - os.makedirs(DIST_DIR, exist_ok=True) - shutil.copy(MSI_FILE, path) - - -def get_package_path(filename_base): - return f"{DIST_DIR}/{filename_base}.msi" - - -def sign_binaries(cert_base64, cert_password): - exe_pattern = f"{BUILD_DIR}/bin/*.exe" - run_codesign(exe_pattern, cert_base64, cert_password) - - -def sign_msi(filename_base, cert_base64, cert_password): - path = get_package_path(filename_base) - run_codesign(path, cert_base64, cert_password) - - def run_codesign(path, cert_base64, cert_password): time_server = "http://timestamp.digicert.com" hashing_algorithm = "SHA256" @@ -187,6 +153,49 @@ def run_codesign(path, cert_base64, cert_password): ) +class WindowsPackager: + + def __init__(self, filename_base, build_dir, dist_dir): + self.filename_base = filename_base + self.build_dir = build_dir + self.dist_dir = dist_dir + self.wix_file = f"{build_dir}/installer/Installer.sln" + self.msi_file = f"{build_dir}/installer/bin/Release/Installer.msi" + + def build_msi(self): + print("Building MSI installer...") + configuration = "Release" + platform = "x64" + + assert_vs_cmd(MSBUILD_CMD) + cmd_utils.run( + [ + MSBUILD_CMD, + self.wix_file, + f"/p:Configuration={configuration}", + f"/p:Platform={platform}", + ], + shell=True, + print_cmd=True, + ) + + path = self.get_package_path() + print(f"Copying MSI installer to {self.dist_dir}") + os.makedirs(self.dist_dir, exist_ok=True) + shutil.copy(self.msi_file, path) + + def get_package_path(self): + return f"{self.dist_dir}/{self.filename_base}.msi" + + def sign_binaries(self, cert_base64, cert_password): + exe_pattern = f"{self.build_dir}/bin/*.exe" + run_codesign(exe_pattern, cert_base64, cert_password) + + def sign_msi(self, cert_base64, cert_password): + path = self.get_package_path() + run_codesign(path, cert_base64, cert_password) + + class WindowsChoco: """Chocolatey for Windows.""" @@ -215,3 +224,136 @@ class WindowsChoco: file=sys.stderr, ) sys.exit(1) + + +class WindowsService: + def __init__(self, script, args): + self.script = script + self.verbose = args.verbose + self.bin_name = args.bin_name + self.source_dir = os.path.abspath(args.source_dir) + self.target_dir = os.path.abspath(args.target_dir) + self.service_id = args.service_id + self.ignore_processes = args.ignore_processes + + def print_verbose(self, message): + if self.verbose: + print(message) + + def ensure_admin(self): + if not is_admin(): + run_elevated(self.script) + sys.exit() + + def restart(self): + """Stops the daemon service, copies files, and restarts the daemon service.""" + + self.ensure_admin() + self.stop() + self.copy_files() + self.start() + + def reinstall(self): + """Stops and uninstalls daemon service, copies files, and reinstalls the daemon service.""" + + self.ensure_admin() + self.stop() + + source_bin_path = f"{os.path.join(self.source_dir, self.bin_name)}.exe" + + self.copy_files() + + print("Removing old daemon service") + try: + subprocess.run([source_bin_path, "/uninstall"], shell=True, check=True) + except subprocess.CalledProcessError as e: + self.check_access_violation(e.returncode, source_bin_path) + if e.returncode != 0: + print( + f"{colors.WARNING_TEXT} Uninstall failed, return code: {e.returncode}", + file=sys.stderr, + ) + + target_bin_path = os.path.join(self.target_dir, self.bin_name + ".exe") + + try: + print("Installing daemon service") + subprocess.run([target_bin_path, "/install"], shell=True, check=True) + except subprocess.CalledProcessError as e: + self.check_access_violation(e.returncode, target_bin_path) + if e.returncode != 0: + print( + f"{colors.WARNING_TEXT} Install failed, return code: {e.returncode}" + ) + + def copy_files(self): + options = file_utils.CopyOptions(ignore_errors=True, verbose=False) + print(f"Copying files from {self.source_dir} to {self.target_dir}") + file_utils.copy(f"{self.source_dir}/*", self.target_dir, options) + + def stop(self): + self.ensure_admin() + print("Stopping daemon service") + try: + subprocess.run(["net", "stop", self.service_id], shell=True, check=True) + except subprocess.CalledProcessError as e: + if e.returncode == SERVICE_NOT_RUNNING_ERROR: + self.print_verbose("Daemon service not running") + else: + raise e + + # Wait for Windows to release the file handles after process termination. + self.wait_for_stop() + + def start(self): + self.ensure_admin() + print("Starting daemon service") + subprocess.run(["net", "start", self.service_id], shell=True, check=True) + + def wait_for_stop(self): + if self.is_any_process_running(self.target_dir): + print("Waiting for file handles to release...", end="", flush=True) + while self.is_any_process_running(self.target_dir): + if not self.verbose: + print(".", end="", flush=True) + time.sleep(1) + if not self.verbose: + print() + + def check_access_violation(self, return_code, bin_path): + if return_code == ERROR_ACCESS_VIOLATION: + print( + f"{colors.WARNING_TEXT} Process crashed with memory access violation: {bin_path}", + file=sys.stderr, + ) + + def is_ignored_process(self, exe): + for ignore_process in self.ignore_processes: + if exe.endswith(ignore_process): + return True + return False + + def is_any_process_running(self, dir): + """Check if there is any running process that contains the given directory.""" + + self.print_verbose(f"Checking if any process is running in: {dir}") + + for proc in psutil.process_iter(attrs=["name", "exe"]): + exe = proc.info["exe"] + + if not exe: + self.print_verbose(f"Skipping process with no exe: {proc}") + continue + + if self.is_ignored_process(exe): + self.print_verbose(f"Ignoring process: {exe}") + continue + + try: + if dir.lower() in exe.lower(): + self.print_verbose(f"Process found: {exe}") + return True + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + + return False diff --git a/scripts/package.py b/scripts/package.py index 303fc2a29..6793a7457 100755 --- a/scripts/package.py +++ b/scripts/package.py @@ -24,8 +24,14 @@ import platform from lib.linux import PackageType from dotenv import load_dotenv # type: ignore -env_file = ".env" -default_package_prefix = "deskflow" +ENV_FILE = ".env" +DEFAULT_PRODUCT_NAME = "Deskflow" +DEFAULT_FILENAME_BASE = "deskflow" +DEFAULT_PROJECT_BUILD_DIR = "build" +DEFAULT_DIST_DIR = "dist" +DEFAULT_TEST_CMD = "deskflow-server --version" +DEFAULT_PACKAGE_NAME = "deskflow" +VERSION_FILE = "VERSION" def main(): @@ -37,28 +43,69 @@ def main(): ) args = parser.parse_args() - load_dotenv(dotenv_path=env_file) + load_dotenv(dotenv_path=ENV_FILE) - version = env.get_app_version() - filename_base = get_filename_base(version) + package( + DEFAULT_FILENAME_BASE, + get_app_version(VERSION_FILE), + DEFAULT_PROJECT_BUILD_DIR, + DEFAULT_DIST_DIR, + DEFAULT_TEST_CMD, + DEFAULT_PRODUCT_NAME, + DEFAULT_PACKAGE_NAME, + leave_test_installed=args.leave_test_installed, + ) + + +def get_app_version(filename): + """ + Returns the version either from the env var, or from the version file. + """ + version = env.get_env("DESKFLOW_VERSION", required=False) + if version: + return version + + with open(filename, "r") as f: + return f.read().strip() + + +def package( + filename_prefix, + version, + project_build_dir, + dist_dir, + test_cmd, + product_name, + package_name, + source_dir=None, + leave_test_installed=False, +): + filename_base = get_filename_base(version, filename_prefix) print(f"Package filename base: {filename_base}") if env.is_windows(): - windows_package(filename_base) + windows_package(filename_base, project_build_dir, dist_dir) elif env.is_mac(): - mac_package(filename_base) + mac_package( + filename_base, source_dir, project_build_dir, dist_dir, product_name + ) elif env.is_linux(): - linux_package(filename_base, version, args.leave_test_installed) + linux_package( + filename_base, + filename_prefix, + dist_dir, + test_cmd, + package_name, + version, + leave_test_installed, + ) else: raise RuntimeError(f"Unsupported platform: {env.get_os()}") -def get_filename_base(version, use_linux_distro=True): +def get_filename_base(version, prefix, use_linux_distro=True): os = env.get_os() machine = platform.machine().lower() - package_base = env.get_env( - "DESKFLOW_PACKAGE_PREFIX", default=default_package_prefix - ) os_part = os if os == "linux" and use_linux_distro: @@ -85,31 +132,54 @@ def get_filename_base(version, use_linux_distro=True): # Underscore is used to delimit different parts of the filename (e.g. version, OS, etc). # Dashes are used to delimit spaces, e.g. "debian-trixie" for "Debian Trixie". - return f"{package_base}_{version}_{os_part}_{machine}" + return f"{prefix}_{version}_{os_part}_{machine}" -def windows_package(filename_base): +def windows_package(filename_base, project_build_dir, dist_dir): import lib.windows as windows - windows.package(filename_base) + windows.package(filename_base, project_build_dir, dist_dir) -def mac_package(filename_base): +def mac_package(filename_base, source_dir, project_build_dir, dist_dir, product_name): import lib.mac as mac - mac.package(filename_base) + mac.package(filename_base, source_dir, project_build_dir, dist_dir, product_name) -def linux_package(filename_base, version, leave_test_installed): +def linux_package( + filename_base, + filename_prefix, + dist_dir, + test_cmd, + package_name, + version, + leave_test_installed, +): import lib.linux as linux extra_packages = env.get_env_bool("LINUX_EXTRA_PACKAGES", False) - linux.package(filename_base, PackageType.DISTRO, leave_test_installed) + linux.package( + filename_base, + dist_dir, + test_cmd, + package_name, + PackageType.DISTRO, + leave_test_installed, + ) if extra_packages: - filename_base = get_filename_base(version, use_linux_distro=False) - linux.package(filename_base, PackageType.TGZ) + filename_base = get_filename_base( + version, filename_base, use_linux_distro=False + ) + linux.package( + filename_prefix, + dist_dir, + test_cmd, + package_name, + PackageType.TGZ, + ) if __name__ == "__main__": diff --git a/scripts/setup_venv.py b/scripts/setup_venv.py index 5d76e6154..d298b2954 100755 --- a/scripts/setup_venv.py +++ b/scripts/setup_venv.py @@ -17,7 +17,7 @@ import lib.env as env -env.ensure_in_venv(__file__, auto_create=True) +env.ensure_in_venv(__file__, create_venv=True) env.install_requirements() import lib.colors as colors diff --git a/src/cmd/deskflow-core/CMakeLists.txt b/src/cmd/deskflow-core/CMakeLists.txt index 09ba27ef6..f6b6c5c7d 100644 --- a/src/cmd/deskflow-core/CMakeLists.txt +++ b/src/cmd/deskflow-core/CMakeLists.txt @@ -14,12 +14,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(target deskflow-core) - +set(target ${CORE_BINARY_NAME}) set(sources ${target}.cpp) if(WIN32) - list(APPEND sources ${target}.exe.manifest ${CMAKE_BINARY_DIR}/src/version.rc) + list(APPEND sources ${target}.exe.manifest + ${PROJECT_BINARY_DIR}/src/version.rc) endif() add_executable(${target} ${sources}) diff --git a/src/cmd/deskflow-core/deskflow-core.cpp b/src/cmd/deskflow-core/deskflow-core.cpp index e5e434108..828b77cb2 100644 --- a/src/cmd/deskflow-core/deskflow-core.cpp +++ b/src/cmd/deskflow-core/deskflow-core.cpp @@ -27,11 +27,14 @@ #endif void showHelp() { - std::cout << "Usage: deskflow-core [...options]" + std::cout << "Usage: " CORE_BINARY_NAME " [...options]" << std::endl; - std::cout << "server - start as a server (deskflows)" << std::endl; - std::cout << "client - start as a client (deskflowc)" << std::endl; - std::cout << "use deskflow-core --help for more information." + std::cout << "server - start as a server (" << SERVER_BINARY_NAME << ")" + << std::endl; + std::cout << "client - start as a client (" << CLIENT_BINARY_NAME << ")" + << std::endl; + std::cout << "use " CORE_BINARY_NAME + " --help for more information." << std::endl; } diff --git a/src/cmd/deskflow-legacy/CMakeLists.txt b/src/cmd/deskflow-legacy/CMakeLists.txt index e3051748e..8d2ea8fb5 100644 --- a/src/cmd/deskflow-legacy/CMakeLists.txt +++ b/src/cmd/deskflow-legacy/CMakeLists.txt @@ -14,13 +14,13 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(target deskflow-legacy) +set(target ${LEGACY_BINARY_NAME}) file(GLOB headers "*.h") file(GLOB sources "*.cpp") if(WIN32) - list(APPEND sources ${CMAKE_BINARY_DIR}/src/version.rc) + list(APPEND sources ${PROJECT_BINARY_DIR}/src/version.rc) endif() add_executable(${target} ${sources}) diff --git a/src/cmd/deskflowc/CMakeLists.txt b/src/cmd/deskflowc/CMakeLists.txt index 0480223cd..efae255b5 100644 --- a/src/cmd/deskflowc/CMakeLists.txt +++ b/src/cmd/deskflowc/CMakeLists.txt @@ -14,9 +14,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(target deskflowc) - -set(sources ${target}.cpp) +set(client_source_name "deskflowc") +set(target ${CLIENT_BINARY_NAME}) +set(sources ${client_source_name}.cpp) if(WIN32) file(GLOB arch_headers "MSWindows*.h") @@ -25,14 +25,14 @@ if(WIN32) APPEND sources resource.h - ${target}.ico - ${target}.rc + ${client_source_name}.ico + ${client_source_name}.rc tb_error.ico tb_idle.ico tb_run.ico tb_wait.ico - ${target}.exe.manifest - ${CMAKE_BINARY_DIR}/src/version.rc) + ${client_source_name}.exe.manifest + ${PROJECT_BINARY_DIR}/src/version.rc) elseif(APPLE) file(GLOB arch_headers "OSX*.h") file(GLOB arch_sources "OSX*.cpp") diff --git a/src/cmd/deskflowd/CMakeLists.txt b/src/cmd/deskflowd/CMakeLists.txt index b63a49124..ebadc9b42 100644 --- a/src/cmd/deskflowd/CMakeLists.txt +++ b/src/cmd/deskflowd/CMakeLists.txt @@ -14,28 +14,28 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(target deskflowd) +set(target ${DAEMON_BINARY_NAME}) file(GLOB headers "*.h") file(GLOB sources "*.cpp") +# Daemon is only needed on Windows for elevating processes to deal with UAC. if(WIN32) - add_executable(${target} WIN32 ${sources} ${CMAKE_BINARY_DIR}/src/version.rc) -else() - add_executable(${target} ${sources}) + add_executable(${target} WIN32 ${sources} + ${PROJECT_BINARY_DIR}/src/version.rc) + + target_link_libraries( + ${target} + arch + base + io + ipc + mt + net + platform + app + ${libs}) + + post_config() + endif() - -target_link_libraries( - ${target} - arch - base - io - ipc - mt - net - platform - app - license - ${libs}) - -post_config() diff --git a/src/cmd/deskflows/CMakeLists.txt b/src/cmd/deskflows/CMakeLists.txt index 13a8ddfca..81e6ec489 100644 --- a/src/cmd/deskflows/CMakeLists.txt +++ b/src/cmd/deskflows/CMakeLists.txt @@ -14,9 +14,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(target deskflows) - -set(sources ${target}.cpp) +set(server_source_name "deskflows") +set(target ${SERVER_BINARY_NAME}) +set(sources ${server_source_name}.cpp) if(WIN32) file(GLOB arch_headers "MSWindows*.h") @@ -25,14 +25,14 @@ if(WIN32) APPEND sources resource.h - ${target}.ico - ${target}.rc + ${server_source_name}.ico + ${server_source_name}.rc tb_error.ico tb_idle.ico tb_run.ico tb_wait.ico - ${target}.exe.manifest - ${CMAKE_BINARY_DIR}/src/version.rc) + ${server_source_name}.exe.manifest + ${PROJECT_BINARY_DIR}/src/version.rc) elseif(APPLE) file(GLOB arch_headers "OSX*.h") file(GLOB arch_sources "OSX*.cpp") diff --git a/src/cmd/deskflows/resource.h b/src/cmd/deskflows/resource.h index 0c4c0b349..f30b20d0c 100644 --- a/src/cmd/deskflows/resource.h +++ b/src/cmd/deskflows/resource.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by deskflows.rc +// Used by server .rc file // #define IDS_FAILED 1 #define IDS_INIT_FAILED 2 diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 163803675..12f0989c3 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -13,15 +13,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(target deskflow) +set(target ${GUI_BINARY_NAME}) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(res_dir ${CMAKE_SOURCE_DIR}/res/gui) -set(qrc_file ${res_dir}/Deskflow.qrc) +set(res_dir ${GUI_RES_DIR}) +set(qrc_file ${GUI_QRC_FILE}) file( GLOB @@ -33,22 +33,25 @@ file( file(GLOB ui_files src/*.ui) if(WIN32) - set(rc_files ${res_dir}/win/Deskflow.rc ${CMAKE_BINARY_DIR}/src/version.rc) + set(rc_files ${res_dir}/win/app.rc ${PROJECT_BINARY_DIR}/src/version.rc) endif() -add_executable(${target} WIN32 ${sources} ${ui_files} ${rc_files} ${qrc_file}) - # regular exe headers include_directories(./src) # gui library autogen headers: # qt doesn't seem to auto include the autogen headers for libraries. -include_directories(${CMAKE_BINARY_DIR}/src/lib/gui/gui_autogen/include) +include_directories(${PROJECT_BINARY_DIR}/src/lib/gui/gui_autogen/include) + +# generated includes +include_directories(${PROJECT_BINARY_DIR}/config) + +add_executable(${target} WIN32 ${sources} ${ui_files} ${rc_files} ${qrc_file}) target_link_libraries( ${target} + ${DESKFLOW_GUI_HOOK_LIB} gui - license Qt6::Core Qt6::Widgets Qt6::Network) @@ -68,7 +71,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") message(STATUS "Found macdeployqt6: ${MACDEPLOYQT_BIN}") set(MACDEPLOYQT_CMD - "${MACDEPLOYQT_BIN} ${DESKFLOW_BUNDLE_APP_DIR} -always-overwrite") + "${MACDEPLOYQT_BIN} ${DESKFLOW_BUNDLE_DIR} -always-overwrite") install(TARGETS ${target} DESTINATION ${DESKFLOW_BUNDLE_BINARY_DIR}) install(CODE "MESSAGE (\"Running: ${MACDEPLOYQT_CMD}\")") @@ -109,7 +112,7 @@ elseif(WIN32) POST_BUILD COMMAND set PATH=%PATH%$${qt6_install_prefix}/bin COMMAND Qt6::windeployqt - "$/$") + "$/$") endif() endif() diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp index b985cde47..1eedb9017 100644 --- a/src/gui/src/AboutDialog.cpp +++ b/src/gui/src/AboutDialog.cpp @@ -23,7 +23,8 @@ #include "gui/style_utils.h" #include -#include +#include +#include using namespace deskflow::gui; @@ -42,6 +43,8 @@ AboutDialog::AboutDialog(MainWindow *parent) QDate buildDate = QLocale("en_US").toDate(buildDateString, "yyyy-MM-dd"); m_pLabelBuildDate->setText( buildDate.toString(QLocale::system().dateFormat(QLocale::LongFormat))); + + this->setWindowTitle(QString("About %1").arg(DESKFLOW_APP_NAME)); } int AboutDialog::exec() { @@ -52,15 +55,22 @@ int AboutDialog::exec() { return QDialog::exec(); } +void AboutDialog::setLogo(const char *const &filename) const { + QPixmap logo(filename); + if (!logo.isNull()) { + m_pLabel_Logo->setPixmap(logo); + } else { + qCritical("logo file not found: %s", qPrintable(filename)); + } +} + void AboutDialog::updateLogo() const { if (isDarkMode()) { - qDebug("dark mode detected, showing dark logo"); - QPixmap logo(":/image/logo-dark.png"); - if (!logo.isNull()) { - m_pLabel_Logo->setPixmap(logo); - } + qDebug("showing dark logo"); + setLogo(":/image/logo-dark.png"); } else { - qDebug("light mode detected, keeping light logo"); + qDebug("showing light logo"); + setLogo(":/image/logo-light.png"); } } diff --git a/src/gui/src/AboutDialog.h b/src/gui/src/AboutDialog.h index 576c794f5..32a9386ed 100644 --- a/src/gui/src/AboutDialog.h +++ b/src/gui/src/AboutDialog.h @@ -36,6 +36,7 @@ public: private: VersionChecker m_versionChecker; void updateLogo() const; + void setLogo(const char *const &filename) const; virtual QString importantDevelopers() const; }; diff --git a/src/gui/src/AboutDialogBase.ui b/src/gui/src/AboutDialogBase.ui index 6c5314969..1af5ea6c7 100644 --- a/src/gui/src/AboutDialogBase.ui +++ b/src/gui/src/AboutDialogBase.ui @@ -105,26 +105,11 @@ - - - 0 - 0 - - - - - 200 - 39 - - - :/image/logo-light.png - - - true + :/image/placeholder.png 0 diff --git a/src/gui/src/ActivationDialog.cpp b/src/gui/src/ActivationDialog.cpp deleted file mode 100644 index 5d36765b9..000000000 --- a/src/gui/src/ActivationDialog.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "ActivationDialog.h" - -#include "CancelActivationDialog.h" -#include "MainWindow.h" -#include "gui/config/AppConfig.h" -#include "gui/constants.h" -#include "gui/license/LicenseHandler.h" -#include "gui/license/license_notices.h" -#include "gui/styles.h" -#include "license/ProductEdition.h" -#include "license/parse_serial_key.h" -#include "ui_ActivationDialog.h" - -#include -#include -#include - -using namespace deskflow::gui; -using namespace deskflow::license; - -ActivationDialog::ActivationDialog( - QWidget *parent, AppConfig &appConfig, LicenseHandler &licenseHandler) - : QDialog(parent), - m_ui(new Ui::ActivationDialog), - m_pAppConfig(&appConfig), - m_licenseHandler(licenseHandler) { - - m_ui->setupUi(this); - - m_ui->m_pLabelNotice->setStyleSheet(kStyleNoticeLabel); - - refreshSerialKey(); - - if (!m_licenseHandler.license().isExpired()) { - m_ui->m_widgetNotice->hide(); - } - - const QString envSerialKey = ::getenv("DESKFLOW_TEST_SERIAL_KEY"); - if (!envSerialKey.isEmpty()) { - qDebug() << "using env test serial key:" << envSerialKey; - m_ui->m_pTextEditSerialKey->setText(envSerialKey); - } -} - -void ActivationDialog::refreshSerialKey() { - m_ui->m_pTextEditSerialKey->setText(m_pAppConfig->serialKey()); - m_ui->m_pTextEditSerialKey->setFocus(); - m_ui->m_pTextEditSerialKey->moveCursor(QTextCursor::End); - - const auto &license = m_licenseHandler.license(); - if (license.isTimeLimited()) { - m_ui->m_pLabelNotice->setText(licenseNotice(license)); - } -} - -ActivationDialog::~ActivationDialog() { delete m_ui; } - -void ActivationDialog::reject() { - // don't show the cancel confirmation dialog if they've already registered, - // since it's not relevant to customers who are changing their serial key. - if (m_licenseHandler.productEdition() != Edition::kUnregistered) { - QDialog::reject(); - return; - } - - // the accept button should be labeled "Exit" on the cancel dialog. - CancelActivationDialog cancelActivationDialog(this); - if (cancelActivationDialog.exec() == QDialog::Accepted) { - QApplication::exit(); - } -} - -void ActivationDialog::accept() { - using Result = LicenseHandler::ChangeSerialKeyResult; - auto serialKey = m_ui->m_pTextEditSerialKey->toPlainText(); - - if (serialKey.isEmpty()) { - QMessageBox::information(this, "Activation", "Please enter a serial key."); - return; - } - - const auto result = m_licenseHandler.changeSerialKey(serialKey); - if (result != Result::kSuccess) { - showResultDialog(result); - return; - } - - showSuccessDialog(); - QDialog::accept(); -} - -void ActivationDialog::showResultDialog( - LicenseHandler::ChangeSerialKeyResult result) { - const QString title = "Activation result"; - - switch (result) { - using enum LicenseHandler::ChangeSerialKeyResult; - - case kUnchanged: - QMessageBox::information( - this, title, - "Heads up, the serial key you entered was the same as last time."); - QDialog::accept(); - break; - - case kInvalid: - QMessageBox::critical( - this, title, - QString( - "Invalid serial key. " - R"(Please contact us for help.)") - .arg(kUrlContact) - .arg(kColorSecondary)); - break; - - case kExpired: - QMessageBox::warning( - this, title, - QString( - "Sorry, that serial key has expired. " - R"(Please renew your license.)") - .arg(kUrlPurchase) - .arg(kColorSecondary)); - break; - - default: - qFatal("unexpected change serial key result: %d", static_cast(result)); - } -} - -void ActivationDialog::showSuccessDialog() { - const auto &license = m_licenseHandler.license(); - - QString title = "Activation successful"; - QString message = tr("

Thanks for activating %1.

") - .arg(m_licenseHandler.productName()); - - TlsUtility tls(*m_pAppConfig, license); - if (tls.isAvailableAndEnabled()) { - message += - "

To ensure that TLS encryption works correctly, " - "please activate all of your computers with the same serial key.

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

There was a problem activating Deskflow.

" - R"(

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

" - "%3") - .arg(kUrlContact) - .arg(kColorSecondary) - .arg(message); - QMessageBox::critical(this, "Activation failed", fullMessage); -} diff --git a/src/gui/src/ActivationDialog.h b/src/gui/src/ActivationDialog.h deleted file mode 100644 index ca1d7c3a3..000000000 --- a/src/gui/src/ActivationDialog.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "gui/license/LicenseHandler.h" - -#include - -namespace Ui { -class ActivationDialog; -} - -class AppConfig; - -class ActivationDialog : public QDialog { - Q_OBJECT - -public: - ActivationDialog( - QWidget *parent, AppConfig &appConfig, LicenseHandler &licenseHandler); - ~ActivationDialog() override; - - class ActivationMessageError : public std::runtime_error { - public: - ActivationMessageError() - : std::runtime_error("could not show activation message") {} - }; - -public slots: - void reject() override; - void accept() override; - -protected: - void refreshSerialKey(); - -private: - void showResultDialog(LicenseHandler::ChangeSerialKeyResult result); - void showSuccessDialog(); - void showErrorDialog(const QString &message); - - Ui::ActivationDialog *m_ui; - AppConfig *m_pAppConfig; - LicenseHandler &m_licenseHandler; -}; diff --git a/src/gui/src/ActivationDialog.ui b/src/gui/src/ActivationDialog.ui deleted file mode 100644 index 634b0ec4a..000000000 --- a/src/gui/src/ActivationDialog.ui +++ /dev/null @@ -1,153 +0,0 @@ - - - ActivationDialog - - - - 0 - 0 - 541 - 241 - - - - Activate Synergy - - - - - - - true - - - - Serial key - - - - - - - <p>Your serial key is on your <a href="https://symless.com/synergy/account?source=gui" style="color:#4285f4;">account</span></a> page. Don't have a license? <a href="https://symless.com/synergy/purchase?source=gui" style="color:#4285f4;">Purchase Synergy</span></a></p> - - - true - - - - - - - true - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> -p, li { white-space: pre-wrap; } -hr { height: 1px; border-width: 0; } -li.unchecked::marker { content: "\2610"; } -li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p></body></html> - - - false - - - - - - - - 2 - - - 0 - - - 0 - - - 8 - - - - - m_pLabelNotice - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - m_pTextEditSerialKey - - - - - buttonBox - accepted() - ActivationDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - ActivationDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/gui/src/CancelActivationDialog.cpp b/src/gui/src/CancelActivationDialog.cpp deleted file mode 100644 index be2810232..000000000 --- a/src/gui/src/CancelActivationDialog.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "CancelActivationDialog.h" - -#include "ui_CancelActivationDialog.h" - -#include "QPushButton" - -CancelActivationDialog::CancelActivationDialog(QWidget *parent) - : QDialog(parent), - ui(new Ui::CancelActivationDialog) { - ui->setupUi(this); - - ui->m_pButtonBox->button(QDialogButtonBox::Cancel)->setText("&Back"); - ui->m_pButtonBox->button(QDialogButtonBox::Ok)->setText("&Exit"); -} - -CancelActivationDialog::~CancelActivationDialog() { delete ui; } diff --git a/src/gui/src/CancelActivationDialog.h b/src/gui/src/CancelActivationDialog.h deleted file mode 100644 index 41debe971..000000000 --- a/src/gui/src/CancelActivationDialog.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -namespace Ui { -class CancelActivationDialog; -} - -class CancelActivationDialog : public QDialog { - Q_OBJECT - -public: - explicit CancelActivationDialog(QWidget *parent = 0); - ~CancelActivationDialog(); - -private: - Ui::CancelActivationDialog *ui; -}; diff --git a/src/gui/src/CancelActivationDialog.ui b/src/gui/src/CancelActivationDialog.ui deleted file mode 100644 index 975c0792b..000000000 --- a/src/gui/src/CancelActivationDialog.ui +++ /dev/null @@ -1,77 +0,0 @@ - - - CancelActivationDialog - - - - 0 - 0 - 429 - 273 - - - - Cancel Activation - - - - - - <html><head/><body><p>You'll need to purchase a license to use this build of Synergy.</p><p><a href="https://symless.com/synergy/purchase?source=gui"><span style=" text-decoration: underline; color:#007af4;">Purchase Synergy</span></a></p><p>If you'd prefer to use the community edition instead, visit us on GitHub.</p><p><a href="https://github.com/symless/synergy"><span style=" text-decoration: underline; color:#007af4;">GitHub project</span></a></p></body></html> - - - true - - - true - - - - - - - Qt::Orientation::Horizontal - - - QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok - - - - - - - - - m_pButtonBox - accepted() - CancelActivationDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - m_pButtonBox - rejected() - CancelActivationDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/gui/src/FailedLoginDialog.cpp b/src/gui/src/FailedLoginDialog.cpp deleted file mode 100644 index c78903f04..000000000 --- a/src/gui/src/FailedLoginDialog.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "FailedLoginDialog.h" -#include "ui_FailedLoginDialog.h" - -FailedLoginDialog::FailedLoginDialog(QWidget *parent, QString message) - : QDialog(parent), - ui(new Ui::FailedLoginDialog) { - ui->setupUi(this); - ui->messageLabel->setText(ui->messageLabel->text().arg(message)); -} - -FailedLoginDialog::~FailedLoginDialog() { delete ui; } diff --git a/src/gui/src/FailedLoginDialog.h b/src/gui/src/FailedLoginDialog.h deleted file mode 100644 index 5934ed075..000000000 --- a/src/gui/src/FailedLoginDialog.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef FAILEDLOGINDIALOG_H -#define FAILEDLOGINDIALOG_H - -#include -#include - -namespace Ui { -class FailedLoginDialog; -} - -class FailedLoginDialog : public QDialog { - Q_OBJECT - -public: - explicit FailedLoginDialog(QWidget *parent = 0, QString message = ""); - ~FailedLoginDialog(); - -private: - Ui::FailedLoginDialog *ui; -}; - -#endif // FAILEDLOGINDIALOG_H diff --git a/src/gui/src/FailedLoginDialog.ui b/src/gui/src/FailedLoginDialog.ui deleted file mode 100644 index d3c6b5077..000000000 --- a/src/gui/src/FailedLoginDialog.ui +++ /dev/null @@ -1,108 +0,0 @@ - - - FailedLoginDialog - - - - 0 - 0 - 400 - 165 - - - - Activation Error - - - - - 50 - 120 - 341 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - 10 - 90 - 382 - 30 - - - - <html><head/><body><p><a href="https://symless.com/account/reset/?source=gui"><span style=" text-decoration: underline; color:#0000ff;">Forgotten your password?</span></a></p></body></html> - - - true - - - - - - 10 - 10 - 382 - 72 - - - - An error occurred while trying to activate Synergy. The Symless server returned the following error: - -%1 - - - true - - - true - - - label_2 - messageLabel - buttonBox - - - - - buttonBox - accepted() - FailedLoginDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - FailedLoginDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index 18f863e50..9a3f0c779 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -19,25 +19,18 @@ #include "MainWindow.h" #include "AboutDialog.h" -#include "ActivationDialog.h" #include "ServerConfigDialog.h" #include "common/constants.h" #include "gui/Logger.h" -#include "gui/TrayIcon.h" -#include "gui/VersionChecker.h" #include "gui/config/ConfigScopes.h" #include "gui/constants.h" #include "gui/core/CoreProcess.h" #include "gui/diagnostic.h" #include "gui/dialogs/SettingsDialog.h" -#include "gui/license/LicenseHandler.h" -#include "gui/license/license_notices.h" -#include "gui/license/license_utils.h" #include "gui/messages.h" #include "gui/string_utils.h" #include "gui/styles.h" #include "gui/tls/TlsFingerprint.h" -#include "license/License.h" #include "platform/wayland.h" #if defined(Q_OS_MAC) @@ -66,8 +59,6 @@ #endif using namespace deskflow::gui; -using namespace deskflow::license; -using namespace deskflow::gui::license; using CoreMode = CoreProcess::Mode; using CoreConnectionState = CoreProcess::ConnectionState; @@ -84,11 +75,11 @@ MainWindow::MainWindow(ConfigScopes &configScopes, AppConfig &appConfig) : m_ConfigScopes(configScopes), m_AppConfig(appConfig), m_ServerConfig(appConfig, *this), - m_CoreProcess(appConfig, m_ServerConfig, m_LicenseHandler.license()), + m_CoreProcess(appConfig, m_ServerConfig), m_ServerConnection( this, appConfig, m_ServerConfig, m_ServerConfigDialogState), m_ClientConnection(this, appConfig), - m_TlsUtility(appConfig, m_LicenseHandler.license()), + m_TlsUtility(appConfig), m_WindowSaveTimer(this) { setupUi(this); @@ -140,16 +131,9 @@ void MainWindow::saveWindow() { } void MainWindow::setupControls() { - if (!isActivationEnabled()) { - updateWindowTitle(); - } + setWindowTitle(kAppName); - if (!isLicensedProduct()) { - m_pActionHelp->setText("Report a bug"); - m_pActionActivate->setText("Purchase"); - } else if (!isActivationEnabled()) { - m_pActionActivate->setVisible(false); - } + m_pActionHelp->setText(DESKFLOW_HELP_TEXT); secureSocket(false); updateLocalFingerprint(); @@ -234,14 +218,6 @@ void MainWindow::connectSlots() { &m_CoreProcess, &CoreProcess::secureSocket, this, &MainWindow::onCoreProcessSecureSocket); - connect( - &m_LicenseHandler, &LicenseHandler::serialKeyChanged, this, - &MainWindow::onLicenseHandlerSerialKeyChanged); - - connect( - &m_LicenseHandler, &LicenseHandler::invalidLicense, this, - &MainWindow::onLicenseHandlerInvalidLicense); - connect(m_pActionMinimize, &QAction::triggered, this, &MainWindow::hide); connect( @@ -293,10 +269,6 @@ void MainWindow::onCreated() { applyCloseToTray(); - if (isActivationEnabled() && !m_AppConfig.serialKey().isEmpty()) { - m_LicenseHandler.changeSerialKey(m_AppConfig.serialKey()); - } - updateScreenName(); applyConfig(); restoreWindow(); @@ -306,14 +278,6 @@ void MainWindow::onCreated() { } void MainWindow::onShown() { - if (isActivationEnabled()) { - const auto &license = m_LicenseHandler.license(); - if (!m_AppConfig.activationHasRun() || !license.isValid() || - license.isExpired()) { - showActivationDialog(); - } - } - // if a critical error was shown just before the main window (i.e. on app // load), it will be hidden behind the main window. therefore we need to raise // it up in front of the main window. @@ -326,26 +290,11 @@ void MainWindow::onShown() { kCriticalDialogDelay, [] { messages::raiseCriticalDialog(); }); } -void MainWindow::onLicenseHandlerSerialKeyChanged(const QString &serialKey) { - updateWindowTitle(); - showLicenseNotice(); - - if (m_AppConfig.serialKey() != serialKey) { - m_AppConfig.setSerialKey(serialKey); - m_ConfigScopes.save(); - } -} - -void MainWindow::onLicenseHandlerInvalidLicense() { - m_CoreProcess.stop(); - showActivationDialog(); -} - void MainWindow::onConfigScopesSaving() { m_ServerConfig.commit(); } void MainWindow::onAppConfigTlsChanged() { updateLocalFingerprint(); - if (m_TlsUtility.isAvailableAndEnabled()) { + if (m_TlsUtility.isEnabled()) { m_TlsUtility.generateCertificate(); } } @@ -441,17 +390,12 @@ void MainWindow::on_m_pActionAbout_triggered() { } void MainWindow::on_m_pActionHelp_triggered() const { - if (isLicensedProduct()) { - QDesktopServices::openUrl(QUrl(kUrlHelp)); - } else { - QDesktopServices::openUrl(QUrl(kUrlBugReport)); - } + QDesktopServices::openUrl(QUrl(kUrlHelp)); } void MainWindow::on_m_pActionSettings_triggered() { - auto dialog = SettingsDialog( - this, m_AppConfig, m_ServerConfig, m_LicenseHandler.license(), - m_CoreProcess); + auto dialog = + SettingsDialog(this, m_AppConfig, m_ServerConfig, m_CoreProcess); if (dialog.exec() == QDialog::Accepted) { m_ConfigScopes.save(); @@ -469,14 +413,6 @@ void MainWindow::on_m_pButtonConfigureServer_clicked() { showConfigureServer(); } -void MainWindow::on_m_pActionActivate_triggered() { - if (isLicensedProduct()) { - showActivationDialog(); - } else { - QDesktopServices::openUrl(QUrl(kUrlPurchase)); - } -} - void MainWindow::on_m_pLineEditHostname_returnPressed() { m_pButtonConnect->click(); } @@ -587,15 +523,6 @@ void MainWindow::open() { void MainWindow::onCoreProcessStarting() { - if (isActivationEnabled()) { - const auto &license = m_LicenseHandler.license(); - if (license.isExpired() && showActivationDialog() == QDialog::Rejected) { - qDebug("license expired, cancelling core start"); - m_CoreProcess.stop(); - return; - } - } - #if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI) if (deskflow::platform::isWayland()) { m_WaylandWarnings.showOnce(this, m_CoreProcess.mode()); @@ -626,8 +553,6 @@ void MainWindow::createMenuBar() { m_pMenuFile->addAction(m_pActionStartCore); m_pMenuFile->addAction(m_pActionStopCore); m_pMenuFile->addSeparator(); - m_pMenuFile->addAction(m_pActionActivate); - m_pMenuFile->addSeparator(); m_pMenuFile->addAction(m_pActionSave); m_pMenuFile->addSeparator(); m_pMenuFile->addAction(m_pActionQuit); @@ -642,6 +567,8 @@ void MainWindow::createMenuBar() { m_pMenuFile->addSeparator(); m_pMenuHelp->addAction(m_pActionClearSettings); + m_pActionAbout->setText(QString("About %1...").arg(kAppName)); + const auto enableTestMenu = strToTrue(qEnvironmentVariable("DESKFLOW_TEST_MENU")); @@ -723,10 +650,6 @@ void MainWindow::handleLogLine(const QString &line) { void MainWindow::updateFromLogLine(const QString &line) { checkConnected(line); checkFingerprint(line); - - if (isActivationEnabled()) { - checkLicense(line); - } } void MainWindow::checkConnected(const QString &line) { @@ -739,12 +662,6 @@ void MainWindow::checkConnected(const QString &line) { } } -void MainWindow::checkLicense(const QString &line) { - if (line.contains("trial has expired")) { - showActivationDialog(); - } -} - void MainWindow::checkFingerprint(const QString &line) { QRegularExpression re(".*server fingerprint: ([A-F0-9:]+)"); auto match = re.match(line); @@ -836,15 +753,10 @@ void MainWindow::showDevThanksMessage() { return; } - if (isActivationEnabled()) { - qDebug("activation enabled, skipping dev thanks message"); - return; - } - m_AppConfig.setShowDevThanks(false); m_ConfigScopes.save(); - messages::showDevThanks(this, kProductName); + messages::showDevThanks(this, kAppName); } void MainWindow::onCoreProcessSecureSocket(bool enabled) { @@ -859,19 +771,19 @@ void MainWindow::updateStatus() { using enum CoreProcessState; case Starting: - setStatus("Deskflow is starting..."); + setStatus(QString("%1 is starting...").arg(kAppName)); break; case RetryPending: - setStatus("Deskflow will retry in a moment..."); + setStatus(QString("%1 will retry in a moment...").arg(kAppName)); break; case Stopping: - setStatus("Deskflow is stopping..."); + setStatus(QString("%1 is stopping...").arg(kAppName)); break; case Stopped: - setStatus("Deskflow is not running"); + setStatus(QString("%1 is not running").arg(kAppName)); break; case Started: { @@ -880,28 +792,29 @@ void MainWindow::updateStatus() { case Listening: { if (m_CoreProcess.mode() == CoreMode::Server) { - setStatus("Deskflow is waiting for clients"); + setStatus(QString("%1 is waiting for clients").arg(kAppName)); } break; } case Connecting: - setStatus("Deskflow is connecting..."); + setStatus(QString("%1 is connecting...").arg(kAppName)); break; case Connected: { if (m_SecureSocket) { - setStatus(QString("Deskflow is connected (with %1)") - .arg(m_CoreProcess.secureSocketVersion())); + setStatus(QString("%1 is connected (with %2)") + .arg(kAppName, m_CoreProcess.secureSocketVersion())); } else { - setStatus("Deskflow is connected (without TLS encryption)"); + setStatus( + QString("%1 is connected (without TLS encryption)").arg(kAppName)); } break; } case Disconnected: - setStatus("Deskflow is disconnected"); + setStatus(QString("%1 is disconnected").arg(kAppName)); break; } } break; @@ -1014,17 +927,6 @@ QString MainWindow::getIPAddresses() const { return result.join(", "); } -void MainWindow::showLicenseNotice() { - const auto &license = m_LicenseHandler.license(); - const bool timeLimited = license.isTimeLimited(); - - m_pLabelNotice->setVisible(timeLimited); - if (timeLimited) { - auto notice = licenseNotice(m_LicenseHandler.license()); - this->m_pLabelNotice->setText(notice); - } -} - void MainWindow::updateLocalFingerprint() { bool fingerprintExists = false; try { @@ -1042,28 +944,8 @@ void MainWindow::updateLocalFingerprint() { } } -QString MainWindow::productName() const { - if (isActivationEnabled()) { - return m_LicenseHandler.productName(); - } else if (!kProductName.isEmpty()) { - return kProductName; - } else { - qFatal("product name not set"); - return ""; - } -} - -void MainWindow::updateWindowTitle() { setWindowTitle(productName()); } - void MainWindow::autoAddScreen(const QString name) { - if (m_ActivationDialogRunning) { - // add this screen to the pending list if the activation dialog is - // running. - m_PendingClientNames.append(name); - return; - } - int r = m_ServerConfig.autoAddScreen(name); if (r != kAutoAddScreenOk) { switch (r) { @@ -1089,48 +971,6 @@ void MainWindow::showConfigureServer(const QString &message) { } } -int MainWindow::showActivationDialog() { - if (!isActivationEnabled()) { - qFatal("cannot show activation dialog when activation is disabled"); - } - - if (m_ActivationDialogRunning) { - return QDialog::Rejected; - } - - ActivationDialog activationDialog(this, m_AppConfig, m_LicenseHandler); - - m_ActivationDialogRunning = true; - int result = activationDialog.exec(); - m_ActivationDialogRunning = false; - - if (result == QDialog::Accepted) { - m_AppConfig.setActivationHasRun(true); - - // customers who are activating a pro license are usually doing so because - // they want tls. so, if it's available, turn it on after activating. - m_AppConfig.setTlsEnabled(m_LicenseHandler.license().isTlsAvailable()); - - m_ConfigScopes.save(); - } - - if (!m_PendingClientNames.empty()) { - foreach (const QString &name, m_PendingClientNames) { - autoAddScreen(name); - } - - m_PendingClientNames.clear(); - } - - // restart core process after activation in case switching on tls. - // this saves customer from having to figure out they need to click apply. - if (m_CoreProcess.isStarted()) { - m_CoreProcess.restart(); - } - - return result; -} - void MainWindow::secureSocket(bool secureSocket) { m_SecureSocket = secureSocket; if (secureSocket) { diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h index 047b30407..5602cb695 100644 --- a/src/gui/src/MainWindow.h +++ b/src/gui/src/MainWindow.h @@ -25,7 +25,6 @@ #include #include -#include "ActivationDialog.h" #include "ServerConfig.h" #include "common/ipc.h" #include "gui/TrayIcon.h" @@ -64,7 +63,6 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase { friend class DeskflowApplication; friend class SetupWizard; - friend class ActivationDialog; friend class SettingsDialog; public: @@ -81,7 +79,6 @@ public: void open(); ServerConfig &serverConfig() { return m_ServerConfig; } void autoAddScreen(const QString name); - int showActivationDialog(); signals: void created(); @@ -105,8 +102,6 @@ private slots: void onCoreConnectionStateChanged(CoreProcess::ConnectionState state); void onCoreProcessStateChanged(CoreProcess::ProcessState state); void onCoreProcessSecureSocket(bool enabled); - void onLicenseHandlerSerialKeyChanged(const QString &serialKey); - void onLicenseHandlerInvalidLicense(); void onVersionCheckerUpdateFound(const QString &version); void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason); void onWindowSaveTimerTimeout(); @@ -127,7 +122,6 @@ private slots: void on_m_pActionAbout_triggered(); void on_m_pActionHelp_triggered() const; void on_m_pActionSettings_triggered(); - void on_m_pActionActivate_triggered(); void on_m_pActionStartCore_triggered(); void on_m_pActionStopCore_triggered(); void on_m_pActionTestFatalError_triggered() const; @@ -156,7 +150,6 @@ private: void enableClient(bool enable); void checkConnected(const QString &line); void checkFingerprint(const QString &line); - void checkLicense(const QString &line); QString getTimeStamp() const; void showEvent(QShowEvent *) override; void closeEvent(QCloseEvent *event) override; @@ -171,7 +164,6 @@ private: QString configFilename(); void showConfigureServer(const QString &message); void showConfigureServer() { showConfigureServer(""); } - void showLicenseNotice(); void restoreWindow(); void saveWindow(); void setupControls(); @@ -179,14 +171,11 @@ private: void moveEvent(QMoveEvent *event) override; void showFirstConnectedMessage(); void showDevThanksMessage(); - QString productName() const; void updateStatus(); void showAndActivate(); VersionChecker m_VersionChecker; deskflow::gui::TrayIcon m_TrayIcon; - bool m_ActivationDialogRunning = false; - QStringList m_PendingClientNames; QMenuBar *m_pMenuBar = nullptr; QMenu *m_pMenuFile = nullptr; QMenu *m_pMenuEdit = nullptr; @@ -195,7 +184,6 @@ private: QAbstractButton *m_pCancelButton = nullptr; bool m_SecureSocket = false; bool m_SaveWindow = false; - LicenseHandler m_LicenseHandler; bool m_Quitting = false; deskflow::gui::config::ServerConfigDialogState m_ServerConfigDialogState; bool m_SaveOnExit = true; diff --git a/src/gui/src/MainWindowBase.ui b/src/gui/src/MainWindowBase.ui index 7995019bd..14f896bb9 100644 --- a/src/gui/src/MainWindowBase.ui +++ b/src/gui/src/MainWindowBase.ui @@ -501,7 +501,7 @@
- :/icons/16x16/padlock.png + :/icons/16x16/padlock.png
@@ -683,14 +683,6 @@
- - - Activate - - - Activate - - Test fatal error diff --git a/src/gui/src/ScreenSetupModel.cpp b/src/gui/src/ScreenSetupModel.cpp index f4ed993f5..d11307874 100644 --- a/src/gui/src/ScreenSetupModel.cpp +++ b/src/gui/src/ScreenSetupModel.cpp @@ -23,7 +23,8 @@ #include #include -const QString ScreenSetupModel::m_MimeType = "application/x-deskflow-screen"; +const QString ScreenSetupModel::m_MimeType = + "application/x-" DESKFLOW_APP_ID "-screen"; ScreenSetupModel::ScreenSetupModel( ScreenList &screens, int numColumns, int numRows) diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp index 0417c1682..591187b53 100644 --- a/src/gui/src/ServerConfigDialog.cpp +++ b/src/gui/src/ServerConfigDialog.cpp @@ -412,10 +412,12 @@ void ServerConfigDialog::on_m_pCheckBoxUseExternalConfig_toggled(bool checked) { bool ServerConfigDialog::on_m_pButtonBrowseConfigFile_clicked() { #if defined(Q_OS_WIN) const QString deskflowConfigFilter( - QObject::tr("Deskflow Configurations (*.sgc);;All files (*.*)")); + QString("%1 Configurations (*.sgc);;All files (*.*)") + .arg(DESKFLOW_APP_NAME)); #else const QString deskflowConfigFilter( - QObject::tr("Deskflow Configurations (*.conf);;All files (*.*)")); + QString("%1 Configurations (*.conf);;All files (*.*)") + .arg(DESKFLOW_APP_NAME)); #endif QString fileName = QFileDialog::getOpenFileName( diff --git a/src/gui/src/ServerConfigDialogBase.ui b/src/gui/src/ServerConfigDialogBase.ui index 822f0504e..a8a420821 100644 --- a/src/gui/src/ServerConfigDialogBase.ui +++ b/src/gui/src/ServerConfigDialogBase.ui @@ -943,7 +943,7 @@ Enabling this setting will disable the server config GUI. - Use Core server config file + Use a server config file diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp index c3b615b60..6da725d53 100644 --- a/src/gui/src/SetupWizard.cpp +++ b/src/gui/src/SetupWizard.cpp @@ -26,6 +26,8 @@ using namespace deskflow::gui; SetupWizard::SetupWizard(AppConfig &appConfig) : m_appConfig(appConfig) { setupUi(this); + setWindowTitle(QString("Setup %1").arg(DESKFLOW_APP_NAME)); + m_pLabelError->setStyleSheet(kStyleErrorActiveLabel); m_pLineEditName->setText(appConfig.screenName()); diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp index 7366b56d7..5130d489b 100644 --- a/src/gui/src/main.cpp +++ b/src/gui/src/main.cpp @@ -24,13 +24,20 @@ #include "gui/Logger.h" #include "gui/config/AppConfig.h" #include "gui/config/ConfigScopes.h" +#include "gui/constants.h" #include "gui/diagnostic.h" #include "gui/dotenv.h" #include "gui/messages.h" #include "gui/string_utils.h" +#include "gui_config.h" // IWYU pragma: keep + +#ifdef DESKFLOW_GUI_HOOK_HEADER +#include DESKFLOW_GUI_HOOK_HEADER +#endif #include #include +#include #include #include #include @@ -67,8 +74,6 @@ int main(int argc, char *argv[]) { #endif QCoreApplication::setApplicationName(kAppName); - - // HACK: set org name to app name for backwards compatibility. QCoreApplication::setOrganizationName(kAppName); // used as a prefix for settings paths, and must not be a url. @@ -78,7 +83,7 @@ int main(int argc, char *argv[]) { qInstallMessageHandler(deskflow::gui::messages::messageHandler); QString version = QString::fromStdString(deskflow::version()); - qInfo("Deskflow v%s", qPrintable(version)); + qInfo(DESKFLOW_APP_NAME " v%s", qPrintable(version)); dotenv(); Logger::instance().loadEnvVars(); @@ -87,8 +92,8 @@ int main(int argc, char *argv[]) { if (app.applicationDirPath().startsWith("/Volumes/")) { QMessageBox::information( - NULL, "Deskflow", - "Please drag Deskflow to the Applications folder, " + NULL, DESKFLOW_APP_NAME, + "Please drag " DESKFLOW_APP_NAME " to the Applications folder, " "and open it from there."); return 1; } @@ -98,8 +103,6 @@ int main(int argc, char *argv[]) { } #endif - qRegisterMetaType("Edition"); - ConfigScopes configScopes; // --no-reset @@ -135,6 +138,11 @@ int main(int argc, char *argv[]) { &MainWindow::onAppAboutToQuit); mainWindow.open(); + +#ifdef DESKFLOW_GUI_HOOK_START + DESKFLOW_GUI_HOOK_START +#endif + return DeskflowApplication::exec(); } @@ -167,10 +175,10 @@ bool checkMacAssistiveDevices() { bool result = AXAPIEnabled(); if (!result) { QMessageBox::information( - NULL, "Deskflow", + NULL, DESKFLOW_APP_NAME, "Please enable access to assistive devices " "System Preferences -> Security & Privacy -> " - "Privacy -> Accessibility, then re-open Deskflow."); + "Privacy -> Accessibility, then re-open " DESKFLOW_APP_NAME "."); } return result; diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 99d51ac12..0228e9bc3 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -24,5 +24,4 @@ add_subdirectory(mt) add_subdirectory(net) add_subdirectory(platform) add_subdirectory(server) -add_subdirectory(license) add_subdirectory(gui) diff --git a/src/lib/arch/unix/ArchDaemonUnix.h b/src/lib/arch/unix/ArchDaemonUnix.h index e8599286c..bdcf36edc 100644 --- a/src/lib/arch/unix/ArchDaemonUnix.h +++ b/src/lib/arch/unix/ArchDaemonUnix.h @@ -32,5 +32,3 @@ public: // IArchDaemon overrides virtual int daemonize(const char *name, DaemonFunc func); }; - -#define CONFIG_FILE "/etc/deskflow/deskflowd.conf" diff --git a/src/lib/arch/unix/ArchFileUnix.cpp b/src/lib/arch/unix/ArchFileUnix.cpp index b678cf856..d51b914b9 100644 --- a/src/lib/arch/unix/ArchFileUnix.cpp +++ b/src/lib/arch/unix/ArchFileUnix.cpp @@ -81,7 +81,7 @@ std::string ArchFileUnix::getInstalledDirectory() { #if WINAPI_XWINDOWS return "/usr/bin"; #else - return "/Applications/Deskflow.app/Contents/MacOS"; + return "/Applications/" DESKFLOW_APP_NAME ".app/Contents/MacOS"; #endif } @@ -105,9 +105,9 @@ std::string ArchFileUnix::getProfileDirectory() { dir = m_profileDirectory; } else { #if WINAPI_XWINDOWS - dir = getUserDirectory().append("/.deskflow"); + dir = getUserDirectory().append("/." DESKFLOW_APP_ID); #else - dir = getUserDirectory().append("/Library/Deskflow"); + dir = getUserDirectory().append("/Library/" DESKFLOW_APP_NAME); #endif } return dir; diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp index 8e1c978e4..b172d870c 100644 --- a/src/lib/arch/unix/ArchSystemUnix.cpp +++ b/src/lib/arch/unix/ArchSystemUnix.cpp @@ -102,8 +102,8 @@ bool ArchSystemUnix::DBusInhibitScreenCall( } reply = screenSaverInterface.call( - "Inhibit", "Deskflow", - "Sleep is manually prevented by the Deskflow preferences"); + "Inhibit", DESKFLOW_APP_NAME, + "Sleep is manually prevented by the " DESKFLOW_APP_NAME " preferences"); if (reply.isValid()) cookies[serviceNum] = reply.value(); } else { diff --git a/src/lib/arch/win32/ArchDaemonWindows.h b/src/lib/arch/win32/ArchDaemonWindows.h index e39039100..2089ecdbb 100644 --- a/src/lib/arch/win32/ArchDaemonWindows.h +++ b/src/lib/arch/win32/ArchDaemonWindows.h @@ -137,12 +137,9 @@ private: std::string m_commandLine; }; -#define DEFAULT_DAEMON_NAME _T("Deskflow") -#define DEFAULT_DAEMON_INFO _T("Manages the Deskflow foreground processes.") +#define DEFAULT_DAEMON_NAME _T(DESKFLOW_APP_NAME) +#define DEFAULT_DAEMON_INFO \ + _T("Manages the " DESKFLOW_APP_NAME " foreground processes.") -#define LEGACY_SERVER_DAEMON_NAME _T("Deskflow Server") -#define LEGACY_CLIENT_DAEMON_NAME _T("Deskflow Client") - -static const TCHAR *const g_daemonKeyPath[] = { - _T("SOFTWARE"), _T("The Deskflow Project"), _T("Deskflow"), _T("Service"), - NULL}; +#define LEGACY_SERVER_DAEMON_NAME _T(DESKFLOW_APP_NAME " Server") +#define LEGACY_CLIENT_DAEMON_NAME _T(DESKFLOW_APP_NAME " Client") diff --git a/src/lib/arch/win32/ArchFileWindows.cpp b/src/lib/arch/win32/ArchFileWindows.cpp index 26bf47fe1..ffaf7cd8d 100644 --- a/src/lib/arch/win32/ArchFileWindows.cpp +++ b/src/lib/arch/win32/ArchFileWindows.cpp @@ -150,7 +150,7 @@ std::string ArchFileWindows::getProfileDirectory() { } // HACK: append program name, this seems wrong. - dir.append("\\Deskflow"); + dir.append("\\" DESKFLOW_APP_NAME); return dir; } diff --git a/src/lib/arch/win32/ArchSystemWindows.cpp b/src/lib/arch/win32/ArchSystemWindows.cpp index b2cd4c59e..7b1b08e38 100644 --- a/src/lib/arch/win32/ArchSystemWindows.cpp +++ b/src/lib/arch/win32/ArchSystemWindows.cpp @@ -28,7 +28,7 @@ #include static const char *s_settingsKeyNames[] = { - _T("SOFTWARE"), _T("Deskflow"), NULL}; + _T("SOFTWARE"), _T(DESKFLOW_APP_NAME), NULL}; // // ArchSystemWindows diff --git a/src/lib/common/constants.h b/src/lib/common/constants.h index 3f8f10662..db280385a 100644 --- a/src/lib/common/constants.h +++ b/src/lib/common/constants.h @@ -22,7 +22,7 @@ #error version was not passed to the compiler #endif -const auto kAppName = "Deskflow"; +const auto kAppName = DESKFLOW_APP_NAME; const auto kAppDescription = "Mouse and keyboard sharing utility"; const auto kVersion = DESKFLOW_VERSION; diff --git a/src/lib/common/ipc.h b/src/lib/common/ipc.h index 85e05396e..e9531ea69 100644 --- a/src/lib/common/ipc.h +++ b/src/lib/common/ipc.h @@ -34,7 +34,7 @@ const auto kIpcHost = "127.0.0.1"; const auto kIpcPort = 24801; // handshake: node/gui -> daemon -// $1 = type, the client identifies it's self as gui or node (deskflowc/s). +// $1 = type, the client identifies it's self as gui or core (server/client). const auto kIpcMsgHello = "IHEL%1i"; // handshake: daemon -> node/gui @@ -42,16 +42,18 @@ const auto kIpcMsgHello = "IHEL%1i"; const auto kIpcMsgHelloBack = "IHEL"; // log line: daemon -> gui -// $1 = aggregate log lines collected from deskflows/c or the daemon itself. +// $1 = aggregate log lines collected from core (server/client) or the daemon +// itself. const auto kIpcMsgLogLine = "ILOG%s"; // command: gui -> daemon // $1 = command; the command for the daemon to launch, typically the full -// path to deskflows/c. $2 = true when process must be elevated on ms windows. +// path to core (server/client). $2 = true when process must be elevated on ms +// windows. const auto kIpcMsgCommand = "ICMD%s%1i"; // shutdown: daemon -> node -// the daemon tells deskflows/c to shut down gracefully. +// the daemon tells core (server/client) to shut down gracefully. const auto kIpcMsgShutdown = "ISDN"; // set setting: gui -> daemon diff --git a/src/lib/deskflow/ArgParser.cpp b/src/lib/deskflow/ArgParser.cpp index 74d2ef4d5..731d27dc6 100644 --- a/src/lib/deskflow/ArgParser.cpp +++ b/src/lib/deskflow/ArgParser.cpp @@ -24,14 +24,11 @@ #include "deskflow/ClientArgs.h" #include "deskflow/ServerArgs.h" #include "deskflow/ToolArgs.h" -#include "license/parse_serial_key.h" #ifdef WINAPI_MSWINDOWS #include #endif -using namespace deskflow::license; - deskflow::ArgsBase *ArgParser::m_argsBase = nullptr; ArgParser::ArgParser(App *app) : m_app(app) {} @@ -54,8 +51,6 @@ bool ArgParser::parseServerArgs( } else if (isArg(i, argc, argv, "-c", "--config", 1)) { // save configuration file path args.m_configFile = argv[++i]; - } else if (isArg(i, argc, argv, "", "--serial-key", 1)) { - args.m_serialKey = parseSerialKey(argv[++i]); } else if (isArg(i, argc, argv, nullptr, "server")) { ++i; continue; @@ -141,10 +136,7 @@ bool ArgParser::parsePlatformArgs( deskflow::ArgsBase &argsBase, const int &argc, const char *const *argv, int &i, bool isServer) { #if WINAPI_MSWINDOWS - if (isArg(i, argc, argv, nullptr, "--service")) { - LOG((CLOG_WARN "obsolete argument --service, use deskflowd instead.")); - argsBase.m_shouldExitFail = true; - } else if (isArg(i, argc, argv, nullptr, "--exit-pause")) { + if (isArg(i, argc, argv, nullptr, "--exit-pause")) { argsBase.m_pauseOnExit = true; } else if (isArg(i, argc, argv, nullptr, "--stop-on-desk-switch")) { argsBase.m_stopOnDeskSwitch = true; diff --git a/src/lib/deskflow/ClientApp.cpp b/src/lib/deskflow/ClientApp.cpp index 79d1402bb..c9f6c0385 100644 --- a/src/lib/deskflow/ClientApp.cpp +++ b/src/lib/deskflow/ClientApp.cpp @@ -132,7 +132,7 @@ void ClientApp::help() { #endif << HELP_SYS_ARGS << HELP_COMMON_ARGS << " " << "\n\n" - << "Connect to a deskflow mouse/keyboard sharing server.\n" + << "Connect to a " DESKFLOW_APP_NAME " mouse/keyboard sharing server.\n" << "\n" << " -a, --address
local network interface address.\n" << HELP_COMMON_INFO_1 << HELP_SYS_INFO @@ -163,9 +163,9 @@ void ClientApp::help() { const char *ClientApp::daemonName() const { #if SYSAPI_WIN32 - return "Deskflow Client"; + return DESKFLOW_APP_NAME " Client"; #elif SYSAPI_UNIX - return "deskflowc"; + return CLIENT_BINARY_NAME; #endif } diff --git a/src/lib/deskflow/ClientAppArguments.md b/src/lib/deskflow/ClientAppArguments.md deleted file mode 100644 index 615ca5e88..000000000 --- a/src/lib/deskflow/ClientAppArguments.md +++ /dev/null @@ -1,159 +0,0 @@ -# ClientApp - List of Command Line Arguments - -As obtained by ```parseArgs()``` - -## Common Arguments - -As obtained by ```updateCommonArgs()``` - -*m_name* -The name of the host as reported by the platform. - -*m_pname* -Name of the process (deskflows\[.exe]) - -## Platform Arguments - -### Windows - -**--service** (deprecated, the program ends if specified) - -**--exit-pause** -*m_pauseOnExit* - -Will wait for a key to be pressed before ending execution. - -**--stop-on-desk-switch** -*m_stopOnDeskSwitch* - -Passed-in to the server screen at its creation. Shuts down the service when the cursor crosses over. - -### X-Windows - -**--display** -*m_display* -Identifies the X server to work on. - -**--no-xinitthreads** -*m_disableXInitThreads* - -Passed-in to the server screen at creation. Avoids calling XInitThreads at the screen constructor. - -## Generic Arguments - -As collected by ```parseGenericArgs()``` - -**-d** / **--debug** -*m_logFilter* (string) - -If present, one value out of the following strings (each string contains all previous information levels): - "FATAL", - "ERROR", - "WARNING", - "NOTE", - "INFO", - "DEBUG", - "DEBUG1", - "DEBUG2", - "DEBUG3", - "DEBUG4", - "DEBUG5". - -**-l** / **--log** -*m_logFile* - -Uses FileLogOutputter to send log to that file. The file will be open/closed for each write operation. When reaching 1Mb, the file will be renamed with the same name +".1". - -**-f** / **--no-daemon** -*m_daemon* false - -**--daemon** -*m_daemon* true - -With m_daemon true, the main loop will be wrapped around a call to "daemonise" and a system logger created. - -**-n** / **--name** -*m_name* (Screen name) - -Used to identify the server's screen. - -**-1** / **--no-restart** -*m_restartable* false - -**--restart** -*m_restartable* true - -With this flag true, ```initServer()``` and ```startServer()``` will setup a one-time timer on the queue for restarting in case of any failures. -The time to wait is 10 seconds in the case of the particular issue "XSocketAddressInUse" but in all other cases will be zero. - -**--no-hooks** -*m_noHooks* true - -Applies only to MS Windows, avoids using hooks. - -**--help** - -Shows help. - -**--version** - -Shows version, then exits. - -**--no-tray** -m_disableTray true - -Avoids the creation of a task bar receiver. - -**--ipc** -m_enableIpc true - -Implements the event queue over IPC. - -**--server** -**--client** -(accepted but ignored) - -**--enable-drag-drop** -*m_enableDragDrop* - -Ignored for XWindows, or MS Windows below Vista. It enables steps required for drag and drop. - -**--enable-crypto** -*m_enableCrypto* true - -Enables secure data sockets. - -**--profile-dir** -*m_profileDirectory* - -If the profile directory is not passed-in, then it is inferred. For XWindows as "~/.deskflow" or else "~/Library/Deskflow". - -**--plugin-dir** -*m_pluginDirectory* - -If unspecified, it is inferred. For XWindows "~/plugins" or else "~/Plugins". - -**--tls-cert** -*m_tlsCertFile* - -If unspecified and used, then it is sought for as prifleDirectory/SSL/Deskflow.pem - -## Uncategorised - -**\** - -Exactly one non-option argument (server-address). The address to connect to a server. - -## Deprecated - -(accepted but effectively ignored) - -**--crypto-pass** - -**--res-w** - -**--res-h** - -**--prm-wc** - -**--prm-hc** diff --git a/src/lib/deskflow/DaemonApp.cpp b/src/lib/deskflow/DaemonApp.cpp index 4c15bb913..730fa87ac 100644 --- a/src/lib/deskflow/DaemonApp.cpp +++ b/src/lib/deskflow/DaemonApp.cpp @@ -59,7 +59,7 @@ using namespace std; -const char *const kLogFilename = "deskflowd.log"; +const char *const kLogFilename = DAEMON_BINARY_NAME ".log"; namespace { void updateSetting(const IpcMessage &message) { @@ -76,8 +76,8 @@ bool isServerCommandLine(const std::vector &cmd) { if (cmd.size() > 1) { isServer = - (cmd[0].find("deskflows") != String::npos) || - (cmd[0].find("deskflow-core") != String::npos && cmd[1] == "server"); + (cmd[0].find(SERVER_BINARY_NAME) != String::npos) || + (cmd[0].find(CORE_BINARY_NAME) != String::npos && cmd[1] == "server"); } return isServer; @@ -165,10 +165,10 @@ int DaemonApp::run(int argc, char **argv) { } else { #if SYSAPI_WIN32 LOG((CLOG_PRINT "daemonizing windows service")); - arch.daemonize("Deskflow", winMainLoopStatic); + arch.daemonize(DESKFLOW_APP_NAME, winMainLoopStatic); #elif SYSAPI_UNIX LOG((CLOG_PRINT "daemonizing unix service")); - arch.daemonize("Deskflow", unixMainLoopStatic); + arch.daemonize(DESKFLOW_APP_NAME, unixMainLoopStatic); #endif } diff --git a/src/lib/deskflow/ServerApp.cpp b/src/lib/deskflow/ServerApp.cpp index c189636a0..90c40e2fd 100644 --- a/src/lib/deskflow/ServerApp.cpp +++ b/src/lib/deskflow/ServerApp.cpp @@ -140,7 +140,7 @@ void ServerApp::help() { #endif << HELP_SYS_ARGS HELP_COMMON_ARGS "\n\n" - << "Start the deskflow mouse/keyboard sharing server.\n" + << "Start the " DESKFLOW_APP_NAME " mouse/keyboard sharing server.\n" << "\n" << " -a, --address
listen for clients on the given address.\n" << " -c, --config use the named configuration file " @@ -720,7 +720,7 @@ int ServerApp::mainLoop() { new TMethodEventJob(this, &ServerApp::forceReconnect)); // to work around the sticky meta keys problem, we'll give users - // the option to reset the state of deskflows + // the option to reset the state of the server. m_events->adoptHandler( m_events->forServerApp().resetServer(), m_events->getSystemTarget(), new TMethodEventJob(this, &ServerApp::resetServer)); @@ -819,9 +819,9 @@ int ServerApp::foregroundStartup(int argc, char **argv) { const char *ServerApp::daemonName() const { #if SYSAPI_WIN32 - return "Deskflow Server"; + return DESKFLOW_APP_NAME " Server"; #elif SYSAPI_UNIX - return "deskflows"; + return SERVER_BINARY_NAME; #endif } diff --git a/src/lib/deskflow/ServerApp.h b/src/lib/deskflow/ServerApp.h index 7fdf88ee7..4577b2894 100644 --- a/src/lib/deskflow/ServerApp.h +++ b/src/lib/deskflow/ServerApp.h @@ -141,9 +141,9 @@ private: // configuration file name #if SYSAPI_WIN32 -#define USER_CONFIG_NAME "deskflow.sgc" -#define SYSTEM_CONFIG_NAME "deskflow.sgc" +#define USER_CONFIG_NAME DESKFLOW_APP_ID ".sgc" +#define SYSTEM_CONFIG_NAME DESKFLOW_APP_ID ".sgc" #elif SYSAPI_UNIX -#define USER_CONFIG_NAME ".deskflow.conf" -#define SYSTEM_CONFIG_NAME "deskflow.conf" +#define USER_CONFIG_NAME "." DESKFLOW_APP_ID ".conf" +#define SYSTEM_CONFIG_NAME DESKFLOW_APP_ID ".conf" #endif diff --git a/src/lib/deskflow/ServerAppArguments.md b/src/lib/deskflow/ServerAppArguments.md deleted file mode 100644 index a9132471c..000000000 --- a/src/lib/deskflow/ServerAppArguments.md +++ /dev/null @@ -1,170 +0,0 @@ -# ServerApp - List of Command Line Arguments - -As obtained by ```parseServerArgs()``` - -## Common Arguments - -As obtained by ```updateCommonArgs()``` - -*m_name* -The name of the host as reported by the platform. - -*m_pname* -Name of the process (deskflows\[.exe]) - -## Platform Arguments - -### Windows - -**--service** (deprecated, the program ends if specified) - -**--exit-pause** -*m_pauseOnExit* - -Will wait for a key to be pressed before ending execution. - -**--stop-on-desk-switch** -*m_stopOnDeskSwitch* - -Passed-in to the server screen at its creation. Shuts down the service when the cursor crosses over. - -### X-Windows - -**--display** -*m_display* -Identifies the X server to work on. - -**--no-xinitthreads** -*m_disableXInitThreads* - -Passed-in to the server screen at creation. Avoids calling XInitThreads at the screen constructor. - -## Generic Arguments - -As collected by ```parseGenericArgs()``` - -**-d** / **--debug** -*m_logFilter* (string) - -If present, one value out of the following strings (each string contains all previous information levels): - "FATAL", - "ERROR", - "WARNING", - "NOTE", - "INFO", - "DEBUG", - "DEBUG1", - "DEBUG2", - "DEBUG3", - "DEBUG4", - "DEBUG5". - -**-l** / **--log** -*m_logFile* - -Uses FileLogOutputter to send log to that file. The file will be open/closed for each write operation. When reaching 1Mb, the file will be renamed with the same name +".1". - -**-f** / **--no-daemon** -*m_daemon* false - -**--daemon** -*m_daemon* true - -With m_daemon true, the main loop will be wrapped around a call to "daemonise" and a system logger created. - -**-n** / **--name** -*m_name* (Screen name) - -Used to identify the server's screen. - -**-1** / **--no-restart** -*m_restartable* false - -**--restart** -*m_restartable* true - -With this flag true, ```initServer()``` and ```startServer()``` will setup a one-time timer on the queue for restarting in case of any failures. -The time to wait is 10 seconds in the case of the particular issue "XSocketAddressInUse" but in all other cases will be zero. - -**--no-hooks** -*m_noHooks* true - -Applies only to MS Windows, avoids using hooks. - -**--help** - -Shows help. - -**--version** - -Shows version, then exits. - -**--no-tray** -m_disableTray true - -Avoids the creation of a task bar receiver. - -**--ipc** -m_enableIpc true - -Implements the event queue over IPC. - -**--server** -**--client** -(accepted but ignored) - -**--enable-drag-drop** -*m_enableDragDrop* - -Ignored for XWindows, or MS Windows below Vista. It enables steps required for drag and drop. - -**--enable-crypto** -*m_enableCrypto* true - -Enables secure data sockets. - -**--profile-dir** -*m_profileDirectory* - -If the profile directory is not passed-in, then it is inferred. For XWindows as "~/.deskflow" or else "~/Library/Deskflow". - -**--plugin-dir** -*m_pluginDirectory* - -If unspecified, it is inferred. For XWindows "~/plugins" or else "~/Plugins". - -**--tls-cert** -*m_tlsCertFile* - -If unspecified and used, then it is sought for as prifleDirectory/SSL/Deskflow.pem - -## Uncategorised - -**-a** / **--address** -*m_deskflowAddress* - -Used as the listening address. - -**-c** / **--config** -*m_configFile* - -Configuration file path. - -"" / **--serial-key** -*m_serial* - -Serial key. - -## Deprecated - -(accepted but effectively ignored) - -**--crypto-pass** - -**--res-w** - -**--res-h** - -**--prm-wc** - -**--prm-hc** diff --git a/src/lib/deskflow/ServerArgs.h b/src/lib/deskflow/ServerArgs.h index c6e6dd9f1..bcae78923 100644 --- a/src/lib/deskflow/ServerArgs.h +++ b/src/lib/deskflow/ServerArgs.h @@ -18,7 +18,6 @@ #pragma once #include "ArgsBase.h" -#include "license/License.h" #include "server/Config.h" #include @@ -26,7 +25,6 @@ namespace deskflow { class ServerArgs : public ArgsBase { - using License = deskflow::license::License; using Config = deskflow::server::Config; public: @@ -40,7 +38,6 @@ public: public: String m_configFile = ""; - license::SerialKey m_serialKey = license::SerialKey::invalid(); std::shared_ptr m_config; }; diff --git a/src/lib/deskflow/unix/AppUtilUnix.cpp b/src/lib/deskflow/unix/AppUtilUnix.cpp index d59194b2b..4f3bb0ec6 100644 --- a/src/lib/deskflow/unix/AppUtilUnix.cpp +++ b/src/lib/deskflow/unix/AppUtilUnix.cpp @@ -177,7 +177,7 @@ void AppUtilUnix::showNotification( LOG( (CLOG_INFO "showing notification, title=\"%s\", text=\"%s\"", title.c_str(), text.c_str())); - if (!notify_init("Deskflow")) { + if (!notify_init(DESKFLOW_APP_NAME)) { LOG((CLOG_INFO "failed to initialize libnotify")); return; } @@ -198,8 +198,8 @@ void AppUtilUnix::showNotification( notify_uninit(); #elif WINAPI_CARBON - // deskflows and deskflowc are not allowed to send native notifications on - // MacOS instead ask main deskflow process to show them instead + // server and client processes are not allowed to show notifications. + // MacOS instead ask main deskflow process to show them instead. LOG((CLOG_INFO "mac notification: %s|%s", title.c_str(), text.c_str())); #endif } diff --git a/src/lib/deskflow/win32/AppUtilWindows.cpp b/src/lib/deskflow/win32/AppUtilWindows.cpp index dbc6953a0..0ea74aa4f 100644 --- a/src/lib/deskflow/win32/AppUtilWindows.cpp +++ b/src/lib/deskflow/win32/AppUtilWindows.cpp @@ -224,9 +224,10 @@ void AppUtilWindows::showNotification( return; } if (!WinToastLib::WinToast::instance()->isInitialized()) { - WinToastLib::WinToast::instance()->setAppName(L"Deskflow"); + WinToastLib::WinToast::instance()->setAppName(L"" DESKFLOW_APP_NAME); const auto aumi = WinToastLib::WinToast::configureAUMI( - L"Symless", L"Deskflow", L"Deskflow App", L"1.14.1+"); + L"" DESKFLOW_AUTHOR_NAME, L"" DESKFLOW_APP_NAME, L"" DESKFLOW_APP_NAME, + L"1.14.1+"); WinToastLib::WinToast::instance()->setAppUserModelId(aumi); if (!WinToastLib::WinToast::instance()->initialize()) { diff --git a/src/lib/gui/CMakeLists.txt b/src/lib/gui/CMakeLists.txt index 496fa1565..e11d0841e 100644 --- a/src/lib/gui/CMakeLists.txt +++ b/src/lib/gui/CMakeLists.txt @@ -15,8 +15,8 @@ set(target gui) -set(res_dir ${CMAKE_SOURCE_DIR}/res/gui) -set(qrc_file ${res_dir}/Deskflow.qrc) +set(res_dir ${GUI_RES_DIR}) +set(qrc_file ${GUI_QRC_FILE}) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) @@ -36,11 +36,14 @@ if(ADD_HEADERS_TO_SOURCES) list(APPEND sources ${headers}) endif() +# generated includes +include_directories(${PROJECT_BINARY_DIR}/config) + add_library(${target} STATIC ${sources} ${ui_files} ${qrc_file}) target_link_libraries( ${target} - license + ${DESKFLOW_GUI_HOOK_LIB} Qt6::Core Qt6::Widgets Qt6::Network) diff --git a/src/lib/gui/Logger.cpp b/src/lib/gui/Logger.cpp index 4dac6b621..c979974c9 100644 --- a/src/lib/gui/Logger.cpp +++ b/src/lib/gui/Logger.cpp @@ -31,12 +31,9 @@ namespace deskflow::gui { const auto kForceDebugMessages = QStringList{ - "Deskflow", // TEST - "No functional TLS backend was found", - "No TLS backend is available", + "No functional TLS backend was found", "No TLS backend is available", "QSslSocket::connectToHostEncrypted: TLS initialization failed", - "Retrying to obtain clipboard.", - "Unable to obtain clipboard."}; + "Retrying to obtain clipboard.", "Unable to obtain clipboard."}; Logger Logger::s_instance; diff --git a/src/lib/gui/OSXHelpers.mm b/src/lib/gui/OSXHelpers.mm index b80bfee99..04319da87 100644 --- a/src/lib/gui/OSXHelpers.mm +++ b/src/lib/gui/OSXHelpers.mm @@ -54,7 +54,7 @@ bool isOSXDevelopmentBuild() { std::string bundleURL = [[[NSBundle mainBundle] bundleURL].absoluteString UTF8String]; - return (bundleURL.find("Applications/Deskflow.app") == std::string::npos); + return (bundleURL.find("Applications/" DESKFLOW_APP_NAME ".app") == std::string::npos); } bool @@ -62,7 +62,7 @@ showOSXNotification(const QString& title, const QString& body) { #if OSX_DEPLOYMENT_TARGET >= 1014 // accessing notification center on unsigned build causes an immidiate - // application shutodown (in this case deskflows) and cannot be caught + // application shutodown (in this case, server) and cannot be caught // to avoid issues with it need to first check if this is a dev build if (isOSXDevelopmentBuild()) { diff --git a/src/lib/gui/VersionChecker.cpp b/src/lib/gui/VersionChecker.cpp index 189ca3aea..d75dcca7b 100644 --- a/src/lib/gui/VersionChecker.cpp +++ b/src/lib/gui/VersionChecker.cpp @@ -17,6 +17,7 @@ #include "VersionChecker.h" +#include "common/constants.h" #include "env_vars.h" #include @@ -27,8 +28,6 @@ #include #include -const char *const kVersion = DESKFLOW_VERSION; - using namespace deskflow::gui; VersionChecker::VersionChecker( @@ -45,13 +44,15 @@ void VersionChecker::checkLatest() const { const QString url = env_vars::versionUrl(); qDebug("checking for updates at: %s", qPrintable(url)); auto request = QNetworkRequest(url); - auto userAgent = QString("Deskflow %1 on %2") + auto userAgent = QString("%1 %2 on %3") + .arg(kAppName) .arg(kVersion) .arg(QSysInfo::prettyProductName()); request.setHeader(QNetworkRequest::UserAgentHeader, userAgent); - request.setRawHeader("X-Deskflow-Version", kVersion); + request.setRawHeader("X-" DESKFLOW_APP_NAME "-Version", kVersion); request.setRawHeader( - "X-Deskflow-Language", QLocale::system().name().toStdString().c_str()); + "X-" DESKFLOW_APP_NAME "-Language", + QLocale::system().name().toStdString().c_str()); m_network->get(request); } diff --git a/src/lib/gui/config/AppConfig.cpp b/src/lib/gui/config/AppConfig.cpp index e22ef1718..d4f800f94 100644 --- a/src/lib/gui/config/AppConfig.cpp +++ b/src/lib/gui/config/AppConfig.cpp @@ -38,15 +38,15 @@ static const char *const kLogLevelNames[] = { "INFO", "DEBUG", "DEBUG1", "DEBUG2"}; #if defined(Q_OS_WIN) -const char AppConfig::m_CoreServerName[] = "deskflows.exe"; -const char AppConfig::m_CoreClientName[] = "deskflowc.exe"; +const char AppConfig::m_CoreServerName[] = SERVER_BINARY_NAME ".exe"; +const char AppConfig::m_CoreClientName[] = CLIENT_BINARY_NAME ".exe"; const char AppConfig::m_LogDir[] = "log/"; -const char AppConfig::m_ConfigFilename[] = "deskflow.sgc"; +const char AppConfig::m_ConfigFilename[] = DESKFLOW_APP_ID ".sgc"; #else -const char AppConfig::m_CoreServerName[] = "deskflows"; -const char AppConfig::m_CoreClientName[] = "deskflowc"; +const char AppConfig::m_CoreServerName[] = SERVER_BINARY_NAME; +const char AppConfig::m_CoreClientName[] = CLIENT_BINARY_NAME; const char AppConfig::m_LogDir[] = "/var/log/"; -const char AppConfig::m_ConfigFilename[] = "deskflow.conf"; +const char AppConfig::m_ConfigFilename[] = DESKFLOW_APP_ID ".conf"; #endif // TODO: instead, use key value pair table, which would be less fragile. @@ -62,12 +62,12 @@ const char *const AppConfig::m_SettingsName[] = { "elevateMode", "elevateModeEnum", "", // 10 = edition, obsolete (using serial key instead) - "cryptoEnabled", // kTlsEnabled (retain legacy string value) + "cryptoEnabled", // 11 = kTlsEnabled (retain legacy string value) "autoHide", - "serialKey", + "", // 13 = serialKey, obsolete "lastVersion", "", // 15 = lastExpiringWarningTime, obsolete - "activationHasRun", + "", // 16 = activationHasRun, obsolete "", // 17 = minimizeToTray, obsolete "", // 18 = ActivateEmail, obsolete "loadFromSystemScope", @@ -82,9 +82,9 @@ const char *const AppConfig::m_SettingsName[] = { "preventSleep", "languageSync", "invertScrollDirection", - "", // 31 = guid, obsolete - "", // 32 = licenseRegistryUrl, obsolete - "licenseNextCheck", + "", // 31 = guid, obsolete + "", // 32 = licenseRegistryUrl, obsolete + "", // 33 = licenseNextCheck, obsolete "initiateConnectionFromServer", // kInvertConnection "", // 35 = clientHostMode, obsolete "", // 36 = serverClientMode, obsolete @@ -122,15 +122,12 @@ void AppConfig::recallFromAllScopes() { m_WizardLastRun = findInAllScopes(kWizardLastRun, m_WizardLastRun).toInt(); m_LoadFromSystemScope = findInAllScopes(kLoadSystemSettings, m_LoadFromSystemScope).toBool(); - m_LicenseNextCheck = - findInAllScopes(kLicenseNextCheck, m_LicenseNextCheck).toULongLong(); } void AppConfig::recallFromCurrentScope() { using enum Setting; recallScreenName(); - recallSerialKey(); recallElevateMode(); m_Port = getFromCurrentScope(kPort, m_Port).toInt(); @@ -142,8 +139,6 @@ void AppConfig::recallFromCurrentScope() { getFromCurrentScope(kStartedBefore, m_StartedBefore).toBool(); m_AutoHide = getFromCurrentScope(kAutoHide, m_AutoHide).toBool(); m_LastVersion = getFromCurrentScope(kLastVersion, m_LastVersion).toString(); - m_ActivationHasRun = - getFromCurrentScope(kActivationHasRun, m_ActivationHasRun).toBool(); m_ServerGroupChecked = getFromCurrentScope(kServerGroupChecked, m_ServerGroupChecked).toBool(); m_UseExternalConfig = @@ -206,7 +201,6 @@ void AppConfig::commit() { saveToAllScopes(kLoadSystemSettings, m_LoadFromSystemScope); saveToAllScopes(kClientGroupChecked, m_ClientGroupChecked); saveToAllScopes(kServerGroupChecked, m_ServerGroupChecked); - saveToAllScopes(kLicenseNextCheck, m_LicenseNextCheck); if (isActiveScopeWritable()) { setInCurrentScope(kScreenName, m_ScreenName); @@ -221,9 +215,7 @@ void AppConfig::commit() { kElevateModeLegacy, m_ElevateMode == ElevateMode::kAlways); setInCurrentScope(kTlsEnabled, m_TlsEnabled); setInCurrentScope(kAutoHide, m_AutoHide); - setInCurrentScope(kSerialKey, m_SerialKey); setInCurrentScope(kLastVersion, m_LastVersion); - setInCurrentScope(kActivationHasRun, m_ActivationHasRun); setInCurrentScope(kUseExternalConfig, m_UseExternalConfig); setInCurrentScope(kConfigFile, m_ConfigFile); setInCurrentScope(kUseInternalConfig, m_UseInternalConfig); @@ -281,25 +273,6 @@ void AppConfig::determineScope() { } } -void AppConfig::recallSerialKey() { - using enum Setting; - - if (!m_Scopes.scopeContains(settingName(kLoadSystemSettings))) { - qDebug("no serial key in current scope, skipping"); - return; - } - - const auto &serialKey = - getFromCurrentScope(kSerialKey, m_SerialKey).toString().trimmed(); - - if (serialKey.isEmpty()) { - qDebug("serial key is empty, skipping"); - return; - } - - m_SerialKey = serialKey; -} - void AppConfig::recallElevateMode() { using enum Setting; @@ -450,10 +423,6 @@ void AppConfig::persistLogDir() const { IConfigScopes &AppConfig::scopes() const { return m_Scopes; } -bool AppConfig::activationHasRun() const { return m_ActivationHasRun; } - -QString AppConfig::serialKey() const { return m_SerialKey; } - const QString &AppConfig::screenName() const { return m_ScreenName; } int AppConfig::port() const { return m_Port; } @@ -494,10 +463,6 @@ bool AppConfig::invertScrollDirection() const { return m_InvertScrollDirection; } -unsigned long long AppConfig::licenseNextCheck() const { - return m_LicenseNextCheck; -} - bool AppConfig::languageSync() const { return m_LanguageSync; } bool AppConfig::preventSleep() const { return m_PreventSleep; } @@ -524,8 +489,6 @@ bool AppConfig::clientGroupChecked() const { return m_ClientGroupChecked; } const QString &AppConfig::serverHostname() const { return m_ServerHostname; } -void AppConfig::setActivationHasRun(bool value) { m_ActivationHasRun = value; } - std::optional AppConfig::mainWindowSize() const { return m_MainWindowSize; } @@ -550,8 +513,6 @@ std::optional AppConfig::enableUpdateCheck() const { // Begin setters /////////////////////////////////////////////////////////////////////////////// -void AppConfig::clearSerialKey() { m_SerialKey.clear(); } - void AppConfig::setTlsEnabled(bool value) { // we purposefully do not set the 'tls changed' flag when enabling/disabling // tls, since that would cause the certificate to regenerate, which could get @@ -578,11 +539,6 @@ void AppConfig::setTlsKeyLength(int value) { m_TlsKeyLength = value; } -void AppConfig::setSerialKey(const QString &serialKey) { - m_SerialKey = serialKey; - saveToAllScopes(Setting::kSerialKey, m_SerialKey); -} - void AppConfig::setServerGroupChecked(bool newValue) { m_ServerGroupChecked = newValue; } @@ -633,10 +589,6 @@ void AppConfig::setElevateMode(ElevateMode em) { m_ElevateMode = em; } void AppConfig::setAutoHide(bool b) { m_AutoHide = b; } -void AppConfig::setLicenseNextCheck(unsigned long long time) { - m_LicenseNextCheck = time; -} - void AppConfig::setInvertScrollDirection(bool newValue) { m_InvertScrollDirection = newValue; } diff --git a/src/lib/gui/config/AppConfig.h b/src/lib/gui/config/AppConfig.h index b99f38bb6..422121d51 100644 --- a/src/lib/gui/config/AppConfig.h +++ b/src/lib/gui/config/AppConfig.h @@ -21,7 +21,6 @@ #include "ElevateMode.h" #include "IAppConfig.h" #include "IConfigScopes.h" -#include "gui/license/license_utils.h" #include "gui/paths.h" #include @@ -36,9 +35,15 @@ namespace deskflow::gui { const ElevateMode kDefaultElevateMode = ElevateMode::kAutomatic; -const QString kDefaultLogFile = "deskflow.log"; +const QString kDefaultLogFile = QString("%1.log").arg(DESKFLOW_APP_ID); const int kDefaultTlsKeyLength = 2048; +#ifdef DESKFLOW_SHOW_DEV_THANKS +const bool kDefaultShowDevThanks = true; +#else +const bool kDefaultShowDevThanks = false; +#endif + #if defined(Q_OS_WIN) const ProcessMode kDefaultProcessMode = ProcessMode::kService; #else @@ -73,13 +78,13 @@ private: kStartedBefore = 7, kElevateModeLegacy = 8, kElevateMode = 9, - // 10 = edition, obsolete (using serial key instead) + // 10 = edition, obsolete (related to obsolete licensing) kTlsEnabled = 11, kAutoHide = 12, - kSerialKey = 13, + // 13 = serial key, obsolete kLastVersion = 14, // 15 = last expire time, obsolete - kActivationHasRun = 16, + // 16 = activation has run, obsolete // 17 = minimize to tray, obsolete // 18 = activate email, obsolete kLoadSystemSettings = 19, @@ -96,7 +101,7 @@ private: kInvertScrollDirection = 30, // 31 = guid, obsolete // 32 = license registry url, obsolete - kLicenseNextCheck = 33, + // 33 = license next check, obsolete kInvertConnection = 34, // 35 = client-host-mode, obsolete // 36 = server-client-mode, obsolete @@ -149,7 +154,6 @@ public: QString coreClientName() const override; bool invertConnection() const override; void persistLogDir() const override; - QString serialKey() const override; bool languageSync() const override; bool invertScrollDirection() const override; int port() const override; @@ -172,11 +176,9 @@ public: bool wizardShouldRun() const; bool startedBefore() const; QString logDir() const; - unsigned long long licenseNextCheck() const; bool serverGroupChecked() const; bool useInternalConfig() const; QString lastVersion() const; - bool activationHasRun() const; std::optional mainWindowSize() const; std::optional mainWindowPosition() const; bool showDevThanks() const; @@ -210,11 +212,7 @@ public: // void setStartedBefore(bool b); - void setActivationHasRun(bool value); void setWizardHasRun(); - void setSerialKey(const QString &serialKey); - void clearSerialKey(); - void setLicenseNextCheck(unsigned long long); void setServerGroupChecked(bool); void setUseExternalConfig(bool); void setConfigFile(const QString &); @@ -240,7 +238,6 @@ private: void recall(); void recallScreenName(); - void recallSerialKey(); void recallElevateMode(); void recallFromAllScopes(); void recallFromCurrentScope(); @@ -309,13 +306,9 @@ private: int m_WizardLastRun = 0; bool m_StartedBefore = false; ElevateMode m_ElevateMode = deskflow::gui::kDefaultElevateMode; - QString m_ActivateEmail = ""; bool m_TlsEnabled = true; bool m_AutoHide = false; - QString m_SerialKey = ""; QString m_LastVersion = ""; - unsigned long long m_LicenseNextCheck = 0; - bool m_ActivationHasRun = false; bool m_InvertScrollDirection = false; bool m_LanguageSync = true; bool m_PreventSleep = false; @@ -332,7 +325,7 @@ private: int m_TlsKeyLength = deskflow::gui::kDefaultTlsKeyLength; std::optional m_MainWindowSize; std::optional m_MainWindowPosition; - bool m_ShowDevThanks = !deskflow::gui::license::isLicensedProduct(); + bool m_ShowDevThanks = deskflow::gui::kDefaultShowDevThanks; bool m_LoadFromSystemScope = false; bool m_ShowCloseReminder = true; std::optional m_EnableUpdateCheck; diff --git a/src/lib/gui/config/IAppConfig.h b/src/lib/gui/config/IAppConfig.h index 8a6efbdf0..107e90fa3 100644 --- a/src/lib/gui/config/IAppConfig.h +++ b/src/lib/gui/config/IAppConfig.h @@ -52,7 +52,6 @@ public: virtual QString coreClientName() const = 0; virtual bool invertConnection() const = 0; virtual void persistLogDir() const = 0; - virtual QString serialKey() const = 0; virtual bool languageSync() const = 0; virtual bool invertScrollDirection() const = 0; virtual int port() const = 0; diff --git a/src/lib/gui/constants.h b/src/lib/gui/constants.h index 29bc4f969..2ef562f4d 100644 --- a/src/lib/gui/constants.h +++ b/src/lib/gui/constants.h @@ -23,34 +23,20 @@ namespace deskflow::gui { // important: this is used for settings paths on some platforms, // and must not be a url. qt automatically converts this to reverse domain -// notation (rdn), e.g. com.symless -const auto kOrgDomain = "deskflow.org"; +// notation (rdn), e.g. org.deskflow +const auto kOrgDomain = DESKFLOW_DOMAIN; -const auto kLinkBuy = R"(Buy now)"; -const auto kLinkRenew = R"(Renew now)"; const auto kLinkDownload = R"(Download now)"; -const auto kUrlWebsite = "https://symless.com"; const auto kUrlSourceQuery = "source=gui"; -const auto kUrlGitHub = "https://github.com/deskflow/deskflow"; +const auto kUrlApp = DESKFLOW_WEBSITE_URL; +const auto kUrlHelp = QString("%1/help?%2").arg(kUrlApp, kUrlSourceQuery); +const auto kUrlDownload = + QString("%1/download?%2").arg(kUrlApp, kUrlSourceQuery); + +#if defined(Q_OS_LINUX) const auto kUrlGnomeTrayFix = "https://extensions.gnome.org/extension/2890/tray-icons-reloaded/"; -const auto kUrlProduct = QString("%1/synergy").arg(kUrlWebsite); -const auto kUrlPurchase = - QString("%1/purchase?%2").arg(kUrlProduct, kUrlSourceQuery); -const auto kUrlUpgrade = - QString("%1/purchase/upgrade?%2").arg(kUrlProduct, kUrlSourceQuery); -const auto kUrlContact = - QString("%1/contact?%2").arg(kUrlProduct, kUrlSourceQuery); -const auto kUrlHelp = QString("%1/help?%2").arg(kUrlProduct, kUrlSourceQuery); -const auto kUrlDownload = - QString("%1/download?%2").arg(kUrlProduct, kUrlSourceQuery); -const auto kUrlBugReport = QString("%1/issues").arg(kUrlGitHub); - -#ifdef DESKFLOW_PRODUCT_NAME -const QString kProductName = DESKFLOW_PRODUCT_NAME; -#else -const QString kProductName; #endif } // namespace deskflow::gui diff --git a/src/lib/gui/core/CoreProcess.cpp b/src/lib/gui/core/CoreProcess.cpp index 3e5e29523..0cb33e4c4 100644 --- a/src/lib/gui/core/CoreProcess.cpp +++ b/src/lib/gui/core/CoreProcess.cpp @@ -19,7 +19,6 @@ #include "gui/config/IAppConfig.h" #include "gui/core/CoreTool.h" -#include "gui/license/license_utils.h" #include "gui/paths.h" #include "tls/TlsUtility.h" @@ -37,13 +36,10 @@ #include #include -using namespace deskflow::license; -using namespace deskflow::gui::license; - namespace deskflow::gui { const int kRetryDelay = 1000; -const auto kServerConfigFilename = "deskflow-server.conf"; +const auto kServerConfigFilename = DESKFLOW_APP_ID "-server.conf"; const auto kLineSplitRegex = QRegularExpression("\r|\n|\r\n"); // @@ -161,10 +157,9 @@ QString CoreProcess::Deps::getProfileRoot() const { CoreProcess::CoreProcess( const IAppConfig &appConfig, const IServerConfig &serverConfig, - const ILicense &license, std::shared_ptr deps) + std::shared_ptr deps) : m_appConfig(appConfig), m_serverConfig(serverConfig), - m_license(license), m_pDeps(deps) { connect( @@ -575,16 +570,11 @@ bool CoreProcess::addServerArgs(QStringList &args, QString &app) { args << "-c" << configFilename; qInfo("core config file: %s", qPrintable(configFilename)); - - if (isActivationEnabled() && !m_appConfig.serialKey().isEmpty()) { - args << "--serial-key" << m_appConfig.serialKey(); - } - // bizarrely, the tls cert path arg was being given to the core client. // since it's not clear why (it is only needed for the server), this has now // been moved to server args. if (m_appConfig.tlsEnabled()) { - TlsUtility tlsUtility(m_appConfig, m_license); + TlsUtility tlsUtility(m_appConfig); if (!tlsUtility.persistCertificate()) { qCritical("failed to persist tls certificate"); return false; @@ -709,8 +699,8 @@ void CoreProcess::checkLogLine(const QString &line) { checkSecureSocket(line); - // subprocess (deskflows, deskflowc) is not allowed to show notifications - // process the log from it and show notification from deskflow instead + // server and client processes are not allowed to show notifications. + // process the log from it and show notification from deskflow instead. #ifdef Q_OS_MAC checkOSXNotification(line); #endif diff --git a/src/lib/gui/core/CoreProcess.h b/src/lib/gui/core/CoreProcess.h index e90c1cf56..a1a32e119 100644 --- a/src/lib/gui/core/CoreProcess.h +++ b/src/lib/gui/core/CoreProcess.h @@ -21,7 +21,6 @@ #include "gui/config/IServerConfig.h" #include "gui/ipc/QIpcClient.h" #include "gui/proxy/QProcessProxy.h" -#include "license/ILicense.h" #include #include @@ -36,7 +35,6 @@ class CoreProcess : public QObject { using IServerConfig = deskflow::gui::IServerConfig; using QProcessProxy = deskflow::gui::proxy::QProcessProxy; using IQIpcClient = deskflow::gui::ipc::IQIpcClient; - using ILicense = deskflow::license::ILicense; Q_OBJECT @@ -67,7 +65,6 @@ public: explicit CoreProcess( const IAppConfig &appConfig, const IServerConfig &serverConfig, - const ILicense &license, std::shared_ptr deps = std::make_shared()); void extracted(QString &app, QStringList &args); @@ -128,7 +125,6 @@ private: const IAppConfig &m_appConfig; const IServerConfig &m_serverConfig; - const ILicense &m_license; std::shared_ptr m_pDeps; QString m_address; ProcessState m_processState = ProcessState::Stopped; diff --git a/src/lib/gui/core/CoreTool.cpp b/src/lib/gui/core/CoreTool.cpp index 5f86001cc..30922a050 100644 --- a/src/lib/gui/core/CoreTool.cpp +++ b/src/lib/gui/core/CoreTool.cpp @@ -24,13 +24,7 @@ #include #include -static const char kCoreBinary[] = "deskflow-legacy"; - -#ifdef Q_WS_WIN -static const char kSerialKeyFilename[] = "Deskflow.subkey"; -#else -static const char kSerialKeyFilename[] = ".deskflow.subkey"; -#endif +static const char kCoreBinary[] = LEGACY_BINARY_NAME; QString CoreTool::getProfileDir() const { QStringList args("--get-profile-dir"); @@ -47,11 +41,6 @@ QString CoreTool::getArch() const { return run(args); } -QString CoreTool::getSerialKeyFilePath() const { - auto filename = getProfileDir() + QDir::separator() + kSerialKeyFilename; - return QDir::cleanPath(filename); -} - QString CoreTool::run(const QStringList &args, const QString &input) const { QString program(QCoreApplication::applicationDirPath() + "/" + kCoreBinary); diff --git a/src/lib/gui/core/CoreTool.h b/src/lib/gui/core/CoreTool.h index ba1277b6a..1fba40abd 100644 --- a/src/lib/gui/core/CoreTool.h +++ b/src/lib/gui/core/CoreTool.h @@ -24,6 +24,5 @@ public: QString getProfileDir() const; QString getInstalledDir() const; QString getArch() const; - QString getSerialKeyFilePath() const; QString run(const QStringList &args, const QString &input = "") const; }; diff --git a/src/lib/gui/dialogs/SettingsDialog.cpp b/src/lib/gui/dialogs/SettingsDialog.cpp index 6cf719692..b840fd44a 100644 --- a/src/lib/gui/dialogs/SettingsDialog.cpp +++ b/src/lib/gui/dialogs/SettingsDialog.cpp @@ -18,14 +18,17 @@ #include "SettingsDialog.h" -#include "UpgradeDialog.h" #include "gui/core/CoreProcess.h" -#include "gui/license/license_utils.h" #include "gui/messages.h" #include "gui/tls/TlsCertificate.h" #include "gui/tls/TlsUtility.h" #include "gui/validators/ScreenNameValidator.h" #include "gui/validators/ValidationError.h" +#include "gui_config.h" // IWYU pragma: keep + +#ifdef DESKFLOW_GUI_HOOK_HEADER +#include DESKFLOW_GUI_HOOK_HEADER +#endif #include #include @@ -33,21 +36,17 @@ #include #include -using namespace deskflow::license; using namespace deskflow::gui; -const char *const kProProductName = "Deskflow"; - SettingsDialog::SettingsDialog( QWidget *parent, IAppConfig &appConfig, const IServerConfig &serverConfig, - const License &license, const CoreProcess &coreProcess) + const CoreProcess &coreProcess) : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), Ui::SettingsDialogBase(), m_appConfig(appConfig), m_serverConfig(serverConfig), - m_license(license), m_coreProcess(coreProcess), - m_tlsUtility(appConfig, license) { + m_tlsUtility(appConfig) { setupUi(this); @@ -63,6 +62,10 @@ SettingsDialog::SettingsDialog( m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator( m_pLineEditScreenName, m_pScreenNameError, &serverConfig.screens())); + connect( + m_pCheckBoxEnableTls, &QCheckBox::toggled, this, + &SettingsDialog::updateTlsControlsEnabled); + connect( this, &SettingsDialog::shown, this, [this] { @@ -71,6 +74,10 @@ SettingsDialog::SettingsDialog( } }, Qt::QueuedConnection); + +#ifdef DESKFLOW_GUI_HOOK_SETTINGS + DESKFLOW_GUI_HOOK_SETTINGS +#endif } // @@ -97,19 +104,6 @@ void SettingsDialog::on_m_pButtonBrowseLog_clicked() { void SettingsDialog::on_m_pCheckBoxEnableTls_clicked(bool) { updateTlsControlsEnabled(); - - if (license::isActivationEnabled() && !m_tlsUtility.isAvailable()) { - m_pCheckBoxEnableTls->setChecked(false); - updateTlsControlsEnabled(); - - auto edition = m_license.productEdition(); - if (edition == Edition::kBasic) { - UpgradeDialog upgradeDialog(this); - upgradeDialog.showDialog( - QString("Upgrade to %1 to enable TLS encryption.") - .arg(kProProductName)); - } - } } void SettingsDialog::on_m_pRadioSystemScope_toggled(bool checked) { @@ -228,8 +222,6 @@ void SettingsDialog::loadFromConfig() { } m_pInvertConnection->setChecked(m_appConfig.invertConnection()); - m_pInvertConnection->setEnabled( - m_license.productEdition() == Edition::kBusiness); updateTlsControls(); } @@ -244,14 +236,12 @@ void SettingsDialog::updateTlsControls() { m_pComboBoxTlsKeyLength->findText(keyLengthText)); } - const auto tlsEnabled = m_tlsUtility.isAvailableAndEnabled(); + const auto tlsEnabled = m_tlsUtility.isEnabled(); const auto writable = m_appConfig.isActiveScopeWritable(); m_pCheckBoxEnableTls->setEnabled(writable); m_pCheckBoxEnableTls->setChecked(writable && tlsEnabled); m_pLineEditTlsCertPath->setText(m_appConfig.tlsCertPath()); - - updateTlsControlsEnabled(); } void SettingsDialog::updateTlsControlsEnabled() { diff --git a/src/lib/gui/dialogs/SettingsDialog.h b/src/lib/gui/dialogs/SettingsDialog.h index ee685f04e..c96685b3e 100644 --- a/src/lib/gui/dialogs/SettingsDialog.h +++ b/src/lib/gui/dialogs/SettingsDialog.h @@ -26,7 +26,6 @@ #include "gui/core/CoreTool.h" #include "gui/tls/TlsUtility.h" #include "gui/validators/ValidationError.h" -#include "license/License.h" #include @@ -34,7 +33,6 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase { using IAppConfig = deskflow::gui::IAppConfig; using IServerConfig = deskflow::gui::IServerConfig; using CoreProcess = deskflow::gui::CoreProcess; - using License = deskflow::license::License; Q_OBJECT @@ -42,7 +40,7 @@ public: void extracted(); SettingsDialog( QWidget *parent, IAppConfig &appConfig, const IServerConfig &serverConfig, - const License &license, const CoreProcess &coreProcess); + const CoreProcess &coreProcess); signals: void shown(); @@ -87,7 +85,6 @@ private: IAppConfig &m_appConfig; const IServerConfig &m_serverConfig; - const License &m_license; const CoreProcess &m_coreProcess; deskflow::gui::TlsUtility m_tlsUtility; }; diff --git a/src/lib/gui/dialogs/SettingsDialogBase.ui b/src/lib/gui/dialogs/SettingsDialogBase.ui index 2bb03ad3a..94381619e 100644 --- a/src/lib/gui/dialogs/SettingsDialogBase.ui +++ b/src/lib/gui/dialogs/SettingsDialogBase.ui @@ -416,11 +416,8 @@ - - false - - Invert server/client TCP connection (business edition) + Invert server/client TCP connection diff --git a/src/lib/gui/dialogs/UpgradeDialog.cpp b/src/lib/gui/dialogs/UpgradeDialog.cpp deleted file mode 100644 index ea0f7bf6a..000000000 --- a/src/lib/gui/dialogs/UpgradeDialog.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "UpgradeDialog.h" - -#include "gui/constants.h" - -#include -#include - -UpgradeDialog::UpgradeDialog(QWidget *parent) : QMessageBox(parent) { - setWindowTitle("Upgrade to access this feature"); - m_cancel = addButton("Cancel", QMessageBox::RejectRole); - m_upgrade = addButton("Upgrade", QMessageBox::AcceptRole); -} - -void UpgradeDialog::showDialog(const QString &text) { - setText(text); - exec(); - - if (clickedButton() == m_upgrade) { - const auto url = QUrl(deskflow::gui::kUrlUpgrade); - if (QDesktopServices::openUrl(url)) { - qDebug("opened url: %s", qUtf8Printable(url.toString())); - } else { - qCritical("failed to open url: %s", qUtf8Printable(url.toString())); - } - } else { - qDebug("upgrade was declined"); - } -} diff --git a/src/lib/gui/dialogs/UpgradeDialog.h b/src/lib/gui/dialogs/UpgradeDialog.h deleted file mode 100644 index d8adcece7..000000000 --- a/src/lib/gui/dialogs/UpgradeDialog.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -class UpgradeDialog : public QMessageBox { -public: - explicit UpgradeDialog(QWidget *parent = nullptr); - void showDialog(const QString &text); - -private: - QPushButton *m_upgrade = nullptr; - QPushButton *m_cancel = nullptr; -}; diff --git a/src/lib/gui/env_vars.h b/src/lib/gui/env_vars.h index f91f03d8e..0ca7e54eb 100644 --- a/src/lib/gui/env_vars.h +++ b/src/lib/gui/env_vars.h @@ -23,6 +23,9 @@ namespace deskflow::gui::env_vars { inline QString versionUrl() { + if (QString(DESKFLOW_VERSION_URL).isEmpty()) { + qFatal("version url is not set"); + } return qEnvironmentVariable("DESKFLOW_VERSION_URL", DESKFLOW_VERSION_URL); } diff --git a/src/lib/gui/license/license_notices.h b/src/lib/gui/gui_config.h.in similarity index 74% rename from src/lib/gui/license/license_notices.h rename to src/lib/gui/gui_config.h.in index 3edc8aea6..08c8bcd09 100644 --- a/src/lib/gui/license/license_notices.h +++ b/src/lib/gui/gui_config.h.in @@ -17,12 +17,10 @@ #pragma once -#include "license/License.h" +// clang-format off -#include +#cmakedefine DESKFLOW_GUI_HOOK_HEADER "${DESKFLOW_GUI_HOOK_HEADER}" +#cmakedefine DESKFLOW_GUI_HOOK_START ${DESKFLOW_GUI_HOOK_START} +#cmakedefine DESKFLOW_GUI_HOOK_SETTINGS ${DESKFLOW_GUI_HOOK_SETTINGS} -namespace deskflow::gui { - -QString licenseNotice(const deskflow::license::License &license); - -} // namespace deskflow::gui +// clang-format on diff --git a/src/lib/gui/license/LicenseHandler.cpp b/src/lib/gui/license/LicenseHandler.cpp deleted file mode 100644 index b2248fd6f..000000000 --- a/src/lib/gui/license/LicenseHandler.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2015 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "LicenseHandler.h" - -#include "constants.h" -#include "gui/license/license_utils.h" -#include "license/ProductEdition.h" - -#include -#include -#include -#include - -using namespace std::chrono; -using namespace deskflow::gui::license; -using License = deskflow::license::License; - -const deskflow::license::License &LicenseHandler::license() const { - return m_license; -} - -Edition LicenseHandler::productEdition() const { - return m_license.productEdition(); -} - -QString LicenseHandler::productName() const { - return QString::fromStdString(m_license.productName()); -} - -LicenseHandler::ChangeSerialKeyResult -LicenseHandler::changeSerialKey(const QString &hexString) { - using enum LicenseHandler::ChangeSerialKeyResult; - - if (!m_enabled) { - qFatal("cannot set serial key, licensing is disabled"); - } - - if (hexString.isEmpty()) { - qFatal("serial key is empty"); - return kFatal; - } - - qDebug() << "changing serial key to:" << hexString; - auto serialKey = parseSerialKey(hexString); - - if (serialKey == m_license.serialKey()) { - qDebug("serial key did not change, ignoring"); - return kUnchanged; - } - - if (!serialKey.isValid) { - qDebug() << "invalid serial key, ignoring"; - return kInvalid; - } - - const auto license = License(serialKey); - if (license.isExpired()) { - qDebug("license is expired, ignoring"); - return kExpired; - } - - m_license = license; - emit serialKeyChanged(hexString); - - if (m_license.isSubscription()) { - auto daysLeft = m_license.daysLeft(); - auto msLeft = duration_cast(daysLeft); - if (msLeft.count() < INT_MAX) { - QTimer::singleShot(msLeft, this, SLOT(validateLicense())); - } else { - qDebug("license expiry too distant to schedule timer"); - } - } - - return kSuccess; -} - -void LicenseHandler::validate() const { - if (!m_license.isValid()) { - qDebug("license validation failed, license invalid"); - emit invalidLicense(); - } - - if (m_license.isExpired()) { - qDebug("license validation failed, license expired"); - emit invalidLicense(); - } -} diff --git a/src/lib/gui/license/LicenseHandler.h b/src/lib/gui/license/LicenseHandler.h deleted file mode 100644 index 0b8cbceef..000000000 --- a/src/lib/gui/license/LicenseHandler.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2015 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "gui/constants.h" -#include "gui/license/license_utils.h" -#include "license/License.h" -#include "license/ProductEdition.h" - -#include - -/** - * @brief A convenience wrapper for `License` that provides Qt signals, etc. - */ -class LicenseHandler : public QObject { - Q_OBJECT - using License = deskflow::license::License; - using SerialKey = deskflow::license::SerialKey; - -public: - enum class ChangeSerialKeyResult { - kSuccess, - kFatal, - kUnchanged, - kInvalid, - kExpired, - }; - - Edition productEdition() const; - const License &license() const; - void validate() const; - QString productName() const; - ChangeSerialKeyResult changeSerialKey(const QString &hexString); - void setEnabled(bool enabled) { m_enabled = enabled; } - -signals: - void serialKeyChanged(const QString &serialKey) const; - void invalidLicense() const; - -private: - bool m_enabled = deskflow::gui::license::isActivationEnabled(); - License m_license = License::invalid(); -}; diff --git a/src/lib/gui/license/license_notices.cpp b/src/lib/gui/license/license_notices.cpp deleted file mode 100644 index e5f496835..000000000 --- a/src/lib/gui/license/license_notices.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "license_notices.h" - -#include "constants.h" -#include "license/License.h" -#include "styles.h" - -using License = deskflow::license::License; - -namespace deskflow::gui { - -QString trialLicenseNotice(const License &license); -QString subscriptionLicenseNotice(const License &license); - -QString licenseNotice(const License &license) { - if (license.isTrial()) { - return trialLicenseNotice(license); - } else if (license.isSubscription()) { - return subscriptionLicenseNotice(license); - } else { - qFatal("license notice only for time limited licenses"); - return ""; - } -} - -QString trialLicenseNotice(const License &license) { - const QString buyLink = QString(kLinkBuy).arg(kUrlPurchase).arg(kColorWhite); - if (license.isExpired()) { - return QString("

Your trial has expired. %1

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

Your trial expires today. %1

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

Your trial expires in %1 %2. %3

") - .arg(daysLeft) - .arg((daysLeft == 1) ? "day" : "days") - .arg(buyLink); - } - } -} - -QString subscriptionLicenseNotice(const License &license) { - const QString renewLink = - QString(kLinkRenew).arg(kUrlPurchase).arg(kColorWhite); - if (license.isExpired()) { - return QString("

Your license has expired. %1

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

Your license expires today. %1

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

Your license expires in %1 %2. %3

") - .arg(daysLeft) - .arg((daysLeft == 1) ? "day" : "days") - .arg(renewLink); - } - } -} - -} // namespace deskflow::gui diff --git a/src/lib/gui/license/license_utils.cpp b/src/lib/gui/license/license_utils.cpp deleted file mode 100644 index 75a6fb950..000000000 --- a/src/lib/gui/license/license_utils.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "license_utils.h" - -#include "license/parse_serial_key.h" -#include "string_utils.h" - -#include -#include - -namespace deskflow::gui::license { - -#ifdef DESKFLOW_LICENSED_PRODUCT -const bool kLicensedProduct = true; -#else -const bool kLicensedProduct = false; -#endif - -#ifdef DESKFLOW_ENABLE_ACTIVATION -#ifndef DESKFLOW_LICENSED_PRODUCT -#error "activation requires licensed product" -#endif -const bool kEnableActivation = true; -#else -const bool kEnableActivation = false; -#endif // DESKFLOW_ENABLE_ACTIVATION - -bool isLicensedProduct() { - if (strToTrue(qEnvironmentVariable("DESKFLOW_LICENSED_PRODUCT"))) { - return true; - } else { - return kLicensedProduct; - } -} - -bool isActivationEnabled() { - if (strToTrue(qEnvironmentVariable("DESKFLOW_ENABLE_ACTIVATION"))) { - return true; - } else { - return kEnableActivation; - } -} - -deskflow::license::SerialKey parseSerialKey(const QString &hexString) { - try { - return deskflow::license::parseSerialKey(hexString.toStdString()); - } catch (const std::exception &e) { - qFatal("failed to parse serial key: %s", e.what()); - abort(); - } catch (...) { - qFatal("failed to parse serial key, unknown error"); - abort(); - } -} - -} // namespace deskflow::gui::license diff --git a/src/lib/gui/license/license_utils.h b/src/lib/gui/license/license_utils.h deleted file mode 100644 index a9bcc5cdc..000000000 --- a/src/lib/gui/license/license_utils.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -#include "license/SerialKey.h" - -namespace deskflow::gui::license { - -bool isLicensedProduct(); -bool isActivationEnabled(); -deskflow::license::SerialKey parseSerialKey(const QString &hexString); - -} // namespace deskflow::gui::license diff --git a/src/lib/gui/messages.cpp b/src/lib/gui/messages.cpp index 2070ecb36..cec0af1e6 100644 --- a/src/lib/gui/messages.cpp +++ b/src/lib/gui/messages.cpp @@ -22,7 +22,6 @@ #include "common/version.h" #include "constants.h" #include "env_vars.h" -#include "gui/license/license_utils.h" #include "styles.h" #include @@ -33,8 +32,6 @@ #include #include -using namespace deskflow::gui::license; - namespace deskflow::gui::messages { struct Errors { @@ -63,16 +60,9 @@ void showErrorDialog( text = "

Sorry, a critical error has occurred.

"; } - if (isLicensedProduct()) { - text += QString(R"(

Please contact us)" - " and copy/paste the following error:

") - .arg(kUrlContact, kColorSecondary); - } else { - text += - QString(R"(

Please report a bug)" - " and copy/paste the following error:

") - .arg(kUrlBugReport, kColorSecondary); - } + text += QString(R"(

Please report a bug)" + " and copy/paste the following error:

") + .arg(kUrlHelp, kColorSecondary); const QString version = QString::fromStdString(deskflow::version()); text += QString("
v%1\n%2\n%3
").arg(version, message, fileLine); @@ -140,17 +130,20 @@ void messageHandler( void showCloseReminder(QWidget *parent) { QString message = - "

Deskflow will continue to run in the background and can be accessed " - "via the Deskflow icon in your system notifications area. This setting " - "can be disabled.

"; + QString( + "

%1 will continue to run in the background and can be accessed " + "via the %1 icon in your system notifications area. This " + "setting " + "can be disabled.

") + .arg(kAppName); #if defined(Q_OS_LINUX) message += QString("

On some Linux systems such as GNOME 3, the " "notification area might be disabled. " "You may need to " R"(enable an extension)" - " to see the Deskflow tray icon.

") - .arg(kUrlGnomeTrayFix, kStyleLink); + " to see the %3 tray icon.

") + .arg(kUrlGnomeTrayFix, kStyleLink, kAppName); #endif QMessageBox::information(parent, "Notification area icon", message); @@ -159,16 +152,17 @@ void showCloseReminder(QWidget *parent) { void showFirstServerStartMessage(QWidget *parent) { QMessageBox::information( parent, "Server is running", - "

Great, the server is now running.

" - "

Now you can connect your other computers to this server. " - "You should see a prompt here on the server when a new client tries to " - "connect.

"); + QString("

Great, the %1 server is now running.

" + "

Now you can connect your client computers to this server. " + "You should see a prompt here on the server when a new client " + "tries to connect.

") + .arg(kAppName)); } void showFirstConnectedMessage( QWidget *parent, bool closeToTray, bool enableService, bool isServer) { - auto message = QString("

Deskflow is now connected!

"); + auto message = QString("

%1 is now connected!

").arg(kAppName); if (isServer) { message += @@ -181,13 +175,17 @@ void showFirstConnectedMessage( if (!closeToTray && !enableService) { message += - "

As you do not have the setting enabled to keep Deskflow running in " - "the background, you'll need to keep this window open or minimized to " - "keep Deskflow running.

"; + QString( + "

As you do not have the setting enabled to keep %1 running in " + "the background, you'll need to keep this window open or minimized " + "to keep %1 running.

") + .arg(kAppName); } else { message += - "

You can now close this window and Deskflow will continue to run in " - "the background. This setting can be disabled.

"; + QString( + "

You can now close this window and %1 will continue to run in " + "the background. This setting can be disabled.

") + .arg(kAppName); } QMessageBox::information(parent, "Connected", message); @@ -201,16 +199,11 @@ void showDevThanks(QWidget *parent, const QString &productName) { QMessageBox::information( parent, "Thank you!", QString("

Thanks for using %1.

" - "

If you enjoy using this app, you can support the
" - "developers by " - R"(purchasing a license)" - " or " - R"(contributing code.)" - "

" + "

If you enjoy using this tool, visit our website:

" + R"(

%2

)" + "

Please report bugs and consider contributing code.

" "

This message will only appear once.

") - .arg( - productName, kUrlPurchase, kColorSecondary, kUrlGitHub, - kColorSecondary)); + .arg(productName, kUrlApp, kColorSecondary)); } void showClientConnectError( @@ -274,8 +267,10 @@ bool showClearSettings(QWidget *parent) { const auto clear = message.addButton(QObject::tr("Clear settings"), QMessageBox::AcceptRole); message.setText( - "

Are you sure you want to clear all settings and restart Deskflow?

" - "

This action cannot be undone.

"); + QString( + "

Are you sure you want to clear all settings and restart %1?

" + "

This action cannot be undone.

") + .arg(kAppName)); message.exec(); return message.clickedButton() == clear; @@ -298,27 +293,27 @@ void showWaylandExperimental(QWidget *parent) { "

Wayland support is experimental and contains bugs.

" R"(

Please report bugs to us if you find any.

)" "

Happy testing!

") - .arg(kUrlBugReport, kColorSecondary)); + .arg(kUrlHelp, kColorSecondary)); } void showWaylandLibraryError(QWidget *parent) { QMessageBox::critical( parent, "Library problem", QString( - "

Sorry, while this version of Deskflow does support Wayland, " + "

Sorry, while this version of %1 does support Wayland, " "this build was not linked with one or more of the required " "libraries.

" "

Please either switch to X from your login screen or use a build " "that uses the correct libraries.

" "

If you think this is incorrect, please " - R"(report a bug.

)" + R"(report a bug.

)" "

Please check the logs for more information.

") - .arg(kUrlBugReport, kColorSecondary)); + .arg(kAppName, kUrlHelp, kColorSecondary)); } bool showUpdateCheckOption(QWidget *parent) { QMessageBox message(parent); - message.addButton(QObject::tr("Close"), QMessageBox::RejectRole); + message.addButton(QObject::tr("No thanks"), QMessageBox::RejectRole); const auto checkButton = message.addButton( QObject::tr("Check for updates"), QMessageBox::AcceptRole); message.setText( diff --git a/src/lib/gui/paths.h b/src/lib/gui/paths.h index 73612d201..b36ec2051 100644 --- a/src/lib/gui/paths.h +++ b/src/lib/gui/paths.h @@ -23,7 +23,7 @@ #include #include -const auto kCertificateFilename = "Deskflow.pem"; +const auto kCertificateFilename = QString("%1.pem").arg(DESKFLOW_APP_NAME); const auto kSslDir = "SSL"; namespace deskflow::gui::paths { diff --git a/src/lib/gui/tls/TlsCertificate.cpp b/src/lib/gui/tls/TlsCertificate.cpp index 77d5625ef..9e58c3755 100644 --- a/src/lib/gui/tls/TlsCertificate.cpp +++ b/src/lib/gui/tls/TlsCertificate.cpp @@ -26,7 +26,7 @@ static const char *const kCertificateKeyLength = "rsa:"; static const char *const kCertificateHashAlgorithm = "-sha256"; static const char *const kCertificateLifetime = "365"; -static const char *const kCertificateSubjectInfo = "/CN=Deskflow"; +static const char *const kCertificateSubjectInfo = "/CN=" DESKFLOW_APP_NAME; #if defined(Q_OS_WIN) static const char *const kWinOpenSslDir = "OpenSSL"; @@ -94,6 +94,12 @@ bool TlsCertificate::runTool(const QStringList &args) { #endif QStringList environment; + +// Windows is special! :) +// For OpenSSL, it's very common to bundle the openssl.exe and openssl.cnf files +// with the application. This is made a little more complex in the Windows dev +// env, because vcpkg can't find the openssl.cnf file by default, so we need to +// give it a bit of guidance by setting the `OPENSSL_CONF` env var. #if defined(Q_OS_WIN) const auto openSslDir = QDir(openSslWindowsDir()); const auto config = QDir::cleanPath(openSslDir.filePath(kConfigFile)); diff --git a/src/lib/gui/tls/TlsUtility.cpp b/src/lib/gui/tls/TlsUtility.cpp index e21031564..c2ff57375 100644 --- a/src/lib/gui/tls/TlsUtility.cpp +++ b/src/lib/gui/tls/TlsUtility.cpp @@ -18,34 +18,24 @@ #include "TlsUtility.h" #include "TlsCertificate.h" -#include "gui/license/license_utils.h" #include #include -using namespace deskflow::license; -using namespace deskflow::gui::license; - namespace deskflow::gui { -TlsUtility::TlsUtility(const IAppConfig &appConfig, const ILicense &license) - : m_appConfig(appConfig), - m_license(license) {} +TlsUtility::TlsUtility(const IAppConfig &appConfig) : m_appConfig(appConfig) {} -bool TlsUtility::isAvailable() const { - return !isActivationEnabled() || m_license.isTlsAvailable(); -} - -bool TlsUtility::isAvailableAndEnabled() const { +bool TlsUtility::isEnabled() const { const auto &config = m_appConfig; - return isAvailable() && config.tlsEnabled(); + return config.tlsEnabled(); } bool TlsUtility::generateCertificate() { qDebug("generating tls certificate, " "all clients must trust the new fingerprint"); - if (!isAvailableAndEnabled()) { + if (!isEnabled()) { qCritical("unable to generate tls certificate, " "tls is either not available or not enabled"); return false; diff --git a/src/lib/gui/tls/TlsUtility.h b/src/lib/gui/tls/TlsUtility.h index 202a87939..6619dfcb3 100644 --- a/src/lib/gui/tls/TlsUtility.h +++ b/src/lib/gui/tls/TlsUtility.h @@ -20,18 +20,16 @@ #include "gui/config/IAppConfig.h" #include "TlsCertificate.h" -#include "license/ILicense.h" #include namespace deskflow::gui { class TlsUtility : public QObject { - using ILicense = deskflow::license::ILicense; Q_OBJECT public: - explicit TlsUtility(const IAppConfig &appConfig, const ILicense &license); + explicit TlsUtility(const IAppConfig &appConfig); bool generateCertificate(); bool persistCertificate(); @@ -43,16 +41,10 @@ public: * If licensing is enabled, it checks whether the product has TLS * available, and if licensing is not enabled, true is returned. */ - bool isAvailableAndEnabled() const; - - /** - * @return true if TLS is available, regardless of whether it is enabled. - */ - bool isAvailable() const; + bool isEnabled() const; private: const IAppConfig &m_appConfig; - const ILicense &m_license; TlsCertificate m_certificate; }; diff --git a/src/lib/license/CMakeLists.txt b/src/lib/license/CMakeLists.txt deleted file mode 100644 index 79e8e7ebd..000000000 --- a/src/lib/license/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# Deskflow -- mouse and keyboard sharing utility -# Copyright (C) 2012-2024 Symless Ltd. -# Copyright (C) 2009-2012 Nick Bolton -# -# This package is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# found in the file LICENSE that should have accompanied this file. -# -# This package is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -file(GLOB headers "*.h") -file(GLOB sources "*.cpp") - -if(ADD_HEADERS_TO_SOURCES) - list(APPEND sources ${headers}) -endif() - -add_library(license STATIC ${sources}) - -target_link_libraries(license arch base) diff --git a/src/lib/license/ILicense.h b/src/lib/license/ILicense.h deleted file mode 100644 index ee34c06dc..000000000 --- a/src/lib/license/ILicense.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -class Server; -class LicenseHandler; -class LicenseTests; - -namespace deskflow::license { - -class ILicense { -public: - virtual ~ILicense() = default; - virtual bool isTlsAvailable() const = 0; -}; - -} // namespace deskflow::license diff --git a/src/lib/license/License.cpp b/src/lib/license/License.cpp deleted file mode 100644 index 3eceb7d4b..000000000 --- a/src/lib/license/License.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "License.h" - -#include "Product.h" -#include "license/SerialKey.h" -#include "license/parse_serial_key.h" - -#include -#include - -using namespace std::chrono; - -namespace deskflow::license { - -License::License(const std::string &hexString) - : m_serialKey(parseSerialKey(hexString)) {} - -License::License(const SerialKey &serialKey) : m_serialKey(serialKey) { - if (!m_serialKey.isValid) { - throw InvalidSerialKey(); - } -} - -bool License::isTrial() const { return m_serialKey.type.isTrial(); } - -bool License::isSubscription() const { - return m_serialKey.type.isSubscription(); -} - -bool License::isTimeLimited() const { - return m_serialKey.type.isSubscription() || m_serialKey.type.isTrial(); -} - -bool License::isTlsAvailable() const { - return m_serialKey.product.isTlsAvailable(); -} - -Edition License::productEdition() const { - return m_serialKey.product.edition(); -} - -bool License::isExpiringSoon() const { - if (!isTimeLimited()) { - return false; - } - - if (!m_serialKey.warnTime.has_value()) { - throw NoTimeLimitError(); - } - - return m_nowFunc() >= m_serialKey.warnTime.value(); -} - -bool License::isExpired() const { - if (!isTimeLimited()) { - return false; - } - - if (!m_serialKey.expireTime.has_value()) { - throw NoTimeLimitError(); - } - - return m_nowFunc() >= m_serialKey.expireTime.value(); -} - -days License::daysLeft() const { - if (!m_serialKey.expireTime.has_value()) { - throw NoTimeLimitError(); - } - - auto expireTime = m_serialKey.expireTime.value(); - - auto timeLeft = expireTime - m_nowFunc(); - return duration_cast(timeLeft); -} - -std::string License::productName() const { - auto name = m_serialKey.product.name(); - if (m_serialKey.type.isTrial()) { - name += " (Trial)"; - } - return name; -} - -} // namespace deskflow::license diff --git a/src/lib/license/License.h b/src/lib/license/License.h deleted file mode 100644 index 05743ddee..000000000 --- a/src/lib/license/License.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "ILicense.h" -#include "SerialKey.h" - -#include -#include -#include -#include - -class Server; -class LicenseHandler; -class LicenseTests; - -namespace deskflow::license { - -class License : public ILicense { - friend class ::Server; - friend class ::LicenseHandler; - friend class ::LicenseTests; - - using days = std::chrono::days; - using system_clock = std::chrono::system_clock; - using time_point = system_clock::time_point; - using NowFunc = std::function; - using LicenseError = std::runtime_error; - -public: - explicit License(const SerialKey &serialKey); - explicit License(const std::string &hexString); - ~License() override = default; - - friend bool operator==(License const &lhs, License const &rhs) { - return lhs.m_serialKey == rhs.m_serialKey; - } - - bool isTlsAvailable() const override; - - bool isValid() const { return m_serialKey.isValid; } - bool isExpiringSoon() const; - bool isExpired() const; - bool isTrial() const; - bool isSubscription() const; - bool isTimeLimited() const; - days daysLeft() const; - Edition productEdition() const; - std::string productName() const; - const SerialKey &serialKey() const { return m_serialKey; } - void invalidate() { m_serialKey = SerialKey::invalid(); } - - class InvalidSerialKey : public LicenseError { - public: - explicit InvalidSerialKey() : LicenseError("invalid serial key") {} - }; - - class NoTimeLimitError : public LicenseError { - public: - explicit NoTimeLimitError() - : LicenseError("serial key has no time limit") {} - }; - -protected: - void setNowFunc(const NowFunc &nowFunc) { m_nowFunc = nowFunc; } - -private: - // for intentionality, force use of `invalid()` static function. - License() = default; - - // prevent copy, so that changes can be reflected in one instance. - License(const License &) = default; - License &operator=(const License &) = default; - - static License invalid() { return License(); } - - SerialKey m_serialKey = SerialKey::invalid(); - NowFunc m_nowFunc = []() { return system_clock::now(); }; -}; - -} // namespace deskflow::license diff --git a/src/lib/license/Product.cpp b/src/lib/license/Product.cpp deleted file mode 100644 index a82e9f7df..000000000 --- a/src/lib/license/Product.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include - -#include "Product.h" - -using SKE = Product::SerialKeyEditionID; - -const char *const kLicensedProductName = "Deskflow"; - -const std::string SKE::Pro = "pro"; -const std::string SKE::Basic = "basic"; -const std::string SKE::Buisiness = "business"; - -using Edition = Product::Edition; - -const std::map> kSerialKeyEditions{ - {SKE::Basic, Edition::kBasic}, - {SKE::Pro, Edition::kPro}, - {SKE::Buisiness, Edition::kBusiness}, -}; - -Product::Product(Edition edition) : m_edition(edition) {} - -Product::Product(const std::string &serialKeyEditionID) { - setEdition(serialKeyEditionID); -} - -Edition Product::edition() const { return m_edition; } - -std::string Product::serialKeyId() const { - switch (edition()) { - using enum Edition; - - case kPro: - return SKE::Pro; - - case kBasic: - return SKE::Basic; - - case kBusiness: - return SKE::Buisiness; - - default: - throw InvalidType(); - } -} - -std::string Product::name() const { - - const std::string nameBase = kLicensedProductName; - switch (edition()) { - using enum Edition; - - case kUnregistered: - return nameBase + " (unregistered)"; - - case kBasic: - return nameBase + " Basic"; - - case kPro: - return nameBase + " Pro"; - - case kBusiness: - return nameBase + " Business"; - - default: - throw InvalidType(); - } -} - -void Product::setEdition(Edition edition) { m_edition = edition; } - -void Product::setEdition(const std::string &name) { - const auto &pType = kSerialKeyEditions.find(name); - - if (pType != kSerialKeyEditions.end()) { - m_edition = pType->second; - } else { - throw InvalidType(); - } -} - -bool Product::isValid() const { - if (m_edition == Edition::kUnregistered) { - return false; - } - return kSerialKeyEditions.contains(serialKeyId()); -} - -bool Product::isTlsAvailable() const { - switch (edition()) { - using enum Edition; - - case kPro: - case kBusiness: - return true; - - case kBasic: - case kUnregistered: - return false; - - default: - throw InvalidType(); - } -} diff --git a/src/lib/license/Product.h b/src/lib/license/Product.h deleted file mode 100644 index 39fe93cde..000000000 --- a/src/lib/license/Product.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -class Product { - friend bool operator==(Product const &, Product const &) = default; - -public: - class InvalidType : public std::runtime_error { - public: - explicit InvalidType() : std::runtime_error("invalid product type") {} - }; - - enum class Edition { - kUnregistered = -1, - kBasic = 0, - kPro = 1, - kBusiness = 4, - }; - - /** - * @brief Product edition IDs found in a decoded serial key. - */ - class SerialKeyEditionID { - public: - static const std::string Basic; - static const std::string Pro; - static const std::string Buisiness; - }; - - Product() = default; - explicit Product(Edition edition); - explicit Product(const std::string &serialKeyEditionID); - - bool isValid() const; - Edition edition() const; - std::string serialKeyId() const; - std::string name() const; - bool isTlsAvailable() const; - - void setEdition(Edition type); - void setEdition(const std::string &serialKeyId); - -private: - Edition m_edition = Edition::kUnregistered; -}; diff --git a/src/lib/license/ProductEdition.h b/src/lib/license/ProductEdition.h deleted file mode 100644 index a04aaeed9..000000000 --- a/src/lib/license/ProductEdition.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2015 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "Product.h" - -/// @deprecated Use `Product::Edition` instead -using Edition = Product::Edition; diff --git a/src/lib/license/SerialKey.h b/src/lib/license/SerialKey.h deleted file mode 100644 index 290164399..000000000 --- a/src/lib/license/SerialKey.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2022 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include "ProductEdition.h" -#include "SerialKeyType.h" -#include "license/Product.h" -#include "license/ProductEdition.h" - -#include -#include -#include -#include - -namespace deskflow::license { - -struct SerialKey { - using time_point = std::chrono::system_clock::time_point; - - friend bool operator==(const SerialKey &lhs, const SerialKey &rhs) { - return (lhs.hexString == rhs.hexString) && (lhs.warnTime == rhs.warnTime) && - (lhs.expireTime == rhs.expireTime) && (lhs.product == rhs.product) && - (lhs.type == rhs.type); - } - - explicit SerialKey(const std::string &key) : hexString(key) {} - - static SerialKey invalid() { - return SerialKey(Product::Edition::kUnregistered); - } - - const std::string &toString() const { return hexString; } - - bool isValid = false; - std::string hexString = ""; - Product product; - SerialKeyType type; - std::optional warnTime = std::nullopt; - std::optional expireTime = std::nullopt; - -private: - explicit SerialKey(Edition edition) : product(edition) {} -}; - -} // namespace deskflow::license diff --git a/src/lib/license/SerialKeyType.cpp b/src/lib/license/SerialKeyType.cpp deleted file mode 100644 index c2432525c..000000000 --- a/src/lib/license/SerialKeyType.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2015 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "SerialKeyType.h" - -const std::string SerialKeyType::Trial = "trial"; -const std::string SerialKeyType::Subscription = "subscription"; - -void SerialKeyType::setType(const std::string_view &type) { - m_isTrial = (type == SerialKeyType::Trial); - m_isSubscription = (type == SerialKeyType::Subscription); -} - -bool SerialKeyType::isTrial() const { return m_isTrial; } - -bool SerialKeyType::isSubscription() const { return m_isSubscription; } diff --git a/src/lib/license/SerialKeyType.h b/src/lib/license/SerialKeyType.h deleted file mode 100644 index 45454082d..000000000 --- a/src/lib/license/SerialKeyType.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include - -class SerialKeyType { -private: - friend bool - operator==(SerialKeyType const &lhs, SerialKeyType const &rhs) = default; - -public: - static const std::string Trial; - static const std::string Subscription; - - explicit SerialKeyType() = default; - explicit SerialKeyType(const std::string_view &type) { setType(type); } - - void setType(const std::string_view &type); - bool isTrial() const; - bool isSubscription() const; - -private: - bool m_isTrial = false; - bool m_isSubscription = false; -}; diff --git a/src/lib/license/parse_serial_key.cpp b/src/lib/license/parse_serial_key.cpp deleted file mode 100644 index f1d60cc5b..000000000 --- a/src/lib/license/parse_serial_key.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "parse_serial_key.h" - -#include "SerialKey.h" -#include "SerialKeyType.h" -#include "utils/string_utils.h" - -#include -#include -#include -#include -#include - -using Parts = std::vector; -using system_clock = std::chrono::system_clock; -using time_point = system_clock::time_point; - -namespace deskflow::license { - -std::string decode(const std::string &hexString); -Parts tokenize(const std::string &plainText); -SerialKey parseV1(const std::string &hexString, const Parts &parts); -SerialKey parseV2(const std::string &hexString, const Parts &parts); -std::optional parseDate(const std::string &unixTimeString); - -SerialKey parseSerialKey(const std::string &hexString) { - const auto &plainText = decode(hexString); - const auto &parts = tokenize(plainText); - const auto &version = parts.at(0); - - if (version == "v1") { - return parseV1(hexString, parts); - } else if (version == "v2") { - return parseV2(hexString, parts); - } else { - throw InvalidSerialKeyVersion(version); - } -} - -std::string decode(const std::string &hexString) { - std::string trimmed = utils::trim(hexString); - - if (trimmed.length() % 2 != 0) { - throw InvalidHexString(); - } - - std::string plainText; - plainText.reserve(trimmed.length() / 2); - - for (size_t i = 0; i < trimmed.length(); i += 2) { - std::string byteString = trimmed.substr(i, 2); - auto byte = static_cast(std::stoi(byteString, nullptr, 16)); - plainText.push_back(byte); - } - - return plainText; -} - -SerialKey parseV1(const std::string &hexString, const Parts &parts) { - if (parts.size() < 8) { - throw InvalidSerialKeyFormat(); - } - - // e.g.: {v1;basic;name;1;email;company name;1398297600;1398384000} - SerialKey serialKey(hexString); - serialKey.product = Product(parts.at(1)); - serialKey.warnTime = parseDate(parts.at(6)); - serialKey.expireTime = parseDate(parts.at(7)); - serialKey.isValid = true; - return serialKey; -} - -SerialKey parseV2(const std::string &hexString, const Parts &parts) { - if (parts.size() < 9) { - throw InvalidSerialKeyFormat(); - } - // e.g.: {v2;trial;basic;name;1;email;company name;1398297600;1398384000} - SerialKey serialKey(hexString); - serialKey.type = SerialKeyType(parts.at(1)); - serialKey.product = Product(parts.at(2)); - serialKey.warnTime = parseDate(parts.at(7)); - serialKey.expireTime = parseDate(parts.at(8)); - serialKey.isValid = true; - return serialKey; -} - -Parts tokenize(const std::string &plainText) { - if (plainText.front() != '{' || plainText.back() != '}') { - throw InvalidSerialKeyFormat(); - } - - const auto serialData = plainText.substr(1, plainText.length() - 2); - - Parts parts; - std::stringstream ss(serialData); - std::string item; - - while (std::getline(ss, item, ';')) { - parts.push_back(item); - } - - // it's possible that the last character is a delimiter, so add an empty part - if (!serialData.empty() && serialData.back() == ';') { - parts.emplace_back(""); - } - - return parts; -} - -std::optional parseDate(const std::string &unixTimeString) { - auto clean = utils::trim(unixTimeString); - if (clean.empty()) { - return std::nullopt; - } - - try { - auto seconds = std::stoll(clean); - if (seconds <= 0) { - return std::nullopt; - } else { - return time_point{std::chrono::seconds{seconds}}; - } - } catch (std::invalid_argument &) { - throw InvalidSerialKeyDate(unixTimeString, "invalid argument"); - } catch (std::out_of_range &) { - throw InvalidSerialKeyDate(unixTimeString, "out of range"); - } catch (std::exception &ex) { - throw InvalidSerialKeyDate(unixTimeString, ex.what()); - } catch (...) { // NOSONAR - throw InvalidSerialKeyDate(unixTimeString, "unknown error"); - } -} - -} // namespace deskflow::license diff --git a/src/lib/license/parse_serial_key.h b/src/lib/license/parse_serial_key.h deleted file mode 100644 index bbb95aee7..000000000 --- a/src/lib/license/parse_serial_key.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "SerialKey.h" - -#include -#include - -namespace deskflow::license { - -class SerialKeyParseError : public std::runtime_error { -public: - explicit SerialKeyParseError(const std::string &message) - : std::runtime_error(message) {} -}; - -class InvalidHexString : public SerialKeyParseError { -public: - explicit InvalidHexString() : SerialKeyParseError("invalid hex string") {} -}; - -class InvalidSerialKeyFormat : public SerialKeyParseError { -public: - explicit InvalidSerialKeyFormat() - : SerialKeyParseError("invalid serial key format") {} -}; - -class InvalidSerialKeyDate : public SerialKeyParseError { -public: - explicit InvalidSerialKeyDate( - const std::string &date, const std::string &cause) - : SerialKeyParseError("invalid serial key date: " + date + "\n" + cause) { - } -}; - -class InvalidSerialKeyVersion : public SerialKeyParseError { -public: - explicit InvalidSerialKeyVersion(const std::string &version) - : SerialKeyParseError("invalid serial key version: " + version) {} -}; - -SerialKey parseSerialKey(const std::string &hexString); - -} // namespace deskflow::license diff --git a/src/lib/mt/Thread.cpp b/src/lib/mt/Thread.cpp index 0d1fbb2ad..a90a57fd8 100644 --- a/src/lib/mt/Thread.cpp +++ b/src/lib/mt/Thread.cpp @@ -121,7 +121,7 @@ void *Thread::threadFunc(void *vjob) { result = e.m_result; LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result)); } catch (XBase &e) { - LOG((CLOG_ERR "deskflow exception on thread 0x%08x: %s", id, e.what())); + LOG((CLOG_ERR "exception on thread 0x%08x: %s", id, e.what())); delete job; throw; } catch (std::exception &e) { diff --git a/src/lib/net/InverseSockets/SecureServerSocket.cpp b/src/lib/net/InverseSockets/SecureServerSocket.cpp index 4cc253371..89b2c5658 100644 --- a/src/lib/net/InverseSockets/SecureServerSocket.cpp +++ b/src/lib/net/InverseSockets/SecureServerSocket.cpp @@ -71,7 +71,8 @@ std::string SecureServerSocket::getCertificateFileName() const { if (certificateFilename.empty()) { // default location of the TLS cert file in users dir certificateFilename = deskflow::string::sprintf( - "%s/SSL/Deskflow.pem", ARCH->getProfileDirectory().c_str()); + "%s/SSL/" DESKFLOW_APP_NAME ".pem", + ARCH->getProfileDirectory().c_str()); } return certificateFilename; diff --git a/src/lib/net/SecureListenSocket.cpp b/src/lib/net/SecureListenSocket.cpp index a0ef2c103..1fc3d0b49 100644 --- a/src/lib/net/SecureListenSocket.cpp +++ b/src/lib/net/SecureListenSocket.cpp @@ -26,7 +26,7 @@ #include "net/TSocketMultiplexerMethodJob.h" static const char s_certificateDir[] = {"SSL"}; -static const char s_certificateFilename[] = {"Deskflow.pem"}; +static const char s_certificateFilename[] = {DESKFLOW_APP_NAME ".pem"}; // // SecureListenSocket diff --git a/src/lib/platform/EiScreen.cpp b/src/lib/platform/EiScreen.cpp index 603aba007..f6221ab84 100644 --- a/src/lib/platform/EiScreen.cpp +++ b/src/lib/platform/EiScreen.cpp @@ -141,7 +141,7 @@ void EiScreen::init_ei() { ei_set_user_data(ei_, this); ei_log_set_priority(ei_, EI_LOG_PRIORITY_DEBUG); ei_log_set_handler(ei_, cb_handle_ei_log_event); - ei_configure_name(ei_, "deskflow client"); + ei_configure_name(ei_, DESKFLOW_APP_ID " client"); // install the platform event queue events_->adoptBuffer(nullptr); diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp index a2cea985b..263a3eba6 100644 --- a/src/lib/platform/MSWindowsClipboard.cpp +++ b/src/lib/platform/MSWindowsClipboard.cpp @@ -193,7 +193,8 @@ void MSWindowsClipboard::clearConverters() { bool MSWindowsClipboard::isOwnedByDeskflow() { // create ownership format if we haven't yet if (s_ownershipFormat == 0) { - s_ownershipFormat = RegisterClipboardFormat(TEXT("DeskflowOwnership")); + s_ownershipFormat = + RegisterClipboardFormat(TEXT(DESKFLOW_APP_NAME "Ownership")); } return (IsClipboardFormatAvailable(getOwnershipFormat()) != 0); } @@ -201,7 +202,8 @@ bool MSWindowsClipboard::isOwnedByDeskflow() { UINT MSWindowsClipboard::getOwnershipFormat() { // create ownership format if we haven't yet if (s_ownershipFormat == 0) { - s_ownershipFormat = RegisterClipboardFormat(TEXT("DeskflowOwnership")); + s_ownershipFormat = + RegisterClipboardFormat(TEXT(DESKFLOW_APP_NAME "Ownership")); } // return the format diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp index 7f5fadf80..67ce88abb 100644 --- a/src/lib/platform/MSWindowsDesks.cpp +++ b/src/lib/platform/MSWindowsDesks.cpp @@ -354,7 +354,7 @@ ATOM MSWindowsDesks::createDeskWindowClass(bool isPrimary) const { classInfo.hCursor = m_cursor; classInfo.hbrBackground = NULL; classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = "DeskflowDesk"; + classInfo.lpszClassName = DESKFLOW_APP_NAME "Desk"; classInfo.hIconSm = NULL; return RegisterClassEx(&classInfo); } @@ -573,7 +573,7 @@ void MSWindowsDesks::deskThread(void *vdesk) { // create a window. we use this window to hide the cursor. try { - desk->m_window = createWindow(m_deskClass, "DeskflowDesk"); + desk->m_window = createWindow(m_deskClass, DESKFLOW_APP_NAME "Desk"); LOG( (CLOG_DEBUG "desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window)); diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp index 9b5928059..5a2d6ce82 100644 --- a/src/lib/platform/MSWindowsScreen.cpp +++ b/src/lib/platform/MSWindowsScreen.cpp @@ -146,7 +146,7 @@ MSWindowsScreen::MSWindowsScreen( updateScreenShape(); m_class = createWindowClass(); - m_window = createWindow(m_class, "Deskflow"); + m_window = createWindow(m_class, DESKFLOW_APP_NAME); forceShowCursor(); LOG( (CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, @@ -826,7 +826,7 @@ ATOM MSWindowsScreen::createWindowClass() const { classInfo.hCursor = NULL; classInfo.hbrBackground = NULL; classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = "Deskflow"; + classInfo.lpszClassName = DESKFLOW_APP_NAME; classInfo.hIconSm = NULL; return RegisterClassEx(&classInfo); } @@ -1438,7 +1438,7 @@ bool MSWindowsScreen::onClipboardChange() { m_events->forClipboard().clipboardGrabbed(), kClipboardSelection); } } else if (!m_ownClipboard) { - LOG((CLOG_DEBUG "clipboard changed: deskflow owned")); + LOG((CLOG_DEBUG "clipboard changed: " DESKFLOW_APP_ID " owned")); m_ownClipboard = true; } diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp index e22cb97a4..f85d4785d 100644 --- a/src/lib/platform/MSWindowsWatchdog.cpp +++ b/src/lib/platform/MSWindowsWatchdog.cpp @@ -571,9 +571,9 @@ void MSWindowsWatchdog::shutdownExistingProcesses() { // make sure we're not checking the system process if (entry.th32ProcessID != 0) { - if (_stricmp(entry.szExeFile, "deskflowc.exe") == 0 || - _stricmp(entry.szExeFile, "deskflows.exe") == 0 || - _stricmp(entry.szExeFile, "deskflow-core.exe") == 0) { + if (_stricmp(entry.szExeFile, CLIENT_BINARY_NAME ".exe") == 0 || + _stricmp(entry.szExeFile, SERVER_BINARY_NAME ".exe") == 0 || + _stricmp(entry.szExeFile, CORE_BINARY_NAME ".exe") == 0) { HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID); @@ -608,7 +608,7 @@ void MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security) { deskflowLegacyCommand.append("\"") .append(installedDir) .append("\\") - .append("deskflow-legacy") + .append(LEGACY_BINARY_NAME) .append("\""); deskflowLegacyCommand.append(" --get-active-desktop"); @@ -625,7 +625,9 @@ void MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security) { DWORD rc = GetLastError(); RevertToSelf(); } else { - LOG((CLOG_DEBUG "launched deskflow-legacy to check active desktop")); + LOG( + (CLOG_DEBUG "launched %s to check active desktop", + LEGACY_BINARY_NAME)); } ARCH->lockMutex(m_mutex); diff --git a/src/lib/platform/OSXPowerManager.cpp b/src/lib/platform/OSXPowerManager.cpp index f8f5ddfb4..b0e413678 100644 --- a/src/lib/platform/OSXPowerManager.cpp +++ b/src/lib/platform/OSXPowerManager.cpp @@ -25,7 +25,7 @@ OSXPowerManager::~OSXPowerManager() { enableSleep(); } void OSXPowerManager::disableSleep() { if (!m_sleepPreventionAssertionID) { - CFStringRef reasonForActivity = CFSTR("Deskflow application"); + CFStringRef reasonForActivity = CFSTR(DESKFLOW_APP_NAME " application"); IOReturn result = IOPMAssertionCreateWithName( kIOPMAssertPreventUserIdleDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &m_sleepPreventionAssertionID); diff --git a/src/lib/server/CMakeLists.txt b/src/lib/server/CMakeLists.txt index a4a75cd72..6792c1d83 100644 --- a/src/lib/server/CMakeLists.txt +++ b/src/lib/server/CMakeLists.txt @@ -23,8 +23,6 @@ endif() add_library(server STATIC ${sources}) -target_link_libraries(server license) - if(UNIX) target_link_libraries(server app) endif() diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp index 8a9be6d93..f6d68ba25 100644 --- a/src/lib/server/Server.cpp +++ b/src/lib/server/Server.cpp @@ -33,7 +33,6 @@ #include "deskflow/StreamChunker.h" #include "deskflow/option_types.h" #include "deskflow/protocol_types.h" -#include "license/License.h" #include "mt/Thread.h" #include "net/TCPSocket.h" #include "server/ClientListener.h" @@ -47,7 +46,6 @@ #include #include -using namespace deskflow::license; using namespace deskflow::server; // @@ -430,15 +428,6 @@ void Server::switchScreen( BaseClientProxy *dst, SInt32 x, SInt32 y, bool forScreensaver) { assert(dst != NULL); - if (m_args.m_serialKey.isValid) { - // if license is expired, exit the process - License license(m_args.m_serialKey); - if (license.isExpired()) { - LOG((CLOG_ERR "trial has expired, aborting server")); - exit(kExitSuccess); - } - } - #ifndef NDEBUG { SInt32 dx, dy, dw, dh; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 0161119ce..808d99c9e 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -16,7 +16,7 @@ macro(config_all_tests) - set(base_dir ${CMAKE_SOURCE_DIR}) + set(base_dir ${PROJECT_SOURCE_DIR}) set(src_dir ${base_dir}/src) set(test_base_dir ${src_dir}/test) set(gui_dir ${src_dir}/gui/src) @@ -63,7 +63,7 @@ macro(set_sources) endif() if(WIN32) - list(APPEND sources ${CMAKE_BINARY_DIR}/src/version.rc) + list(APPEND sources ${PROJECT_BINARY_DIR}/src/version.rc) endif() replace_platform_sources() @@ -126,7 +126,7 @@ macro(config_test_deps) # gui library autogen headers: # qt doesn't seem to auto include the autogen headers for libraries. - include_directories(${CMAKE_BINARY_DIR}/src/lib/gui/gui_autogen/include) + include_directories(${PROJECT_BINARY_DIR}/src/lib/gui/gui_autogen/include) set(test_libs arch @@ -140,7 +140,6 @@ macro(config_test_deps) app mt ipc - license gui ${GMOCK_LIB} ${GTEST_LIB} diff --git a/src/test/integtests/gui/core/CoreToolTests.cpp b/src/test/integtests/gui/core/CoreToolTests.cpp index 6ac377cf4..24b979a8a 100644 --- a/src/test/integtests/gui/core/CoreToolTests.cpp +++ b/src/test/integtests/gui/core/CoreToolTests.cpp @@ -46,12 +46,3 @@ TEST(CoreToolTests, getArch_noMock_returnsNonEmpty) { EXPECT_FALSE(arch.isEmpty()); } - -TEST(CoreToolTests, getSerialKeyFilePath_noMock_returnsNonEmpty) { - TestQtCoreApp app; - CoreTool coreTool; - - QString serialKeyFilePath = coreTool.getSerialKeyFilePath(); - - EXPECT_FALSE(serialKeyFilePath.isEmpty()); -} diff --git a/src/test/shared/gui/mocks/AppConfigMock.h b/src/test/shared/gui/mocks/AppConfigMock.h index 618e0f591..e62915ab3 100644 --- a/src/test/shared/gui/mocks/AppConfigMock.h +++ b/src/test/shared/gui/mocks/AppConfigMock.h @@ -57,7 +57,6 @@ public: MOCK_METHOD(QString, coreClientName, (), (const, override)); MOCK_METHOD(bool, invertConnection, (), (const, override)); MOCK_METHOD(void, persistLogDir, (), (const, override)); - MOCK_METHOD(QString, serialKey, (), (const, override)); MOCK_METHOD(bool, languageSync, (), (const, override)); MOCK_METHOD(bool, invertScrollDirection, (), (const, override)); MOCK_METHOD(int, port, (), (const, override)); diff --git a/src/test/unittests/deskflow/ArgParserTests.cpp b/src/test/unittests/deskflow/ArgParserTests.cpp index 7cc8d9cae..11eeae0ae 100644 --- a/src/test/unittests/deskflow/ArgParserTests.cpp +++ b/src/test/unittests/deskflow/ArgParserTests.cpp @@ -211,12 +211,12 @@ TEST(ArgParserTests, parseToolArgs_matches_correspondingly) { for (auto const &test : tests) { ToolArgs toolArgs; EXPECT_FALSE(test.second(toolArgs)); - std::array twoArgs{"deskflow-legacy", test.first}; + std::array twoArgs{LEGACY_BINARY_NAME, test.first}; EXPECT_TRUE(parser.parseToolArgs(toolArgs, 2, twoArgs.data())); EXPECT_TRUE(test.second(toolArgs)); } ToolArgs toolArgs; - std::array twoArgs{"deskflow-legacy", "--garbage"}; + std::array twoArgs{LEGACY_BINARY_NAME, "--garbage"}; EXPECT_FALSE(parser.parseToolArgs(toolArgs, 2, twoArgs.data())); } diff --git a/src/test/unittests/deskflow/ServerAppTests.cpp b/src/test/unittests/deskflow/ServerAppTests.cpp index d6e107fee..da5e634c7 100644 --- a/src/test/unittests/deskflow/ServerAppTests.cpp +++ b/src/test/unittests/deskflow/ServerAppTests.cpp @@ -36,7 +36,7 @@ TEST(ServerAppTests, runInner_will_handle_configuration_lifetime) { EXPECT_FALSE(app.args().m_config); - const char *argv[]{"deskflowc"}; + const char *argv[]{SERVER_BINARY_NAME}; app.runInner( 1, const_cast(argv), nullptr, [](int, char **) { return 0; }); diff --git a/src/test/unittests/deskflow/ServerArgsParsingTests.cpp b/src/test/unittests/deskflow/ServerArgsParsingTests.cpp index 102f6f892..cca761917 100644 --- a/src/test/unittests/deskflow/ServerArgsParsingTests.cpp +++ b/src/test/unittests/deskflow/ServerArgsParsingTests.cpp @@ -69,23 +69,6 @@ TEST(ServerArgsParsingTests, parseServerArgs_configArg_setConfigFile) { EXPECT_EQ("mock_configFile", serverArgs.m_configFile); } -TEST(ServerArgsParsingTests, parseServerArgs_serialKey_isSet) { - NiceMock argParser; - ON_CALL(argParser, parseGenericArgs(_, _, _)) - .WillByDefault(Invoke(server_stubParseGenericArgs)); - ON_CALL(argParser, checkUnexpectedArgs()) - .WillByDefault(Invoke(server_stubCheckUnexpectedArgs)); - deskflow::ServerArgs serverArgs; - const int argc = 3; - const char *serial = - "7B76323B737562736372697074696F6E3B62617369633B426F623B313B656D61696C3B63" - "6F6D70616E79206E616D653B303B38363430307D"; - std::array kSerialCmd = {"stub", "--serial-key", serial}; - - argParser.parseServerArgs(serverArgs, argc, kSerialCmd.data()); - EXPECT_EQ(serial, serverArgs.m_serialKey.toString()); -} - TEST(ServerArgsParsingTests, parseServerArgs_checkUnexpectedParams) { NiceMock argParser; ON_CALL(argParser, parseGenericArgs(_, _, _)) diff --git a/src/test/unittests/gui/core/CoreProcessTests.cpp b/src/test/unittests/gui/core/CoreProcessTests.cpp index 02ef3238e..01454cf72 100644 --- a/src/test/unittests/gui/core/CoreProcessTests.cpp +++ b/src/test/unittests/gui/core/CoreProcessTests.cpp @@ -19,7 +19,6 @@ #include "gui/core/CoreProcess.h" #include "gui/ipc/IQIpcClient.h" #include "gui/proxy/QProcessProxy.h" -#include "license/ILicense.h" #include "shared/gui/mocks/AppConfigMock.h" #include "shared/gui/mocks/ServerConfigMock.h" @@ -28,7 +27,6 @@ #include using namespace deskflow::gui; -using namespace deskflow::license; using namespace testing; namespace { @@ -91,23 +89,12 @@ public: NiceMock m_ipcClient; }; -class LicenseMock : public ILicense { -public: - LicenseMock() { - ON_CALL(*this, isTlsAvailable()).WillByDefault(Return(true)); - } - - MOCK_METHOD(bool, isTlsAvailable, (), (const, override)); -}; - class CoreProcessTests : public Test { public: - CoreProcessTests() - : m_coreProcess(m_appConfig, m_serverConfig, m_license, m_pDeps) {} + CoreProcessTests() : m_coreProcess(m_appConfig, m_serverConfig, m_pDeps) {} NiceMock m_appConfig; NiceMock m_serverConfig; - NiceMock m_license; std::shared_ptr> m_pDeps = std::make_shared>(); CoreProcess m_coreProcess; diff --git a/src/test/unittests/gui/dialogs/SettingsDialogTests.cpp b/src/test/unittests/gui/dialogs/SettingsDialogTests.cpp index a2762942e..b9a190ac4 100644 --- a/src/test/unittests/gui/dialogs/SettingsDialogTests.cpp +++ b/src/test/unittests/gui/dialogs/SettingsDialogTests.cpp @@ -20,7 +20,6 @@ #include "gui/core/CoreProcess.h" #include "gui/dialogs/SettingsDialog.h" -#include "license/SerialKey.h" #include "shared/gui/TestQtFullApp.h" #include "shared/gui/mocks/AppConfigMock.h" #include "shared/gui/mocks/ServerConfigMock.h" @@ -28,23 +27,18 @@ #include using namespace testing; -using namespace deskflow::license; using namespace deskflow::gui; TEST(SettingsDialogTests, ctor_getsScreenName) { TestQtFullApp app; NiceMock appConfig; NiceMock serverConfig; - SerialKey serialKey = SerialKey::invalid(); - serialKey.isValid = true; - License license(serialKey); auto cpDeps = std::make_shared(); - CoreProcess coreProcess(appConfig, serverConfig, license, cpDeps); + CoreProcess coreProcess(appConfig, serverConfig, cpDeps); EXPECT_CALL(appConfig, screenName()).Times(1); - SettingsDialog settingsDialog( - nullptr, appConfig, serverConfig, license, coreProcess); + SettingsDialog settingsDialog(nullptr, appConfig, serverConfig, coreProcess); } #endif diff --git a/src/test/unittests/gui/license/LicenseHandlerTests.cpp b/src/test/unittests/gui/license/LicenseHandlerTests.cpp deleted file mode 100644 index 838cda5d9..000000000 --- a/src/test/unittests/gui/license/LicenseHandlerTests.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "gui/license/LicenseHandler.h" - -#include -#include - -using namespace deskflow::license; -using namespace std::chrono; - -const auto kPast = system_clock::now() - hours(1); -const auto kFuture = system_clock::now() + hours(1); - -TEST(LicenseHandlerTests, changeSerialKey_validExpiredLicense_returnsTrue) { - LicenseHandler licenseHandler; - licenseHandler.setEnabled(true); - auto hexString = // - "7B76313B70726F3B6E69636B20626F6C746F6E3B313B6" - "E69636B4073796D6C6573732E636F6D3B203B303B307D"; - - auto result = licenseHandler.changeSerialKey(hexString); - - ASSERT_EQ(LicenseHandler::ChangeSerialKeyResult::kSuccess, result); -} diff --git a/src/test/unittests/gui/license/license_notices_tests.cpp b/src/test/unittests/gui/license/license_notices_tests.cpp deleted file mode 100644 index 507508e45..000000000 --- a/src/test/unittests/gui/license/license_notices_tests.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2024 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "gui/license/license_notices.h" - -#include -#include -#include - -using namespace std::chrono; -using namespace deskflow::license; -using namespace deskflow::gui; -using ::testing::HasSubstr; - -const auto kPast = system_clock::now() - hours(1); -const auto kFutureOneHour = system_clock::now() + hours(1); -const auto kFutureOneDay = system_clock::now() + days(1) + hours(1); -const auto kFutureOneWeek = system_clock::now() + days(7) + hours(1); - -TEST(license_notices_tests, licenseNotice_trialExpired_correctText) { - SerialKey serialKey(""); - serialKey.isValid = true; - serialKey.warnTime = kPast; - serialKey.expireTime = kPast; - serialKey.type.setType("trial"); - License license(serialKey); - - QString notice = licenseNotice(license); - - EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial has expired")); -} - -TEST(license_notices_tests, licenseNotice_trialExpiringInOneHour_correctText) { - SerialKey serialKey(""); - serialKey.isValid = true; - serialKey.warnTime = kFutureOneHour; - serialKey.expireTime = kFutureOneHour; - serialKey.type.setType("trial"); - License license(serialKey); - - QString notice = licenseNotice(license); - - EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires today")); -} - -TEST(license_notices_tests, licenseNotice_trialExpiringInOneDay_correctText) { - SerialKey serialKey(""); - serialKey.isValid = true; - serialKey.warnTime = kFutureOneDay; - serialKey.expireTime = kFutureOneDay; - serialKey.type.setType("trial"); - License license(serialKey); - - QString notice = licenseNotice(license); - - EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires in 1 day")); -} - -TEST(license_notices_tests, licenseNotice_trialExpiringInOneWeek_correctText) { - SerialKey serialKey(""); - serialKey.isValid = true; - serialKey.warnTime = kFutureOneWeek; - serialKey.expireTime = kFutureOneWeek; - serialKey.type.setType("trial"); - License license(serialKey); - - QString notice = licenseNotice(license); - - EXPECT_THAT(notice.toStdString(), HasSubstr("Your trial expires in 7 days")); -} - -TEST(license_notices_tests, licenseNotice_subscriptionExpired_correctText) { - SerialKey serialKey(""); - serialKey.isValid = true; - serialKey.warnTime = kPast; - serialKey.expireTime = kPast; - serialKey.type.setType("subscription"); - License license(serialKey); - - QString notice = licenseNotice(license); - - EXPECT_THAT(notice.toStdString(), HasSubstr("Your license has expired")); -} - -TEST( - license_notices_tests, - licenseNotice_subscriptionExpiringInOneHour_correctText) { - SerialKey serialKey(""); - serialKey.isValid = true; - serialKey.warnTime = kFutureOneHour; - serialKey.expireTime = kFutureOneHour; - serialKey.type.setType("subscription"); - License license(serialKey); - - QString notice = licenseNotice(license); - - EXPECT_THAT(notice.toStdString(), HasSubstr("Your license expires today")); -} - -TEST( - license_notices_tests, - licenseNotice_subscriptionExpiringInOneDay_correctText) { - SerialKey serialKey(""); - serialKey.isValid = true; - serialKey.warnTime = kFutureOneDay; - serialKey.expireTime = kFutureOneDay; - serialKey.type.setType("subscription"); - License license(serialKey); - - QString notice = licenseNotice(license); - - EXPECT_THAT(notice.toStdString(), HasSubstr("Your license expires in 1 day")); -} - -TEST( - license_notices_tests, - licenseNotice_subscriptionExpiringInOneWeek_correctText) { - SerialKey serialKey(""); - serialKey.isValid = true; - serialKey.warnTime = kFutureOneWeek; - serialKey.expireTime = kFutureOneWeek; - serialKey.type.setType("subscription"); - License license(serialKey); - - QString notice = licenseNotice(license); - - EXPECT_THAT( - notice.toStdString(), HasSubstr("Your license expires in 7 days")); -} diff --git a/src/test/unittests/shared/LicenseTests.cpp b/src/test/unittests/shared/LicenseTests.cpp deleted file mode 100644 index 3477fd1f2..000000000 --- a/src/test/unittests/shared/LicenseTests.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#define TEST_ENV - -#include "license/ProductEdition.h" - -#include "license/License.h" - -#include -#include - -using enum Edition; -using namespace deskflow::license; -using time_point = std::chrono::system_clock::time_point; -using seconds = std::chrono::seconds; - -class LicenseTests : public ::testing::Test { -protected: - void setNow(License &license, int unixTime) const { - license.setNowFunc([unixTime]() { return time_point{seconds{unixTime}}; }); - } -}; - -TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isTrial) { - // {v2;trial;basic;Bob;1;email;company name;1;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B313B38363430307D"); - setNow(license, 0); - - EXPECT_TRUE(license.isTrial()); -} - -TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isTimeLimited) { - // {v2;trial;basic;Bob;1;email;company name;1;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B313B38363430307D"); - setNow(license, 0); - - EXPECT_TRUE(license.isTimeLimited()); -} - -TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isNotSubscription) { - // {v2;trial;basic;Bob;1;email;company name;1;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B313B38363430307D"); - setNow(license, 0); - - EXPECT_FALSE(license.isSubscription()); -} - -TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isExpiring) { - // {v2;trial;basic;Bob;1;email;company name;1;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B313B38363430307D"); - setNow(license, 0); - - EXPECT_FALSE(license.isExpiringSoon()); -} - -TEST_F(LicenseTests, isExpiring_validV2TrialBasicSerial_isBasicEdition) { - // {v2;trial;basic;Bob;1;email;company name;1;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B313B38363430307D"); - setNow(license, 0); - - EXPECT_EQ(kBasic, license.productEdition()); -} - -TEST_F(LicenseTests, isExpiring_expiringV2TrialBasicSerial_returnTrue) { - // {v2;trial;basic;Bob;1;email;company name;86400;0} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B38363430303B307D"); - setNow(license, 86401); - - EXPECT_TRUE(license.isTrial()); - EXPECT_TRUE(license.isExpiringSoon()); -} - -TEST_F(LicenseTests, isExpired_validV2TrialBasicSerial_returnFalse) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - setNow(license, 0); - - EXPECT_TRUE(license.isTrial()); - EXPECT_FALSE(license.isExpired()); -} - -TEST_F(LicenseTests, isExpired_expiringV2TrialBasicSerial_returnFalse) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - setNow(license, 1); - - EXPECT_TRUE(license.isTrial()); - EXPECT_FALSE(license.isExpired()); -} - -TEST_F(LicenseTests, isExpired_expiredV2TrialBasicSerial_returnTrue) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - setNow(license, 86401); - - EXPECT_TRUE(license.isTrial()); - EXPECT_TRUE(license.isExpired()); -} - -TEST_F(LicenseTests, daysLeft_validExactlyOneDayV2TrialBasicSerial_returnOne) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - setNow(license, 0); - - EXPECT_EQ(1, license.daysLeft().count()); -} - -TEST_F(LicenseTests, daysLeft_validWithinOneDayV2TrialBasicSerial_returnOne) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - setNow(license, 0); - - EXPECT_EQ(1, license.daysLeft().count()); -} - -TEST_F(LicenseTests, daysLeft_expiredV2TrialBasicSerial_returnZero) { - // {v2;trial;basic;Bob;1;email;company name;0;86400} - License license("7B76323B747269616C3B62617369633B426F623B313B656D61696C3B636" - "F6D70616E79206E616D653B303B38363430307D"); - setNow(license, 86401); - - EXPECT_EQ(0, license.daysLeft().count()); -} - -// Subscription license tests -TEST_F(LicenseTests, isExpiring_validV2SubscriptionBasicSerial_returnFalse) { - // {v2;subscription;basic;Bob;1;email;company name;1;86400} - License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" - "56D61696C3B636F6D70616E79206E616D653B313B38363430307D"); - setNow(license, 0); - - EXPECT_TRUE(license.isSubscription()); - EXPECT_FALSE(license.isExpiringSoon()); - EXPECT_EQ(kBasic, license.productEdition()); -} - -TEST_F(LicenseTests, isExpiring_expiringV2SubscriptionBasicSerial_returnTrue) { - // {v2;subscription;basic;Bob;1;email;company name;86400;0} - License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" - "56D61696C3B636F6D70616E79206E616D653B38363430303B307D"); - setNow(license, 86401); - - EXPECT_TRUE(license.isSubscription()); - EXPECT_TRUE(license.isExpiringSoon()); -} - -TEST_F(LicenseTests, isExpired_expiredV2SubscriptionBasicSerial_returnTrue) { - // {v2;subscription;basic;Bob;1;email;company name;0;86400} - License license("7B76323B737562736372697074696F6E3B62617369633B426F623B313B6" - "56D61696C3B636F6D70616E79206E616D653B303B38363430307D"); - setNow(license, 86401); - - EXPECT_TRUE(license.isSubscription()); - EXPECT_TRUE(license.isExpired()); -} diff --git a/src/test/unittests/shared/ProductTests.cpp b/src/test/unittests/shared/ProductTests.cpp deleted file mode 100644 index 03f0263c4..000000000 --- a/src/test/unittests/shared/ProductTests.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -// TODO: Move these tests and the code under test downstream to Synergy - -#define TEST_ENV - -#include "license/Product.h" - -#include - -using enum Product::Edition; - -TEST(ProductTests, equal_operator) { - Product product1(kPro); - Product product2(kPro); - - EXPECT_EQ(product1, product2); -} - -TEST(ProductTests, ctor_businessName_isValid) { - Product product(Product::SerialKeyEditionID::Buisiness); - - EXPECT_EQ(kBusiness, product.edition()); - EXPECT_TRUE(product.isValid()); -} - -TEST(ProductTests, ctor_basicType_isValid) { - Product product(kBasic); - - EXPECT_TRUE(product.isValid()); -} - -TEST(ProductTests, setEdition_invalidType_throws) { - Product product; - - EXPECT_THROW(product.setEdition("test"), Product::InvalidType); -} - -TEST(ProductTests, setEdition_pro_isValid) { - Product product; - - product.setEdition(kPro); - - EXPECT_EQ(kPro, product.edition()); - EXPECT_EQ(Product::SerialKeyEditionID::Pro, product.serialKeyId()); - EXPECT_EQ("Deskflow Pro", product.name()); - EXPECT_TRUE(product.isValid()); -} - -TEST(ProductTests, setEdition_basic_isValid) { - Product product; - - product.setEdition(kBasic); - - EXPECT_EQ(kBasic, product.edition()); - EXPECT_EQ(Product::SerialKeyEditionID::Basic, product.serialKeyId()); - EXPECT_EQ("Deskflow Basic", product.name()); -} - -TEST(ProductTests, setEdition_business_isValid) { - Product product; - - product.setEdition(kBusiness); - - EXPECT_EQ(kBusiness, product.edition()); - EXPECT_EQ(Product::SerialKeyEditionID::Buisiness, product.serialKeyId()); - EXPECT_EQ("Deskflow Business", product.name()); -} diff --git a/src/test/unittests/shared/SerialKeyTypeTests.cpp b/src/test/unittests/shared/SerialKeyTypeTests.cpp deleted file mode 100644 index 140bc7e90..000000000 --- a/src/test/unittests/shared/SerialKeyTypeTests.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * Copyright (C) 2016 Symless Ltd. - * - * This package is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * found in the file LICENSE that should have accompanied this file. - * - * This package is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#define TEST_ENV - -#include "license/SerialKeyType.h" - -#include - -TEST(SerialKeyTypeTests, TrialTemporaryKeyType_false) { - SerialKeyType KeyType; - EXPECT_FALSE(KeyType.isTrial()); - EXPECT_FALSE(KeyType.isSubscription()); -} - -TEST(SerialKeyTypeTests, TrialTemporaryKeyType_true) { - SerialKeyType KeyType; - KeyType.setType("trial"); - EXPECT_TRUE(KeyType.isTrial()); - EXPECT_FALSE(KeyType.isSubscription()); -} - -TEST(SerialKeyTypeTests, TimeLimitedKeyType_true) { - SerialKeyType KeyType; - KeyType.setType("subscription"); - EXPECT_FALSE(KeyType.isTrial()); - EXPECT_TRUE(KeyType.isSubscription()); -}