chore!: Relocate commercial code downstream
@ -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
|
||||
#
|
||||
|
||||
69
.github/actions/dist-upload/action.yml
vendored
@ -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 }}
|
||||
9
.github/actions/run-tests/action.yml
vendored
@ -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
|
||||
|
||||
4
.github/workflows/ci-linux.json
vendored
@ -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",
|
||||
|
||||
67
.github/workflows/ci.yml
vendored
@ -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:
|
||||
|
||||
8
.vscode/launch.json
vendored
@ -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"
|
||||
|
||||
4
.vscode/tasks.json
vendored
@ -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"
|
||||
|
||||
2
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
|
||||
|
||||
@ -14,8 +14,22 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
|
||||
@ -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 <maintainers@deskflow.org>"
|
||||
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()
|
||||
|
||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
|
||||
@ -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 <maintainers@deskflow.org>")
|
||||
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 <maintainers@deskflow.org>")
|
||||
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()
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -52,6 +52,7 @@
|
||||
"Poschta",
|
||||
"Povilas",
|
||||
"Priddy",
|
||||
"psutil",
|
||||
"pyproject",
|
||||
"qputenv",
|
||||
"Regen",
|
||||
|
||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// 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
|
||||
|
||||
6
res/dist/arch/PKGBUILD.in
vendored
@ -1,10 +1,10 @@
|
||||
# Maintainer: Deskflow <maintainers@deskflow.org>
|
||||
# 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=(
|
||||
|
||||
2
res/dist/flatpak/deskflow.yml
vendored
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -4,21 +4,21 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Deskflow</string>
|
||||
<string>@DESKFLOW_APP_NAME@</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>deskflow</string>
|
||||
<string>@DESKFLOW_APP_ID@</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Deskflow.icns</string>
|
||||
<string>@DESKFLOW_APP_NAME@.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>deskflow</string>
|
||||
<string>@DESKFLOW_APP_ID@</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Deskflow</string>
|
||||
<string>@DESKFLOW_APP_NAME@</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>SYN1</string>
|
||||
<string>@DESKFLOW_MAC_BUNDLE_CODE@</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>@DESKFLOW_VERSION@</string>
|
||||
<key>CFBundleVersion</key>
|
||||
1
res/dist/mac/bundle/Contents/PkgInfo.in
vendored
Normal file
@ -0,0 +1 @@
|
||||
APPL@DESKFLOW_MAC_BUNDLE_CODE@
|
||||
BIN
res/dist/mac/bundle/Contents/Resources/App.icns
vendored
Normal file
BIN
res/dist/mac/bundle/Contents/Resources/Background.tiff
vendored
Normal file
BIN
res/dist/mac/bundle/Contents/Resources/Volume.icns
vendored
Normal file
69
res/dist/mac/dmgbuild/settings.py
vendored
Normal file
@ -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",
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
APPLDFLW
|
||||
150
res/dist/macos/dmgbuild/settings.py
vendored
@ -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",
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Fragment>
|
||||
<UI>
|
||||
<Dialog Id="DeskflowBrowseDlg" Width="370" Height="270" Title="!(loc.BrowseDlg_Title)">
|
||||
<Dialog Id="AppBrowseDlg" Width="370" Height="270" Title="!(loc.BrowseDlg_Title)">
|
||||
<Control Id="PathEdit" Type="PathEdit" X="25" Y="202" Width="320" Height="18" Property="_BrowseProperty" Indirect="yes" />
|
||||
<Control Id="OK" Type="PushButton" X="240" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUIOK)">
|
||||
<Publish Event="SetTargetPath" Value="[_BrowseProperty]">1</Publish>
|
||||
72
res/dist/wix/AppDlgSequence.wxs
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
First-time install dialog sequence:
|
||||
- WixUI_WelcomeDlg
|
||||
- WixUI_LicenseAgreementDlg
|
||||
- WixUI_InstallDirDlg
|
||||
- WixUI_VerifyReadyDlg
|
||||
- WixUI_DiskCostDlg
|
||||
Maintenance dialog sequence:
|
||||
- WixUI_MaintenanceWelcomeDlg
|
||||
- WixUI_MaintenanceTypeDlg
|
||||
- WixUI_InstallDirDlg
|
||||
- WixUI_VerifyReadyDlg
|
||||
Patch dialog sequence:
|
||||
- WixUI_WelcomeDlg
|
||||
- WixUI_VerifyReadyDlg
|
||||
-->
|
||||
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Fragment>
|
||||
<UI Id="AppDlgSequence">
|
||||
<TextStyle Id="WixUI_Font_Normal" FaceName="Roboto" Size="9" />
|
||||
<TextStyle Id="WixUI_Font_Bigger" FaceName="Roboto" Size="14" />
|
||||
<TextStyle Id="WixUI_Font_Title" FaceName="Roboto" Size="12" Bold="yes" Blue="255" Red="255" Green="255"/>
|
||||
|
||||
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
|
||||
<Property Id="WixUI_Mode" Value="InstallDir" />
|
||||
|
||||
<DialogRef Id="BrowseDlg" />
|
||||
<DialogRef Id="DiskCostDlg" />
|
||||
<DialogRef Id="ErrorDlg" />
|
||||
<DialogRef Id="FatalError" />
|
||||
<DialogRef Id="FilesInUse" />
|
||||
<DialogRef Id="MsiRMFilesInUse" />
|
||||
<DialogRef Id="PrepareDlg" />
|
||||
<DialogRef Id="ProgressDlg" />
|
||||
<DialogRef Id="ResumeDlg" />
|
||||
<DialogRef Id="UserExit" />
|
||||
|
||||
<Publish Dialog="AppBrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
|
||||
<Publish Dialog="AppBrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
|
||||
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
|
||||
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="StartGui">NOT Installed</Publish>
|
||||
|
||||
<Publish Dialog="AppWelcome" Control="Next" Event="NewDialog" Value="AppInstallDirDlg" Order="1">1</Publish>
|
||||
<Publish Dialog="AppWelcome" Control="Next" Event="NewDialog" Value="AppVerifyReadyDlg">Installed AND PATCH</Publish>
|
||||
|
||||
<Publish Dialog="AppInstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
|
||||
<Publish Dialog="AppInstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish>
|
||||
<Publish Dialog="AppInstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
|
||||
<Publish Dialog="AppInstallDirDlg" Control="Next" Event="NewDialog" Value="AppVerifyReadyDlg" Order="4">WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1"</Publish>
|
||||
<Publish Dialog="AppInstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
|
||||
<Publish Dialog="AppInstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="AppBrowseDlg" Order="2">1</Publish>
|
||||
<Publish Dialog="AppInstallDirDlg" Control="Back" Event="NewDialog" Value="AppWelcome" Order="2">1</Publish>
|
||||
|
||||
<Publish Dialog="AppVerifyReadyDlg" Control="Back" Event="NewDialog" Value="AppInstallDirDlg" Order="1">NOT Installed</Publish>
|
||||
<Publish Dialog="AppVerifyReadyDlg" Control="Back" Event="NewDialog" Value="AppMaintenanceTypeDlg" Order="2">Installed AND NOT PATCH</Publish>
|
||||
<Publish Dialog="AppVerifyReadyDlg" Control="Back" Event="NewDialog" Value="AppInstallDirDlg" Order="2">Installed AND PATCH</Publish>
|
||||
|
||||
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="AppMaintenanceTypeDlg">1</Publish>
|
||||
|
||||
<Publish Dialog="AppMaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="AppVerifyReadyDlg">1</Publish>
|
||||
<Publish Dialog="AppMaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="AppVerifyReadyDlg">1</Publish>
|
||||
<Publish Dialog="AppMaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg">1</Publish>
|
||||
|
||||
<Property Id="ARPNOMODIFY" Value="1" />
|
||||
</UI>
|
||||
|
||||
<UIRef Id="WixUI_Common" />
|
||||
</Fragment>
|
||||
</Wix>
|
||||
@ -2,8 +2,8 @@
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<?include Include.wxi?>
|
||||
<Fragment>
|
||||
<UI Id="DeskflowInstallDirDlg">
|
||||
<Dialog Id="DeskflowInstallDirDlg" Width="370" Height="270" Title="!(loc.InstallDirDlg_Title)">
|
||||
<UI Id="AppInstallDirDlg">
|
||||
<Dialog Id="AppInstallDirDlg" Width="370" Height="270" Title="!(loc.InstallDirDlg_Title)">
|
||||
<Control Id="Title" Type="Text" X="15" Y="15" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgTitle)" />
|
||||
|
||||
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" />
|
||||
@ -12,7 +12,7 @@
|
||||
<Publish Event="SpawnDialog" Value="CancelDlg" />
|
||||
</Control>
|
||||
|
||||
<Control Id="InstallDirDlgBackground" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="[DeskflowCommonBackground]" />
|
||||
<Control Id="InstallDirDlgBackground" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="[CommonBackground]" />
|
||||
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
|
||||
|
||||
<Control Id="FolderLabel" Transparent="yes" Type="Text" X="20" Y="70" Width="290" Height="30" NoPrefix="yes" Text="!(loc.InstallDirDlgFolderLabel)" />
|
||||
@ -13,8 +13,8 @@
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Fragment>
|
||||
<UI>
|
||||
<Dialog Id="DeskflowMaintenanceTypeDlg" Width="370" Height="270" Title="!(loc.MaintenanceTypeDlg_Title)">
|
||||
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="[DeskflowCommonBackground]" />
|
||||
<Dialog Id="AppMaintenanceTypeDlg" Width="370" Height="270" Title="!(loc.MaintenanceTypeDlg_Title)">
|
||||
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="[CommonBackground]" />
|
||||
<Control Id="ChangeButton" Type="PushButton" X="40" Y="65" Width="80" Height="17" ToolTip="!(loc.MaintenanceTypeDlgChangeButtonTooltip)" Default="yes" Text="!(loc.MaintenanceTypeDlgChangeButton)">
|
||||
<Publish Property="WixUI_InstallMode" Value="Change">1</Publish>
|
||||
<Condition Action="disable">ARPNOMODIFY</Condition>
|
||||
@ -3,7 +3,7 @@
|
||||
<?include Include.wxi?>
|
||||
<Fragment>
|
||||
<UI>
|
||||
<Dialog Id="DeskflowVerifyReadyDlg" Width="370" Height="270" Title="!(loc.VerifyReadyDlg_Title)" TrackDiskSpace="yes">
|
||||
<Dialog Id="AppVerifyReadyDlg" Width="370" Height="270" Title="!(loc.VerifyReadyDlg_Title)" TrackDiskSpace="yes">
|
||||
<Control Id="Install" Type="PushButton" ElevationShield="yes" X="212" Y="243" Width="80" Height="17" Default="yes" Hidden="yes" Disabled="yes" Text="!(loc.VerifyReadyDlgInstall)">
|
||||
<Condition Action="show">NOT Installed AND ALLUSERS</Condition>
|
||||
<Condition Action="enable">NOT Installed</Condition>
|
||||
@ -132,7 +132,7 @@
|
||||
<Control Id="Back" Type="PushButton" X="156" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)">
|
||||
<Condition Action="default">WixUI_InstallMode = "Remove"</Condition>
|
||||
</Control>
|
||||
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="[DeskflowCommonBackground]" />
|
||||
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="[CommonBackground]" />
|
||||
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
|
||||
</Dialog>
|
||||
</UI>
|
||||
@ -5,24 +5,24 @@
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<?include Include.wxi?>
|
||||
<Fragment>
|
||||
<UI Id="DeskflowWelcome">
|
||||
<Property Id="DeskflowWelcomeBackground">welcome_background</Property>
|
||||
<Binary Id="welcome_background" SourceFile="$(var.ResPath)\dist\wix\images\welcome_background.png"/>
|
||||
<UI Id="AppWelcome">
|
||||
<Property Id="AppWelcomeBackground">welcome_background</Property>
|
||||
<Binary Id="welcome_background" SourceFile="$(var.ResDir)\dist\wix\images\welcome_background.png"/>
|
||||
|
||||
<Dialog Id="DeskflowWelcome" Width="370" Height="270" Title="!(loc.WelcomeDlg_Title)">
|
||||
<Dialog Id="AppWelcome" Width="370" Height="270" Title="!(loc.WelcomeDlg_Title)">
|
||||
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
|
||||
<Publish Property="WixUI_InstallMode" Value="Update">Installed AND PATCH</Publish>
|
||||
</Control>
|
||||
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
|
||||
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
|
||||
</Control>
|
||||
<Control Id="Bitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="[DeskflowWelcomeBackground]" />
|
||||
<Control Id="Bitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="[AppWelcomeBackground]" />
|
||||
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUIBack)" />
|
||||
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
|
||||
</Dialog>
|
||||
|
||||
<InstallUISequence>
|
||||
<Show Dialog="DeskflowWelcome" Before="ProgressDlg" Overridable="yes">NOT Installed OR PATCH</Show>
|
||||
<Show Dialog="AppWelcome" Before="ProgressDlg" Overridable="yes">NOT Installed OR PATCH</Show>
|
||||
</InstallUISequence>
|
||||
</UI>
|
||||
</Fragment>
|
||||
27
res/dist/wix/Include.wxi.in
vendored
@ -1,23 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Include>
|
||||
<?define Name="Deskflow"?>
|
||||
<?define AppId="@DESKFLOW_APP_ID@"?>
|
||||
<?define Name="@DESKFLOW_APP_NAME@"?>
|
||||
<?define Version="@DESKFLOW_VERSION_MS@"?>
|
||||
<?define Author="Symless"?>
|
||||
<?define BinPath="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@"?>
|
||||
<?define ResPath="@CMAKE_CURRENT_SOURCE_DIR@/res"?>
|
||||
<?define ExtPath="@CMAKE_CURRENT_SOURCE_DIR@/ext"?>
|
||||
<?define Author="@DESKFLOW_AUTHOR_NAME@"?>
|
||||
<?define BinDir="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@"?>
|
||||
<?define ProjectResDir="@DESKFLOW_PROJECT_RES_DIR@"?>
|
||||
<?define ResDir="@DESKFLOW_RES_DIR@"?>
|
||||
<?define QtDir="@QT_PATH@"?>
|
||||
<?define QtBinDir="$(var.QtDir)\bin"?>
|
||||
<?if $(var.Platform) = "x64"?>
|
||||
<?define ProgramFilesFolder="ProgramFiles64Folder"?>
|
||||
<?define PlatformSimpleName="64-bit"?>
|
||||
<?define UpgradeGuid="E8A4FA54-14B9-4FD1-8E00-7BC46555FDA0"?>
|
||||
<?define UpgradeGuid="@DESKFLOW_MSI_64_GUID@"?>
|
||||
<?else?>
|
||||
<?define ProgramFilesFolder="ProgramFilesFolder"?>
|
||||
<?define PlatformSimpleName="32-bit"?>
|
||||
<?define UpgradeGuid="BE0B9FD8-45E2-4A8E-A0D8-1F774D074A78"?>
|
||||
<?define UpgradeGuid="@DESKFLOW_MSI_32_GUID@"?>
|
||||
<?endif?>
|
||||
<?define QtPath="@QT_PATH@"?>
|
||||
<?define QtBinPath="$(var.QtPath)\bin"?>
|
||||
<?define QtPluginsPath="$(var.QtPath)\plugins"?>
|
||||
<?define QtPluginsPath="$(var.QtDir)\plugins"?>
|
||||
<?define OpenSslExeDir="@OPENSSL_EXE_DIR@"?>
|
||||
<?define OpenSslDllDir="@OPENSSL_ROOT_DIR@/bin"?>
|
||||
<?define GuiBin="@GUI_BINARY_NAME@.exe"?>
|
||||
<?define ServerBin="@SERVER_BINARY_NAME@.exe"?>
|
||||
<?define ClientBin="@CLIENT_BINARY_NAME@.exe"?>
|
||||
<?define CoreBin="@CORE_BINARY_NAME@.exe"?>
|
||||
<?define DaemonBin="@DAEMON_BINARY_NAME@.exe"?>
|
||||
<?define LegacyBin="@LEGACY_BINARY_NAME@.exe"?>
|
||||
</Include>
|
||||
@ -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
|
||||
@ -1,21 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0"
|
||||
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProductVersion>3.11</ProductVersion>
|
||||
<ProjectGuid>{d4ba9f39-6a35-4c8f-9cb2-67fcbe5cab17}</ProjectGuid>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<OutputName>Deskflow</OutputName>
|
||||
<OutputName>Installer</OutputName>
|
||||
<OutputType>Package</OutputType>
|
||||
<OutputPath>bin\$(Configuration)\</OutputPath>
|
||||
<IntermediateOutputPath>wix\obj\$(Configuration)\</IntermediateOutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(SolutionDir)/DeskflowWelcome.wxs" />
|
||||
<Compile Include="$(SolutionDir)/DeskflowInstallDlg.wxs" />
|
||||
<Compile Include="$(SolutionDir)/DeskflowBrowseDlg.wxs" />
|
||||
<Compile Include="$(SolutionDir)/DeskflowVerifyReadyDlg.wxs" />
|
||||
<Compile Include="$(SolutionDir)/DeskflowMaintenanceTypeDlg.wxs" />
|
||||
<Compile Include="$(SolutionDir)/DeskflowDlgSequence.wxs" />
|
||||
<Compile Include="$(SolutionDir)/AppWelcome.wxs" />
|
||||
<Compile Include="$(SolutionDir)/AppInstallDirDlg.wxs" />
|
||||
<Compile Include="$(SolutionDir)/AppBrowseDlg.wxs" />
|
||||
<Compile Include="$(SolutionDir)/AppVerifyReadyDlg.wxs" />
|
||||
<Compile Include="$(SolutionDir)/AppMaintenanceTypeDlg.wxs" />
|
||||
<Compile Include="$(SolutionDir)/AppDlgSequence.wxs" />
|
||||
<Compile Include="$(SolutionDir)/Product.wxs" />
|
||||
<Content Include="$(SolutionDir)/Include.wxi" />
|
||||
|
||||
@ -35,5 +36,6 @@
|
||||
</WixExtension>
|
||||
</ItemGroup>
|
||||
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
|
||||
</Project>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets"
|
||||
Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
|
||||
</Project>
|
||||
84
res/dist/wix/Product.wxs
vendored
@ -19,39 +19,39 @@
|
||||
</Feature>
|
||||
<DirectoryRef Id="TARGETDIR">
|
||||
<Component Guid="7CF3564D-1F8E-4D3D-9781-E1EE22D5BD67" Id="RegistryEntries">
|
||||
<RegistryKey Id="deskflows" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes" Key="Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" Root="HKLM">
|
||||
<RegistryValue Name="[INSTALLFOLDER]deskflows.exe" Type="string" Value="~ HIGHDPIAWARE WIN7RTM"/>
|
||||
<RegistryKey Id="$(var.AppId)_server" ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes" Key="Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" Root="HKLM">
|
||||
<RegistryValue Name="[INSTALLFOLDER]$(var.ServerBin)" Type="string" Value="~ HIGHDPIAWARE WIN7RTM"/>
|
||||
</RegistryKey>
|
||||
|
||||
<RegistryKey Id="deskflowc" Root="HKLM"
|
||||
<RegistryKey Id="$(var.AppId)_client" Root="HKLM"
|
||||
Key="Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
|
||||
ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes">
|
||||
<RegistryValue Type="string" Name="[INSTALLFOLDER]deskflowc.exe" Value="~ HIGHDPIAWARE WIN7RTM"/>
|
||||
<RegistryValue Type="string" Name="[INSTALLFOLDER]$(var.ClientBin)" Value="~ HIGHDPIAWARE WIN7RTM"/>
|
||||
</RegistryKey>
|
||||
|
||||
<RegistryKey Id="deskflow" Root="HKLM"
|
||||
<RegistryKey Id="$(var.AppId)" Root="HKLM"
|
||||
Key="Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"
|
||||
ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes">
|
||||
<RegistryValue Type="string" Name="[INSTALLFOLDER]deskflow.exe" Value="~ HIGHDPIAWARE WIN7RTM"/>
|
||||
<RegistryValue Type="string" Name="[INSTALLFOLDER]$(var.GuiBin)" Value="~ HIGHDPIAWARE WIN7RTM"/>
|
||||
</RegistryKey>
|
||||
<!-- Windows 8 and later only -->
|
||||
<Condition><![CDATA[Installed OR (VersionNT >= 602)]]></Condition>
|
||||
</Component>
|
||||
<?if $(var.Platform) = x64 ?>
|
||||
<Merge Id="VC_Redist" SourceFile="$(var.ResPath)\dist\wix\msm\Microsoft_VC142_CRT_x64.msm" DiskId="1" Language="0"/>
|
||||
<Merge Id="VC_Redist" SourceFile="$(var.ProjectResDir)\dist\wix\msm\Microsoft_VC142_CRT_x64.msm" DiskId="1" Language="0"/>
|
||||
<?else ?>
|
||||
<Merge Id="VC_Redist" SourceFile="$(var.ResPath)\dist\wix\msm\Microsoft_VC142_CRT_x86.msm" DiskId="1" Language="0"/>
|
||||
<Merge Id="VC_Redist" SourceFile="$(var.ProjectResDir)\dist\wix\msm\Microsoft_VC142_CRT_x86.msm" DiskId="1" Language="0"/>
|
||||
<?endif ?>
|
||||
</DirectoryRef>
|
||||
<Property Id="DeskflowCommonBackground">common_background</Property>
|
||||
<Binary Id="common_background" SourceFile="$(var.ResPath)\dist\wix\images\common_background.png"/>
|
||||
<Icon Id="deskflow.ico" SourceFile="$(var.ResPath)/deskflow.ico"/>
|
||||
<WixVariable Id="WixUIBannerBmp" Value="$(var.ResPath)\dist\wix\images\banner.png"/>
|
||||
<WixVariable Id="WixUIDialogBmp" Value="$(var.ResPath)\dist\wix\images\dialog.png"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="deskflow.ico"/>
|
||||
<Property Id="CommonBackground">CommonBackground</Property>
|
||||
<Binary Id="CommonBackground" SourceFile="$(var.ResDir)\dist\wix\images\common_background.png"/>
|
||||
<Icon Id="AppIcon" SourceFile="$(var.ResDir)/app.ico"/>
|
||||
<WixVariable Id="WixUIBannerBmp" Value="$(var.ResDir)\dist\wix\images\banner.png"/>
|
||||
<WixVariable Id="WixUIDialogBmp" Value="$(var.ResDir)\dist\wix\images\dialog.png"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="AppIcon"/>
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER"/>
|
||||
<Property Id="LEGACY_UNINSTALL_EXISTS">
|
||||
<RegistrySearch Id="LegacyRegistrySearch" Key="SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Deskflow" Name="UninstallString" Root="HKLM" Type="file" Win64="no">
|
||||
<RegistrySearch Id="LegacyRegistrySearch" Key="SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\$(var.Name)" Name="UninstallString" Root="HKLM" Type="file" Win64="no">
|
||||
<FileSearch Id="LegacyFileSearch" Name="uninstall.exe"/>
|
||||
</RegistrySearch>
|
||||
</Property>
|
||||
@ -59,7 +59,7 @@
|
||||
</Condition>
|
||||
<CustomAction ExeCommand="" FileKey="GuiProgram" Id="StartGui" Return="asyncNoWait"/>
|
||||
<UI>
|
||||
<UIRef Id="DeskflowDlgSequence" />
|
||||
<UIRef Id="AppDlgSequence" />
|
||||
</UI>
|
||||
</Product>
|
||||
<Fragment>
|
||||
@ -77,18 +77,18 @@
|
||||
<Fragment>
|
||||
<ComponentGroup Directory="INSTALLFOLDER" Id="ProductComponents">
|
||||
<Component Guid="EC9AD3B0-277C-4157-B5C8-5FD5B6A5F4AD" Id="Core">
|
||||
<File KeyPath="yes" Source="$(var.BinPath)/deskflowd.exe"/>
|
||||
<ServiceInstall Description="Controls the $(var.Name) foreground processes." DisplayName="$(var.Name)" ErrorControl="normal" Id="ServiceInstall" Name="Deskflow" Start="auto" Type="ownProcess">
|
||||
<File KeyPath="yes" Source="$(var.BinDir)/$(var.DaemonBin)"/>
|
||||
<ServiceInstall Description="Controls the $(var.Name) foreground processes." DisplayName="$(var.Name)" ErrorControl="normal" Id="ServiceInstall" Name="$(var.Name)" Start="auto" Type="ownProcess">
|
||||
<util:ServiceConfig FirstFailureActionType="restart" ResetPeriodInDays="1" RestartServiceDelayInSeconds="1" SecondFailureActionType="restart" ThirdFailureActionType="restart"/>
|
||||
</ServiceInstall>
|
||||
<ServiceControl Id="ServiceControl" Name="Deskflow" Remove="uninstall" Start="install" Stop="both"/>
|
||||
<File Source="$(var.BinPath)/deskflows.exe">
|
||||
<fire:FirewallException Id="ServerFirewallException" IgnoreFailure="yes" Name="Deskflow server" Scope="any"/>
|
||||
<ServiceControl Id="ServiceControl" Name="$(var.Name)" Remove="uninstall" Start="install" Stop="both"/>
|
||||
<File Source="$(var.BinDir)/$(var.ServerBin)">
|
||||
<fire:FirewallException Id="ServerFirewallException" IgnoreFailure="yes" Name="$(var.Name) Server" Scope="any"/>
|
||||
</File>
|
||||
<File Source="$(var.BinPath)/deskflowc.exe">
|
||||
<fire:FirewallException Id="ClientFirewallException" IgnoreFailure="yes" Name="Deskflow client" Scope="any"/>
|
||||
<File Source="$(var.BinDir)/$(var.ClientBin)">
|
||||
<fire:FirewallException Id="ClientFirewallException" IgnoreFailure="yes" Name="$(var.Name) Client" Scope="any"/>
|
||||
</File>
|
||||
<File Source="$(var.BinPath)/deskflow-legacy.exe"/>
|
||||
<File Source="$(var.BinDir)/$(var.LegacyBin)" />
|
||||
<?if $(var.Platform) = x64 ?>
|
||||
<File Source="$(var.OpenSslDllDir)/libssl-3-x64.dll"/>
|
||||
<File Source="$(var.OpenSslDllDir)/libcrypto-3-x64.dll"/>
|
||||
@ -98,42 +98,42 @@
|
||||
<?endif ?>
|
||||
</Component>
|
||||
<Component Guid="BAC8149B-6287-45BF-9C27-43D71ED40214" Id="Gui">
|
||||
<File Id="GuiProgram" KeyPath="yes" Source="$(var.BinPath)/deskflow.exe">
|
||||
<Shortcut Advertise="yes" Directory="ProgramMenuFolder" Icon="deskflow.exe" Id="GuiShortcut" Name="$(var.Name)">
|
||||
<Icon Id="deskflow.exe" SourceFile="$(var.ResPath)/deskflow.ico"/>
|
||||
<File Id="GuiProgram" KeyPath="yes" Source="$(var.BinDir)/$(var.GuiBin)">
|
||||
<Shortcut Advertise="yes" Directory="ProgramMenuFolder" Icon="$(var.GuiBin)" Id="GuiShortcut" Name="$(var.Name)">
|
||||
<Icon Id="$(var.GuiBin)" SourceFile="$(var.ResDir)/app.ico"/>
|
||||
</Shortcut>
|
||||
<fire:FirewallException Id="GuiFirewallException" IgnoreFailure="yes" Name="$(var.Name)" Scope="any"/>
|
||||
</File>
|
||||
<?if $(var.Configuration) = "Debug" ?>
|
||||
<File Source="$(var.BinPath)\Qt6Cored.dll"/>
|
||||
<File Source="$(var.BinPath)\Qt6Guid.dll"/>
|
||||
<File Source="$(var.BinPath)\Qt6Networkd.dll"/>
|
||||
<File Source="$(var.BinPath)\Qt6Svgd.dll"/>
|
||||
<File Source="$(var.BinPath)\Qt6Widgetsd.dll"/>
|
||||
<File Source="$(var.BinPath)\styles\qmodernwindowsstyle.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Cored.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Guid.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Networkd.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Svgd.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Widgetsd.dll"/>
|
||||
<File Source="$(var.BinDir)\styles\qmodernwindowsstyle.dll"/>
|
||||
<!-- HACK: Normally the C++ redistributable solves this dependency, including it can cause problems -->
|
||||
<File Source="C:\Program Files (x86)\Windows Kits\10\bin\$(var.Platform)\ucrt\ucrtbased.dll"/>
|
||||
<?else ?>
|
||||
<File Source="$(var.BinPath)\Qt6Core.dll"/>
|
||||
<File Source="$(var.BinPath)\Qt6Gui.dll"/>
|
||||
<File Source="$(var.BinPath)\Qt6Network.dll"/>
|
||||
<File Source="$(var.BinPath)\Qt6Svg.dll"/>
|
||||
<File Source="$(var.BinPath)\Qt6Widgets.dll"/>
|
||||
<File Source="$(var.BinPath)\styles\qmodernwindowsstyle.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Core.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Gui.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Network.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Svg.dll"/>
|
||||
<File Source="$(var.BinDir)\Qt6Widgets.dll"/>
|
||||
<File Source="$(var.BinDir)\styles\qmodernwindowsstyle.dll"/>
|
||||
<?endif ?>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
<ComponentGroup Directory="QTStylesDir" Id="ProductQtStylesComponents">
|
||||
<Component Guid="96E0F8D8-64FD-4CE8-94D1-F6EDCBBB4995" Id="Styles">
|
||||
<File Id="qmodernwindowsstyle" Source="$(var.BinPath)\styles\qmodernwindowsstyle.dll"/>
|
||||
<File Id="qmodernwindowsstyle" Source="$(var.BinDir)\styles\qmodernwindowsstyle.dll"/>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
<ComponentGroup Directory="PlatformsDir" Id="ProductQtPluginComponents">
|
||||
<Component Guid="684EFA14-856B-440E-A5E6-E90E04E36B41" Id="QtPlatformPlugin">
|
||||
<?if $(var.Configuration) = "Debug" ?>
|
||||
<File Source="$(var.BinPath)\platforms\qwindowsd.dll"/>
|
||||
<File Source="$(var.BinDir)\platforms\qwindowsd.dll"/>
|
||||
<?else ?>
|
||||
<File Source="$(var.BinPath)\platforms\qwindows.dll"/>
|
||||
<File Source="$(var.BinDir)\platforms\qwindows.dll"/>
|
||||
<?endif ?>
|
||||
</Component>
|
||||
</ComponentGroup>
|
||||
|
||||
BIN
res/dist/wix/images/banner.png
vendored
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 11 KiB |
BIN
res/dist/wix/images/common_background.png
vendored
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 21 KiB |
BIN
res/dist/wix/images/dialog.png
vendored
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 94 KiB |
BIN
res/dist/wix/images/welcome_background.png
vendored
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 154 KiB |
@ -12,5 +12,6 @@
|
||||
<file>icons/64x64/tray-light.png</file>
|
||||
<file>image/welcome.png</file>
|
||||
<file>icons/64x64/folder.png</file>
|
||||
<file>image/placeholder.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
BIN
res/gui/image/placeholder.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
@ -1,32 +0,0 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Deskflow</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Deskflow</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>Deskflow.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>deskflow</string>
|
||||
<!-- TODO: Fix this in v2.0 //-->
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Deskflow</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.8.8</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.8.8</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>© 2012-2016, Symless Ltd</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<string>True</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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."""
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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}")
|
||||
|
||||
|
||||
@ -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...")
|
||||
|
||||
@ -13,16 +13,22 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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:
|
||||
|
||||
@ -13,9 +13,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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])
|
||||
|
||||
@ -13,21 +13,22 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
|
||||
@ -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__":
|
||||
|
||||
@ -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
|
||||
|
||||
@ -14,12 +14,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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})
|
||||
|
||||
@ -27,11 +27,14 @@
|
||||
#endif
|
||||
|
||||
void showHelp() {
|
||||
std::cout << "Usage: deskflow-core <server | client> [...options]"
|
||||
std::cout << "Usage: " CORE_BINARY_NAME " <server | client> [...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 <server|client> --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
|
||||
" <server|client> --help for more information."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
|
||||
@ -14,13 +14,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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})
|
||||
|
||||
@ -14,9 +14,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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")
|
||||
|
||||
@ -14,28 +14,28 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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()
|
||||
|
||||
@ -14,9 +14,9 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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")
|
||||
|
||||
@ -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
|
||||
|
||||
@ -13,15 +13,15 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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%$<SEMICOLON>${qt6_install_prefix}/bin
|
||||
COMMAND Qt6::windeployqt
|
||||
"$<TARGET_FILE_DIR:deskflow>/$<TARGET_FILE_NAME:deskflow>")
|
||||
"$<TARGET_FILE_DIR:${target}>/$<TARGET_FILE_NAME:${target}>")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
@ -23,7 +23,8 @@
|
||||
#include "gui/style_utils.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <qguiapplication.h>
|
||||
#include <QGuiApplication>
|
||||
#include <QtCore>
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ public:
|
||||
private:
|
||||
VersionChecker m_versionChecker;
|
||||
void updateLogo() const;
|
||||
void setLogo(const char *const &filename) const;
|
||||
|
||||
virtual QString importantDevelopers() const;
|
||||
};
|
||||
|
||||
@ -105,26 +105,11 @@
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="m_pLabel_Logo">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>39</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/image/logo-light.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
<pixmap>:/image/placeholder.png</pixmap>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
|
||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QThread>
|
||||
|
||||
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 <a href="%1" style="color: %2">contact us</a> for help.)")
|
||||
.arg(kUrlContact)
|
||||
.arg(kColorSecondary));
|
||||
break;
|
||||
|
||||
case kExpired:
|
||||
QMessageBox::warning(
|
||||
this, title,
|
||||
QString(
|
||||
"Sorry, that serial key has expired. "
|
||||
R"(Please <a href="%1" style="color: %1">renew</a> your license.)")
|
||||
.arg(kUrlPurchase)
|
||||
.arg(kColorSecondary));
|
||||
break;
|
||||
|
||||
default:
|
||||
qFatal("unexpected change serial key result: %d", static_cast<int>(result));
|
||||
}
|
||||
}
|
||||
|
||||
void ActivationDialog::showSuccessDialog() {
|
||||
const auto &license = m_licenseHandler.license();
|
||||
|
||||
QString title = "Activation successful";
|
||||
QString message = tr("<p>Thanks for activating %1.</p>")
|
||||
.arg(m_licenseHandler.productName());
|
||||
|
||||
TlsUtility tls(*m_pAppConfig, license);
|
||||
if (tls.isAvailableAndEnabled()) {
|
||||
message +=
|
||||
"<p>To ensure that TLS encryption works correctly, "
|
||||
"please activate all of your computers with the same serial key.</p>";
|
||||
}
|
||||
|
||||
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("<p>There was a problem activating Deskflow.</p>"
|
||||
R"(<p>Please <a href="%1" style="color: %2">contact us</a> )"
|
||||
"and provide the following information:</p>"
|
||||
"%3")
|
||||
.arg(kUrlContact)
|
||||
.arg(kColorSecondary)
|
||||
.arg(message);
|
||||
QMessageBox::critical(this, "Activation failed", fullMessage);
|
||||
}
|
||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gui/license/LicenseHandler.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
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;
|
||||
};
|
||||
@ -1,153 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ActivationDialog</class>
|
||||
<widget class="QDialog" name="ActivationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>541</width>
|
||||
<height>241</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Activate Synergy</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Serial key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="m_pTextEditSerialKey">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tabChangesFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string><!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></string>
|
||||
</property>
|
||||
<property name="acceptRichText">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="m_widgetNotice" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_pLabelNotice">
|
||||
<property name="text">
|
||||
<string>m_pLabelNotice</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>m_pTextEditSerialKey</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ActivationDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ActivationDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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; }
|
||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class CancelActivationDialog;
|
||||
}
|
||||
|
||||
class CancelActivationDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CancelActivationDialog(QWidget *parent = 0);
|
||||
~CancelActivationDialog();
|
||||
|
||||
private:
|
||||
Ui::CancelActivationDialog *ui;
|
||||
};
|
||||
@ -1,77 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CancelActivationDialog</class>
|
||||
<widget class="QDialog" name="CancelActivationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>429</width>
|
||||
<height>273</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Cancel Activation</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item alignment="Qt::AlignmentFlag::AlignTop">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="m_pButtonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>m_pButtonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>CancelActivationDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>m_pButtonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>CancelActivationDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@ -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; }
|
||||
@ -1,22 +0,0 @@
|
||||
#ifndef FAILEDLOGINDIALOG_H
|
||||
#define FAILEDLOGINDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QString>
|
||||
|
||||
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
|
||||
@ -1,108 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FailedLoginDialog</class>
|
||||
<widget class="QDialog" name="FailedLoginDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>165</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Activation Error</string>
|
||||
</property>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>50</x>
|
||||
<y>120</y>
|
||||
<width>341</width>
|
||||
<height>32</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>90</y>
|
||||
<width>382</width>
|
||||
<height>30</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="messageLabel">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>382</width>
|
||||
<height>72</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>An error occurred while trying to activate Synergy. The Symless server returned the following error:
|
||||
|
||||
%1</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<zorder>label_2</zorder>
|
||||
<zorder>messageLabel</zorder>
|
||||
<zorder>buttonBox</zorder>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>FailedLoginDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>FailedLoginDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@ -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) {
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QThread>
|
||||
|
||||
#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;
|
||||
|
||||
@ -501,7 +501,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/icons/16x16/padlock.png</pixmap>
|
||||
<pixmap resource="../../../../res/gui/app.qrc">:/icons/16x16/padlock.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -683,14 +683,6 @@
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</action>
|
||||
<action name="m_pActionActivate">
|
||||
<property name="text">
|
||||
<string>Activate</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Activate</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="m_pActionTestFatalError">
|
||||
<property name="text">
|
||||
<string>Test fatal error</string>
|
||||
|
||||
@ -23,7 +23,8 @@
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
|
||||
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)
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -943,7 +943,7 @@ Enabling this setting will disable the server config GUI.</string>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use Core server config file</string>
|
||||
<string>Use a server config file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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 <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QGuiApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QObject>
|
||||
#include <QtCore>
|
||||
@ -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>("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;
|
||||
|
||||
|
||||
@ -24,5 +24,4 @@ add_subdirectory(mt)
|
||||
add_subdirectory(net)
|
||||
add_subdirectory(platform)
|
||||
add_subdirectory(server)
|
||||
add_subdirectory(license)
|
||||
add_subdirectory(gui)
|
||||
|
||||
@ -32,5 +32,3 @@ public:
|
||||
// IArchDaemon overrides
|
||||
virtual int daemonize(const char *name, DaemonFunc func);
|
||||
};
|
||||
|
||||
#define CONFIG_FILE "/etc/deskflow/deskflowd.conf"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
#include <windows.h>
|
||||
|
||||
static const char *s_settingsKeyNames[] = {
|
||||
_T("SOFTWARE"), _T("Deskflow"), NULL};
|
||||
_T("SOFTWARE"), _T(DESKFLOW_APP_NAME), NULL};
|
||||
|
||||
//
|
||||
// ArchSystemWindows
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 <VersionHelpers.h>
|
||||
#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;
|
||||
|
||||