mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-07-22 07:08:44 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c20599800 | |||
| f5c4e2ba4a |
@@ -134,17 +134,21 @@ jobs:
|
||||
build-macos:
|
||||
name: Build on macOS
|
||||
runs-on: ${{ matrix.runner }}
|
||||
env:
|
||||
MACOSX_DEPLOYMENT_TARGET: "14.0"
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x64
|
||||
binary_arch: x86_64
|
||||
runner: macos-15-intel
|
||||
cache-key: intel
|
||||
cache-key: intel-macos14
|
||||
out-dir: ./build/macosx/x86_64/release/crossdesk
|
||||
package_script: ./scripts/macosx/pkg_x64.sh
|
||||
- arch: arm64
|
||||
binary_arch: arm64
|
||||
runner: macos-14
|
||||
cache-key: arm
|
||||
cache-key: arm-macos14
|
||||
out-dir: ./build/macosx/arm64/release/crossdesk
|
||||
package_script: ./scripts/macosx/pkg_arm64.sh
|
||||
|
||||
@@ -211,7 +215,7 @@ jobs:
|
||||
|
||||
- name: Build CrossDesk
|
||||
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
|
||||
|
||||
- name: Package CrossDesk app
|
||||
@@ -219,6 +223,22 @@ jobs:
|
||||
chmod +x ${{ matrix.package_script }}
|
||||
${{ 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
|
||||
uses: actions/upload-artifact@v6
|
||||
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
|
||||
set -e
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="crossdesk"
|
||||
APP_NAME_UPPER="CrossDesk"
|
||||
EXECUTABLE_PATH="./build/macosx/arm64/release/crossdesk"
|
||||
PLATFORM="macos"
|
||||
ARCH="arm64"
|
||||
BINARY_ARCH="arm64"
|
||||
IDENTIFIER="cn.crossdesk.app"
|
||||
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() {
|
||||
local input="$1"
|
||||
@@ -95,6 +97,9 @@ EOF
|
||||
xattr -cr "${APP_BUNDLE}" 2>/dev/null || true
|
||||
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."
|
||||
|
||||
mkdir -p build_pkg_scripts
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME="crossdesk"
|
||||
APP_NAME_UPPER="CrossDesk"
|
||||
EXECUTABLE_PATH="build/macosx/x86_64/release/crossdesk"
|
||||
PLATFORM="macos"
|
||||
ARCH="x64"
|
||||
BINARY_ARCH="x86_64"
|
||||
IDENTIFIER="cn.crossdesk.app"
|
||||
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() {
|
||||
local input="$1"
|
||||
@@ -95,6 +97,9 @@ EOF
|
||||
xattr -cr "${APP_BUNDLE}" 2>/dev/null || true
|
||||
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."
|
||||
|
||||
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 <X11/Xlib.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
#ifndef _KEYBOARD_CAPTURER_H_
|
||||
#define _KEYBOARD_CAPTURER_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
@@ -21,6 +17,7 @@
|
||||
|
||||
struct DBusConnection;
|
||||
struct DBusMessageIter;
|
||||
struct _XDisplay;
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
@@ -48,8 +45,8 @@ class KeyboardCapturer : public DeviceController {
|
||||
append_args);
|
||||
|
||||
private:
|
||||
Display* display_;
|
||||
Window root_;
|
||||
_XDisplay* display_;
|
||||
unsigned long root_;
|
||||
std::atomic<bool> running_;
|
||||
std::thread event_thread_;
|
||||
bool use_wayland_portal_ = false;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "mouse_controller.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "platform.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
#ifndef _MOUSE_CONTROLLER_H_
|
||||
#define _MOUSE_CONTROLLER_H_
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
@@ -20,6 +16,7 @@
|
||||
|
||||
struct DBusConnection;
|
||||
struct DBusMessageIter;
|
||||
struct _XDisplay;
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
@@ -53,8 +50,8 @@ class MouseController : public DeviceController {
|
||||
|
||||
enum class WaylandAbsoluteMode { kUnknown, kPixels, kNormalized, kDisabled };
|
||||
|
||||
Display* display_ = nullptr;
|
||||
Window root_ = 0;
|
||||
_XDisplay* display_ = nullptr;
|
||||
unsigned long root_ = 0;
|
||||
std::vector<DisplayInfo> display_info_list_;
|
||||
int screen_width_ = 0;
|
||||
int screen_height_ = 0;
|
||||
|
||||
@@ -186,6 +186,6 @@ struct ApplicationState : MainWindowState,
|
||||
ServerWindowState,
|
||||
UiState {};
|
||||
|
||||
} // namespace crossdesk::gui_detail
|
||||
} // namespace crossdesk::gui_detail
|
||||
|
||||
#endif // CROSSDESK_GUI_APPLICATION_STATE_H_
|
||||
#endif // CROSSDESK_GUI_APPLICATION_STATE_H_
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -179,6 +179,7 @@ struct TranslationRow {
|
||||
X(notification, u8"通知", "Notification", u8"Уведомление") \
|
||||
X(new_version_available, u8"新版本可用", "New Version Available", \
|
||||
u8"Доступна новая версия") \
|
||||
X(release_notes, u8"更新内容", "Release Notes", u8"Содержание обновления") \
|
||||
X(version, u8"版本", "Version", u8"Версия") \
|
||||
X(release_date, u8"发布日期: ", "Release Date: ", u8"Дата релиза: ") \
|
||||
X(access_website, u8"访问官网: ", \
|
||||
|
||||
@@ -9,6 +9,11 @@ export struct RecentConnection {
|
||||
thumbnail: image,
|
||||
}
|
||||
|
||||
export struct ReleaseNoteBlock {
|
||||
content: styled-text,
|
||||
section-gap: bool,
|
||||
}
|
||||
|
||||
export global UiStrings {
|
||||
in-out property <string> local-desktop: "Local 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> coturn-port: "TURN port";
|
||||
in-out property <string> version: "Version";
|
||||
in-out property <string> copyright: "Copyright © CrossDesk contributors";
|
||||
in-out property <string> license: "Licensed under GPL-3.0-only";
|
||||
in-out property <string> copyright: "© 2026 by JUNKUN DI. All right reserved.";
|
||||
in-out property <string> license: "Licensed under GNU GPL v3.";
|
||||
in-out property <string> signal-connected: "Signal server connected";
|
||||
in-out property <string> signal-disconnected: "Signal server disconnected";
|
||||
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> latest-version: "";
|
||||
in property <string> release-name: "";
|
||||
in property <string> release-notes: "";
|
||||
in property <[ReleaseNoteBlock]> release-note-blocks;
|
||||
in property <string> release-date: "";
|
||||
in-out property <string> offline-warning: "";
|
||||
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; }
|
||||
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; }
|
||||
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; }
|
||||
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: 60px; width: 240px; height: 20px; text: UiStrings.update-available + ":"; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
||||
if root.update-available: Rectangle {
|
||||
x: 30px;
|
||||
y: 70px;
|
||||
y: 60px;
|
||||
width: 240px;
|
||||
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-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: 118px; 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; } }
|
||||
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: 105px; width: 240px; height: 20px; text: UiStrings.license; color: #202124; font-size: ImGuiFontStyle.body; vertical-alignment: center; }
|
||||
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(); } }
|
||||
DialogSurface {
|
||||
x: 120px;
|
||||
y: 65px;
|
||||
y: 52px;
|
||||
width: 400px;
|
||||
height: 320px;
|
||||
height: 346px;
|
||||
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 {
|
||||
x: 40px;
|
||||
x: 30px;
|
||||
y: 47px;
|
||||
width: 320px;
|
||||
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(); } }
|
||||
}
|
||||
Text {
|
||||
x: 30px;
|
||||
y: 75px;
|
||||
width: 320px;
|
||||
height: 20px;
|
||||
text: UiStrings.release-notes;
|
||||
color: #202124;
|
||||
font-size: 12px;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
Rectangle {
|
||||
x: 20px;
|
||||
y: 74px;
|
||||
x: 30px;
|
||||
y: 105px;
|
||||
width: 360px;
|
||||
height: 200px;
|
||||
height: 190px;
|
||||
background: white;
|
||||
border-width: 1px;
|
||||
border-color: #b9bdc3;
|
||||
@@ -1392,19 +1416,24 @@ export component MainWindow inherits Window {
|
||||
height: parent.height - 2px;
|
||||
VerticalLayout {
|
||||
width: 340px;
|
||||
padding-left: 19px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
spacing: 7px;
|
||||
if root.release-name != "": Text { width: 312px; text: root.release-name; color: #202124; font-size: ImGuiFontStyle.body; wrap: word-wrap; }
|
||||
Text { width: 312px; text: root.release-notes; color: #202124; font-size: ImGuiFontStyle.body; wrap: word-wrap; }
|
||||
if root.release-date != "": Text { width: 312px; text: UiStrings.release-date-label + root.release-date; color: #4f555e; font-size: ImGuiFontStyle.small; wrap: word-wrap; }
|
||||
VerticalLayout {
|
||||
width: 312px;
|
||||
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 "application/gui_application.h"
|
||||
@@ -8,51 +7,9 @@
|
||||
|
||||
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() {
|
||||
if (show_update_notification_window_ && update_available_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
|
||||
float update_notification_window_width = title_bar_button_width_ * 10.0f;
|
||||
float update_notification_window_height = title_bar_button_width_ * 8.0f;
|
||||
@@ -142,8 +99,7 @@ int GuiApplication::UpdateNotificationWindow() {
|
||||
// release notes
|
||||
if (!release_notes_.empty()) {
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
|
||||
std::string cleaned_notes = CleanMarkdown(release_notes_);
|
||||
ImGui::TextWrapped("%s", cleaned_notes.c_str());
|
||||
ImGui::TextWrapped("%s", release_notes_.c_str());
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
@@ -204,4 +160,4 @@ int GuiApplication::UpdateNotificationWindow() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
} // namespace crossdesk
|
||||
|
||||
@@ -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,
|
||||
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) {
|
||||
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) {
|
||||
int dst_offset =
|
||||
((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;
|
||||
}
|
||||
|
||||
memcpy(aes128_key_, aes128_key, sizeof(aes128_key_));
|
||||
memcpy(aes128_iv_, aes128_iv, sizeof(aes128_iv_));
|
||||
std::memcpy(aes128_key_, aes128_key, sizeof(aes128_key_));
|
||||
std::memcpy(aes128_iv_, aes128_iv, sizeof(aes128_iv_));
|
||||
std::filesystem::create_directories(save_path_);
|
||||
}
|
||||
|
||||
@@ -116,7 +117,7 @@ int Thumbnail::SaveToThumbnail(const char* yuv420p, int width, int height,
|
||||
thumbnail_height_, rgba_buffer_);
|
||||
} else {
|
||||
// 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) {
|
||||
// Set alpha channel to opaque
|
||||
rgba_buffer_[i * 4 + 3] = static_cast<char>(0xFF);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#ifndef _THUMBNAIL_H_
|
||||
#define _THUMBNAIL_H_
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
@@ -52,18 +53,18 @@ class Thumbnail {
|
||||
int DeleteAllFilesInDirectory();
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
int GetKeyAndIv(unsigned char* aes128_key, unsigned char* aes128_iv) {
|
||||
memcpy(aes128_key, aes128_key_, sizeof(aes128_key_));
|
||||
memcpy(aes128_iv, aes128_iv_, sizeof(aes128_iv_));
|
||||
std::memcpy(aes128_key, aes128_key_, sizeof(aes128_key_));
|
||||
std::memcpy(aes128_iv, aes128_iv_, sizeof(aes128_iv_));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -240,6 +240,7 @@ function setup_targets()
|
||||
"src/gui/features/settings/*.cpp", "src/gui/ui/crossdesk_ui.slint")
|
||||
add_includedirs("src/gui", {public = true})
|
||||
if is_os("windows") then
|
||||
add_cxxflags("/bigobj")
|
||||
add_files("src/gui/platform/tray/win_tray.cpp")
|
||||
add_includedirs("src/service/windows", {public = true})
|
||||
elseif is_os("macosx") then
|
||||
|
||||
Reference in New Issue
Block a user