From 9550731681c0aaf6cd03e948cfd8c48ed13f5923 Mon Sep 17 00:00:00 2001 From: dijunkun Date: Thu, 8 Oct 2026 14:43:58 +0800 Subject: [PATCH] [fix] make Xfce optional with separate Linux deb packages, refs #106 --- .github/scripts/update_version_manifest.py | 2 + .github/workflows/build.yml | 38 ++++++--- README.md | 19 ++++- README_EN.md | 19 ++++- apps/desktop/scripts/linux/package_deb.sh | 79 ++++++++++++------- .../src/gui/application/gui_application.cpp | 6 +- .../linux/headless/headless_session.cpp | 17 +++- .../version_checker/linux_update_download.cpp | 55 +++++++++++++ .../version_checker/linux_update_download.h | 29 +++++++ .../src/version_checker/version_checker.cpp | 17 +++- apps/desktop/xmake/targets.lua | 3 +- docker/linux-build/README.md | 2 +- docs/BUILD.md | 10 ++- docs/BUILD_EN.md | 10 ++- docs/HEADLESS.md | 6 +- docs/HEADLESS_EN.md | 6 +- libs/wire/include/app_version.h | 2 + 17 files changed, 256 insertions(+), 64 deletions(-) create mode 100644 apps/desktop/src/version_checker/linux_update_download.cpp create mode 100644 apps/desktop/src/version_checker/linux_update_download.h diff --git a/.github/scripts/update_version_manifest.py b/.github/scripts/update_version_manifest.py index f24469cc..8550681f 100644 --- a/.github/scripts/update_version_manifest.py +++ b/.github/scripts/update_version_manifest.py @@ -16,6 +16,8 @@ ARTIFACTS = { "macos-arm64": ("macos-arm64", "pkg"), "linux-amd64": ("linux-amd64", "deb"), "linux-arm64": ("linux-arm64", "deb"), + "linux-amd64-virtual-desktop-xfce": ("virtual-desktop-xfce-linux-amd64", "deb"), + "linux-arm64-virtual-desktop-xfce": ("virtual-desktop-xfce-linux-arm64", "deb"), "ios-arm64": ("ios-arm64-unsigned", "zip"), "android-arm64": ("android-arm64(?:-unsigned)?", "apk"), } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a227414e..bdd85516 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -173,23 +173,29 @@ jobs: - name: Verify Ubuntu 20.04 package dependency resolution run: | - PACKAGE_FILE="${GITHUB_WORKSPACE}/crossdesk-linux-${{ matrix.arch }}-${LEGAL_VERSION}.deb" apt-get update - apt-get install --simulate "${PACKAGE_FILE}" + for PACKAGE in crossdesk crossdesk-virtual-desktop-xfce; do + apt-get install --simulate "${GITHUB_WORKSPACE}/${PACKAGE}-linux-${{ matrix.arch }}-${LEGAL_VERSION}.deb" + done - name: Verify packaged Ubuntu 20.04 glibc baseline run: | - PACKAGE_FILE="${GITHUB_WORKSPACE}/crossdesk-linux-${{ matrix.arch }}-${LEGAL_VERSION}.deb" - VERIFY_DIR="${RUNNER_TEMP}/crossdesk-glibc-${{ matrix.arch }}" - install -d "${VERIFY_DIR}" - dpkg-deb --extract "${PACKAGE_FILE}" "${VERIFY_DIR}" - verify-crossdesk-glibc-baseline "${VERIFY_DIR}" 2.31 + for PACKAGE in crossdesk crossdesk-virtual-desktop-xfce; do + PACKAGE_FILE="${GITHUB_WORKSPACE}/${PACKAGE}-linux-${{ matrix.arch }}-${LEGAL_VERSION}.deb" + VERIFY_DIR="${RUNNER_TEMP}/${PACKAGE}-glibc-${{ matrix.arch }}" + install -d "${VERIFY_DIR}" + dpkg-deb --extract "${PACKAGE_FILE}" "${VERIFY_DIR}" + verify-crossdesk-glibc-baseline "${VERIFY_DIR}" 2.31 + done - name: Upload artifact uses: actions/upload-artifact@v6 with: name: crossdesk-linux-${{ matrix.arch }}-${{ env.LEGAL_VERSION }} - path: ${{ github.workspace }}/crossdesk-linux-${{ matrix.arch }}-${{ env.LEGAL_VERSION }}.deb + path: | + ${{ github.workspace }}/crossdesk-linux-${{ matrix.arch }}-${{ env.LEGAL_VERSION }}.deb + ${{ github.workspace }}/crossdesk-virtual-desktop-xfce-linux-${{ matrix.arch }}-${{ env.LEGAL_VERSION }}.deb + if-no-files-found: error # macOS build-macos: @@ -689,6 +695,14 @@ jobs: The Android arm64 Release APK is signed with the CrossDesk publishing key and can be installed directly. It includes the exact application and MiniRTC revisions, license documents and native build recipes. + ## Linux packages + + Choose one package for your architecture; both contain the complete CrossDesk application: + - Standard (recommended): crossdesk-linux-ARCH-VERSION.deb, for existing desktops. + - Virtual desktop: crossdesk-virtual-desktop-xfce-linux-ARCH-VERSION.deb, which additionally installs Xfce, Xvfb and D-Bus session tools through APT. + The packages replace each other and preserve user settings. Installation does not switch your default desktop or start a virtual session. + [Linux setup and switching packages](https://github.com/${GITHUB_REPOSITORY}/blob/${GITHUB_SHA}/docs/HEADLESS_EN.md). + ## Code signing policy Free code signing provided by [SignPath.io](https://signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). @@ -699,8 +713,12 @@ jobs: mkdir -p release cp artifacts/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-macos-x64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg cp artifacts/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-macos-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.pkg - cp artifacts/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-linux-amd64-${{ steps.version.outputs.VERSION_WITH_V }}.deb - cp artifacts/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-linux-arm64-${{ steps.version.outputs.VERSION_WITH_V }}.deb + for ARCH in amd64 arm64; do + for PACKAGE in crossdesk crossdesk-virtual-desktop-xfce; do + FILE="${PACKAGE}-linux-${ARCH}-${{ steps.version.outputs.VERSION_WITH_V }}.deb" + cp "artifacts/crossdesk-linux-${ARCH}-${{ steps.version.outputs.VERSION_WITH_V }}/${FILE}" "release/${FILE}" + done + done cp artifacts/crossdesk-ios-arm64-unsigned-${{ needs.build-ios.outputs.version }}/* release/crossdesk-ios-arm64-unsigned-${{ needs.build-ios.outputs.version }}.zip cp artifacts/crossdesk-android-arm64-${{ needs.build-android.outputs.version }}/*.apk release/crossdesk-android-arm64-${{ needs.build-android.outputs.version }}.apk cp artifacts/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}/* release/crossdesk-win-x64-${{ steps.version.outputs.VERSION_WITH_V }}.exe diff --git a/README.md b/README.md index c5f27506..218c66a0 100644 --- a/README.md +++ b/README.md @@ -68,17 +68,32 @@ CrossDesk 支持 Windows、macOS、Linux 之间的远程访问,也可从浏览 | --- | --- | --- | | Windows | Windows 10+,x64 | `.exe` 安装包;便携构建也受支持 | | macOS | macOS 14.0+,Intel / Apple Silicon | 选择 x64 或 arm64 的 `.pkg` | -| Linux | Ubuntu 20.04+,amd64 / arm64,glibc 2.31 基线 | `.deb` 安装包 | +| Linux | Ubuntu 20.04+,amd64 / arm64,glibc 2.31 基线 | 普通版 / Xfce 虚拟桌面版 `.deb` | | iOS / iPadOS | iOS 16.0+,arm64 真机 | 原生客户端;使用 Xcode 构建并签名,CI 产物为未签名应用 | | Android | Android 8.0+,arm64 | 原生控制端;使用 Gradle 构建 APK,见 [开发说明](apps/android/README.md) | | Web | 支持 WebRTC 的浏览器 | 访问 [web.crossdesk.cn](https://web.crossdesk.cn/) | -Linux 下载后,在下载目录中将文件名替换为实际安装包名称: +Linux 每个架构提供两个可独立安装的包,**选择其中一个**: + +| 安装包 | 适用场景 | +| --- | --- | +| `crossdesk-linux--.deb`(推荐) | 已有桌面的电脑;不会额外安装 Xfce 或 Xvfb | +| `crossdesk-virtual-desktop-xfce-linux--.deb` | 需要创建独立桌面的服务器;APT 会额外安装 **Xfce、Xvfb 和 D-Bus 会话工具** | + +普通版安装示例(将文件名替换为实际下载名称,arm64 选择对应架构): ```bash sudo apt install "./crossdesk-linux-amd64-.deb" ``` +如需虚拟桌面版,改为安装: + +```bash +sudo apt install "./crossdesk-virtual-desktop-xfce-linux-amd64-.deb" +``` + +两个包包含相同的完整程序,互相替换并保留用户配置;普通版自行补齐依赖后也能创建虚拟桌面。安装虚拟桌面版不会自动启动会话或切换默认桌面,启动方法见 [Linux 无头运行](docs/HEADLESS.md)。应用更新的下载按钮会选择已安装的包类型。已有版本升级后不会自动删除先前安装的 Xfce。 + **macOS 首次运行:** 按应用提示,在“系统设置 → 隐私与安全性”中授予 CrossDesk **屏幕录制**(新系统可能显示为“屏幕与系统音频录制”)及 **辅助功能**权限,再按系统提示重新打开应用。前者用于捕获桌面,后者用于远程键鼠输入。 diff --git a/README_EN.md b/README_EN.md index 8f1d46ff..dbb49e4e 100644 --- a/README_EN.md +++ b/README_EN.md @@ -68,17 +68,32 @@ Choose a package matching your OS and CPU architecture from [GitHub Releases](ht | --- | --- | --- | | Windows | Windows 10+, x64 | `.exe` installer; portable builds are also supported | | macOS | macOS 14.0+, Intel / Apple Silicon | x64 or arm64 `.pkg` | -| Linux | Ubuntu 20.04+, amd64 / arm64, glibc 2.31 baseline | `.deb` package | +| Linux | Ubuntu 20.04+, amd64 / arm64, glibc 2.31 baseline | Standard / Xfce virtual desktop `.deb` | | iOS / iPadOS | iOS 16.0+, arm64 physical device | Native controller; build and sign with Xcode, or sign the unsigned CI app | | Android | Android 8.0+, arm64 | Native controller; build an APK with Gradle ([guide](apps/android/README.md)) | | Web | A WebRTC-capable browser | Open [web.crossdesk.cn](https://web.crossdesk.cn/) | -On Linux, replace the example filename with the downloaded package name: +Linux provides two standalone packages per architecture. **Choose one**: + +| Package | Intended use | +| --- | --- | +| `crossdesk-linux--.deb` (recommended) | Computers with an existing desktop; does not install Xfce or Xvfb | +| `crossdesk-virtual-desktop-xfce-linux--.deb` | Servers needing an independent desktop; APT additionally installs **Xfce, Xvfb and D-Bus session tools** | + +To install the standard package, replace the example filename with your download (use arm64 for that architecture): ```bash sudo apt install "./crossdesk-linux-amd64-.deb" ``` +For the virtual desktop package, install this file instead: + +```bash +sudo apt install "./crossdesk-virtual-desktop-xfce-linux-amd64-.deb" +``` + +Both contain the same complete application, replace each other and preserve user settings. The standard package can also create virtual desktops after you install the optional dependencies. Installing the virtual desktop package does not start a session or change the default desktop; see [Linux headless operation](docs/HEADLESS_EN.md) to start one. The app's update download button selects the installed package type. Upgrading an existing installation does not automatically remove previously installed Xfce components. + **First launch on macOS:** follow the app prompts to grant **Screen Recording** (called **Screen & System Audio Recording** on newer systems) and **Accessibility** under **System Settings → Privacy & Security**. Reopen the app if prompted. These permissions allow desktop capture and remote keyboard/mouse input respectively. diff --git a/apps/desktop/scripts/linux/package_deb.sh b/apps/desktop/scripts/linux/package_deb.sh index 3e52dd0d..35fec013 100755 --- a/apps/desktop/scripts/linux/package_deb.sh +++ b/apps/desktop/scripts/linux/package_deb.sh @@ -15,14 +15,15 @@ INPUT_VERSION="$3" EXTRA_RECOMMENDATION="${4:-}" PKG_NAME="crossdesk" +VIRTUAL_PACKAGE_NAME="crossdesk-virtual-desktop-xfce" APP_NAME="CrossDesk" MAINTAINER="Junkun Di " DESCRIPTION="A simple cross-platform remote desktop client." ALSA_RUNTIME_DEP="libasound2 | libasound2t64" PORTAL_RUNTIME_RECOMMENDS="xdg-desktop-portal, xdg-desktop-portal-gtk | xdg-desktop-portal-kde | xdg-desktop-portal-wlr" TRAY_RUNTIME_RECOMMENDS="libayatana-appindicator3-1 | libappindicator3-1" -# Every published package includes the prerequisites for all Linux headless -# paths, including a complete independent desktop. Older Ubuntu releases ship +# Only the virtual desktop package installs a desktop environment. Both packages +# contain the same application, including headless support. Older Ubuntu releases ship # dbus-run-session in dbus; newer releases split it into dbus-daemon. HEADLESS_RUNTIME_DEPS="xvfb, xfce4, dbus-daemon | dbus" @@ -107,7 +108,6 @@ BIN_DIR="$DEB_DIR/usr/bin" PRIVATE_DIR="$DEB_DIR/usr/lib/$PKG_NAME" ICON_BASE_DIR="$DEB_DIR/usr/share/icons/hicolor" DESKTOP_DIR="$DEB_DIR/usr/share/applications" -OUTPUT_FILE="$PROJECT_ROOT/${PKG_NAME}-linux-${DEBIAN_ARCH}-${APP_VERSION}.deb" install -d "$DEBIAN_DIR" "$BIN_DIR" "$PRIVATE_DIR" "$DESKTOP_DIR" install -m 0755 "$BUILD_BINARY" "$PRIVATE_DIR/crossdesk-bin" @@ -161,23 +161,6 @@ if [[ -n "$EXTRA_RECOMMENDATION" ]]; then RECOMMENDS="$RECOMMENDS, $EXTRA_RECOMMENDATION" fi -cat > "$DEBIAN_DIR/control" <= 2.31), libstdc++6 (>= 10), libx11-6, libxext6, - libxrender1, libxft2, libxrandr2, libxfixes3, libxcursor1, libxi6, libxcb1, libxcb-randr0, - libxcb-xtest0, libxcb-xinerama0, libxcb-shape0, libxcb-xkb1, - libxcb-xfixes0, libxv1, libxtst6, $ALSA_RUNTIME_DEP, libsndio7.0, - libxcb-shm0, libpulse0, libdrm2, libdbus-1-3, libgl1, - $HEADLESS_RUNTIME_DEPS -Recommends: $RECOMMENDS -Priority: optional -Section: utils -EOF - cat > "$DESKTOP_DIR/$PKG_NAME.desktop" </dev/null -dpkg-deb --contents "$OUTPUT_FILE" > "$WORK_DIR/package-contents.txt" +# Stage and strip the application once, then package it with two dependency sets. +# Keep the executable, launcher and configuration paths identical when switching. +for package_name in "$PKG_NAME" "$VIRTUAL_PACKAGE_NAME"; do + extra_depends="" + description="$DESCRIPTION" + other_package="$VIRTUAL_PACKAGE_NAME" + if [[ "$package_name" == "$VIRTUAL_PACKAGE_NAME" ]]; then + extra_depends=", $HEADLESS_RUNTIME_DEPS" + description="CrossDesk with Xfce and Xvfb for independent virtual desktops." + other_package="$PKG_NAME" + fi + cat > "$DEBIAN_DIR/control" <= 2.31), libstdc++6 (>= 10), libx11-6, libxext6, + libxrender1, libxft2, libxrandr2, libxfixes3, libxcursor1, libxi6, libxcb1, libxcb-randr0, + libxcb-xtest0, libxcb-xinerama0, libxcb-shape0, libxcb-xkb1, + libxcb-xfixes0, libxv1, libxtst6, $ALSA_RUNTIME_DEP, libsndio7.0, + libxcb-shm0, libpulse0, libdrm2, libdbus-1-3, libgl1$extra_depends +Recommends: $RECOMMENDS +Conflicts: $other_package +Replaces: $other_package +Priority: optional +Section: utils +EOF + if [[ "$package_name" == "$VIRTUAL_PACKAGE_NAME" ]]; then + echo "Provides: $PKG_NAME (= $DEB_VERSION)" >> "$DEBIAN_DIR/control" + fi + # Read beside the executable by the updater; never store this in user config. + echo "$package_name" > "$PRIVATE_DIR/package-name" + chmod 0644 "$PRIVATE_DIR/package-name" + OUTPUT_FILE="$PROJECT_ROOT/${package_name}-linux-${DEBIAN_ARCH}-${APP_VERSION}.deb" + rm -f "$OUTPUT_FILE" + dpkg-deb --root-owner-group --build "$DEB_DIR" "$OUTPUT_FILE" + dpkg-deb --info "$OUTPUT_FILE" >/dev/null + dpkg-deb --contents "$OUTPUT_FILE" > "$WORK_DIR/package-contents.txt" -if [[ -n "$SLINT_NEEDED" ]] && \ - ! grep -q "usr/lib/$PKG_NAME/$SLINT_NEEDED" "$WORK_DIR/package-contents.txt"; then - echo "Packaged artifact is missing $SLINT_NEEDED" >&2 - exit 1 -fi + if [[ -n "$SLINT_NEEDED" ]] && \ + ! grep -q "usr/lib/$PKG_NAME/$SLINT_NEEDED" "$WORK_DIR/package-contents.txt"; then + echo "Packaged artifact is missing $SLINT_NEEDED" >&2 + exit 1 + fi -echo "Deb package created: $OUTPUT_FILE" + echo "Deb package created: $OUTPUT_FILE" +done diff --git a/apps/desktop/src/gui/application/gui_application.cpp b/apps/desktop/src/gui/application/gui_application.cpp index 24646ee6..fce85f37 100644 --- a/apps/desktop/src/gui/application/gui_application.cpp +++ b/apps/desktop/src/gui/application/gui_application.cpp @@ -1498,7 +1498,11 @@ void GuiApplication::BindMainCallbacks() { ? UiText(std::to_string(signal_port)) : slint::SharedString{}); }); - main->on_open_download([this] { OpenUrl("https://crossdesk.cn"); }); + main->on_open_download([this] { + const auto& latest = update_checker_->latest(); + OpenUrl(latest && !latest->download_url.empty() ? latest->download_url + : "https://crossdesk.cn"); + }); main->on_check_for_updates([this] { update_checker_->RequestCheck(); SyncMainWindow(); diff --git a/apps/desktop/src/platform/linux/headless/headless_session.cpp b/apps/desktop/src/platform/linux/headless/headless_session.cpp index 0ec2c732..0574d757 100644 --- a/apps/desktop/src/platform/linux/headless/headless_session.cpp +++ b/apps/desktop/src/platform/linux/headless/headless_session.cpp @@ -524,7 +524,10 @@ int RunHeadless(const Options& options, const std::function& run) { const std::string xvfb = FindExecutable("Xvfb"); if (xvfb.empty()) { Diagnostic() << "No usable display. Headless mode requires Xvfb; on Debian/Ubuntu " - "install it with: sudo apt install xvfb\n"; + "install it with: sudo apt install xvfb\n" + "For a complete independent desktop, install the " + "crossdesk-virtual-desktop-xfce .deb and use " + "--headless-session startxfce4.\n"; return 1; } std::string session; @@ -532,9 +535,15 @@ int RunHeadless(const Options& options, const std::function& run) { if (!options.session.empty() && options.session != "none") { session = FindExecutable(options.session); dbus = FindExecutable("dbus-run-session"); - if (session.empty() || dbus.empty()) { - Diagnostic() << "Headless desktop requires an executable --headless-session " - "and dbus-run-session (Debian/Ubuntu: dbus-daemon)\n"; + if (session.empty()) { + Diagnostic() << "Desktop session executable not found: " << options.session + << ". Install that desktop environment first; for Xfce use the " + "crossdesk-virtual-desktop-xfce .deb.\n"; + return 1; + } + if (dbus.empty()) { + Diagnostic() << "Headless desktop requires dbus-run-session. Install " + "dbus-daemon (or dbus on older Debian/Ubuntu releases).\n"; return 1; } } diff --git a/apps/desktop/src/version_checker/linux_update_download.cpp b/apps/desktop/src/version_checker/linux_update_download.cpp new file mode 100644 index 00000000..69ca97fc --- /dev/null +++ b/apps/desktop/src/version_checker/linux_update_download.cpp @@ -0,0 +1,55 @@ +#include "linux_update_download.h" + +#include +#include + +namespace crossdesk { + +std::string LinuxUpdateDownloadUrl(const nlohmann::json& manifest, + const VersionInfo& latest, + const std::string& architecture, + const std::filesystem::path& executable_dir) { + const std::string fallback = "https://github.com/kunkundi/crossdesk/releases"; + if ((architecture != "amd64" && architecture != "arm64") || + executable_dir.empty()) { + return fallback; + } + + std::string package_name = "crossdesk"; + const auto marker = executable_dir / "package-name"; + std::error_code error; + const bool has_marker = std::filesystem::exists(marker, error); + if (error) return fallback; + if (has_marker) { + std::ifstream file(marker); + if (!std::getline(file, package_name)) return fallback; + } + std::string key = "linux-" + architecture; + if (package_name == "crossdesk-virtual-desktop-xfce") { + key += "-virtual-desktop-xfce"; + } else if (package_name != "crossdesk") { + return fallback; + } + + try { + const auto& download = manifest.at("downloads").at(key); + const auto version = ParseVersionInfoJSON(download.dump()); + // A partial release can leave older downloads in the merged manifest. + if (!version || version->version != latest.version || + IsNewerVersionWithMetadata(version->version, latest.version, + latest.release_date, latest.patch)) { + return fallback; + } + const std::string filename = package_name + "-linux-" + architecture + + "-v" + latest.version + ".deb"; + const std::string url = "https://downloads.crossdesk.cn/" + filename; + // Verify the variant in the asset name too, not just the manifest key. + if (download.at("filename") == filename && download.at("url") == url) { + return url; + } + } catch (const nlohmann::json::exception&) { + } + return fallback; +} + +} // namespace crossdesk diff --git a/apps/desktop/src/version_checker/linux_update_download.h b/apps/desktop/src/version_checker/linux_update_download.h new file mode 100644 index 00000000..de3fd188 --- /dev/null +++ b/apps/desktop/src/version_checker/linux_update_download.h @@ -0,0 +1,29 @@ +/* + * @Author: DI JUNKUN + * @Date: 2026-10-08 + * Copyright (c) 2026 by DI JUNKUN, All Rights Reserved. + */ + +#ifndef _LINUX_UPDATE_DOWNLOAD_H_ +#define _LINUX_UPDATE_DOWNLOAD_H_ + +#include +#include + +#include + +#include "app_version.h" + +namespace crossdesk { + +// Uses package-name beside the executable, so source builds cannot inherit an +// unrelated system installation's variant. Missing/invalid downloads open the +// release list rather than silently offering a different package or old release. +std::string LinuxUpdateDownloadUrl(const nlohmann::json& manifest, + const VersionInfo& latest, + const std::string& architecture, + const std::filesystem::path& executable_dir); + +} // namespace crossdesk + +#endif \ No newline at end of file diff --git a/apps/desktop/src/version_checker/version_checker.cpp b/apps/desktop/src/version_checker/version_checker.cpp index 798f2953..88926c41 100644 --- a/apps/desktop/src/version_checker/version_checker.cpp +++ b/apps/desktop/src/version_checker/version_checker.cpp @@ -10,6 +10,7 @@ #include #include "rd_log.h" +#include "linux_update_download.h" #include #include @@ -118,8 +119,22 @@ std::optional CheckUpdate() { if (res) { if (res->status == 200) { try { - auto info = ParseVersionInfo(nlohmann::json::parse(res->body)); + const auto manifest = nlohmann::json::parse(res->body); + auto info = ParseVersionInfo(manifest); if (info) { +#if defined(__linux__) + std::string architecture; +#if defined(__x86_64__) + architecture = "amd64"; +#elif defined(__aarch64__) + architecture = "arm64"; +#endif + std::error_code error; + const auto executable = std::filesystem::read_symlink("/proc/self/exe", error); + info->download_url = LinuxUpdateDownloadUrl( + manifest, *info, architecture, + error ? std::filesystem::path{} : executable.parent_path()); +#endif LOG_INFO("Fetched version.json: latest_version={}, releaseDate={}, patch={}", info->version, info->release_date, info->patch); } else { diff --git a/apps/desktop/xmake/targets.lua b/apps/desktop/xmake/targets.lua index 8dce5a33..44917b8d 100644 --- a/apps/desktop/xmake/targets.lua +++ b/apps/desktop/xmake/targets.lua @@ -237,7 +237,8 @@ function setup_targets() add_deps("rd_log", "crossdesk_wire") add_includedirs("apps/desktop/src/version_checker") add_files("apps/desktop/tests/version_checker_test.cpp", - "apps/desktop/src/version_checker/version_checker.cpp") + "apps/desktop/src/version_checker/version_checker.cpp", + "apps/desktop/src/version_checker/linux_update_download.cpp") if is_os("macosx") then add_defines("CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN") add_frameworks("Security", "CoreFoundation") diff --git a/docker/linux-build/README.md b/docker/linux-build/README.md index ab72a6a4..fad2b0fa 100644 --- a/docker/linux-build/README.md +++ b/docker/linux-build/README.md @@ -28,7 +28,7 @@ crossdesk/ubuntu20.04:buildenv-cuda12.6.3-gcc10-cmake3.31.6-pipewire0.3.48-xmake Docker 会根据运行机器自动选择正确架构。amd64 变体以 `nvidia/cuda:12.6.3-devel-ubuntu20.04` 为基础;arm64 变体不包含当前构建 用不到的 CUDA SDK。两个变体都使用 Ubuntu 20.04/glibc 2.31,因此生成的 -单一 Linux 安装包可运行在 Ubuntu 20.04 及更高版本。 +普通版和 Xfce 虚拟桌面版安装包均可运行在 Ubuntu 20.04 及更高版本。 Ubuntu 20.04 仓库只提供 PipeWire 0.2。镜像通过 `install-pipewire-sdk.sh` 安装固定版本的 PipeWire 0.3.48/SPA 头文件,但不 diff --git a/docs/BUILD.md b/docs/BUILD.md index cc1daf65..36b53ad2 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -57,13 +57,13 @@ xmake r crossdesk ## Linux Linux 构建支持 Ubuntu 20.04 及以上版本的 amd64 和 arm64。发布安装包以 -Ubuntu 20.04/glibc 2.31 为兼容基线。以下依赖命令以 Ubuntu 20.04 为例;其他发行版或较新版本需按其软件源调整包名和工具链,复现 CI 可使用下文的构建镜像。先安装基础编译和无头运行依赖: +Ubuntu 20.04/glibc 2.31 为兼容基线。以下依赖命令以 Ubuntu 20.04 为例;其他发行版或较新版本需按其软件源调整包名和工具链,复现 CI 可使用下文的构建镜像。先安装基础编译依赖: ```bash sudo apt-get update sudo apt-get install -y \ git curl unzip build-essential gcc-10 g++-10 python3-pip \ - pkg-config binutils dpkg-dev xvfb xfce4 dbus \ + pkg-config binutils dpkg-dev \ libx11-dev libxext-dev libxrender-dev libxft-dev libxrandr-dev \ libxinerama-dev libxcursor-dev libxi-dev libxfixes-dev libxv-dev \ libxtst-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev \ @@ -119,7 +119,9 @@ xmake b -vy crossdesk ./apps/desktop/scripts/linux/pkg_arm64.sh 1.4.3 ``` -打包脚本会将 Slint 共享运行库安装到软件包私有目录 `/usr/lib/crossdesk`,无需用户另外安装 `libslint_cpp.so`。 +每次打包从同一份编译产物生成普通版 `crossdesk-linux--.deb` 和虚拟桌面版 `crossdesk-virtual-desktop-xfce-linux--.deb`。两者都包含完整程序,只有后者强依赖 Xvfb、Xfce 和 D-Bus 会话工具;编译和打包本身不需要安装这些组件。源码运行时如需创建完整虚拟桌面,请按 [无头运行说明](HEADLESS.md) 准备可选依赖。项目构建镜像和 CI 可保留这些组件用于测试。 + +打包脚本会将 Slint 共享运行库安装到软件包私有目录 `/usr/lib/crossdesk`,无需用户另外安装 `libslint_cpp.so`。同目录的 `package-name` 标记由安装包管理,用于选择更新下载,不写入用户配置。 PipeWire 不属于强制运行时依赖:程序在运行时检测宿主系统的 PipeWire 0.3, 没有该运行库时仍可使用 X11(以及构建时启用的 DRM)捕获。 @@ -145,7 +147,7 @@ xmake r crossdesk ## 无 CUDA 环境下的开发支持 -对于**未安装 CUDA 环境的 Linux 开发者,如果希望编译后的成果物拥有硬件编解码能力**,这里提供了预配置的 [Ubuntu 20.04 兼容构建镜像](https://hub.docker.com/r/crossdesk/ubuntu20.04)。该镜像内置必要的构建依赖,可生成兼容 glibc 2.31 的单一 Linux 安装包。 +对于**未安装 CUDA 环境的 Linux 开发者,如果希望编译后的成果物拥有硬件编解码能力**,这里提供了预配置的 [Ubuntu 20.04 兼容构建镜像](https://hub.docker.com/r/crossdesk/ubuntu20.04)。该镜像内置必要的构建依赖,可生成兼容 glibc 2.31 的两种 Linux 安装包。 镜像版本和 amd64 / arm64 差异见 [构建镜像说明](../docker/linux-build/README.md)。以下 CUDA 示例适用于 amd64 镜像;进入容器、下载工程后执行: diff --git a/docs/BUILD_EN.md b/docs/BUILD_EN.md index c90b4639..da9fcee5 100644 --- a/docs/BUILD_EN.md +++ b/docs/BUILD_EN.md @@ -60,13 +60,13 @@ Linux builds support Ubuntu 20.04 or later on amd64 and arm64. Release packages use Ubuntu 20.04/glibc 2.31 as their compatibility baseline. The dependency commands below target Ubuntu 20.04; adapt package names and toolchains for other distributions or newer versions, or use the build image below to -reproduce CI. Install the base build and headless runtime dependencies first: +reproduce CI. Install the base build dependencies first: ```bash sudo apt-get update sudo apt-get install -y \ git curl unzip build-essential gcc-10 g++-10 python3-pip \ - pkg-config binutils dpkg-dev xvfb xfce4 dbus \ + pkg-config binutils dpkg-dev \ libx11-dev libxext-dev libxrender-dev libxft-dev libxrandr-dev \ libxinerama-dev libxcursor-dev libxi-dev libxfixes-dev libxv-dev \ libxtst-dev libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev \ @@ -127,7 +127,9 @@ Build a Debian package after compiling a release binary on the matching architec ./apps/desktop/scripts/linux/pkg_arm64.sh 1.4.3 ``` -The package scripts install the shared Slint runtime in the private `/usr/lib/crossdesk` directory, so users do not need to install `libslint_cpp.so` separately. +Each packaging run uses the same compiled application to create the standard `crossdesk-linux--.deb` and `crossdesk-virtual-desktop-xfce-linux--.deb`. Both contain the complete application; only the latter requires Xvfb, Xfce and D-Bus session tools. Building and packaging need none of these optional runtime components. To run a complete virtual desktop from source, install them as described in [headless operation](HEADLESS_EN.md). The build image and CI can retain them for testing. + +The package scripts install the shared Slint runtime in the private `/usr/lib/crossdesk` directory, so users do not need to install `libslint_cpp.so` separately. The package-managed `package-name` file beside it identifies the update download variant without changing user settings. PipeWire is not a mandatory runtime dependency. CrossDesk detects the host's PipeWire 0.3 runtime dynamically and can still use X11 capture (and DRM when enabled at build time) when it is unavailable. @@ -154,7 +156,7 @@ xmake r crossdesk ## Development Without CUDA Environment For **Linux developers who do not have a CUDA environment installed and want to enable hardware codec feature**, a preconfigured [Ubuntu 20.04 compatibility build image](https://hub.docker.com/r/crossdesk/ubuntu20.04) is provided. -This image contains the required build dependencies and produces a single Linux package compatible with the glibc 2.31 baseline. +This image contains the required build dependencies and produces both Linux package variants with the glibc 2.31 baseline. See the [build image notes](../docker/linux-build/README.md) for image tags and architecture differences. The CUDA example below targets the amd64 image. Inside the container, download the project and run: diff --git a/docs/HEADLESS.md b/docs/HEADLESS.md index 97bac797..8f6c36e3 100644 --- a/docs/HEADLESS.md +++ b/docs/HEADLESS.md @@ -90,9 +90,11 @@ settings set file_path /home/user/Received Files ## 创建独立虚拟桌面 -Debian/Ubuntu 安装包将 Xvfb、Xfce 和 D-Bus 会话工具声明为强依赖,使用 APT 安装时会自动补齐,即使指定 `--no-install-recommends` 也不会跳过。源码直接运行时,需在开发环境中准备同样的运行依赖;项目构建镜像和 CI 已包含这些组件。 +Debian/Ubuntu 的普通版 `crossdesk` 不依赖 Xvfb 或 Xfce,复用已有桌面无需额外安装它们。需要完整独立桌面时,可选择 `crossdesk-virtual-desktop-xfce` 安装包:它包含同一份 CrossDesk 程序,并通过 APT 强依赖安装 Xvfb、Xfce 和 D-Bus 会话工具(`--no-install-recommends` 也不会跳过)。这些组件由系统软件源提供,安装包不是完整的离线桌面环境。 -原来没有图形会话,或者希望单独开一套桌面时,可启动独立 Xfce 会话。Ubuntu / Debian 示例(安装包用户无需重复安装依赖): +两个包只能安装一个。先退出 CrossDesk,使用 `sudo apt install ./所选安装包.deb` 升级或切换版本,再启动程序,原有用户配置会保留。安装与切换不会自动启动虚拟会话、修改默认桌面或删除此前安装的 Xfce。更新下载按已安装的包类型选择;若对应下载尚未发布或元数据不完整,会打开 Releases 列表供选择,不会自动改用另一种包。 + +原来没有图形会话,或者希望单独开一套桌面时,可启动独立 Xfce 会话。Ubuntu / Debian 示例(虚拟桌面版已包含这些依赖;普通版和源码运行用户按需安装): ```bash sudo apt install xvfb xfce4 dbus diff --git a/docs/HEADLESS_EN.md b/docs/HEADLESS_EN.md index 248c898c..a0d32043 100644 --- a/docs/HEADLESS_EN.md +++ b/docs/HEADLESS_EN.md @@ -89,9 +89,11 @@ The interactive console stays in the foreground regardless of the built-in daemo ## Create a separate virtual desktop -Debian/Ubuntu packages require Xvfb, Xfce and the D-Bus session tools through `Depends`. APT installs them even with `--no-install-recommends`. Source builds need the same runtime prerequisites in the development environment; the project build image and CI include them. +The standard Debian/Ubuntu package, `crossdesk`, does not depend on Xvfb or Xfce; reusing an existing desktop needs neither. For a complete independent desktop, choose `crossdesk-virtual-desktop-xfce`. It contains the same CrossDesk application and requires Xvfb, Xfce and D-Bus session tools through `Depends`, so APT installs them even with `--no-install-recommends`. These components come from the system repositories; the package is not a complete offline desktop environment. -For a host without a desktop session, or to start an independent Xfce session (package users do not need to reinstall the dependencies): +Install only one package. Exit CrossDesk, use `sudo apt install ./chosen-package.deb` to upgrade or switch variants, then restart the app; user settings are preserved. Installation and switching do not start a virtual session, change the default desktop or remove previously installed Xfce components. Update downloads retain the installed package type. If its download is missing or incomplete, the app opens the Releases list instead of selecting another package type. + +For a host without a desktop session, or to start an independent Xfce session (the virtual desktop package already supplies the dependencies; standard-package and source-build users install them as needed): ```bash sudo apt install xvfb xfce4 dbus diff --git a/libs/wire/include/app_version.h b/libs/wire/include/app_version.h index 716f494b..859cbf22 100644 --- a/libs/wire/include/app_version.h +++ b/libs/wire/include/app_version.h @@ -19,6 +19,8 @@ struct VersionInfo { std::string release_notes; std::string release_date; int patch = -1; + // Filled by the desktop updater for the installed package variant. + std::string download_url; }; // Shared release parsing and comparison for desktop and mobile controllers.