chore!: Relocate commercial code downstream

This commit is contained in:
Nick Bolton
2024-10-01 14:30:24 +01:00
parent 2d732a4b9d
commit 6bb1bcad8c
174 changed files with 1334 additions and 4238 deletions

View File

@ -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
#

View File

@ -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 }}

View File

@ -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

View File

@ -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",

View File

@ -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
View File

@ -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
View File

@ -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"

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -52,6 +52,7 @@
"Poschta",
"Povilas",
"Priddy",
"psutil",
"pyproject",
"qputenv",
"Regen",

View File

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -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

View File

@ -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=(

View File

@ -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

View File

@ -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;

View File

@ -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>

View File

@ -0,0 +1 @@
APPL@DESKFLOW_MAC_BUNDLE_CODE@

Binary file not shown.

Binary file not shown.

Binary file not shown.

69
res/dist/mac/dmgbuild/settings.py vendored Normal file
View 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",
}

View File

@ -1 +0,0 @@
APPLDFLW

View File

@ -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",
}

View File

@ -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
View 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>

View File

@ -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)" />

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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>

View File

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 154 KiB

View File

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

View File

@ -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>

View File

@ -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

View File

@ -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()

View File

@ -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."""

View File

@ -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

View File

@ -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}")

View File

@ -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...")

View File

@ -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:

View File

@ -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])

View File

@ -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

View File

@ -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__":

View File

@ -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

View File

@ -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})

View File

@ -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;
}

View File

@ -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})

View File

@ -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")

View File

@ -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()

View File

@ -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")

View File

@ -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

View File

@ -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()

View File

@ -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");
}
}

View File

@ -36,6 +36,7 @@ public:
private:
VersionChecker m_versionChecker;
void updateLogo() const;
void setLogo(const char *const &filename) const;
virtual QString importantDevelopers() const;
};

View File

@ -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>

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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>&lt;p&gt;Your serial key is on your &lt;a href=&quot;https://symless.com/synergy/account?source=gui&quot; style=&quot;color:#4285f4;&quot;&gt;account&lt;/span&gt;&lt;/a&gt; page. Don't have a license? &lt;a href=&quot;https://symless.com/synergy/purchase?source=gui&quot; style=&quot;color:#4285f4;&quot;&gt;Purchase Synergy&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: &quot;\2610&quot;; }
li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-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;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>

View File

@ -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; }

View File

@ -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;
};

View File

@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;You'll need to purchase a license to use this build of Synergy.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://symless.com/synergy/purchase?source=gui&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;Purchase Synergy&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If you'd prefer to use the community edition instead, visit us on GitHub.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/symless/synergy&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;GitHub project&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>

View File

@ -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; }

View File

@ -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

View File

@ -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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://symless.com/account/reset/?source=gui&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Forgotten your password?&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>

View File

@ -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) {

View File

@ -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;

View File

@ -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>

View File

@ -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)

View File

@ -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(

View File

@ -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>

View File

@ -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());

View File

@ -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;

View File

@ -24,5 +24,4 @@ add_subdirectory(mt)
add_subdirectory(net)
add_subdirectory(platform)
add_subdirectory(server)
add_subdirectory(license)
add_subdirectory(gui)

View File

@ -32,5 +32,3 @@ public:
// IArchDaemon overrides
virtual int daemonize(const char *name, DaemonFunc func);
};
#define CONFIG_FILE "/etc/deskflow/deskflowd.conf"

View File

@ -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;

View File

@ -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 {

View File

@ -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")

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

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