mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-07-22 23:28:48 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 522f519d38 | |||
| 37c4fe2e8a | |||
| 2c20599800 | |||
| f5c4e2ba4a |
@@ -134,17 +134,21 @@ jobs:
|
|||||||
build-macos:
|
build-macos:
|
||||||
name: Build on macOS
|
name: Build on macOS
|
||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
|
env:
|
||||||
|
MACOSX_DEPLOYMENT_TARGET: "14.0"
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- arch: x64
|
- arch: x64
|
||||||
|
binary_arch: x86_64
|
||||||
runner: macos-15-intel
|
runner: macos-15-intel
|
||||||
cache-key: intel
|
cache-key: intel-macos14
|
||||||
out-dir: ./build/macosx/x86_64/release/crossdesk
|
out-dir: ./build/macosx/x86_64/release/crossdesk
|
||||||
package_script: ./scripts/macosx/pkg_x64.sh
|
package_script: ./scripts/macosx/pkg_x64.sh
|
||||||
- arch: arm64
|
- arch: arm64
|
||||||
|
binary_arch: arm64
|
||||||
runner: macos-14
|
runner: macos-14
|
||||||
cache-key: arm
|
cache-key: arm-macos14
|
||||||
out-dir: ./build/macosx/arm64/release/crossdesk
|
out-dir: ./build/macosx/arm64/release/crossdesk
|
||||||
package_script: ./scripts/macosx/pkg_arm64.sh
|
package_script: ./scripts/macosx/pkg_arm64.sh
|
||||||
|
|
||||||
@@ -211,7 +215,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build CrossDesk
|
- name: Build CrossDesk
|
||||||
run: |
|
run: |
|
||||||
xmake f --CROSSDESK_VERSION=${VERSION_NUM} --USE_CUDA=true -y
|
xmake f --target_minver=${MACOSX_DEPLOYMENT_TARGET} --CROSSDESK_VERSION=${VERSION_NUM} --USE_CUDA=true -y
|
||||||
xmake b -vy crossdesk
|
xmake b -vy crossdesk
|
||||||
|
|
||||||
- name: Package CrossDesk app
|
- name: Package CrossDesk app
|
||||||
@@ -219,6 +223,22 @@ jobs:
|
|||||||
chmod +x ${{ matrix.package_script }}
|
chmod +x ${{ matrix.package_script }}
|
||||||
${{ matrix.package_script }} ${VERSION_NUM}
|
${{ matrix.package_script }} ${VERSION_NUM}
|
||||||
|
|
||||||
|
- name: Verify packaged app
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
PACKAGE_FILE="${GITHUB_WORKSPACE}/crossdesk-macos-${{ matrix.arch }}-${VERSION_NUM}.pkg"
|
||||||
|
VERIFY_DIR="$(mktemp -d "${RUNNER_TEMP}/crossdesk-macos-pkg.XXXXXX")"
|
||||||
|
trap 'rm -rf "${VERIFY_DIR}"' EXIT
|
||||||
|
|
||||||
|
pkgutil --expand-full "${PACKAGE_FILE}" "${VERIFY_DIR}/expanded"
|
||||||
|
APP_PATH="$(find "${VERIFY_DIR}/expanded" -type d -name 'CrossDesk.app' -print -quit)"
|
||||||
|
if [[ -z "${APP_PATH}" ]]; then
|
||||||
|
echo "CrossDesk.app is missing from ${PACKAGE_FILE}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
./scripts/macosx/verify_app.sh "${APP_PATH}" "${{ matrix.binary_arch }}"
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
|
|||||||
Executable
+131
@@ -0,0 +1,131 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -ne 2 ]]; then
|
||||||
|
echo "Usage: $0 <app-bundle> <expected-architecture>" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
APP_BUNDLE="$1"
|
||||||
|
EXPECTED_ARCH="$2"
|
||||||
|
APP_EXECUTABLE="$APP_BUNDLE/Contents/MacOS/CrossDesk"
|
||||||
|
FRAMEWORKS_DIR="$APP_BUNDLE/Contents/Frameworks"
|
||||||
|
|
||||||
|
for command_name in codesign install_name_tool lipo otool; do
|
||||||
|
if ! command -v "$command_name" >/dev/null 2>&1; then
|
||||||
|
echo "Required bundling command is missing: $command_name" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -x "$APP_EXECUTABLE" ]]; then
|
||||||
|
echo "Missing app executable: $APP_EXECUTABLE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! lipo "$APP_EXECUTABLE" -verify_arch "$EXPECTED_ARCH" >/dev/null 2>&1; then
|
||||||
|
echo "App executable does not contain the expected architecture: $EXPECTED_ARCH" >&2
|
||||||
|
lipo -archs "$APP_EXECUTABLE" >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SLINT_REFERENCE="$(otool -L "$APP_EXECUTABLE" | awk \
|
||||||
|
'$1 ~ /(^|\/)libslint_cpp[^\/[:space:]]*\.dylib$/ { print $1; exit }')"
|
||||||
|
if [[ -z "$SLINT_REFERENCE" ]]; then
|
||||||
|
echo "The app executable does not reference the Slint runtime" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SLINT_NAME="${SLINT_REFERENCE##*/}"
|
||||||
|
SLINT_CURRENT_VERSION="$(otool -L "$APP_EXECUTABLE" | awk -v dependency="$SLINT_REFERENCE" '
|
||||||
|
$1 == dependency {
|
||||||
|
for (i = 1; i <= NF; ++i) {
|
||||||
|
if ($i == "current") {
|
||||||
|
version = $(i + 2)
|
||||||
|
gsub(/[),]/, "", version)
|
||||||
|
print version
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}')"
|
||||||
|
|
||||||
|
slint_runtime_version() {
|
||||||
|
local library="$1"
|
||||||
|
otool -L "$library" | awk -v name="$SLINT_NAME" '
|
||||||
|
index($1, "/" name) || $1 == "@rpath/" name {
|
||||||
|
for (i = 1; i <= NF; ++i) {
|
||||||
|
if ($i == "current") {
|
||||||
|
version = $(i + 2)
|
||||||
|
gsub(/[),]/, "", version)
|
||||||
|
print version
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
find_slint_runtime() {
|
||||||
|
local candidate=""
|
||||||
|
local candidate_version=""
|
||||||
|
local package_root=""
|
||||||
|
local package_roots=()
|
||||||
|
|
||||||
|
if [[ "$SLINT_REFERENCE" == /* && -f "$SLINT_REFERENCE" ]]; then
|
||||||
|
if lipo "$SLINT_REFERENCE" -verify_arch "$EXPECTED_ARCH" >/dev/null 2>&1; then
|
||||||
|
echo "$SLINT_REFERENCE"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${XMAKE_GLOBALDIR:-}" ]]; then
|
||||||
|
package_roots+=("$XMAKE_GLOBALDIR/.xmake/packages")
|
||||||
|
fi
|
||||||
|
package_roots+=("$HOME/.xmake/packages" "/data/.xmake/packages")
|
||||||
|
|
||||||
|
for package_root in "${package_roots[@]}"; do
|
||||||
|
[[ -d "$package_root/s/slint" ]] || continue
|
||||||
|
while IFS= read -r candidate; do
|
||||||
|
if ! lipo "$candidate" -verify_arch "$EXPECTED_ARCH" >/dev/null 2>&1; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
candidate_version="$(slint_runtime_version "$candidate")"
|
||||||
|
if [[ -n "$SLINT_CURRENT_VERSION" && "$candidate_version" != "$SLINT_CURRENT_VERSION" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$candidate"
|
||||||
|
return 0
|
||||||
|
done < <(find "$package_root/s/slint" -type f -path "*/lib/$SLINT_NAME" -print 2>/dev/null)
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
SLINT_LIBRARY="$(find_slint_runtime || true)"
|
||||||
|
if [[ -z "$SLINT_LIBRARY" ]]; then
|
||||||
|
echo "Unable to locate $SLINT_NAME for $EXPECTED_ARCH (current version $SLINT_CURRENT_VERSION)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
install -d "$FRAMEWORKS_DIR"
|
||||||
|
install -m 0755 "$SLINT_LIBRARY" "$FRAMEWORKS_DIR/$SLINT_NAME"
|
||||||
|
|
||||||
|
install_name_tool -id "@rpath/$SLINT_NAME" "$FRAMEWORKS_DIR/$SLINT_NAME"
|
||||||
|
if [[ "$SLINT_REFERENCE" != "@rpath/$SLINT_NAME" ]]; then
|
||||||
|
install_name_tool -change "$SLINT_REFERENCE" "@rpath/$SLINT_NAME" "$APP_EXECUTABLE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! otool -l "$APP_EXECUTABLE" | awk '
|
||||||
|
$1 == "cmd" { in_rpath = ($2 == "LC_RPATH"); next }
|
||||||
|
in_rpath && $1 == "path" && $2 == "@executable_path/../Frameworks" { found = 1 }
|
||||||
|
END { exit(found ? 0 : 1) }'; then
|
||||||
|
install_name_tool -add_rpath "@executable_path/../Frameworks" "$APP_EXECUTABLE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# install_name_tool invalidates the linker's ad-hoc signature. Sign nested code
|
||||||
|
# first, then seal the complete app bundle so local packages remain launchable.
|
||||||
|
codesign --force --sign - "$FRAMEWORKS_DIR/$SLINT_NAME"
|
||||||
|
codesign --force --deep --sign - "$APP_BUNDLE"
|
||||||
|
|
||||||
|
echo "Bundled Slint runtime: $SLINT_LIBRARY"
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
APP_NAME="crossdesk"
|
APP_NAME="crossdesk"
|
||||||
APP_NAME_UPPER="CrossDesk"
|
APP_NAME_UPPER="CrossDesk"
|
||||||
EXECUTABLE_PATH="./build/macosx/arm64/release/crossdesk"
|
EXECUTABLE_PATH="./build/macosx/arm64/release/crossdesk"
|
||||||
PLATFORM="macos"
|
PLATFORM="macos"
|
||||||
ARCH="arm64"
|
ARCH="arm64"
|
||||||
|
BINARY_ARCH="arm64"
|
||||||
IDENTIFIER="cn.crossdesk.app"
|
IDENTIFIER="cn.crossdesk.app"
|
||||||
ICON_PATH="icons/macos/crossdesk.icns"
|
ICON_PATH="icons/macos/crossdesk.icns"
|
||||||
MACOS_MIN_VERSION="10.12"
|
MACOS_MIN_VERSION="14.0"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
normalize_app_version() {
|
normalize_app_version() {
|
||||||
local input="$1"
|
local input="$1"
|
||||||
@@ -95,6 +97,9 @@ EOF
|
|||||||
xattr -cr "${APP_BUNDLE}" 2>/dev/null || true
|
xattr -cr "${APP_BUNDLE}" 2>/dev/null || true
|
||||||
find "${APP_BUNDLE}" -name '._*' -delete
|
find "${APP_BUNDLE}" -name '._*' -delete
|
||||||
|
|
||||||
|
"${SCRIPT_DIR}/bundle_app.sh" "${APP_BUNDLE}" "${BINARY_ARCH}"
|
||||||
|
"${SCRIPT_DIR}/verify_app.sh" "${APP_BUNDLE}" "${BINARY_ARCH}"
|
||||||
|
|
||||||
echo ".app created successfully."
|
echo ".app created successfully."
|
||||||
|
|
||||||
mkdir -p build_pkg_scripts
|
mkdir -p build_pkg_scripts
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -euo pipefail
|
||||||
|
|
||||||
APP_NAME="crossdesk"
|
APP_NAME="crossdesk"
|
||||||
APP_NAME_UPPER="CrossDesk"
|
APP_NAME_UPPER="CrossDesk"
|
||||||
EXECUTABLE_PATH="build/macosx/x86_64/release/crossdesk"
|
EXECUTABLE_PATH="build/macosx/x86_64/release/crossdesk"
|
||||||
PLATFORM="macos"
|
PLATFORM="macos"
|
||||||
ARCH="x64"
|
ARCH="x64"
|
||||||
|
BINARY_ARCH="x86_64"
|
||||||
IDENTIFIER="cn.crossdesk.app"
|
IDENTIFIER="cn.crossdesk.app"
|
||||||
ICON_PATH="icons/macos/crossdesk.icns"
|
ICON_PATH="icons/macos/crossdesk.icns"
|
||||||
MACOS_MIN_VERSION="10.12"
|
MACOS_MIN_VERSION="14.0"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
normalize_app_version() {
|
normalize_app_version() {
|
||||||
local input="$1"
|
local input="$1"
|
||||||
@@ -95,6 +97,9 @@ EOF
|
|||||||
xattr -cr "${APP_BUNDLE}" 2>/dev/null || true
|
xattr -cr "${APP_BUNDLE}" 2>/dev/null || true
|
||||||
find "${APP_BUNDLE}" -name '._*' -delete
|
find "${APP_BUNDLE}" -name '._*' -delete
|
||||||
|
|
||||||
|
"${SCRIPT_DIR}/bundle_app.sh" "${APP_BUNDLE}" "${BINARY_ARCH}"
|
||||||
|
"${SCRIPT_DIR}/verify_app.sh" "${APP_BUNDLE}" "${BINARY_ARCH}"
|
||||||
|
|
||||||
echo ".app created successfully."
|
echo ".app created successfully."
|
||||||
|
|
||||||
mkdir -p build_pkg_scripts
|
mkdir -p build_pkg_scripts
|
||||||
|
|||||||
Executable
+99
@@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -ne 2 ]]; then
|
||||||
|
echo "Usage: $0 <app-bundle> <expected-architecture>" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
APP_BUNDLE="$1"
|
||||||
|
EXPECTED_ARCH="$2"
|
||||||
|
APP_EXECUTABLE="$APP_BUNDLE/Contents/MacOS/CrossDesk"
|
||||||
|
FRAMEWORKS_DIR="$APP_BUNDLE/Contents/Frameworks"
|
||||||
|
INFO_PLIST="$APP_BUNDLE/Contents/Info.plist"
|
||||||
|
|
||||||
|
if [[ ! -x "$APP_EXECUTABLE" ]]; then
|
||||||
|
echo "Missing app executable: $APP_EXECUTABLE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! lipo "$APP_EXECUTABLE" -verify_arch "$EXPECTED_ARCH" >/dev/null 2>&1; then
|
||||||
|
echo "Unexpected app architecture; expected $EXPECTED_ARCH" >&2
|
||||||
|
lipo -archs "$APP_EXECUTABLE" >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! otool -l "$APP_EXECUTABLE" | awk '
|
||||||
|
$1 == "cmd" { in_rpath = ($2 == "LC_RPATH"); next }
|
||||||
|
in_rpath && $1 == "path" && $2 == "@executable_path/../Frameworks" { found = 1 }
|
||||||
|
END { exit(found ? 0 : 1) }'; then
|
||||||
|
echo "Missing app rpath: @executable_path/../Frameworks" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
verify_dependencies() {
|
||||||
|
local macho="$1"
|
||||||
|
local dependency=""
|
||||||
|
local relative_path=""
|
||||||
|
|
||||||
|
while IFS= read -r dependency; do
|
||||||
|
case "$dependency" in
|
||||||
|
/System/Library/*|/usr/lib/*)
|
||||||
|
;;
|
||||||
|
@rpath/*)
|
||||||
|
relative_path="${dependency#@rpath/}"
|
||||||
|
if [[ ! -f "$FRAMEWORKS_DIR/$relative_path" ]]; then
|
||||||
|
echo "Unbundled dependency in $macho: $dependency" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
@executable_path/../Frameworks/*)
|
||||||
|
relative_path="${dependency#@executable_path/../Frameworks/}"
|
||||||
|
if [[ ! -f "$FRAMEWORKS_DIR/$relative_path" ]]; then
|
||||||
|
echo "Unbundled dependency in $macho: $dependency" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
@loader_path/*)
|
||||||
|
relative_path="${dependency#@loader_path/}"
|
||||||
|
if [[ ! -f "$(dirname "$macho")/$relative_path" ]]; then
|
||||||
|
echo "Unbundled dependency in $macho: $dependency" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "External non-system dependency in $macho: $dependency" >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < <(otool -L "$macho" | tail -n +2 | awk '{ print $1 }')
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_dependencies "$APP_EXECUTABLE"
|
||||||
|
|
||||||
|
SLINT_LIBRARY="$(find "$FRAMEWORKS_DIR" -maxdepth 1 -type f -name 'libslint_cpp*.dylib' -print -quit 2>/dev/null || true)"
|
||||||
|
if [[ -z "$SLINT_LIBRARY" ]]; then
|
||||||
|
echo "The packaged app does not contain libslint_cpp.dylib" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! lipo "$SLINT_LIBRARY" -verify_arch "$EXPECTED_ARCH" >/dev/null 2>&1; then
|
||||||
|
echo "Unexpected Slint runtime architecture; expected $EXPECTED_ARCH" >&2
|
||||||
|
lipo -archs "$SLINT_LIBRARY" >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
verify_dependencies "$SLINT_LIBRARY"
|
||||||
|
|
||||||
|
PLIST_MIN_VERSION="$(/usr/libexec/PlistBuddy -c 'Print :LSMinimumSystemVersion' "$INFO_PLIST")"
|
||||||
|
BINARY_MIN_VERSION="$(otool -l "$APP_EXECUTABLE" | awk '
|
||||||
|
$1 == "cmd" { build_version = ($2 == "LC_BUILD_VERSION"); version_min = ($2 == "LC_VERSION_MIN_MACOSX"); next }
|
||||||
|
build_version && $1 == "minos" { print $2; exit }
|
||||||
|
version_min && $1 == "version" { print $2; exit }')"
|
||||||
|
if [[ -z "$BINARY_MIN_VERSION" || "$PLIST_MIN_VERSION" != "$BINARY_MIN_VERSION" ]]; then
|
||||||
|
echo "Minimum macOS version mismatch: plist=$PLIST_MIN_VERSION binary=${BINARY_MIN_VERSION:-unknown}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
codesign --verify --deep --strict --verbose=2 "$APP_BUNDLE"
|
||||||
|
|
||||||
|
echo "Verified $APP_BUNDLE ($EXPECTED_ARCH, macOS $BINARY_MIN_VERSION+)"
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
#include "keyboard_capturer.h"
|
#include "keyboard_capturer.h"
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/extensions/XTest.h>
|
||||||
|
#include <X11/keysym.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,6 @@
|
|||||||
#ifndef _KEYBOARD_CAPTURER_H_
|
#ifndef _KEYBOARD_CAPTURER_H_
|
||||||
#define _KEYBOARD_CAPTURER_H_
|
#define _KEYBOARD_CAPTURER_H_
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/extensions/XTest.h>
|
|
||||||
#include <X11/keysym.h>
|
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -21,6 +17,7 @@
|
|||||||
|
|
||||||
struct DBusConnection;
|
struct DBusConnection;
|
||||||
struct DBusMessageIter;
|
struct DBusMessageIter;
|
||||||
|
struct _XDisplay;
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
@@ -48,8 +45,8 @@ class KeyboardCapturer : public DeviceController {
|
|||||||
append_args);
|
append_args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Display* display_;
|
_XDisplay* display_;
|
||||||
Window root_;
|
unsigned long root_;
|
||||||
std::atomic<bool> running_;
|
std::atomic<bool> running_;
|
||||||
std::thread event_thread_;
|
std::thread event_thread_;
|
||||||
bool use_wayland_portal_ = false;
|
bool use_wayland_portal_ = false;
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#if defined(CROSSDESK_HAS_WAYLAND_CAPTURER) && CROSSDESK_HAS_WAYLAND_CAPTURER
|
#if defined(CROSSDESK_HAS_WAYLAND_CAPTURER) && CROSSDESK_HAS_WAYLAND_CAPTURER
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/keysym.h>
|
||||||
|
|
||||||
#include <dbus/dbus.h>
|
#include <dbus/dbus.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#include "mouse_controller.h"
|
#include "mouse_controller.h"
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
#include <X11/extensions/XTest.h>
|
#include <X11/extensions/XTest.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "rd_log.h"
|
#include "rd_log.h"
|
||||||
|
|||||||
@@ -7,10 +7,6 @@
|
|||||||
#ifndef _MOUSE_CONTROLLER_H_
|
#ifndef _MOUSE_CONTROLLER_H_
|
||||||
#define _MOUSE_CONTROLLER_H_
|
#define _MOUSE_CONTROLLER_H_
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/Xutil.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -20,6 +16,7 @@
|
|||||||
|
|
||||||
struct DBusConnection;
|
struct DBusConnection;
|
||||||
struct DBusMessageIter;
|
struct DBusMessageIter;
|
||||||
|
struct _XDisplay;
|
||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
@@ -53,8 +50,8 @@ class MouseController : public DeviceController {
|
|||||||
|
|
||||||
enum class WaylandAbsoluteMode { kUnknown, kPixels, kNormalized, kDisabled };
|
enum class WaylandAbsoluteMode { kUnknown, kPixels, kNormalized, kDisabled };
|
||||||
|
|
||||||
Display* display_ = nullptr;
|
_XDisplay* display_ = nullptr;
|
||||||
Window root_ = 0;
|
unsigned long root_ = 0;
|
||||||
std::vector<DisplayInfo> display_info_list_;
|
std::vector<DisplayInfo> display_info_list_;
|
||||||
int screen_width_ = 0;
|
int screen_width_ = 0;
|
||||||
int screen_height_ = 0;
|
int screen_height_ = 0;
|
||||||
|
|||||||
@@ -96,54 +96,94 @@ std::string FormatRemotePeerIdInput(std::string_view input) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string CompactPeerId(std::string id) {
|
std::string CompactPeerId(std::string id) {
|
||||||
id.erase(std::remove_if(id.begin(), id.end(), [](unsigned char ch) {
|
id.erase(
|
||||||
return std::isspace(ch) != 0;
|
std::remove_if(id.begin(), id.end(),
|
||||||
}),
|
[](unsigned char ch) { return std::isspace(ch) != 0; }),
|
||||||
id.end());
|
id.end());
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Trim(std::string value) {
|
std::string Trim(std::string value) {
|
||||||
const auto first = std::find_if_not(value.begin(), value.end(),
|
const auto first =
|
||||||
[](unsigned char ch) {
|
std::find_if_not(value.begin(), value.end(),
|
||||||
|
[](unsigned char ch) { return std::isspace(ch) != 0; });
|
||||||
|
const auto last =
|
||||||
|
std::find_if_not(value.rbegin(), value.rend(), [](unsigned char ch) {
|
||||||
return std::isspace(ch) != 0;
|
return std::isspace(ch) != 0;
|
||||||
});
|
}).base();
|
||||||
const auto last = std::find_if_not(value.rbegin(), value.rend(),
|
|
||||||
[](unsigned char ch) {
|
|
||||||
return std::isspace(ch) != 0;
|
|
||||||
})
|
|
||||||
.base();
|
|
||||||
if (first >= last) {
|
if (first >= last) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return std::string(first, last);
|
return std::string(first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CleanReleaseNotesForUi(std::string markdown) {
|
struct ParsedReleaseNoteBlock {
|
||||||
|
slint::StyledText content;
|
||||||
|
bool section_gap = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ParsedReleaseNoteBlock> ParseReleaseNotesMarkdownForSlint(
|
||||||
|
std::string_view markdown) {
|
||||||
|
std::vector<ParsedReleaseNoteBlock> blocks;
|
||||||
|
bool section_gap = false;
|
||||||
|
|
||||||
size_t line_start = 0;
|
size_t line_start = 0;
|
||||||
while (line_start < markdown.size()) {
|
while (line_start < markdown.size()) {
|
||||||
size_t content_start = line_start;
|
|
||||||
while (content_start < markdown.size() && markdown[content_start] == '#') {
|
|
||||||
++content_start;
|
|
||||||
}
|
|
||||||
if (content_start > line_start && content_start < markdown.size() &&
|
|
||||||
markdown[content_start] == ' ') {
|
|
||||||
++content_start;
|
|
||||||
}
|
|
||||||
if (content_start > line_start) {
|
|
||||||
markdown.erase(line_start, content_start - line_start);
|
|
||||||
}
|
|
||||||
const size_t newline = markdown.find('\n', line_start);
|
const size_t newline = markdown.find('\n', line_start);
|
||||||
if (newline == std::string::npos) {
|
const size_t line_end = newline == std::string_view::npos
|
||||||
|
? markdown.size()
|
||||||
|
: newline;
|
||||||
|
std::string_view line = markdown.substr(line_start, line_end - line_start);
|
||||||
|
if (!line.empty() && line.back() == '\r') {
|
||||||
|
line.remove_suffix(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::all_of(line.begin(), line.end(), [](unsigned char ch) {
|
||||||
|
return std::isspace(ch) != 0;
|
||||||
|
})) {
|
||||||
|
section_gap = !blocks.empty();
|
||||||
|
if (newline == std::string_view::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line_start = newline + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t heading_end = 0;
|
||||||
|
while (heading_end < line.size() && heading_end < 6 &&
|
||||||
|
line[heading_end] == '#') {
|
||||||
|
++heading_end;
|
||||||
|
}
|
||||||
|
const bool is_heading =
|
||||||
|
heading_end > 0 && heading_end < line.size() &&
|
||||||
|
std::isspace(static_cast<unsigned char>(line[heading_end])) != 0;
|
||||||
|
if (is_heading) {
|
||||||
|
if (newline == std::string_view::npos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
line_start = newline + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string block_markdown(line);
|
||||||
|
ParsedReleaseNoteBlock block;
|
||||||
|
if (const auto parsed =
|
||||||
|
slint::StyledText::from_markdown(block_markdown)) {
|
||||||
|
block.content = *parsed;
|
||||||
|
} else {
|
||||||
|
LOG_WARN("Failed to parse a release-note Markdown block; using plain text");
|
||||||
|
block.content = slint::StyledText::from_plain_text(block_markdown);
|
||||||
|
}
|
||||||
|
block.section_gap = section_gap;
|
||||||
|
blocks.push_back(std::move(block));
|
||||||
|
section_gap = false;
|
||||||
|
|
||||||
|
if (newline == std::string_view::npos) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
line_start = newline + 1;
|
line_start = newline + 1;
|
||||||
}
|
}
|
||||||
size_t marker = 0;
|
return blocks;
|
||||||
while ((marker = markdown.find("**", marker)) != std::string::npos) {
|
|
||||||
markdown.erase(marker, 2);
|
|
||||||
}
|
|
||||||
return markdown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DecodeDroppedPath(std::string text) {
|
std::string DecodeDroppedPath(std::string text) {
|
||||||
@@ -232,8 +272,7 @@ std::string FormatBitrate(uint32_t bits_per_second) {
|
|||||||
} else if (bits_per_second < 1000000) {
|
} else if (bits_per_second < 1000000) {
|
||||||
std::snprintf(text, sizeof(text), "%u kbps", bits_per_second / 1000);
|
std::snprintf(text, sizeof(text), "%u kbps", bits_per_second / 1000);
|
||||||
} else {
|
} else {
|
||||||
std::snprintf(text, sizeof(text), "%.1f mbps",
|
std::snprintf(text, sizeof(text), "%.1f mbps", bits_per_second / 1000000.0);
|
||||||
bits_per_second / 1000000.0);
|
|
||||||
}
|
}
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@@ -259,8 +298,7 @@ std::string FormatFileSize(uint64_t bytes) {
|
|||||||
} else if (bytes < 1024 * 1024) {
|
} else if (bytes < 1024 * 1024) {
|
||||||
std::snprintf(text, sizeof(text), "%.2f KB", bytes / 1024.0);
|
std::snprintf(text, sizeof(text), "%.2f KB", bytes / 1024.0);
|
||||||
} else if (bytes < 1024ULL * 1024ULL * 1024ULL) {
|
} else if (bytes < 1024ULL * 1024ULL * 1024ULL) {
|
||||||
std::snprintf(text, sizeof(text), "%.2f MB",
|
std::snprintf(text, sizeof(text), "%.2f MB", bytes / (1024.0 * 1024.0));
|
||||||
bytes / (1024.0 * 1024.0));
|
|
||||||
} else {
|
} else {
|
||||||
std::snprintf(text, sizeof(text), "%.2f GB",
|
std::snprintf(text, sizeof(text), "%.2f GB",
|
||||||
bytes / (1024.0 * 1024.0 * 1024.0));
|
bytes / (1024.0 * 1024.0 * 1024.0));
|
||||||
@@ -464,16 +502,15 @@ SDL_DisplayID DisplayForSlintWindow(const slint::Window &window) {
|
|||||||
const auto position = window.position();
|
const auto position = window.position();
|
||||||
const auto size = window.size();
|
const auto size = window.size();
|
||||||
const SDL_Point center{
|
const SDL_Point center{
|
||||||
static_cast<int>(std::lround(position.x / scale +
|
static_cast<int>(
|
||||||
size.width / (2.0f * scale))),
|
std::lround(position.x / scale + size.width / (2.0f * scale))),
|
||||||
static_cast<int>(std::lround(position.y / scale +
|
static_cast<int>(
|
||||||
size.height / (2.0f * scale)))};
|
std::lround(position.y / scale + size.height / (2.0f * scale)))};
|
||||||
const SDL_DisplayID display = SDL_GetDisplayForPoint(¢er);
|
const SDL_DisplayID display = SDL_GetDisplayForPoint(¢er);
|
||||||
return display != 0 ? display : SDL_GetPrimaryDisplay();
|
return display != 0 ? display : SDL_GetPrimaryDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PositionWindowAtCenter(slint::Window &window,
|
bool PositionWindowAtCenter(slint::Window& window, const slint::Window& anchor,
|
||||||
const slint::Window &anchor,
|
|
||||||
float logical_width, float logical_height) {
|
float logical_width, float logical_height) {
|
||||||
if (!CanUseGlobalPointerPosition()) {
|
if (!CanUseGlobalPointerPosition()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -481,21 +518,20 @@ bool PositionWindowAtCenter(slint::Window &window,
|
|||||||
|
|
||||||
const SDL_DisplayID display = DisplayForSlintWindow(anchor);
|
const SDL_DisplayID display = DisplayForSlintWindow(anchor);
|
||||||
SDL_Rect usable_bounds{};
|
SDL_Rect usable_bounds{};
|
||||||
if (display == 0 ||
|
if (display == 0 || !SDL_GetDisplayUsableBounds(display, &usable_bounds)) {
|
||||||
!SDL_GetDisplayUsableBounds(display, &usable_bounds)) {
|
|
||||||
LOG_WARN("Unable to obtain usable display bounds for stream window: {}",
|
LOG_WARN("Unable to obtain usable display bounds for stream window: {}",
|
||||||
SDL_GetError());
|
SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float target_x = static_cast<float>(usable_bounds.x) +
|
const float target_x =
|
||||||
(static_cast<float>(usable_bounds.w) - logical_width) /
|
static_cast<float>(usable_bounds.x) +
|
||||||
2.0f;
|
(static_cast<float>(usable_bounds.w) - logical_width) / 2.0f;
|
||||||
const float target_y = static_cast<float>(usable_bounds.y) +
|
const float target_y =
|
||||||
(static_cast<float>(usable_bounds.h) - logical_height) /
|
static_cast<float>(usable_bounds.y) +
|
||||||
2.0f;
|
(static_cast<float>(usable_bounds.h) - logical_height) / 2.0f;
|
||||||
window.set_position(slint::LogicalPosition(
|
window.set_position(
|
||||||
slint::Point<float>{target_x, target_y}));
|
slint::LogicalPosition(slint::Point<float>{target_x, target_y}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,18 +546,17 @@ bool PositionWindowAtBottomRight(slint::Window &window, float logical_width,
|
|||||||
const auto position = window.position();
|
const auto position = window.position();
|
||||||
const auto size = window.size();
|
const auto size = window.size();
|
||||||
const SDL_Point center{
|
const SDL_Point center{
|
||||||
static_cast<int>(std::lround(position.x / scale +
|
static_cast<int>(
|
||||||
size.width / (2.0f * scale))),
|
std::lround(position.x / scale + size.width / (2.0f * scale))),
|
||||||
static_cast<int>(std::lround(position.y / scale +
|
static_cast<int>(
|
||||||
size.height / (2.0f * scale)))};
|
std::lround(position.y / scale + size.height / (2.0f * scale)))};
|
||||||
display = SDL_GetDisplayForPoint(¢er);
|
display = SDL_GetDisplayForPoint(¢er);
|
||||||
if (display == 0) {
|
if (display == 0) {
|
||||||
display = SDL_GetPrimaryDisplay();
|
display = SDL_GetPrimaryDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Rect usable_bounds{};
|
SDL_Rect usable_bounds{};
|
||||||
if (display == 0 ||
|
if (display == 0 || !SDL_GetDisplayUsableBounds(display, &usable_bounds)) {
|
||||||
!SDL_GetDisplayUsableBounds(display, &usable_bounds)) {
|
|
||||||
LOG_WARN("Unable to obtain usable display bounds for server window: {}",
|
LOG_WARN("Unable to obtain usable display bounds for server window: {}",
|
||||||
SDL_GetError());
|
SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
@@ -533,8 +568,8 @@ bool PositionWindowAtBottomRight(slint::Window &window, float logical_width,
|
|||||||
const float target_y =
|
const float target_y =
|
||||||
static_cast<float>(usable_bounds.y) +
|
static_cast<float>(usable_bounds.y) +
|
||||||
std::max(0.0f, static_cast<float>(usable_bounds.h) - logical_height);
|
std::max(0.0f, static_cast<float>(usable_bounds.h) - logical_height);
|
||||||
window.set_position(slint::LogicalPosition(
|
window.set_position(
|
||||||
slint::Point<float>{target_x, target_y}));
|
slint::LogicalPosition(slint::Point<float>{target_x, target_y}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,11 +620,9 @@ void DragWindow(WindowHandle &component, int phase, float mouse_x,
|
|||||||
float pointer_y = 0;
|
float pointer_y = 0;
|
||||||
SDL_GetGlobalMouseState(&pointer_x, &pointer_y);
|
SDL_GetGlobalMouseState(&pointer_x, &pointer_y);
|
||||||
const int delta_x = static_cast<int>(std::lround(
|
const int delta_x = static_cast<int>(std::lround(
|
||||||
(pointer_x - state.pointer_start_x) *
|
(pointer_x - state.pointer_start_x) * state.global_to_physical_scale));
|
||||||
state.global_to_physical_scale));
|
|
||||||
const int delta_y = static_cast<int>(std::lround(
|
const int delta_y = static_cast<int>(std::lround(
|
||||||
(pointer_y - state.pointer_start_y) *
|
(pointer_y - state.pointer_start_y) * state.global_to_physical_scale));
|
||||||
state.global_to_physical_scale));
|
|
||||||
const slint::PhysicalPosition target(slint::Point<int32_t>{
|
const slint::PhysicalPosition target(slint::Point<int32_t>{
|
||||||
state.window_start.x + delta_x, state.window_start.y + delta_y});
|
state.window_start.x + delta_x, state.window_start.y + delta_y});
|
||||||
if (target.x != state.last_target.x || target.y != state.last_target.y) {
|
if (target.x != state.last_target.x || target.y != state.last_target.y) {
|
||||||
@@ -602,14 +635,13 @@ void DragWindow(WindowHandle &component, int phase, float mouse_x,
|
|||||||
// Retain the previous behavior for backends without global pointer access.
|
// Retain the previous behavior for backends without global pointer access.
|
||||||
const float scale = window.scale_factor();
|
const float scale = window.scale_factor();
|
||||||
const auto position = window.position();
|
const auto position = window.position();
|
||||||
const int delta_x = static_cast<int>(
|
const int delta_x =
|
||||||
std::lround((mouse_x - state.last_local_x) * scale));
|
static_cast<int>(std::lround((mouse_x - state.last_local_x) * scale));
|
||||||
const int delta_y = static_cast<int>(
|
const int delta_y =
|
||||||
std::lround((mouse_y - state.last_local_y) * scale));
|
static_cast<int>(std::lround((mouse_y - state.last_local_y) * scale));
|
||||||
if (delta_x != 0 || delta_y != 0) {
|
if (delta_x != 0 || delta_y != 0) {
|
||||||
window.set_position(
|
window.set_position(slint::PhysicalPosition(
|
||||||
slint::PhysicalPosition(slint::Point<int32_t>{
|
slint::Point<int32_t>{position.x + delta_x, position.y + delta_y}));
|
||||||
position.x + delta_x, position.y + delta_y}));
|
|
||||||
}
|
}
|
||||||
state.last_local_x = mouse_x;
|
state.last_local_x = mouse_x;
|
||||||
state.last_local_y = mouse_y;
|
state.last_local_y = mouse_y;
|
||||||
@@ -619,6 +651,9 @@ void DragWindow(WindowHandle &component, int phase, float mouse_x,
|
|||||||
|
|
||||||
struct GuiApplication::SlintUi {
|
struct GuiApplication::SlintUi {
|
||||||
slint::ComponentHandle<ui::MainWindow> main = ui::MainWindow::create();
|
slint::ComponentHandle<ui::MainWindow> main = ui::MainWindow::create();
|
||||||
|
std::shared_ptr<slint::VectorModel<ui::ReleaseNoteBlock>>
|
||||||
|
release_note_blocks_model =
|
||||||
|
std::make_shared<slint::VectorModel<ui::ReleaseNoteBlock>>();
|
||||||
std::optional<slint::ComponentHandle<ui::StreamWindow>> stream;
|
std::optional<slint::ComponentHandle<ui::StreamWindow>> stream;
|
||||||
std::optional<slint::ComponentHandle<ui::ServerWindow>> server;
|
std::optional<slint::ComponentHandle<ui::ServerWindow>> server;
|
||||||
std::shared_ptr<slint::VectorModel<ui::RecentConnection>> recent_model =
|
std::shared_ptr<slint::VectorModel<ui::RecentConnection>> recent_model =
|
||||||
@@ -633,7 +668,8 @@ struct GuiApplication::SlintUi {
|
|||||||
std::make_shared<slint::VectorModel<ui::NetworkStatsRow>>();
|
std::make_shared<slint::VectorModel<ui::NetworkStatsRow>>();
|
||||||
std::shared_ptr<slint::VectorModel<ui::ControllerEntry>> controller_model =
|
std::shared_ptr<slint::VectorModel<ui::ControllerEntry>> controller_model =
|
||||||
std::make_shared<slint::VectorModel<ui::ControllerEntry>>();
|
std::make_shared<slint::VectorModel<ui::ControllerEntry>>();
|
||||||
std::shared_ptr<slint::VectorModel<slint::SharedString>> controller_name_model =
|
std::shared_ptr<slint::VectorModel<slint::SharedString>>
|
||||||
|
controller_name_model =
|
||||||
std::make_shared<slint::VectorModel<slint::SharedString>>();
|
std::make_shared<slint::VectorModel<slint::SharedString>>();
|
||||||
std::unordered_map<std::string, uint64_t> displayed_frame_sequence;
|
std::unordered_map<std::string, uint64_t> displayed_frame_sequence;
|
||||||
std::vector<std::string> tab_order;
|
std::vector<std::string> tab_order;
|
||||||
@@ -674,8 +710,7 @@ int GuiApplication::Run() {
|
|||||||
exec_log_path_ = path_manager_->GetLogPath().string();
|
exec_log_path_ = path_manager_->GetLogPath().string();
|
||||||
dll_log_path_ = exec_log_path_;
|
dll_log_path_ = exec_log_path_;
|
||||||
cache_path_ = path_manager_->GetCachePath().string();
|
cache_path_ = path_manager_->GetCachePath().string();
|
||||||
config_center_ =
|
config_center_ = std::make_unique<ConfigCenter>(cache_path_ + "/config.ini");
|
||||||
std::make_unique<ConfigCenter>(cache_path_ + "/config.ini");
|
|
||||||
|
|
||||||
InitializeLogger();
|
InitializeLogger();
|
||||||
LOG_INFO("CrossDesk version: {} (Slint UI)", CROSSDESK_VERSION);
|
LOG_INFO("CrossDesk version: {} (Slint UI)", CROSSDESK_VERSION);
|
||||||
@@ -701,8 +736,7 @@ int GuiApplication::Run() {
|
|||||||
latest_version_ = version.empty() ? std::string{} : "v" + version;
|
latest_version_ = version.empty() ? std::string{} : "v" + version;
|
||||||
if (latest_version_info_.contains("releaseNotes") &&
|
if (latest_version_info_.contains("releaseNotes") &&
|
||||||
latest_version_info_["releaseNotes"].is_string()) {
|
latest_version_info_["releaseNotes"].is_string()) {
|
||||||
release_notes_ =
|
release_notes_ = latest_version_info_["releaseNotes"].get<std::string>();
|
||||||
latest_version_info_["releaseNotes"].get<std::string>();
|
|
||||||
}
|
}
|
||||||
if (latest_version_info_.contains("releaseName") &&
|
if (latest_version_info_.contains("releaseName") &&
|
||||||
latest_version_info_["releaseName"].is_string()) {
|
latest_version_info_["releaseName"].is_string()) {
|
||||||
@@ -782,6 +816,16 @@ void GuiApplication::InitializeModules() {
|
|||||||
|
|
||||||
void GuiApplication::InitializeUi() {
|
void GuiApplication::InitializeUi() {
|
||||||
ui_ = std::make_unique<SlintUi>();
|
ui_ = std::make_unique<SlintUi>();
|
||||||
|
std::vector<ui::ReleaseNoteBlock> release_note_blocks;
|
||||||
|
for (const auto& parsed :
|
||||||
|
ParseReleaseNotesMarkdownForSlint(release_notes_)) {
|
||||||
|
ui::ReleaseNoteBlock block;
|
||||||
|
block.content = parsed.content;
|
||||||
|
block.section_gap = parsed.section_gap;
|
||||||
|
release_note_blocks.push_back(std::move(block));
|
||||||
|
}
|
||||||
|
ui_->release_note_blocks_model->set_vector(std::move(release_note_blocks));
|
||||||
|
ui_->main->set_release_note_blocks(ui_->release_note_blocks_model);
|
||||||
RegisterFontAwesome(ui_->main->window());
|
RegisterFontAwesome(ui_->main->window());
|
||||||
#ifdef CROSSDESK_DEBUG
|
#ifdef CROSSDESK_DEBUG
|
||||||
auto read_capture_override = [](const char* path) -> std::string {
|
auto read_capture_override = [](const char* path) -> std::string {
|
||||||
@@ -798,13 +842,13 @@ void GuiApplication::InitializeUi() {
|
|||||||
}
|
}
|
||||||
std::string capture_language_override;
|
std::string capture_language_override;
|
||||||
if (ui_->capture_mode) {
|
if (ui_->capture_mode) {
|
||||||
if (const auto page = read_capture_override(
|
if (const auto page =
|
||||||
"/tmp/crossdesk-ui-capture-page");
|
read_capture_override("/tmp/crossdesk-ui-capture-page");
|
||||||
!page.empty()) {
|
!page.empty()) {
|
||||||
ui_->capture_page = page;
|
ui_->capture_page = page;
|
||||||
}
|
}
|
||||||
capture_language_override = read_capture_override(
|
capture_language_override =
|
||||||
"/tmp/crossdesk-ui-capture-language");
|
read_capture_override("/tmp/crossdesk-ui-capture-language");
|
||||||
}
|
}
|
||||||
const char* language = capture_language_override.empty()
|
const char* language = capture_language_override.empty()
|
||||||
? std::getenv("CROSSDESK_UI_CAPTURE_LANGUAGE")
|
? std::getenv("CROSSDESK_UI_CAPTURE_LANGUAGE")
|
||||||
@@ -817,8 +861,8 @@ void GuiApplication::InitializeUi() {
|
|||||||
localization_language_index_ =
|
localization_language_index_ =
|
||||||
localization::detail::ClampLanguageIndex(capture_language);
|
localization::detail::ClampLanguageIndex(capture_language);
|
||||||
language_button_value_ = localization_language_index_;
|
language_button_value_ = localization_language_index_;
|
||||||
localization_language_ = static_cast<ConfigCenter::LANGUAGE>(
|
localization_language_ =
|
||||||
localization_language_index_);
|
static_cast<ConfigCenter::LANGUAGE>(localization_language_index_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -889,7 +933,8 @@ void GuiApplication::InitializeUi() {
|
|||||||
ui_->display_model->set_vector({slint::SharedString("Display 1")});
|
ui_->display_model->set_vector({slint::SharedString("Display 1")});
|
||||||
ui_->localized_language = -1;
|
ui_->localized_language = -1;
|
||||||
UpdateLocalization();
|
UpdateLocalization();
|
||||||
(*ui_->stream)->set_status_text(
|
(*ui_->stream)
|
||||||
|
->set_status_text(
|
||||||
UiText(localization::p2p_connected[localization_language_index_]));
|
UiText(localization::p2p_connected[localization_language_index_]));
|
||||||
#ifdef CROSSDESK_DEBUG
|
#ifdef CROSSDESK_DEBUG
|
||||||
// A bordered 16:9 frame makes stream scaling/cropping regressions visible
|
// A bordered 16:9 frame makes stream scaling/cropping regressions visible
|
||||||
@@ -898,20 +943,16 @@ void GuiApplication::InitializeUi() {
|
|||||||
constexpr int preview_height = 360;
|
constexpr int preview_height = 360;
|
||||||
slint::SharedPixelBuffer<slint::Rgb8Pixel> preview_pixels(preview_width,
|
slint::SharedPixelBuffer<slint::Rgb8Pixel> preview_pixels(preview_width,
|
||||||
preview_height);
|
preview_height);
|
||||||
auto *preview_data =
|
auto* preview_data = reinterpret_cast<uint8_t*>(preview_pixels.begin());
|
||||||
reinterpret_cast<uint8_t *>(preview_pixels.begin());
|
|
||||||
for (int y = 0; y < preview_height; ++y) {
|
for (int y = 0; y < preview_height; ++y) {
|
||||||
for (int x = 0; x < preview_width; ++x) {
|
for (int x = 0; x < preview_width; ++x) {
|
||||||
const bool border = x < 8 || y < 8 || x >= preview_width - 8 ||
|
const bool border =
|
||||||
y >= preview_height - 8;
|
x < 8 || y < 8 || x >= preview_width - 8 || y >= preview_height - 8;
|
||||||
const size_t offset =
|
const size_t offset = (static_cast<size_t>(y) * preview_width + x) * 3;
|
||||||
(static_cast<size_t>(y) * preview_width + x) * 3;
|
preview_data[offset] =
|
||||||
preview_data[offset] = border ? 240 : static_cast<uint8_t>(
|
border ? 240 : static_cast<uint8_t>(35 + 150 * x / preview_width);
|
||||||
35 + 150 * x /
|
preview_data[offset + 1] =
|
||||||
preview_width);
|
border ? 70 : static_cast<uint8_t>(35 + 150 * y / preview_height);
|
||||||
preview_data[offset + 1] = border ? 70 : static_cast<uint8_t>(
|
|
||||||
35 + 150 * y /
|
|
||||||
preview_height);
|
|
||||||
preview_data[offset + 2] = border ? 70 : 90;
|
preview_data[offset + 2] = border ? 70 : 90;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -934,22 +975,29 @@ void GuiApplication::InitializeUi() {
|
|||||||
const float server_width =
|
const float server_width =
|
||||||
ServerWindowLogicalWidth(localization_language_index_);
|
ServerWindowLogicalWidth(localization_language_index_);
|
||||||
const float server_height = ServerWindowLogicalHeight(false);
|
const float server_height = ServerWindowLogicalHeight(false);
|
||||||
(*ui_->server)->window().set_size(slint::LogicalSize(
|
(*ui_->server)
|
||||||
|
->window()
|
||||||
|
.set_size(slint::LogicalSize(
|
||||||
slint::Size<float>{server_width, server_height}));
|
slint::Size<float>{server_width, server_height}));
|
||||||
ui::ControllerEntry preview_controller;
|
ui::ControllerEntry preview_controller;
|
||||||
preview_controller.remote_id = "589173341";
|
preview_controller.remote_id = "589173341";
|
||||||
preview_controller.display_name = "Mac";
|
preview_controller.display_name = "Mac";
|
||||||
ui_->controller_model->set_vector({preview_controller});
|
ui_->controller_model->set_vector({preview_controller});
|
||||||
ui_->controller_name_model->set_vector({slint::SharedString("Mac")});
|
ui_->controller_name_model->set_vector({slint::SharedString("Mac")});
|
||||||
(*ui_->server)->set_controller_label(
|
(*ui_->server)
|
||||||
|
->set_controller_label(
|
||||||
UiText(localization::controller[localization_language_index_]));
|
UiText(localization::controller[localization_language_index_]));
|
||||||
(*ui_->server)->set_connection_label(
|
(*ui_->server)
|
||||||
UiText(localization::connection_status[localization_language_index_]));
|
->set_connection_label(UiText(
|
||||||
(*ui_->server)->set_connection_status(
|
localization::connection_status[localization_language_index_]));
|
||||||
|
(*ui_->server)
|
||||||
|
->set_connection_status(
|
||||||
UiText(localization::p2p_connected[localization_language_index_]));
|
UiText(localization::p2p_connected[localization_language_index_]));
|
||||||
(*ui_->server)->set_file_transfer_label(
|
(*ui_->server)
|
||||||
|
->set_file_transfer_label(
|
||||||
UiText(localization::file_transfer[localization_language_index_]));
|
UiText(localization::file_transfer[localization_language_index_]));
|
||||||
(*ui_->server)->set_select_file_label(
|
(*ui_->server)
|
||||||
|
->set_select_file_label(
|
||||||
UiText(localization::select_file[localization_language_index_]));
|
UiText(localization::select_file[localization_language_index_]));
|
||||||
BindServerCallbacks();
|
BindServerCallbacks();
|
||||||
(*ui_->server)->show();
|
(*ui_->server)->show();
|
||||||
@@ -1002,9 +1050,8 @@ void GuiApplication::InitializeSystemTray() {
|
|||||||
LoadSlintTrayIcon(), L"CrossDesk", localization_language_index_);
|
LoadSlintTrayIcon(), L"CrossDesk", localization_language_index_);
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
ui_->tray = std::make_unique<MacTray>(
|
ui_->tray = std::make_unique<MacTray>(
|
||||||
std::move(show_window), std::move(hide_window),
|
std::move(show_window), std::move(hide_window), std::move(open_settings),
|
||||||
std::move(open_settings), std::move(exit_app), "CrossDesk",
|
std::move(exit_app), "CrossDesk", localization_language_index_);
|
||||||
localization_language_index_);
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
ui_->tray = std::make_unique<LinuxTray>(
|
ui_->tray = std::make_unique<LinuxTray>(
|
||||||
std::move(show_window), std::move(hide_window), std::move(exit_app),
|
std::move(show_window), std::move(hide_window), std::move(exit_app),
|
||||||
@@ -1106,15 +1153,13 @@ void GuiApplication::BindMainCallbacks() {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
main->on_connect_requested([this](slint::SharedString id) {
|
main->on_connect_requested(
|
||||||
ConnectFromUi(std::string(id));
|
[this](slint::SharedString id) { ConnectFromUi(std::string(id)); });
|
||||||
});
|
|
||||||
main->on_format_remote_id([](slint::SharedString value) {
|
main->on_format_remote_id([](slint::SharedString value) {
|
||||||
return UiText(FormatRemotePeerIdInput(std::string_view(value)));
|
return UiText(FormatRemotePeerIdInput(std::string_view(value)));
|
||||||
});
|
});
|
||||||
main->on_recent_connect([this](slint::SharedString id) {
|
main->on_recent_connect(
|
||||||
ConnectFromUi(std::string(id));
|
[this](slint::SharedString id) { ConnectFromUi(std::string(id)); });
|
||||||
});
|
|
||||||
main->on_recent_edit_alias(
|
main->on_recent_edit_alias(
|
||||||
[this](slint::SharedString id, slint::SharedString alias) {
|
[this](slint::SharedString id, slint::SharedString alias) {
|
||||||
const std::string remote_id(id);
|
const std::string remote_id(id);
|
||||||
@@ -1143,7 +1188,8 @@ void GuiApplication::BindMainCallbacks() {
|
|||||||
const char* folder = tinyfd_selectFolderDialog(
|
const char* folder = tinyfd_selectFolderDialog(
|
||||||
localization::file_transfer_save_path[localization_language_index_]
|
localization::file_transfer_save_path[localization_language_index_]
|
||||||
.c_str(),
|
.c_str(),
|
||||||
file_transfer_save_path_buf_[0] ? file_transfer_save_path_buf_ : nullptr);
|
file_transfer_save_path_buf_[0] ? file_transfer_save_path_buf_
|
||||||
|
: nullptr);
|
||||||
if (folder) {
|
if (folder) {
|
||||||
ui_->main->set_file_save_path(folder);
|
ui_->main->set_file_save_path(folder);
|
||||||
}
|
}
|
||||||
@@ -1172,8 +1218,8 @@ void GuiApplication::BindMainCallbacks() {
|
|||||||
}
|
}
|
||||||
if (coturn_port) {
|
if (coturn_port) {
|
||||||
config_center_->SetCoturnServerPort(*coturn_port);
|
config_center_->SetCoturnServerPort(*coturn_port);
|
||||||
std::snprintf(coturn_server_port_self_,
|
std::snprintf(coturn_server_port_self_, sizeof(coturn_server_port_self_),
|
||||||
sizeof(coturn_server_port_self_), "%d", *coturn_port);
|
"%d", *coturn_port);
|
||||||
std::snprintf(coturn_server_port_, sizeof(coturn_server_port_), "%d",
|
std::snprintf(coturn_server_port_, sizeof(coturn_server_port_), "%d",
|
||||||
*coturn_port);
|
*coturn_port);
|
||||||
}
|
}
|
||||||
@@ -1183,11 +1229,11 @@ void GuiApplication::BindMainCallbacks() {
|
|||||||
ui_->main->set_server_host(UiText(host));
|
ui_->main->set_server_host(UiText(host));
|
||||||
const int signal_port = config_center_->GetSignalServerPort();
|
const int signal_port = config_center_->GetSignalServerPort();
|
||||||
const int coturn_port = config_center_->GetCoturnServerPort();
|
const int coturn_port = config_center_->GetCoturnServerPort();
|
||||||
ui_->main->set_server_port(
|
ui_->main->set_server_port(signal_port > 0
|
||||||
signal_port > 0 ? UiText(std::to_string(signal_port))
|
? UiText(std::to_string(signal_port))
|
||||||
: slint::SharedString{});
|
: slint::SharedString{});
|
||||||
ui_->main->set_coturn_port(
|
ui_->main->set_coturn_port(coturn_port > 0
|
||||||
coturn_port > 0 ? UiText(std::to_string(coturn_port))
|
? UiText(std::to_string(coturn_port))
|
||||||
: slint::SharedString{});
|
: slint::SharedString{});
|
||||||
});
|
});
|
||||||
main->on_open_download([this] { OpenUrl("https://crossdesk.cn"); });
|
main->on_open_download([this] { OpenUrl("https://crossdesk.cn"); });
|
||||||
@@ -1219,8 +1265,8 @@ void GuiApplication::BindMainCallbacks() {
|
|||||||
}
|
}
|
||||||
show_connection_status_window_ = false;
|
show_connection_status_window_ = false;
|
||||||
});
|
});
|
||||||
main->on_connection_submit_password(
|
main->on_connection_submit_password([this](slint::SharedString value,
|
||||||
[this](slint::SharedString value, bool remember) {
|
bool remember) {
|
||||||
const auto props = FindRemoteSession(ui_->connection_dialog_remote_id);
|
const auto props = FindRemoteSession(ui_->connection_dialog_remote_id);
|
||||||
const std::string password(value);
|
const std::string password(value);
|
||||||
if (!props || password.empty() || password.size() > 6) {
|
if (!props || password.empty() || password.size() > 6) {
|
||||||
@@ -1308,9 +1354,8 @@ void GuiApplication::BindStreamCallbacks() {
|
|||||||
stream->on_reorder_tab([this](int from, float drop_x, float tab_width) {
|
stream->on_reorder_tab([this](int from, float drop_x, float tab_width) {
|
||||||
ReorderStreamTab(from, drop_x, tab_width);
|
ReorderStreamTab(from, drop_x, tab_width);
|
||||||
});
|
});
|
||||||
stream->on_close_tab([this](slint::SharedString id) {
|
stream->on_close_tab(
|
||||||
CloseStreamTab(std::string(id));
|
[this](slint::SharedString id) { CloseStreamTab(std::string(id)); });
|
||||||
});
|
|
||||||
stream->on_switch_display([this](int index) {
|
stream->on_switch_display([this](int index) {
|
||||||
auto props = SelectedSession();
|
auto props = SelectedSession();
|
||||||
if (!props || index < 0 ||
|
if (!props || index < 0 ||
|
||||||
@@ -1379,9 +1424,8 @@ void GuiApplication::BindStreamCallbacks() {
|
|||||||
props->file_transfer_.file_transfer_window_visible_ = false;
|
props->file_transfer_.file_transfer_window_visible_ = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stream->on_can_drop_file([](const slint::DataTransfer &data) {
|
stream->on_can_drop_file(
|
||||||
return data.has_plain_text();
|
[](const slint::DataTransfer& data) { return data.has_plain_text(); });
|
||||||
});
|
|
||||||
stream->on_file_dropped([this](const slint::DataTransfer& data) {
|
stream->on_file_dropped([this](const slint::DataTransfer& data) {
|
||||||
const auto text = data.plain_text();
|
const auto text = data.plain_text();
|
||||||
auto props = SelectedSession();
|
auto props = SelectedSession();
|
||||||
@@ -1436,9 +1480,8 @@ void GuiApplication::BindStreamCallbacks() {
|
|||||||
stream->on_scroll_input([this](float dx, float dy, float x, float y) {
|
stream->on_scroll_input([this](float dx, float dy, float x, float y) {
|
||||||
SendScrollInput(dx, dy, x, y);
|
SendScrollInput(dx, dy, x, y);
|
||||||
});
|
});
|
||||||
stream->on_key_input(
|
stream->on_key_input([this](slint::SharedString text, bool pressed,
|
||||||
[this](slint::SharedString text, bool pressed, bool control, bool alt,
|
bool control, bool alt, bool shift, bool meta) {
|
||||||
bool shift, bool meta) {
|
|
||||||
SendKeyInput(std::string(text), pressed, control, alt, shift, meta);
|
SendKeyInput(std::string(text), pressed, control, alt, shift, meta);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1456,9 +1499,10 @@ void GuiApplication::BindServerCallbacks() {
|
|||||||
server_window_collapsed_ = collapsed;
|
server_window_collapsed_ = collapsed;
|
||||||
const int language = (*ui_->server)->get_language_index();
|
const int language = (*ui_->server)->get_language_index();
|
||||||
const float width = ServerWindowLogicalWidth(language);
|
const float width = ServerWindowLogicalWidth(language);
|
||||||
(*ui_->server)->window().set_size(
|
(*ui_->server)
|
||||||
slint::LogicalSize(slint::Size<float>{
|
->window()
|
||||||
width, ServerWindowLogicalHeight(collapsed)}));
|
.set_size(slint::LogicalSize(
|
||||||
|
slint::Size<float>{width, ServerWindowLogicalHeight(collapsed)}));
|
||||||
});
|
});
|
||||||
server->on_controller_selected([this](int index) {
|
server->on_controller_selected([this](int index) {
|
||||||
if (index < 0 || index >= static_cast<int>(ui_->controller_ids.size())) {
|
if (index < 0 || index >= static_cast<int>(ui_->controller_ids.size())) {
|
||||||
@@ -1580,7 +1624,8 @@ void GuiApplication::UpdateLocalization() {
|
|||||||
strings.set_ok(UiText(localization::ok[language]));
|
strings.set_ok(UiText(localization::ok[language]));
|
||||||
strings.set_cancel(UiText(localization::cancel[language]));
|
strings.set_cancel(UiText(localization::cancel[language]));
|
||||||
strings.set_new_password(UiText(localization::new_password[language]));
|
strings.set_new_password(UiText(localization::new_password[language]));
|
||||||
strings.set_invalid_password(UiText(localization::max_password_len[language]));
|
strings.set_invalid_password(
|
||||||
|
UiText(localization::max_password_len[language]));
|
||||||
strings.set_edit_alias(
|
strings.set_edit_alias(
|
||||||
UiText(localization::input_connection_alias[language]));
|
UiText(localization::input_connection_alias[language]));
|
||||||
strings.set_delete_connection(
|
strings.set_delete_connection(
|
||||||
@@ -1618,6 +1663,7 @@ void GuiApplication::UpdateLocalization() {
|
|||||||
strings.set_tls_error(UiText(localization::signal_tls_cert_error[language]));
|
strings.set_tls_error(UiText(localization::signal_tls_cert_error[language]));
|
||||||
strings.set_update_available(
|
strings.set_update_available(
|
||||||
UiText(localization::new_version_available[language]));
|
UiText(localization::new_version_available[language]));
|
||||||
|
strings.set_release_notes(UiText(localization::release_notes[language]));
|
||||||
strings.set_download(UiText(localization::update[language]));
|
strings.set_download(UiText(localization::update[language]));
|
||||||
strings.set_input_password(UiText(localization::input_password[language]));
|
strings.set_input_password(UiText(localization::input_password[language]));
|
||||||
strings.set_reinput_password(
|
strings.set_reinput_password(
|
||||||
@@ -1647,8 +1693,8 @@ void GuiApplication::UpdateLocalization() {
|
|||||||
strings.set_do_not_remind(
|
strings.set_do_not_remind(
|
||||||
UiText(localization::do_not_remind_again[language]));
|
UiText(localization::do_not_remind_again[language]));
|
||||||
strings.set_notification(UiText(localization::notification[language]));
|
strings.set_notification(UiText(localization::notification[language]));
|
||||||
strings.set_service_suppressed_message(
|
strings.set_service_suppressed_message(UiText(
|
||||||
UiText(localization::windows_service_prompt_suppressed_message[language]));
|
localization::windows_service_prompt_suppressed_message[language]));
|
||||||
strings.set_quality_low(UiText(localization::video_quality_low[language]));
|
strings.set_quality_low(UiText(localization::video_quality_low[language]));
|
||||||
strings.set_quality_medium(
|
strings.set_quality_medium(
|
||||||
UiText(localization::video_quality_medium[language]));
|
UiText(localization::video_quality_medium[language]));
|
||||||
@@ -1671,8 +1717,7 @@ void GuiApplication::UpdateLocalization() {
|
|||||||
UiText(localization::release_mouse[language]));
|
UiText(localization::release_mouse[language]));
|
||||||
stream_strings.set_audio(UiText(localization::audio_capture[language]));
|
stream_strings.set_audio(UiText(localization::audio_capture[language]));
|
||||||
stream_strings.set_mute(UiText(localization::mute[language]));
|
stream_strings.set_mute(UiText(localization::mute[language]));
|
||||||
stream_strings.set_select_file(
|
stream_strings.set_select_file(UiText(localization::select_file[language]));
|
||||||
UiText(localization::select_file[language]));
|
|
||||||
stream_strings.set_show_stats(
|
stream_strings.set_show_stats(
|
||||||
UiText(localization::show_net_traffic_stats[language]));
|
UiText(localization::show_net_traffic_stats[language]));
|
||||||
stream_strings.set_hide_stats(
|
stream_strings.set_hide_stats(
|
||||||
@@ -1680,8 +1725,7 @@ void GuiApplication::UpdateLocalization() {
|
|||||||
stream_strings.set_fullscreen(UiText(localization::fullscreen[language]));
|
stream_strings.set_fullscreen(UiText(localization::fullscreen[language]));
|
||||||
stream_strings.set_exit_fullscreen(
|
stream_strings.set_exit_fullscreen(
|
||||||
UiText(localization::exit_fullscreen[language]));
|
UiText(localization::exit_fullscreen[language]));
|
||||||
stream_strings.set_disconnect(
|
stream_strings.set_disconnect(UiText(localization::disconnect[language]));
|
||||||
UiText(localization::disconnect[language]));
|
|
||||||
stream_strings.set_file_transfer(
|
stream_strings.set_file_transfer(
|
||||||
UiText(localization::file_transfer_progress[language]));
|
UiText(localization::file_transfer_progress[language]));
|
||||||
stream_strings.set_expand_control(
|
stream_strings.set_expand_control(
|
||||||
@@ -1700,8 +1744,7 @@ void GuiApplication::UpdateLocalization() {
|
|||||||
auto& main_stream_strings = ui_->main->global<ui::StreamStrings>();
|
auto& main_stream_strings = ui_->main->global<ui::StreamStrings>();
|
||||||
apply_stream_strings(main_stream_strings);
|
apply_stream_strings(main_stream_strings);
|
||||||
if (ui_->stream) {
|
if (ui_->stream) {
|
||||||
auto &active_stream_strings =
|
auto& active_stream_strings = (*ui_->stream)->global<ui::StreamStrings>();
|
||||||
(*ui_->stream)->global<ui::StreamStrings>();
|
|
||||||
apply_stream_strings(active_stream_strings);
|
apply_stream_strings(active_stream_strings);
|
||||||
}
|
}
|
||||||
ui_->localized_language = language;
|
ui_->localized_language = language;
|
||||||
@@ -1728,13 +1771,12 @@ void GuiApplication::SyncMainWindow() {
|
|||||||
ui_->main->set_local_password(password_saved_);
|
ui_->main->set_local_password(password_saved_);
|
||||||
ui_->main->set_password_visible(show_password_);
|
ui_->main->set_password_visible(show_password_);
|
||||||
ui_->main->set_signal_connected(signal_connected_);
|
ui_->main->set_signal_connected(signal_connected_);
|
||||||
ui_->main->set_signal_tls_error(
|
ui_->main->set_signal_tls_error(signal_status_ ==
|
||||||
signal_status_ == SignalStatus::SignalTlsCertError);
|
SignalStatus::SignalTlsCertError);
|
||||||
ui_->main->set_update_available(update_available_);
|
ui_->main->set_update_available(update_available_);
|
||||||
ui_->main->set_current_version(CROSSDESK_VERSION);
|
ui_->main->set_current_version(CROSSDESK_VERSION);
|
||||||
ui_->main->set_latest_version(UiText(latest_version_));
|
ui_->main->set_latest_version(UiText(latest_version_));
|
||||||
ui_->main->set_release_name(UiText(release_name_));
|
ui_->main->set_release_name(UiText(release_name_));
|
||||||
ui_->main->set_release_notes(UiText(CleanReleaseNotesForUi(release_notes_)));
|
|
||||||
ui_->main->set_release_date(UiText(release_date_));
|
ui_->main->set_release_date(UiText(release_date_));
|
||||||
ui_->main->set_settings_session_active(stream_window_inited_);
|
ui_->main->set_settings_session_active(stream_window_inited_);
|
||||||
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
|
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
|
||||||
@@ -1873,8 +1915,8 @@ void GuiApplication::SyncPlatformDialogs() {
|
|||||||
state == PortableServiceInstallState::succeeded);
|
state == PortableServiceInstallState::succeeded);
|
||||||
const std::string* status = nullptr;
|
const std::string* status = nullptr;
|
||||||
if (state == PortableServiceInstallState::installing) {
|
if (state == PortableServiceInstallState::installing) {
|
||||||
status = &localization::installing_windows_service
|
status =
|
||||||
[localization_language_index_];
|
&localization::installing_windows_service[localization_language_index_];
|
||||||
} else if (state == PortableServiceInstallState::succeeded) {
|
} else if (state == PortableServiceInstallState::succeeded) {
|
||||||
status = &localization::windows_service_install_success
|
status = &localization::windows_service_install_success
|
||||||
[localization_language_index_];
|
[localization_language_index_];
|
||||||
@@ -1882,8 +1924,8 @@ void GuiApplication::SyncPlatformDialogs() {
|
|||||||
status = &localization::windows_service_install_failed
|
status = &localization::windows_service_install_failed
|
||||||
[localization_language_index_];
|
[localization_language_index_];
|
||||||
}
|
}
|
||||||
ui_->main->set_portable_service_status(
|
ui_->main->set_portable_service_status(status ? UiText(*status)
|
||||||
status ? UiText(*status) : slint::SharedString{});
|
: slint::SharedString{});
|
||||||
if (show_portable_service_install_window_) {
|
if (show_portable_service_install_window_) {
|
||||||
if (!ui_->portable_service_dialog_initialized) {
|
if (!ui_->portable_service_dialog_initialized) {
|
||||||
ui_->main->set_portable_service_do_not_remind(
|
ui_->main->set_portable_service_do_not_remind(
|
||||||
@@ -1907,14 +1949,12 @@ void GuiApplication::SyncPlatformDialogs() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RefreshMacPermissionStatus(false);
|
RefreshMacPermissionStatus(false);
|
||||||
show_request_permission_window_ =
|
show_request_permission_window_ = !mac_screen_recording_permission_granted_ ||
|
||||||
!mac_screen_recording_permission_granted_ ||
|
|
||||||
!mac_accessibility_permission_granted_;
|
!mac_accessibility_permission_granted_;
|
||||||
ui_->main->set_permission_dialog_open(show_request_permission_window_);
|
ui_->main->set_permission_dialog_open(show_request_permission_window_);
|
||||||
ui_->main->set_screen_recording_granted(
|
ui_->main->set_screen_recording_granted(
|
||||||
mac_screen_recording_permission_granted_);
|
mac_screen_recording_permission_granted_);
|
||||||
ui_->main->set_accessibility_granted(
|
ui_->main->set_accessibility_granted(mac_accessibility_permission_granted_);
|
||||||
mac_accessibility_permission_granted_);
|
|
||||||
#else
|
#else
|
||||||
ui_->main->set_permission_dialog_open(false);
|
ui_->main->set_permission_dialog_open(false);
|
||||||
#endif
|
#endif
|
||||||
@@ -1999,11 +2039,10 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
}
|
}
|
||||||
ui::StreamTab tab;
|
ui::StreamTab tab;
|
||||||
tab.remote_id = UiText(id);
|
tab.remote_id = UiText(id);
|
||||||
tab.title = UiText(props->remote_host_name_.empty()
|
tab.title = UiText(
|
||||||
? id
|
props->remote_host_name_.empty() ? id : props->remote_host_name_);
|
||||||
: props->remote_host_name_);
|
tab.connected =
|
||||||
tab.connected = props->connection_status_.load() ==
|
props->connection_status_.load() == ConnectionStatus::Connected;
|
||||||
ConnectionStatus::Connected;
|
|
||||||
tabs_by_id.emplace(id, std::move(tab));
|
tabs_by_id.emplace(id, std::move(tab));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2053,12 +2092,14 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
// Once the peer is connected, the frame-waiting hint replaces the
|
// Once the peer is connected, the frame-waiting hint replaces the
|
||||||
// connection status. Keeping these states mutually exclusive also avoids a
|
// connection status. Keeping these states mutually exclusive also avoids a
|
||||||
// transient overlap when both properties are synchronized in one tick.
|
// transient overlap when both properties are synchronized in one tick.
|
||||||
(*ui_->stream)->set_status_text(UiText(
|
(*ui_->stream)
|
||||||
|
->set_status_text(UiText(
|
||||||
status == ConnectionStatus::Connected
|
status == ConnectionStatus::Connected
|
||||||
? std::string{}
|
? std::string{}
|
||||||
: ConnectionStatusText(status, localization_language_index_)));
|
: ConnectionStatusText(status, localization_language_index_)));
|
||||||
(*ui_->stream)->set_receiving_text(
|
(*ui_->stream)
|
||||||
UiText(status == ConnectionStatus::Connected
|
->set_receiving_text(UiText(
|
||||||
|
status == ConnectionStatus::Connected
|
||||||
? localization::receiving_screen[localization_language_index_]
|
? localization::receiving_screen[localization_language_index_]
|
||||||
: std::string{}));
|
: std::string{}));
|
||||||
(*ui_->stream)->set_mouse_control_enabled(props->control_mouse_);
|
(*ui_->stream)->set_mouse_control_enabled(props->control_mouse_);
|
||||||
@@ -2069,8 +2110,7 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
fullscreen_button_pressed_ = (*ui_->stream)->window().is_fullscreen();
|
fullscreen_button_pressed_ = (*ui_->stream)->window().is_fullscreen();
|
||||||
#endif
|
#endif
|
||||||
(*ui_->stream)->set_fullscreen_enabled(fullscreen_button_pressed_);
|
(*ui_->stream)->set_fullscreen_enabled(fullscreen_button_pressed_);
|
||||||
(*ui_->stream)->set_stats_visible(
|
(*ui_->stream)->set_stats_visible(props->net_traffic_stats_button_pressed_);
|
||||||
props->net_traffic_stats_button_pressed_);
|
|
||||||
|
|
||||||
std::vector<slint::SharedString> displays;
|
std::vector<slint::SharedString> displays;
|
||||||
displays.reserve(props->display_info_list_.size());
|
displays.reserve(props->display_info_list_.size());
|
||||||
@@ -2084,8 +2124,7 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
std::vector<ui::NetworkStatsRow> stats_rows;
|
std::vector<ui::NetworkStatsRow> stats_rows;
|
||||||
stats_rows.reserve(4);
|
stats_rows.reserve(4);
|
||||||
const auto append_stats_row = [&](const std::string& label,
|
const auto append_stats_row = [&](const std::string& label,
|
||||||
const auto &inbound,
|
const auto& inbound, const auto& outbound) {
|
||||||
const auto &outbound) {
|
|
||||||
ui::NetworkStatsRow row;
|
ui::NetworkStatsRow row;
|
||||||
row.label = UiText(label);
|
row.label = UiText(label);
|
||||||
row.inbound = UiText(FormatBitrate(inbound.bitrate));
|
row.inbound = UiText(FormatBitrate(inbound.bitrate));
|
||||||
@@ -2103,13 +2142,16 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
net.total_inbound_stats, net.total_outbound_stats);
|
net.total_inbound_stats, net.total_outbound_stats);
|
||||||
ui_->stats_model->set_vector(std::move(stats_rows));
|
ui_->stats_model->set_vector(std::move(stats_rows));
|
||||||
(*ui_->stream)->set_stats_fps(UiText(std::to_string(props->fps_)));
|
(*ui_->stream)->set_stats_fps(UiText(std::to_string(props->fps_)));
|
||||||
(*ui_->stream)->set_stats_resolution(UiText(
|
(*ui_->stream)
|
||||||
std::to_string(props->video_width_) + "x" +
|
->set_stats_resolution(UiText(std::to_string(props->video_width_) + "x" +
|
||||||
std::to_string(props->video_height_)));
|
std::to_string(props->video_height_)));
|
||||||
(*ui_->stream)->set_stats_connection_mode(UiText(
|
(*ui_->stream)
|
||||||
props->traversal_mode_ == TraversalMode::P2P
|
->set_stats_connection_mode(
|
||||||
? localization::connection_mode_direct[localization_language_index_]
|
UiText(props->traversal_mode_ == TraversalMode::P2P
|
||||||
: localization::connection_mode_relay[localization_language_index_]));
|
? localization::connection_mode_direct
|
||||||
|
[localization_language_index_]
|
||||||
|
: localization::connection_mode_relay
|
||||||
|
[localization_language_index_]));
|
||||||
|
|
||||||
std::vector<RemoteSession::FileTransferInfo> file_list;
|
std::vector<RemoteSession::FileTransferInfo> file_list;
|
||||||
{
|
{
|
||||||
@@ -2146,15 +2188,14 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
if (status_index == 1)
|
if (status_index == 1)
|
||||||
item.status = UiText(localization::sending[localization_language_index_]);
|
item.status = UiText(localization::sending[localization_language_index_]);
|
||||||
if (status_index == 2)
|
if (status_index == 2)
|
||||||
item.status = UiText(localization::completed[localization_language_index_]);
|
item.status =
|
||||||
|
UiText(localization::completed[localization_language_index_]);
|
||||||
if (status_index == 3)
|
if (status_index == 3)
|
||||||
item.status = UiText(localization::failed[localization_language_index_]);
|
item.status = UiText(localization::failed[localization_language_index_]);
|
||||||
item.progress = status_index == 2
|
item.progress = status_index == 2 ? 1.0f
|
||||||
? 1.0f
|
|
||||||
: info.file_size == 0
|
: info.file_size == 0
|
||||||
? 0.0f
|
? 0.0f
|
||||||
: std::clamp(
|
: std::clamp(static_cast<float>(info.sent_bytes) /
|
||||||
static_cast<float>(info.sent_bytes) /
|
|
||||||
static_cast<float>(info.file_size),
|
static_cast<float>(info.file_size),
|
||||||
0.0f, 1.0f);
|
0.0f, 1.0f);
|
||||||
item.speed = UiText(FormatTransferRate(info.rate_bps));
|
item.speed = UiText(FormatTransferRate(info.rate_bps));
|
||||||
@@ -2162,7 +2203,8 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
transfers.push_back(std::move(item));
|
transfers.push_back(std::move(item));
|
||||||
}
|
}
|
||||||
ui_->transfer_model->set_vector(std::move(transfers));
|
ui_->transfer_model->set_vector(std::move(transfers));
|
||||||
(*ui_->stream)->set_file_transfer_visible(
|
(*ui_->stream)
|
||||||
|
->set_file_transfer_visible(
|
||||||
props->file_transfer_.file_transfer_window_visible_);
|
props->file_transfer_.file_transfer_window_visible_);
|
||||||
|
|
||||||
std::shared_ptr<std::vector<unsigned char>> frame;
|
std::shared_ptr<std::vector<unsigned char>> frame;
|
||||||
@@ -2181,9 +2223,9 @@ void GuiApplication::SyncStreamWindow() {
|
|||||||
ui_->displayed_frame_sequence[props->remote_id_] != sequence) {
|
ui_->displayed_frame_sequence[props->remote_id_] != sequence) {
|
||||||
slint::SharedPixelBuffer<slint::Rgb8Pixel> pixels(width, height);
|
slint::SharedPixelBuffer<slint::Rgb8Pixel> pixels(width, height);
|
||||||
const int result = libyuv::NV12ToRAW(
|
const int result = libyuv::NV12ToRAW(
|
||||||
frame->data(), width, frame->data() + static_cast<size_t>(width) * height,
|
frame->data(), width,
|
||||||
width, reinterpret_cast<uint8_t *>(pixels.begin()), width * 3, width,
|
frame->data() + static_cast<size_t>(width) * height, width,
|
||||||
height);
|
reinterpret_cast<uint8_t*>(pixels.begin()), width * 3, width, height);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
(*ui_->stream)->set_frame(slint::Image(std::move(pixels)));
|
(*ui_->stream)->set_frame(slint::Image(std::move(pixels)));
|
||||||
(*ui_->stream)->set_has_frame(true);
|
(*ui_->stream)->set_has_frame(true);
|
||||||
@@ -2203,7 +2245,9 @@ void GuiApplication::SyncServerWindow() {
|
|||||||
(*ui_->server)->set_controller_names(ui_->controller_name_model);
|
(*ui_->server)->set_controller_names(ui_->controller_name_model);
|
||||||
(*ui_->server)->set_language_index(localization_language_index_);
|
(*ui_->server)->set_language_index(localization_language_index_);
|
||||||
server_window_collapsed_ = false;
|
server_window_collapsed_ = false;
|
||||||
(*ui_->server)->window().set_size(slint::LogicalSize(slint::Size<float>{
|
(*ui_->server)
|
||||||
|
->window()
|
||||||
|
.set_size(slint::LogicalSize(slint::Size<float>{
|
||||||
ServerWindowLogicalWidth(localization_language_index_),
|
ServerWindowLogicalWidth(localization_language_index_),
|
||||||
ServerWindowLogicalHeight(false)}));
|
ServerWindowLogicalHeight(false)}));
|
||||||
BindServerCallbacks();
|
BindServerCallbacks();
|
||||||
@@ -2267,7 +2311,9 @@ void GuiApplication::SyncServerWindow() {
|
|||||||
ServerWindowLogicalWidth(localization_language_index_);
|
ServerWindowLogicalWidth(localization_language_index_);
|
||||||
const float server_height =
|
const float server_height =
|
||||||
ServerWindowLogicalHeight(server_window_collapsed_);
|
ServerWindowLogicalHeight(server_window_collapsed_);
|
||||||
(*ui_->server)->window().set_size(slint::LogicalSize(
|
(*ui_->server)
|
||||||
|
->window()
|
||||||
|
.set_size(slint::LogicalSize(
|
||||||
slint::Size<float>{server_width, server_height}));
|
slint::Size<float>{server_width, server_height}));
|
||||||
if (PositionWindowAtBottomRight((*ui_->server)->window(), server_width,
|
if (PositionWindowAtBottomRight((*ui_->server)->window(), server_width,
|
||||||
server_height)) {
|
server_height)) {
|
||||||
@@ -2276,13 +2322,17 @@ void GuiApplication::SyncServerWindow() {
|
|||||||
ui_->server_initial_position_attempts = 0;
|
ui_->server_initial_position_attempts = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*ui_->server)->set_controller_label(
|
(*ui_->server)
|
||||||
|
->set_controller_label(
|
||||||
UiText(localization::controller[localization_language_index_]));
|
UiText(localization::controller[localization_language_index_]));
|
||||||
(*ui_->server)->set_connection_label(
|
(*ui_->server)
|
||||||
UiText(localization::connection_status[localization_language_index_]));
|
->set_connection_label(UiText(
|
||||||
(*ui_->server)->set_file_transfer_label(
|
localization::connection_status[localization_language_index_]));
|
||||||
|
(*ui_->server)
|
||||||
|
->set_file_transfer_label(
|
||||||
UiText(localization::file_transfer[localization_language_index_]));
|
UiText(localization::file_transfer[localization_language_index_]));
|
||||||
(*ui_->server)->set_select_file_label(
|
(*ui_->server)
|
||||||
|
->set_select_file_label(
|
||||||
UiText(localization::select_file[localization_language_index_]));
|
UiText(localization::select_file[localization_language_index_]));
|
||||||
|
|
||||||
ConnectionStatus status = ConnectionStatus::Closed;
|
ConnectionStatus status = ConnectionStatus::Closed;
|
||||||
@@ -2293,21 +2343,23 @@ void GuiApplication::SyncServerWindow() {
|
|||||||
status = found->second;
|
status = found->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*ui_->server)->set_connection_status(UiText(
|
(*ui_->server)
|
||||||
ConnectionStatusText(status, localization_language_index_)));
|
->set_connection_status(
|
||||||
|
UiText(ConnectionStatusText(status, localization_language_index_)));
|
||||||
|
|
||||||
auto& transfer = transfers_.global_state();
|
auto& transfer = transfers_.global_state();
|
||||||
const auto sent = transfer.file_sent_bytes_.load();
|
const auto sent = transfer.file_sent_bytes_.load();
|
||||||
const auto total = transfer.file_total_bytes_.load();
|
const auto total = transfer.file_total_bytes_.load();
|
||||||
(*ui_->server)->set_file_transfer_visible(
|
(*ui_->server)
|
||||||
transfer.file_transfer_window_visible_);
|
->set_file_transfer_visible(transfer.file_transfer_window_visible_);
|
||||||
(*ui_->server)->set_sending_file(transfer.file_sending_.load());
|
(*ui_->server)->set_sending_file(transfer.file_sending_.load());
|
||||||
(*ui_->server)->set_file_progress(
|
(*ui_->server)
|
||||||
total == 0 ? 0.0f
|
->set_file_progress(total == 0 ? 0.0f
|
||||||
: std::clamp(static_cast<float>(sent) /
|
: std::clamp(static_cast<float>(sent) /
|
||||||
static_cast<float>(total),
|
static_cast<float>(total),
|
||||||
0.0f, 1.0f));
|
0.0f, 1.0f));
|
||||||
(*ui_->server)->set_file_progress_text(
|
(*ui_->server)
|
||||||
|
->set_file_progress_text(
|
||||||
UiText(FormatTransferRate(transfer.file_send_rate_bps_.load())));
|
UiText(FormatTransferRate(transfer.file_send_rate_bps_.load())));
|
||||||
std::string current_file_name;
|
std::string current_file_name;
|
||||||
const uint32_t current_file_id = transfer.current_file_id_.load();
|
const uint32_t current_file_id = transfer.current_file_id_.load();
|
||||||
@@ -2321,22 +2373,21 @@ void GuiApplication::SyncServerWindow() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*ui_->server)->set_current_file_name(UiText(current_file_name));
|
(*ui_->server)->set_current_file_name(UiText(current_file_name));
|
||||||
(*ui_->server)->set_file_size_text(
|
(*ui_->server)
|
||||||
total == 0 ? slint::SharedString{}
|
->set_file_size_text(total == 0 ? slint::SharedString{}
|
||||||
: UiText(FormatFileSize(sent) + " / " +
|
: UiText(FormatFileSize(sent) + " / " +
|
||||||
FormatFileSize(total)));
|
FormatFileSize(total)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiApplication::SaveSettingsFromUi() {
|
void GuiApplication::SaveSettingsFromUi() {
|
||||||
auto& main = ui_->main;
|
auto& main = ui_->main;
|
||||||
language_button_value_ = localization::detail::ClampLanguageIndex(
|
language_button_value_ =
|
||||||
main->get_language_index());
|
localization::detail::ClampLanguageIndex(main->get_language_index());
|
||||||
video_quality_button_value_ =
|
video_quality_button_value_ =
|
||||||
std::clamp(main->get_video_quality_index(), 0, 2);
|
std::clamp(main->get_video_quality_index(), 0, 2);
|
||||||
video_frame_rate_button_value_ =
|
video_frame_rate_button_value_ =
|
||||||
std::clamp(main->get_frame_rate_index(), 0, 1);
|
std::clamp(main->get_frame_rate_index(), 0, 1);
|
||||||
video_encode_format_button_value_ =
|
video_encode_format_button_value_ = std::clamp(main->get_codec_index(), 0, 1);
|
||||||
std::clamp(main->get_codec_index(), 0, 1);
|
|
||||||
enable_hardware_video_codec_ = main->get_hardware_codec_enabled();
|
enable_hardware_video_codec_ = main->get_hardware_codec_enabled();
|
||||||
enable_turn_ = main->get_turn_enabled();
|
enable_turn_ = main->get_turn_enabled();
|
||||||
enable_srtp_ = main->get_srtp_enabled();
|
enable_srtp_ = main->get_srtp_enabled();
|
||||||
@@ -2385,8 +2436,7 @@ void GuiApplication::SaveSettingsFromUi() {
|
|||||||
language_button_value_last_ = language_button_value_;
|
language_button_value_last_ = language_button_value_;
|
||||||
video_quality_button_value_last_ = video_quality_button_value_;
|
video_quality_button_value_last_ = video_quality_button_value_;
|
||||||
video_frame_rate_button_value_last_ = video_frame_rate_button_value_;
|
video_frame_rate_button_value_last_ = video_frame_rate_button_value_;
|
||||||
video_encode_format_button_value_last_ =
|
video_encode_format_button_value_last_ = video_encode_format_button_value_;
|
||||||
video_encode_format_button_value_;
|
|
||||||
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
|
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
|
||||||
enable_turn_last_ = enable_turn_;
|
enable_turn_last_ = enable_turn_;
|
||||||
enable_srtp_last_ = enable_srtp_;
|
enable_srtp_last_ = enable_srtp_;
|
||||||
@@ -2439,15 +2489,14 @@ void GuiApplication::SelectStreamTab(int index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiApplication::ReorderStreamTab(int from, float drop_x,
|
void GuiApplication::ReorderStreamTab(int from, float drop_x, float tab_width) {
|
||||||
float tab_width) {
|
if (!ui_ || from < 0 || from >= static_cast<int>(ui_->tab_order.size()) ||
|
||||||
if (!ui_ || from < 0 ||
|
tab_width <= 0.0f) {
|
||||||
from >= static_cast<int>(ui_->tab_order.size()) || tab_width <= 0.0f) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const float slot_width = tab_width + 1.0f;
|
const float slot_width = tab_width + 1.0f;
|
||||||
const int target = std::clamp(static_cast<int>(std::floor(drop_x / slot_width)),
|
const int target =
|
||||||
0,
|
std::clamp(static_cast<int>(std::floor(drop_x / slot_width)), 0,
|
||||||
static_cast<int>(ui_->tab_order.size()) - 1);
|
static_cast<int>(ui_->tab_order.size()) - 1);
|
||||||
if (target == from) {
|
if (target == from) {
|
||||||
return;
|
return;
|
||||||
@@ -2513,8 +2562,7 @@ void GuiApplication::SendPointerInput(int button, int kind, float x, float y) {
|
|||||||
const float scale = (*ui_->stream)->window().scale_factor();
|
const float scale = (*ui_->stream)->window().scale_factor();
|
||||||
const float available_width = size.width / scale;
|
const float available_width = size.width / scale;
|
||||||
const float available_height =
|
const float available_height =
|
||||||
size.height / scale -
|
size.height / scale - (fullscreen_button_pressed_
|
||||||
(fullscreen_button_pressed_
|
|
||||||
? 0.0f
|
? 0.0f
|
||||||
: (ui_->tab_ids.size() > 1 ? 30.0f : 0.0f));
|
: (ui_->tab_ids.size() > 1 ? 30.0f : 0.0f));
|
||||||
float render_width = available_width;
|
float render_width = available_width;
|
||||||
@@ -2548,15 +2596,23 @@ void GuiApplication::SendPointerInput(int button, int kind, float x, float y) {
|
|||||||
if (kind == 3) {
|
if (kind == 3) {
|
||||||
action.m.flag = MouseFlag::move;
|
action.m.flag = MouseFlag::move;
|
||||||
} else if (kind == 1) {
|
} else if (kind == 1) {
|
||||||
if (button == 1) action.m.flag = MouseFlag::left_down;
|
if (button == 1)
|
||||||
else if (button == 2) action.m.flag = MouseFlag::right_down;
|
action.m.flag = MouseFlag::left_down;
|
||||||
else if (button == 3) action.m.flag = MouseFlag::middle_down;
|
else if (button == 2)
|
||||||
else return;
|
action.m.flag = MouseFlag::right_down;
|
||||||
|
else if (button == 3)
|
||||||
|
action.m.flag = MouseFlag::middle_down;
|
||||||
|
else
|
||||||
|
return;
|
||||||
} else if (kind == 2) {
|
} else if (kind == 2) {
|
||||||
if (button == 1) action.m.flag = MouseFlag::left_up;
|
if (button == 1)
|
||||||
else if (button == 2) action.m.flag = MouseFlag::right_up;
|
action.m.flag = MouseFlag::left_up;
|
||||||
else if (button == 3) action.m.flag = MouseFlag::middle_up;
|
else if (button == 2)
|
||||||
else return;
|
action.m.flag = MouseFlag::right_up;
|
||||||
|
else if (button == 3)
|
||||||
|
action.m.flag = MouseFlag::middle_up;
|
||||||
|
else
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2577,8 +2633,7 @@ void GuiApplication::SendScrollInput(float delta_x, float delta_y, float x,
|
|||||||
const float scale = (*ui_->stream)->window().scale_factor();
|
const float scale = (*ui_->stream)->window().scale_factor();
|
||||||
const float width = size.width / scale;
|
const float width = size.width / scale;
|
||||||
const float height =
|
const float height =
|
||||||
size.height / scale -
|
size.height / scale - (fullscreen_button_pressed_
|
||||||
(fullscreen_button_pressed_
|
|
||||||
? 0.0f
|
? 0.0f
|
||||||
: (ui_->tab_ids.size() > 1 ? 30.0f : 0.0f));
|
: (ui_->tab_ids.size() > 1 ? 30.0f : 0.0f));
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ struct TranslationRow {
|
|||||||
X(notification, u8"通知", "Notification", u8"Уведомление") \
|
X(notification, u8"通知", "Notification", u8"Уведомление") \
|
||||||
X(new_version_available, u8"新版本可用", "New Version Available", \
|
X(new_version_available, u8"新版本可用", "New Version Available", \
|
||||||
u8"Доступна новая версия") \
|
u8"Доступна новая версия") \
|
||||||
|
X(release_notes, u8"更新内容", "Release Notes", u8"Содержание обновления") \
|
||||||
X(version, u8"版本", "Version", u8"Версия") \
|
X(version, u8"版本", "Version", u8"Версия") \
|
||||||
X(release_date, u8"发布日期: ", "Release Date: ", u8"Дата релиза: ") \
|
X(release_date, u8"发布日期: ", "Release Date: ", u8"Дата релиза: ") \
|
||||||
X(access_website, u8"访问官网: ", \
|
X(access_website, u8"访问官网: ", \
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ export struct RecentConnection {
|
|||||||
thumbnail: image,
|
thumbnail: image,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export struct ReleaseNoteBlock {
|
||||||
|
content: styled-text,
|
||||||
|
section-gap: bool,
|
||||||
|
}
|
||||||
|
|
||||||
export global UiStrings {
|
export global UiStrings {
|
||||||
in-out property <string> local-desktop: "Local Desktop";
|
in-out property <string> local-desktop: "Local Desktop";
|
||||||
in-out property <string> remote-desktop: "Remote Desktop";
|
in-out property <string> remote-desktop: "Remote Desktop";
|
||||||
@@ -48,8 +53,8 @@ export global UiStrings {
|
|||||||
in-out property <string> server-port: "Signal port";
|
in-out property <string> server-port: "Signal port";
|
||||||
in-out property <string> coturn-port: "TURN port";
|
in-out property <string> coturn-port: "TURN port";
|
||||||
in-out property <string> version: "Version";
|
in-out property <string> version: "Version";
|
||||||
in-out property <string> copyright: "Copyright © CrossDesk contributors";
|
in-out property <string> copyright: "© 2026 by JUNKUN DI. All right reserved.";
|
||||||
in-out property <string> license: "Licensed under GPL-3.0-only";
|
in-out property <string> license: "Licensed under GNU GPL v3.";
|
||||||
in-out property <string> signal-connected: "Signal server connected";
|
in-out property <string> signal-connected: "Signal server connected";
|
||||||
in-out property <string> signal-disconnected: "Signal server disconnected";
|
in-out property <string> signal-disconnected: "Signal server disconnected";
|
||||||
in-out property <string> tls-error: "Signal server TLS certificate error";
|
in-out property <string> tls-error: "Signal server TLS certificate error";
|
||||||
@@ -199,7 +204,7 @@ export component MainWindow inherits Window {
|
|||||||
in property <string> current-version: "";
|
in property <string> current-version: "";
|
||||||
in property <string> latest-version: "";
|
in property <string> latest-version: "";
|
||||||
in property <string> release-name: "";
|
in property <string> release-name: "";
|
||||||
in property <string> release-notes: "";
|
in property <[ReleaseNoteBlock]> release-note-blocks;
|
||||||
in property <string> release-date: "";
|
in property <string> release-date: "";
|
||||||
in-out property <string> offline-warning: "";
|
in-out property <string> offline-warning: "";
|
||||||
in property <bool> connection-dialog-open: false;
|
in property <bool> connection-dialog-open: false;
|
||||||
@@ -1341,19 +1346,19 @@ export component MainWindow inherits Window {
|
|||||||
Rectangle { y: parent.height - 7px; height: 7px; background: #d3d3d3; }
|
Rectangle { y: parent.height - 7px; height: 7px; background: #d3d3d3; }
|
||||||
Text { x: 6px; text: UiStrings.about; font-size: ImGuiFontStyle.base; font-weight: 600; color: #202124; vertical-alignment: center; }
|
Text { x: 6px; text: UiStrings.about; font-size: ImGuiFontStyle.base; font-weight: 600; color: #202124; vertical-alignment: center; }
|
||||||
}
|
}
|
||||||
Text { x: 30px; y: 29px; width: 240px; height: 20px; text: UiStrings.version + ": CrossDesk " + root.current-version; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
Text { x: 30px; y: 40px; width: 240px; height: 20px; text: UiStrings.version + ": CrossDesk " + root.current-version; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
||||||
if root.update-available: Text { x: 30px; y: 51px; width: 240px; height: 20px; text: UiStrings.update-available + ":"; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
if root.update-available: Text { x: 30px; y: 60px; width: 240px; height: 20px; text: UiStrings.update-available + ":"; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
||||||
if root.update-available: Rectangle {
|
if root.update-available: Rectangle {
|
||||||
x: 30px;
|
x: 30px;
|
||||||
y: 70px;
|
y: 60px;
|
||||||
width: 240px;
|
width: 240px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
version-link := Text { text: root.latest-version; color: version-touch.has-hover ? #174ea6 : #334fd1; font-size: ImGuiFontStyle.body; horizontal-alignment: center; vertical-alignment: center; }
|
version-link := Text { text: root.latest-version; color: version-touch.has-hover ? #174ea6 : #334fd1; font-size: ImGuiFontStyle.body; horizontal-alignment: center; vertical-alignment: center; }
|
||||||
version-touch := TouchArea { mouse-cursor: pointer; clicked => { root.open-download(); } }
|
version-touch := TouchArea { mouse-cursor: pointer; clicked => { root.open-download(); } }
|
||||||
}
|
}
|
||||||
Text { x: 30px; y: 96px; width: 240px; height: 20px; text: UiStrings.copyright; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
Text { x: 30px; y: 85px; width: 240px; height: 20px; text: UiStrings.copyright; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
||||||
Text { x: 30px; y: 118px; width: 240px; height: 20px; text: UiStrings.license; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
Text { x: 30px; y: 105px; width: 240px; height: 20px; text: UiStrings.license; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
||||||
CompactButton { x: 133px; y: 157px; primary: true; text: UiStrings.ok; clicked => { root.about-open = false; } }
|
CompactButton { x: 133px; y: 145px; primary: true; text: UiStrings.ok; clicked => { root.about-open = false; } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1364,24 +1369,43 @@ export component MainWindow inherits Window {
|
|||||||
TouchArea { clicked => { focus-sink.focus(); } }
|
TouchArea { clicked => { focus-sink.focus(); } }
|
||||||
DialogSurface {
|
DialogSurface {
|
||||||
x: 120px;
|
x: 120px;
|
||||||
y: 65px;
|
y: 52px;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 320px;
|
height: 346px;
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
Text { x: 40px; y: 22px; width: 320px; height: 24px; text: UiStrings.update-available + ": " + root.latest-version; color: #202124; font-size: ImGuiFontStyle.prominent; vertical-alignment: center; }
|
Text { x: 30px; y: 22px; width: 320px; height: 24px; text: UiStrings.update-available + ": " + root.latest-version; color: #202124; font-size: ImGuiFontStyle.prominent; vertical-alignment: center; }
|
||||||
Rectangle {
|
Rectangle {
|
||||||
x: 40px;
|
x: 30px;
|
||||||
y: 47px;
|
y: 47px;
|
||||||
width: 320px;
|
width: 320px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
Text { text: UiStrings.access-website + "https://crossdesk.cn"; color: website-touch.has-hover ? #174ea6 : #334fd1; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
Text {
|
||||||
|
x: 0px;
|
||||||
|
width: parent.width;
|
||||||
|
height: parent.height;
|
||||||
|
text: UiStrings.access-website + "https://crossdesk.cn";
|
||||||
|
color: website-touch.has-hover ? #174ea6 : #334fd1;
|
||||||
|
font-size: ImGuiFontStyle.body;
|
||||||
|
horizontal-alignment: left;
|
||||||
|
vertical-alignment: center;
|
||||||
|
}
|
||||||
website-touch := TouchArea { mouse-cursor: pointer; clicked => { root.open-download(); } }
|
website-touch := TouchArea { mouse-cursor: pointer; clicked => { root.open-download(); } }
|
||||||
}
|
}
|
||||||
|
Text {
|
||||||
|
x: 30px;
|
||||||
|
y: 75px;
|
||||||
|
width: 320px;
|
||||||
|
height: 20px;
|
||||||
|
text: UiStrings.release-notes;
|
||||||
|
color: #202124;
|
||||||
|
font-size: 12px;
|
||||||
|
vertical-alignment: center;
|
||||||
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
x: 20px;
|
x: 30px;
|
||||||
y: 74px;
|
y: 105px;
|
||||||
width: 360px;
|
width: 360px;
|
||||||
height: 200px;
|
height: 190px;
|
||||||
background: white;
|
background: white;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
border-color: #b9bdc3;
|
border-color: #b9bdc3;
|
||||||
@@ -1392,19 +1416,24 @@ export component MainWindow inherits Window {
|
|||||||
height: parent.height - 2px;
|
height: parent.height - 2px;
|
||||||
VerticalLayout {
|
VerticalLayout {
|
||||||
width: 340px;
|
width: 340px;
|
||||||
padding-left: 19px;
|
padding-left: 8px;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
spacing: 7px;
|
spacing: 7px;
|
||||||
if root.release-name != "": Text { width: 312px; text: root.release-name; color: #202124; font-size: ImGuiFontStyle.body; wrap: word-wrap; }
|
VerticalLayout {
|
||||||
Text { width: 312px; text: root.release-notes; color: #202124; font-size: ImGuiFontStyle.body; wrap: word-wrap; }
|
width: 312px;
|
||||||
if root.release-date != "": Text { width: 312px; text: UiStrings.release-date-label + root.release-date; color: #4f555e; font-size: ImGuiFontStyle.small; wrap: word-wrap; }
|
spacing: 10px;
|
||||||
|
for block in root.release-note-blocks: VerticalLayout {
|
||||||
|
padding-top: block.section-gap ? 10px : 0px;
|
||||||
|
StyledText { width: 312px; text: block.content; default-color: #202124; default-font-size: ImGuiFontStyle.body; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CompactButton { x: root.compact-language ? 163px : 130px; y: 283px; width: root.compact-language ? 34px : 70px; primary: true; text: UiStrings.download; clicked => { root.open-download(); root.update-open = false; } }
|
}
|
||||||
CompactButton { x: root.compact-language ? 204px : 207px; y: 283px; width: root.compact-language ? 34px : 56px; text: UiStrings.later; clicked => { root.update-open = false; } }
|
}
|
||||||
|
CompactButton { x: root.compact-language ? 163px : 130px; y: 309px; width: root.compact-language ? 34px : 70px; primary: true; text: UiStrings.download; clicked => { root.open-download(); root.update-open = false; } }
|
||||||
|
CompactButton { x: root.compact-language ? 204px : 207px; y: 309px; width: root.compact-language ? 34px : 56px; text: UiStrings.later; clicked => { root.update-open = false; } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "application/gui_application.h"
|
#include "application/gui_application.h"
|
||||||
@@ -8,48 +7,6 @@
|
|||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
std::string CleanMarkdown(const std::string &markdown) {
|
|
||||||
std::string result = markdown;
|
|
||||||
|
|
||||||
// remove # title mark
|
|
||||||
size_t pos = 0;
|
|
||||||
while (pos < result.length()) {
|
|
||||||
if (result[pos] == '\n' || pos == 0) {
|
|
||||||
size_t line_start = (result[pos] == '\n') ? pos + 1 : pos;
|
|
||||||
if (line_start < result.length() && result[line_start] == '#') {
|
|
||||||
size_t hash_end = line_start;
|
|
||||||
while (hash_end < result.length() &&
|
|
||||||
(result[hash_end] == '#' || result[hash_end] == ' ')) {
|
|
||||||
hash_end++;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.erase(line_start, hash_end - line_start);
|
|
||||||
pos = line_start;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove ** bold mark
|
|
||||||
pos = 0;
|
|
||||||
while ((pos = result.find("**", pos)) != std::string::npos) {
|
|
||||||
result.erase(pos, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove all spaces
|
|
||||||
result.erase(std::remove(result.begin(), result.end(), ' '), result.end());
|
|
||||||
|
|
||||||
// replace . with 、
|
|
||||||
pos = 0;
|
|
||||||
while ((pos = result.find('.', pos)) != std::string::npos) {
|
|
||||||
result.replace(pos, 1, "、");
|
|
||||||
pos += 1; // Move to next position after the replacement
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GuiApplication::UpdateNotificationWindow() {
|
int GuiApplication::UpdateNotificationWindow() {
|
||||||
if (show_update_notification_window_ && update_available_) {
|
if (show_update_notification_window_ && update_available_) {
|
||||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||||
@@ -142,8 +99,7 @@ int GuiApplication::UpdateNotificationWindow() {
|
|||||||
// release notes
|
// release notes
|
||||||
if (!release_notes_.empty()) {
|
if (!release_notes_.empty()) {
|
||||||
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
|
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
|
||||||
std::string cleaned_notes = CleanMarkdown(release_notes_);
|
ImGui::TextWrapped("%s", release_notes_.c_str());
|
||||||
ImGui::TextWrapped("%s", cleaned_notes.c_str());
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ void ScaleNv12ToABGR(char* src, int src_w, int src_h, int dst_w, int dst_h,
|
|||||||
v_fit.data(), (fit_w + 1) / 2, abgr.data(), fit_w * 4,
|
v_fit.data(), (fit_w + 1) / 2, abgr.data(), fit_w * 4,
|
||||||
fit_w, fit_h);
|
fit_w, fit_h);
|
||||||
|
|
||||||
memset(dst_rgba, 0, dst_w * dst_h * 4);
|
std::memset(dst_rgba, 0, dst_w * dst_h * 4);
|
||||||
for (int i = 0; i < dst_w * dst_h; ++i) {
|
for (int i = 0; i < dst_w * dst_h; ++i) {
|
||||||
dst_rgba[i * 4 + 3] = static_cast<char>(0xFF);
|
dst_rgba[i * 4 + 3] = static_cast<char>(0xFF);
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,8 @@ void ScaleNv12ToABGR(char* src, int src_w, int src_h, int dst_w, int dst_h,
|
|||||||
for (int row = 0; row < fit_h; ++row) {
|
for (int row = 0; row < fit_h; ++row) {
|
||||||
int dst_offset =
|
int dst_offset =
|
||||||
((row + (dst_h - fit_h) / 2) * dst_w + (dst_w - fit_w) / 2) * 4;
|
((row + (dst_h - fit_h) / 2) * dst_w + (dst_w - fit_w) / 2) * 4;
|
||||||
memcpy(dst_rgba + dst_offset, abgr.data() + row * fit_w * 4, fit_w * 4);
|
std::memcpy(dst_rgba + dst_offset, abgr.data() + row * fit_w * 4,
|
||||||
|
fit_w * 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +86,8 @@ Thumbnail::Thumbnail(std::string save_path, unsigned char* aes128_key,
|
|||||||
save_path_ = save_path;
|
save_path_ = save_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(aes128_key_, aes128_key, sizeof(aes128_key_));
|
std::memcpy(aes128_key_, aes128_key, sizeof(aes128_key_));
|
||||||
memcpy(aes128_iv_, aes128_iv, sizeof(aes128_iv_));
|
std::memcpy(aes128_iv_, aes128_iv, sizeof(aes128_iv_));
|
||||||
std::filesystem::create_directories(save_path_);
|
std::filesystem::create_directories(save_path_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ int Thumbnail::SaveToThumbnail(const char* yuv420p, int width, int height,
|
|||||||
thumbnail_height_, rgba_buffer_);
|
thumbnail_height_, rgba_buffer_);
|
||||||
} else {
|
} else {
|
||||||
// If yuv420p is null, fill the buffer with black pixels
|
// If yuv420p is null, fill the buffer with black pixels
|
||||||
memset(rgba_buffer_, 0x00, thumbnail_width_ * thumbnail_height_ * 4);
|
std::memset(rgba_buffer_, 0x00, thumbnail_width_ * thumbnail_height_ * 4);
|
||||||
for (int i = 0; i < thumbnail_width_ * thumbnail_height_; ++i) {
|
for (int i = 0; i < thumbnail_width_ * thumbnail_height_; ++i) {
|
||||||
// Set alpha channel to opaque
|
// Set alpha channel to opaque
|
||||||
rgba_buffer_[i * 4 + 3] = static_cast<char>(0xFF);
|
rgba_buffer_[i * 4 + 3] = static_cast<char>(0xFF);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#ifndef _THUMBNAIL_H_
|
#ifndef _THUMBNAIL_H_
|
||||||
#define _THUMBNAIL_H_
|
#define _THUMBNAIL_H_
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -52,18 +53,18 @@ class Thumbnail {
|
|||||||
int DeleteAllFilesInDirectory();
|
int DeleteAllFilesInDirectory();
|
||||||
|
|
||||||
int GetKey(unsigned char* aes128_key) {
|
int GetKey(unsigned char* aes128_key) {
|
||||||
memcpy(aes128_key, aes128_key_, sizeof(aes128_key_));
|
std::memcpy(aes128_key, aes128_key_, sizeof(aes128_key_));
|
||||||
return sizeof(aes128_key_);
|
return sizeof(aes128_key_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetIv(unsigned char* aes128_iv) {
|
int GetIv(unsigned char* aes128_iv) {
|
||||||
memcpy(aes128_iv, aes128_iv_, sizeof(aes128_iv_));
|
std::memcpy(aes128_iv, aes128_iv_, sizeof(aes128_iv_));
|
||||||
return sizeof(aes128_iv_);
|
return sizeof(aes128_iv_);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GetKeyAndIv(unsigned char* aes128_key, unsigned char* aes128_iv) {
|
int GetKeyAndIv(unsigned char* aes128_key, unsigned char* aes128_iv) {
|
||||||
memcpy(aes128_key, aes128_key_, sizeof(aes128_key_));
|
std::memcpy(aes128_key, aes128_key_, sizeof(aes128_key_));
|
||||||
memcpy(aes128_iv, aes128_iv_, sizeof(aes128_iv_));
|
std::memcpy(aes128_iv, aes128_iv_, sizeof(aes128_iv_));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package("rust")
|
||||||
|
set_kind("toolchain")
|
||||||
|
set_homepage("https://rust-lang.org")
|
||||||
|
set_description("Rust is a general-purpose programming language emphasizing performance, type safety, and concurrency.")
|
||||||
|
|
||||||
|
add_versions("1.92.0", "")
|
||||||
|
|
||||||
|
add_deps("ca-certificates", {host = true, private = true})
|
||||||
|
add_deps("rustup", {host = true, private = true, system = false})
|
||||||
|
|
||||||
|
on_install(function(package)
|
||||||
|
import("core.tools.rustc.target_triple")
|
||||||
|
|
||||||
|
local host_target = assert(target_triple(package:plat(), package:arch()),
|
||||||
|
"failed to determine the Rust host target triple")
|
||||||
|
local version = package:version():shortstr()
|
||||||
|
local toolchain_name = version .. "-" .. host_target
|
||||||
|
local rustup_home = assert(os.getenv("RUSTUP_HOME"), "cannot find rustup home")
|
||||||
|
|
||||||
|
os.vrunv("rustup", {"install", "--no-self-update", toolchain_name})
|
||||||
|
os.vmv(path.join(rustup_home, "toolchains", toolchain_name, "*"), package:installdir())
|
||||||
|
|
||||||
|
-- Keep the Xmake-managed toolchain isolated from rustup after installation.
|
||||||
|
os.vrm(path.join(rustup_home, "toolchains", toolchain_name))
|
||||||
|
os.vrm(path.join(rustup_home, "update-hashes", toolchain_name))
|
||||||
|
|
||||||
|
package:addenv("RC", "bin/rustc" .. (is_host("windows") and ".exe" or ""))
|
||||||
|
package:mark_as_pathenv("RC")
|
||||||
|
end)
|
||||||
|
|
||||||
|
on_test(function(package)
|
||||||
|
os.vrun("cargo --version")
|
||||||
|
os.vrun("rustc --version")
|
||||||
|
end)
|
||||||
|
package_end()
|
||||||
Vendored
+1
@@ -10,6 +10,7 @@ package("slint")
|
|||||||
add_configs("shared", {description = "Build the Slint runtime as a shared library", default = true, type = "boolean", readonly = true})
|
add_configs("shared", {description = "Build the Slint runtime as a shared library", default = true, type = "boolean", readonly = true})
|
||||||
|
|
||||||
add_deps("cmake")
|
add_deps("cmake")
|
||||||
|
add_deps("rust 1.92.0", {host = true, private = true, system = false})
|
||||||
|
|
||||||
on_load(function(package)
|
on_load(function(package)
|
||||||
package:add("includedirs", "include/slint")
|
package:add("includedirs", "include/slint")
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
includes("cpp-httplib", "slint")
|
includes("cpp-httplib", "rust", "slint")
|
||||||
|
|||||||
@@ -240,6 +240,7 @@ function setup_targets()
|
|||||||
"src/gui/features/settings/*.cpp", "src/gui/ui/crossdesk_ui.slint")
|
"src/gui/features/settings/*.cpp", "src/gui/ui/crossdesk_ui.slint")
|
||||||
add_includedirs("src/gui", {public = true})
|
add_includedirs("src/gui", {public = true})
|
||||||
if is_os("windows") then
|
if is_os("windows") then
|
||||||
|
add_cxxflags("/bigobj")
|
||||||
add_files("src/gui/platform/tray/win_tray.cpp")
|
add_files("src/gui/platform/tray/win_tray.cpp")
|
||||||
add_includedirs("src/service/windows", {public = true})
|
add_includedirs("src/service/windows", {public = true})
|
||||||
elseif is_os("macosx") then
|
elseif is_os("macosx") then
|
||||||
|
|||||||
Reference in New Issue
Block a user