mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-21 12:07:59 +08:00
[fix] make Xfce optional with separate Linux deb packages, refs #106
This commit is contained in:
@@ -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 <junkun.di@hotmail.com>"
|
||||
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" <<EOF
|
||||
Package: $PKG_NAME
|
||||
Version: $DEB_VERSION
|
||||
Architecture: $DEBIAN_ARCH
|
||||
Maintainer: $MAINTAINER
|
||||
Description: $DESCRIPTION
|
||||
Depends: libc6 (>= 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" <<EOF
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
@@ -191,15 +174,51 @@ Type=Application
|
||||
Categories=Utility;
|
||||
EOF
|
||||
|
||||
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"
|
||||
# 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" <<EOF
|
||||
Package: $package_name
|
||||
Version: $DEB_VERSION
|
||||
Architecture: $DEBIAN_ARCH
|
||||
Maintainer: $MAINTAINER
|
||||
Description: $description
|
||||
Depends: libc6 (>= 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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -524,7 +524,10 @@ int RunHeadless(const Options& options, const std::function<int()>& 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<int()>& 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "linux_update_download.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
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
|
||||
@@ -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 <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
#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
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "rd_log.h"
|
||||
#include "linux_update_download.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
@@ -118,8 +119,22 @@ std::optional<VersionInfo> 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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user