mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-28 08:31:52 +08:00
feat!: 移除内置公共服务器
- 移除公共 RustDesk ID 服务器 (用户需自行配置) - 移除公共 TURN 服务器 (仅保留 Google STUN) - 清理废弃代码: PublicServerInfo, is_using_public_server 等 - 更新前端 UI 和国际化文本 - 重新生成 TypeScript 类型 破坏性变更: 不再提供内置公共服务器。用户必须配置自己的 RustDesk 服务器和 TURN 服务器才能在生产环境中使用。
This commit is contained in:
143
build.rs
143
build.rs
@@ -20,14 +20,13 @@ fn main() {
|
|||||||
// Compile protobuf files for RustDesk protocol
|
// Compile protobuf files for RustDesk protocol
|
||||||
compile_protos();
|
compile_protos();
|
||||||
|
|
||||||
// Generate secrets module from secrets.toml
|
// Generate minimal secrets module
|
||||||
generate_secrets();
|
generate_secrets();
|
||||||
|
|
||||||
// Rerun if the script itself changes
|
// Rerun if the script itself changes
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
println!("cargo:rerun-if-changed=protos/rendezvous.proto");
|
println!("cargo:rerun-if-changed=protos/rendezvous.proto");
|
||||||
println!("cargo:rerun-if-changed=protos/message.proto");
|
println!("cargo:rerun-if-changed=protos/message.proto");
|
||||||
println!("cargo:rerun-if-changed=secrets.toml");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile protobuf files using protobuf-codegen (same as RustDesk server)
|
/// Compile protobuf files using protobuf-codegen (same as RustDesk server)
|
||||||
@@ -52,123 +51,59 @@ pub mod message;
|
|||||||
std::fs::write(protos_dir.join("mod.rs"), mod_content).unwrap();
|
std::fs::write(protos_dir.join("mod.rs"), mod_content).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate secrets module from secrets.toml
|
/// Generate minimal secrets module with Google STUN server hardcoded
|
||||||
///
|
|
||||||
/// This reads the secrets.toml file and generates a Rust module with
|
|
||||||
/// compile-time constants for sensitive configuration values.
|
|
||||||
fn generate_secrets() {
|
fn generate_secrets() {
|
||||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
let dest_path = Path::new(&out_dir).join("secrets_generated.rs");
|
let dest_path = Path::new(&out_dir).join("secrets_generated.rs");
|
||||||
|
|
||||||
// Default values if secrets.toml doesn't exist
|
// Generate the secrets module - no public servers provided
|
||||||
let mut rustdesk_public_server = String::new();
|
let code = r#"// Auto-generated secrets module
|
||||||
let mut rustdesk_public_key = String::new();
|
// DO NOT EDIT - This file is generated by build.rs
|
||||||
let mut rustdesk_relay_key = String::new();
|
|
||||||
let mut ice_stun_server = String::new();
|
|
||||||
let mut ice_turn_urls = String::new();
|
|
||||||
let mut ice_turn_username = String::new();
|
|
||||||
let mut ice_turn_password = String::new();
|
|
||||||
|
|
||||||
// Try to read secrets.toml
|
|
||||||
if let Ok(content) = fs::read_to_string("secrets.toml") {
|
|
||||||
if let Ok(value) = content.parse::<toml::Value>() {
|
|
||||||
// RustDesk section
|
|
||||||
if let Some(rustdesk) = value.get("rustdesk") {
|
|
||||||
if let Some(v) = rustdesk.get("public_server").and_then(|v| v.as_str()) {
|
|
||||||
rustdesk_public_server = v.to_string();
|
|
||||||
}
|
|
||||||
if let Some(v) = rustdesk.get("public_key").and_then(|v| v.as_str()) {
|
|
||||||
rustdesk_public_key = v.to_string();
|
|
||||||
}
|
|
||||||
if let Some(v) = rustdesk.get("relay_key").and_then(|v| v.as_str()) {
|
|
||||||
rustdesk_relay_key = v.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ICE section (for WebRTC)
|
|
||||||
if let Some(ice) = value.get("ice") {
|
|
||||||
if let Some(v) = ice.get("stun_server").and_then(|v| v.as_str()) {
|
|
||||||
ice_stun_server = v.to_string();
|
|
||||||
}
|
|
||||||
if let Some(v) = ice.get("turn_urls").and_then(|v| v.as_str()) {
|
|
||||||
ice_turn_urls = v.to_string();
|
|
||||||
}
|
|
||||||
if let Some(v) = ice.get("turn_username").and_then(|v| v.as_str()) {
|
|
||||||
ice_turn_username = v.to_string();
|
|
||||||
}
|
|
||||||
if let Some(v) = ice.get("turn_password").and_then(|v| v.as_str()) {
|
|
||||||
ice_turn_password = v.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("cargo:warning=Failed to parse secrets.toml");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("cargo:warning=secrets.toml not found, using empty defaults");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate the secrets module
|
|
||||||
let code = format!(
|
|
||||||
r#"// Auto-generated secrets module
|
|
||||||
// DO NOT EDIT - This file is generated by build.rs from secrets.toml
|
|
||||||
|
|
||||||
/// RustDesk public server configuration
|
|
||||||
pub mod rustdesk {{
|
|
||||||
/// Public RustDesk ID server address (used when user leaves field empty)
|
|
||||||
pub const PUBLIC_SERVER: &str = "{}";
|
|
||||||
|
|
||||||
/// Public key for the RustDesk server (for client connection)
|
|
||||||
pub const PUBLIC_KEY: &str = "{}";
|
|
||||||
|
|
||||||
/// Relay server authentication key (licence_key for relay server)
|
|
||||||
pub const RELAY_KEY: &str = "{}";
|
|
||||||
|
|
||||||
/// Check if public server is configured
|
|
||||||
pub const fn has_public_server() -> bool {{
|
|
||||||
!PUBLIC_SERVER.is_empty()
|
|
||||||
}}
|
|
||||||
}}
|
|
||||||
|
|
||||||
/// ICE server configuration (for WebRTC NAT traversal)
|
/// ICE server configuration (for WebRTC NAT traversal)
|
||||||
pub mod ice {{
|
pub mod ice {
|
||||||
/// Public STUN server URL
|
/// Google public STUN server URL (hardcoded)
|
||||||
pub const STUN_SERVER: &str = "{}";
|
pub const STUN_SERVER: &str = "stun:stun.l.google.com:19302";
|
||||||
|
|
||||||
/// Public TURN server URLs (comma-separated)
|
/// TURN server URLs - not provided, users must configure their own
|
||||||
pub const TURN_URLS: &str = "{}";
|
pub const TURN_URLS: &str = "";
|
||||||
|
|
||||||
/// TURN authentication username
|
/// TURN authentication username
|
||||||
pub const TURN_USERNAME: &str = "{}";
|
pub const TURN_USERNAME: &str = "";
|
||||||
|
|
||||||
/// TURN authentication password
|
/// TURN authentication password
|
||||||
pub const TURN_PASSWORD: &str = "{}";
|
pub const TURN_PASSWORD: &str = "";
|
||||||
|
|
||||||
/// Check if public ICE servers are configured
|
/// Always returns true since we have STUN
|
||||||
pub const fn is_configured() -> bool {{
|
pub const fn is_configured() -> bool {
|
||||||
!STUN_SERVER.is_empty() || !TURN_URLS.is_empty()
|
true
|
||||||
}}
|
}
|
||||||
|
|
||||||
/// Check if TURN servers are configured (requires credentials)
|
/// Always returns false since TURN is not provided
|
||||||
pub const fn has_turn() -> bool {{
|
pub const fn has_turn() -> bool {
|
||||||
!TURN_URLS.is_empty() && !TURN_USERNAME.is_empty() && !TURN_PASSWORD.is_empty()
|
false
|
||||||
}}
|
}
|
||||||
}}
|
|
||||||
"#,
|
|
||||||
escape_string(&rustdesk_public_server),
|
|
||||||
escape_string(&rustdesk_public_key),
|
|
||||||
escape_string(&rustdesk_relay_key),
|
|
||||||
escape_string(&ice_stun_server),
|
|
||||||
escape_string(&ice_turn_urls),
|
|
||||||
escape_string(&ice_turn_username),
|
|
||||||
escape_string(&ice_turn_password),
|
|
||||||
);
|
|
||||||
|
|
||||||
fs::write(&dest_path, code).expect("Failed to write secrets_generated.rs");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Escape special characters in a string for use in Rust string literals
|
/// RustDesk public server configuration - NOT PROVIDED
|
||||||
fn escape_string(s: &str) -> String {
|
pub mod rustdesk {
|
||||||
s.replace('\\', "\\\\").replace('"', "\\\"")
|
/// Public RustDesk ID server - NOT PROVIDED
|
||||||
|
pub const PUBLIC_SERVER: &str = "";
|
||||||
|
|
||||||
|
/// Public key for the RustDesk server - NOT PROVIDED
|
||||||
|
pub const PUBLIC_KEY: &str = "";
|
||||||
|
|
||||||
|
/// Relay server authentication key - NOT PROVIDED
|
||||||
|
pub const RELAY_KEY: &str = "";
|
||||||
|
|
||||||
|
/// Always returns false
|
||||||
|
pub const fn has_public_server() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
fs::write(&dest_path, code).expect("Failed to write secrets_generated.rs");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert days since Unix epoch to year-month-day
|
/// Convert days since Unix epoch to year-month-day
|
||||||
|
|||||||
@@ -8,40 +8,28 @@ FROM debian:12-slim
|
|||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
# Install runtime dependencies and create directories
|
# Install runtime dependencies in a single layer
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
# Static linked: FFmpeg core, libyuv, libvpx, libjpeg-turbo
|
||||||
# Runtime libraries
|
# Dynamic linked: hardware acceleration drivers, GPL codecs (x264/x265)
|
||||||
libasound2 \
|
RUN apt-get update && \
|
||||||
libv4l-0 \
|
apt-get install -y --no-install-recommends \
|
||||||
libudev1 \
|
# Core runtime (all platforms)
|
||||||
zlib1g \
|
libasound2 \
|
||||||
libjpeg62-turbo \
|
libv4l-0 \
|
||||||
libyuv0 \
|
libudev1 \
|
||||||
# FFmpeg runtime
|
libdrm2 \
|
||||||
libavcodec59 \
|
libopus0 \
|
||||||
libavformat59 \
|
ca-certificates \
|
||||||
libavutil57 \
|
# GPL codecs (must be dynamic for license compliance)
|
||||||
libswscale6 \
|
libx264-164 \
|
||||||
libswresample4 \
|
libx265-199 && \
|
||||||
# Video codecs
|
# Platform-specific hardware acceleration
|
||||||
libx264-164 \
|
if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
|
||||||
libx265-199 \
|
apt-get install -y --no-install-recommends \
|
||||||
libvpx7 \
|
libva2 libva-drm2 libva-x11-2 libx11-6 libxcb1 libmfx1; \
|
||||||
# Audio codec
|
fi && \
|
||||||
libopus0 \
|
rm -rf /var/lib/apt/lists/* && \
|
||||||
# Hardware acceleration
|
mkdir -p /etc/one-kvm/ventoy
|
||||||
libva2 \
|
|
||||||
libva-drm2 \
|
|
||||||
libdrm2 \
|
|
||||||
# X11 (for VAAPI)
|
|
||||||
libx11-6 \
|
|
||||||
libxcb1 \
|
|
||||||
# Utilities
|
|
||||||
ca-certificates \
|
|
||||||
# Intel Media SDK (x86_64 only, ignored on other archs)
|
|
||||||
$([ "$TARGETPLATFORM" = "linux/amd64" ] && echo "libmfx1") \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* \
|
|
||||||
&& mkdir -p /etc/one-kvm/ventoy
|
|
||||||
|
|
||||||
# Copy init script
|
# Copy init script
|
||||||
COPY --chmod=755 init.sh /init.sh
|
COPY --chmod=755 init.sh /init.sh
|
||||||
|
|||||||
@@ -28,37 +28,83 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
git \
|
git \
|
||||||
libclang-dev \
|
libclang-dev \
|
||||||
llvm \
|
llvm \
|
||||||
protobuf-compiler \
|
|
||||||
mold \
|
mold \
|
||||||
meson \
|
meson \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
wget \
|
wget \
|
||||||
|
file \
|
||||||
gcc-aarch64-linux-gnu \
|
gcc-aarch64-linux-gnu \
|
||||||
g++-aarch64-linux-gnu \
|
g++-aarch64-linux-gnu \
|
||||||
libc6-dev-arm64-cross \
|
libc6-dev-arm64-cross \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install ARM64 development libraries (without system ffmpeg)
|
# Install ARM64 development libraries (without VAAPI/X11 for ARM)
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
libssl-dev:arm64 \
|
|
||||||
libasound2-dev:arm64 \
|
libasound2-dev:arm64 \
|
||||||
libv4l-dev:arm64 \
|
libv4l-dev:arm64 \
|
||||||
libudev-dev:arm64 \
|
libudev-dev:arm64 \
|
||||||
zlib1g-dev:arm64 \
|
zlib1g-dev:arm64 \
|
||||||
libjpeg62-turbo-dev:arm64 \
|
# Note: libjpeg-turbo, libyuv, libvpx are built from source below for static linking
|
||||||
libyuv-dev:arm64 \
|
|
||||||
libx264-dev:arm64 \
|
libx264-dev:arm64 \
|
||||||
libx265-dev:arm64 \
|
libx265-dev:arm64 \
|
||||||
libvpx-dev:arm64 \
|
|
||||||
libopus-dev:arm64 \
|
libopus-dev:arm64 \
|
||||||
libva-dev:arm64 \
|
|
||||||
libdrm-dev:arm64 \
|
libdrm-dev:arm64 \
|
||||||
libx11-dev:arm64 \
|
|
||||||
libxcb1-dev:arm64 \
|
|
||||||
libxau-dev:arm64 \
|
|
||||||
libxdmcp-dev:arm64 \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Build static libjpeg-turbo from source (cross-compile for ARM64)
|
||||||
|
RUN git clone --depth 1 https://github.com/libjpeg-turbo/libjpeg-turbo /tmp/libjpeg-turbo \
|
||||||
|
&& cd /tmp/libjpeg-turbo \
|
||||||
|
&& cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
|
-DCMAKE_SYSTEM_NAME=Linux \
|
||||||
|
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
|
||||||
|
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||||
|
-DENABLE_SHARED=OFF -DENABLE_STATIC=ON \
|
||||||
|
&& cmake --build build -j$(nproc) \
|
||||||
|
&& cmake --install build \
|
||||||
|
&& rm -rf /tmp/libjpeg-turbo
|
||||||
|
|
||||||
|
# Build static libyuv from source (cross-compile for ARM64)
|
||||||
|
RUN git clone --depth 1 https://github.com/lemenkov/libyuv /tmp/libyuv \
|
||||||
|
&& cd /tmp/libyuv \
|
||||||
|
&& mkdir build && cd build \
|
||||||
|
&& cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
|
-DCMAKE_SYSTEM_NAME=Linux \
|
||||||
|
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
|
||||||
|
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
|
||||||
|
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
|
||||||
|
-DCMAKE_PREFIX_PATH=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||||
|
&& make -j$(nproc) \
|
||||||
|
&& mkdir -p /opt/one-kvm-libs/aarch64-linux-gnu/lib \
|
||||||
|
&& cp libyuv.a /opt/one-kvm-libs/aarch64-linux-gnu/lib/ \
|
||||||
|
&& cp -r ../include /opt/one-kvm-libs/aarch64-linux-gnu/ \
|
||||||
|
&& rm -rf /tmp/libyuv
|
||||||
|
|
||||||
|
# Build static libvpx from source (cross-compile for ARM64)
|
||||||
|
# CC/CXX/LD/AR must be environment variables, not configure arguments
|
||||||
|
RUN git clone --depth 1 https://github.com/webmproject/libvpx /tmp/libvpx \
|
||||||
|
&& cd /tmp/libvpx \
|
||||||
|
&& echo "=== libvpx: Configuring for ARM64 ===" \
|
||||||
|
&& export CC=aarch64-linux-gnu-gcc \
|
||||||
|
&& export CXX=aarch64-linux-gnu-g++ \
|
||||||
|
&& export LD=aarch64-linux-gnu-ld \
|
||||||
|
&& export AR=aarch64-linux-gnu-ar \
|
||||||
|
&& export CROSS=aarch64-linux-gnu- \
|
||||||
|
&& ./configure --prefix=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||||
|
--target=arm64-linux-gcc \
|
||||||
|
--enable-static --disable-shared --enable-pic \
|
||||||
|
--disable-examples --disable-tools --disable-docs \
|
||||||
|
--disable-unit-tests \
|
||||||
|
&& echo "=== libvpx: Building ===" \
|
||||||
|
&& make -j$(nproc) \
|
||||||
|
&& echo "=== libvpx: Checking architecture ===" \
|
||||||
|
&& file libvpx.a \
|
||||||
|
&& make install \
|
||||||
|
&& echo "=== libvpx: Verifying installed library ===" \
|
||||||
|
&& file /opt/one-kvm-libs/aarch64-linux-gnu/lib/libvpx.a \
|
||||||
|
&& rm -rf /tmp/libvpx
|
||||||
|
|
||||||
# Download and build FFmpeg with RKMPP support
|
# Download and build FFmpeg with RKMPP support
|
||||||
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||||
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
||||||
@@ -73,7 +119,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
|
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr/aarch64-linux-gnu \
|
-DCMAKE_INSTALL_PREFIX=/usr/aarch64-linux-gnu \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DBUILD_SHARED_LIBS=ON \
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
-DBUILD_TEST=OFF \
|
-DBUILD_TEST=OFF \
|
||||||
&& make -j$(nproc) \
|
&& make -j$(nproc) \
|
||||||
&& make install \
|
&& make install \
|
||||||
@@ -94,6 +140,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
&& meson setup build --prefix=/usr/aarch64-linux-gnu --libdir=lib \
|
&& meson setup build --prefix=/usr/aarch64-linux-gnu --libdir=lib \
|
||||||
--cross-file=/tmp/aarch64-cross.txt \
|
--cross-file=/tmp/aarch64-cross.txt \
|
||||||
--buildtype=release \
|
--buildtype=release \
|
||||||
|
--default-library=static \
|
||||||
-Dcpp_args=-fpermissive \
|
-Dcpp_args=-fpermissive \
|
||||||
-Dlibdrm=false \
|
-Dlibdrm=false \
|
||||||
-Dlibrga_demo=false \
|
-Dlibrga_demo=false \
|
||||||
@@ -102,7 +149,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
&& cd .. \
|
&& cd .. \
|
||||||
# Create pkg-config wrapper for cross-compilation
|
# Create pkg-config wrapper for cross-compilation
|
||||||
&& echo '#!/bin/sh' > /tmp/aarch64-pkg-config \
|
&& echo '#!/bin/sh' > /tmp/aarch64-pkg-config \
|
||||||
&& echo 'export PKG_CONFIG_LIBDIR=/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig' >> /tmp/aarch64-pkg-config \
|
&& echo 'export PKG_CONFIG_LIBDIR=/opt/one-kvm-libs/aarch64-linux-gnu/lib/pkgconfig:/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig' >> /tmp/aarch64-pkg-config \
|
||||||
&& echo 'export PKG_CONFIG_PATH=""' >> /tmp/aarch64-pkg-config \
|
&& echo 'export PKG_CONFIG_PATH=""' >> /tmp/aarch64-pkg-config \
|
||||||
&& echo 'export PKG_CONFIG_SYSROOT_DIR=""' >> /tmp/aarch64-pkg-config \
|
&& echo 'export PKG_CONFIG_SYSROOT_DIR=""' >> /tmp/aarch64-pkg-config \
|
||||||
&& echo 'exec pkg-config "$@"' >> /tmp/aarch64-pkg-config \
|
&& echo 'exec pkg-config "$@"' >> /tmp/aarch64-pkg-config \
|
||||||
@@ -110,7 +157,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
# Build FFmpeg with RKMPP (minimal build for encoding only)
|
# Build FFmpeg with RKMPP (minimal build for encoding only)
|
||||||
&& cd ffmpeg-rockchip \
|
&& cd ffmpeg-rockchip \
|
||||||
&& ./configure \
|
&& ./configure \
|
||||||
--prefix=/usr/aarch64-linux-gnu \
|
--prefix=/opt/one-kvm-libs/aarch64-linux-gnu \
|
||||||
--cross-prefix=aarch64-linux-gnu- \
|
--cross-prefix=aarch64-linux-gnu- \
|
||||||
--arch=aarch64 \
|
--arch=aarch64 \
|
||||||
--target-os=linux \
|
--target-os=linux \
|
||||||
@@ -118,13 +165,14 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
--pkg-config=/tmp/aarch64-pkg-config \
|
--pkg-config=/tmp/aarch64-pkg-config \
|
||||||
--enable-gpl \
|
--enable-gpl \
|
||||||
--enable-version3 \
|
--enable-version3 \
|
||||||
--enable-shared \
|
--disable-shared \
|
||||||
--disable-static \
|
--enable-static \
|
||||||
# Hardware acceleration
|
--enable-pic \
|
||||||
|
# Hardware acceleration (ARM: RKMPP + V4L2, no VAAPI)
|
||||||
--enable-libdrm \
|
--enable-libdrm \
|
||||||
--enable-rkmpp \
|
--enable-rkmpp \
|
||||||
--enable-rkrga \
|
--enable-rkrga \
|
||||||
--enable-vaapi \
|
--disable-vaapi \
|
||||||
--enable-v4l2-m2m \
|
--enable-v4l2-m2m \
|
||||||
# Software encoding libraries
|
# Software encoding libraries
|
||||||
--enable-libx264 \
|
--enable-libx264 \
|
||||||
@@ -151,10 +199,6 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
--disable-decoders \
|
--disable-decoders \
|
||||||
# Disable all encoders, enable only needed ones
|
# Disable all encoders, enable only needed ones
|
||||||
--disable-encoders \
|
--disable-encoders \
|
||||||
--enable-encoder=h264_vaapi \
|
|
||||||
--enable-encoder=hevc_vaapi \
|
|
||||||
--enable-encoder=vp8_vaapi \
|
|
||||||
--enable-encoder=vp9_vaapi \
|
|
||||||
--enable-encoder=h264_rkmpp \
|
--enable-encoder=h264_rkmpp \
|
||||||
--enable-encoder=hevc_rkmpp \
|
--enable-encoder=hevc_rkmpp \
|
||||||
--enable-encoder=h264_v4l2m2m \
|
--enable-encoder=h264_v4l2m2m \
|
||||||
@@ -194,14 +238,22 @@ RUN rustup target add aarch64-unknown-linux-gnu
|
|||||||
# Create symlink for mold to work with cross-compiler
|
# Create symlink for mold to work with cross-compiler
|
||||||
RUN ln -s /usr/bin/mold /usr/bin/aarch64-linux-gnu-ld.mold
|
RUN ln -s /usr/bin/mold /usr/bin/aarch64-linux-gnu-ld.mold
|
||||||
|
|
||||||
|
# Copy entrypoint script
|
||||||
|
COPY build/cross/entrypoint.sh /usr/local/bin/cross-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/cross-entrypoint.sh
|
||||||
|
|
||||||
# Configure environment for cross-compilation
|
# Configure environment for cross-compilation
|
||||||
# Use PKG_CONFIG_LIBDIR to completely replace default search paths
|
|
||||||
# This ensures we use our custom-built FFmpeg instead of system FFmpeg
|
|
||||||
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
|
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
|
||||||
CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \
|
CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \
|
||||||
CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \
|
CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \
|
||||||
AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar \
|
AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar \
|
||||||
PKG_CONFIG_LIBDIR=/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig \
|
PKG_CONFIG_LIBDIR=/opt/one-kvm-libs/aarch64-linux-gnu/lib/pkgconfig:/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig \
|
||||||
PKG_CONFIG_PATH="" \
|
PKG_CONFIG_PATH="" \
|
||||||
PKG_CONFIG_ALLOW_CROSS=1 \
|
PKG_CONFIG_ALLOW_CROSS=1 \
|
||||||
|
LIBRARY_PATH="/opt/one-kvm-libs/aarch64-linux-gnu/lib" \
|
||||||
|
CPATH="/opt/one-kvm-libs/aarch64-linux-gnu/include" \
|
||||||
|
FFMPEG_STATIC=1 \
|
||||||
|
LIBYUV_STATIC=1 \
|
||||||
RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=mold"
|
RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc -C link-arg=-fuse-ld=mold"
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/cross-entrypoint.sh"]
|
||||||
|
|||||||
@@ -28,37 +28,79 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
git \
|
git \
|
||||||
libclang-dev \
|
libclang-dev \
|
||||||
llvm \
|
llvm \
|
||||||
protobuf-compiler \
|
|
||||||
mold \
|
mold \
|
||||||
meson \
|
meson \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
wget \
|
wget \
|
||||||
|
file \
|
||||||
gcc-arm-linux-gnueabihf \
|
gcc-arm-linux-gnueabihf \
|
||||||
g++-arm-linux-gnueabihf \
|
g++-arm-linux-gnueabihf \
|
||||||
libc6-dev-armhf-cross \
|
libc6-dev-armhf-cross \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install ARMv7 development libraries (without system ffmpeg)
|
# Install ARMv7 development libraries (without VAAPI/X11 for ARM)
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
libssl-dev:armhf \
|
|
||||||
libasound2-dev:armhf \
|
libasound2-dev:armhf \
|
||||||
libv4l-dev:armhf \
|
libv4l-dev:armhf \
|
||||||
libudev-dev:armhf \
|
libudev-dev:armhf \
|
||||||
zlib1g-dev:armhf \
|
zlib1g-dev:armhf \
|
||||||
libjpeg62-turbo-dev:armhf \
|
# Note: libjpeg-turbo, libyuv, libvpx are built from source below for static linking
|
||||||
libyuv-dev:armhf \
|
|
||||||
libx264-dev:armhf \
|
libx264-dev:armhf \
|
||||||
libx265-dev:armhf \
|
libx265-dev:armhf \
|
||||||
libvpx-dev:armhf \
|
|
||||||
libopus-dev:armhf \
|
libopus-dev:armhf \
|
||||||
libva-dev:armhf \
|
|
||||||
libdrm-dev:armhf \
|
libdrm-dev:armhf \
|
||||||
libx11-dev:armhf \
|
|
||||||
libxcb1-dev:armhf \
|
|
||||||
libxau-dev:armhf \
|
|
||||||
libxdmcp-dev:armhf \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Build static libjpeg-turbo from source (cross-compile for ARMv7)
|
||||||
|
RUN git clone --depth 1 https://github.com/libjpeg-turbo/libjpeg-turbo /tmp/libjpeg-turbo \
|
||||||
|
&& cd /tmp/libjpeg-turbo \
|
||||||
|
&& cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
|
-DCMAKE_SYSTEM_NAME=Linux \
|
||||||
|
-DCMAKE_SYSTEM_PROCESSOR=arm \
|
||||||
|
-DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||||
|
-DENABLE_SHARED=OFF -DENABLE_STATIC=ON \
|
||||||
|
&& cmake --build build -j$(nproc) \
|
||||||
|
&& cmake --install build \
|
||||||
|
&& rm -rf /tmp/libjpeg-turbo
|
||||||
|
|
||||||
|
# Build static libyuv from source (cross-compile for ARMv7)
|
||||||
|
RUN git clone --depth 1 https://github.com/lemenkov/libyuv /tmp/libyuv \
|
||||||
|
&& cd /tmp/libyuv \
|
||||||
|
&& mkdir build && cd build \
|
||||||
|
&& cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
|
-DCMAKE_SYSTEM_NAME=Linux \
|
||||||
|
-DCMAKE_SYSTEM_PROCESSOR=arm \
|
||||||
|
-DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc \
|
||||||
|
-DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ \
|
||||||
|
-DCMAKE_PREFIX_PATH=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||||
|
&& make -j$(nproc) \
|
||||||
|
&& mkdir -p /opt/one-kvm-libs/armv7-linux-gnueabihf/lib \
|
||||||
|
&& cp libyuv.a /opt/one-kvm-libs/armv7-linux-gnueabihf/lib/ \
|
||||||
|
&& cp -r ../include /opt/one-kvm-libs/armv7-linux-gnueabihf/ \
|
||||||
|
&& rm -rf /tmp/libyuv
|
||||||
|
|
||||||
|
# Build static libvpx from source (cross-compile for ARMv7)
|
||||||
|
# CC/CXX/LD/AR must be environment variables, not configure arguments
|
||||||
|
RUN git clone --depth 1 https://github.com/webmproject/libvpx /tmp/libvpx \
|
||||||
|
&& cd /tmp/libvpx \
|
||||||
|
&& export CC=arm-linux-gnueabihf-gcc \
|
||||||
|
&& export CXX=arm-linux-gnueabihf-g++ \
|
||||||
|
&& export LD=arm-linux-gnueabihf-ld \
|
||||||
|
&& export AR=arm-linux-gnueabihf-ar \
|
||||||
|
&& export CROSS=arm-linux-gnueabihf- \
|
||||||
|
&& ./configure --prefix=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||||
|
--target=armv7-linux-gcc \
|
||||||
|
--enable-static --disable-shared --enable-pic \
|
||||||
|
--disable-examples --disable-tools --disable-docs \
|
||||||
|
--disable-unit-tests \
|
||||||
|
&& make -j$(nproc) \
|
||||||
|
&& echo "=== libvpx: Checking architecture ===" \
|
||||||
|
&& file libvpx.a \
|
||||||
|
&& make install \
|
||||||
|
&& rm -rf /tmp/libvpx
|
||||||
|
|
||||||
# Download and build FFmpeg with RKMPP support
|
# Download and build FFmpeg with RKMPP support
|
||||||
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||||
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
||||||
@@ -73,7 +115,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
-DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ \
|
-DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ \
|
||||||
-DCMAKE_INSTALL_PREFIX=/usr/arm-linux-gnueabihf \
|
-DCMAKE_INSTALL_PREFIX=/usr/arm-linux-gnueabihf \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DBUILD_SHARED_LIBS=ON \
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
-DBUILD_TEST=OFF \
|
-DBUILD_TEST=OFF \
|
||||||
&& make -j$(nproc) \
|
&& make -j$(nproc) \
|
||||||
&& make install \
|
&& make install \
|
||||||
@@ -94,6 +136,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
&& meson setup build --prefix=/usr/arm-linux-gnueabihf --libdir=lib \
|
&& meson setup build --prefix=/usr/arm-linux-gnueabihf --libdir=lib \
|
||||||
--cross-file=/tmp/armhf-cross.txt \
|
--cross-file=/tmp/armhf-cross.txt \
|
||||||
--buildtype=release \
|
--buildtype=release \
|
||||||
|
--default-library=static \
|
||||||
-Dcpp_args=-fpermissive \
|
-Dcpp_args=-fpermissive \
|
||||||
-Dlibdrm=false \
|
-Dlibdrm=false \
|
||||||
-Dlibrga_demo=false \
|
-Dlibrga_demo=false \
|
||||||
@@ -102,7 +145,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
&& cd .. \
|
&& cd .. \
|
||||||
# Create pkg-config wrapper for cross-compilation
|
# Create pkg-config wrapper for cross-compilation
|
||||||
&& echo '#!/bin/sh' > /tmp/armhf-pkg-config \
|
&& echo '#!/bin/sh' > /tmp/armhf-pkg-config \
|
||||||
&& echo 'export PKG_CONFIG_LIBDIR=/usr/arm-linux-gnueabihf/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig' >> /tmp/armhf-pkg-config \
|
&& echo 'export PKG_CONFIG_LIBDIR=/opt/one-kvm-libs/armv7-linux-gnueabihf/lib/pkgconfig:/usr/arm-linux-gnueabihf/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig' >> /tmp/armhf-pkg-config \
|
||||||
&& echo 'export PKG_CONFIG_PATH=""' >> /tmp/armhf-pkg-config \
|
&& echo 'export PKG_CONFIG_PATH=""' >> /tmp/armhf-pkg-config \
|
||||||
&& echo 'export PKG_CONFIG_SYSROOT_DIR=""' >> /tmp/armhf-pkg-config \
|
&& echo 'export PKG_CONFIG_SYSROOT_DIR=""' >> /tmp/armhf-pkg-config \
|
||||||
&& echo 'exec pkg-config "$@"' >> /tmp/armhf-pkg-config \
|
&& echo 'exec pkg-config "$@"' >> /tmp/armhf-pkg-config \
|
||||||
@@ -110,7 +153,7 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
# Build FFmpeg with RKMPP (minimal build for encoding only)
|
# Build FFmpeg with RKMPP (minimal build for encoding only)
|
||||||
&& cd ffmpeg-rockchip \
|
&& cd ffmpeg-rockchip \
|
||||||
&& ./configure \
|
&& ./configure \
|
||||||
--prefix=/usr/arm-linux-gnueabihf \
|
--prefix=/opt/one-kvm-libs/armv7-linux-gnueabihf \
|
||||||
--cross-prefix=arm-linux-gnueabihf- \
|
--cross-prefix=arm-linux-gnueabihf- \
|
||||||
--arch=arm \
|
--arch=arm \
|
||||||
--target-os=linux \
|
--target-os=linux \
|
||||||
@@ -118,13 +161,14 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
--pkg-config=/tmp/armhf-pkg-config \
|
--pkg-config=/tmp/armhf-pkg-config \
|
||||||
--enable-gpl \
|
--enable-gpl \
|
||||||
--enable-version3 \
|
--enable-version3 \
|
||||||
--enable-shared \
|
--disable-shared \
|
||||||
--disable-static \
|
--enable-static \
|
||||||
# Hardware acceleration
|
--enable-pic \
|
||||||
|
# Hardware acceleration (ARM: RKMPP + V4L2, no VAAPI)
|
||||||
--enable-libdrm \
|
--enable-libdrm \
|
||||||
--enable-rkmpp \
|
--enable-rkmpp \
|
||||||
--enable-rkrga \
|
--enable-rkrga \
|
||||||
--enable-vaapi \
|
--disable-vaapi \
|
||||||
--enable-v4l2-m2m \
|
--enable-v4l2-m2m \
|
||||||
# Software encoding libraries
|
# Software encoding libraries
|
||||||
--enable-libx264 \
|
--enable-libx264 \
|
||||||
@@ -151,10 +195,6 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
--disable-decoders \
|
--disable-decoders \
|
||||||
# Disable all encoders, enable only needed ones
|
# Disable all encoders, enable only needed ones
|
||||||
--disable-encoders \
|
--disable-encoders \
|
||||||
--enable-encoder=h264_vaapi \
|
|
||||||
--enable-encoder=hevc_vaapi \
|
|
||||||
--enable-encoder=vp8_vaapi \
|
|
||||||
--enable-encoder=vp9_vaapi \
|
|
||||||
--enable-encoder=h264_rkmpp \
|
--enable-encoder=h264_rkmpp \
|
||||||
--enable-encoder=hevc_rkmpp \
|
--enable-encoder=hevc_rkmpp \
|
||||||
--enable-encoder=h264_v4l2m2m \
|
--enable-encoder=h264_v4l2m2m \
|
||||||
@@ -194,14 +234,22 @@ RUN rustup target add armv7-unknown-linux-gnueabihf
|
|||||||
# Create symlink for mold to work with cross-compiler
|
# Create symlink for mold to work with cross-compiler
|
||||||
RUN ln -s /usr/bin/mold /usr/bin/arm-linux-gnueabihf-ld.mold
|
RUN ln -s /usr/bin/mold /usr/bin/arm-linux-gnueabihf-ld.mold
|
||||||
|
|
||||||
|
# Copy entrypoint script
|
||||||
|
COPY build/cross/entrypoint.sh /usr/local/bin/cross-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/cross-entrypoint.sh
|
||||||
|
|
||||||
# Configure environment for cross-compilation
|
# Configure environment for cross-compilation
|
||||||
# Use PKG_CONFIG_LIBDIR to completely replace default search paths
|
|
||||||
# This ensures we use our custom-built FFmpeg instead of system FFmpeg
|
|
||||||
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
|
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
|
||||||
CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \
|
CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc \
|
||||||
CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ \
|
CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++ \
|
||||||
AR_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-ar \
|
AR_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-ar \
|
||||||
PKG_CONFIG_LIBDIR=/usr/arm-linux-gnueabihf/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig \
|
PKG_CONFIG_LIBDIR=/opt/one-kvm-libs/armv7-linux-gnueabihf/lib/pkgconfig:/usr/arm-linux-gnueabihf/lib/pkgconfig:/usr/lib/arm-linux-gnueabihf/pkgconfig \
|
||||||
PKG_CONFIG_PATH="" \
|
PKG_CONFIG_PATH="" \
|
||||||
PKG_CONFIG_ALLOW_CROSS=1 \
|
PKG_CONFIG_ALLOW_CROSS=1 \
|
||||||
|
LIBRARY_PATH="/opt/one-kvm-libs/armv7-linux-gnueabihf/lib" \
|
||||||
|
CPATH="/opt/one-kvm-libs/armv7-linux-gnueabihf/include" \
|
||||||
|
FFMPEG_STATIC=1 \
|
||||||
|
LIBYUV_STATIC=1 \
|
||||||
RUSTFLAGS="-C linker=arm-linux-gnueabihf-gcc -C link-arg=-fuse-ld=mold"
|
RUSTFLAGS="-C linker=arm-linux-gnueabihf-gcc -C link-arg=-fuse-ld=mold"
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/cross-entrypoint.sh"]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
ENV PATH="/root/.cargo/bin:${PATH}"
|
ENV PATH="/root/.cargo/bin:${PATH}"
|
||||||
|
|
||||||
# Install build dependencies
|
# Install build dependencies
|
||||||
|
# Note: libyuv, libvpx are built from source below for static linking
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
# Build tools
|
# Build tools
|
||||||
build-essential \
|
build-essential \
|
||||||
@@ -27,8 +28,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
git \
|
git \
|
||||||
libclang-dev \
|
libclang-dev \
|
||||||
llvm \
|
llvm \
|
||||||
protobuf-compiler \
|
|
||||||
libssl-dev \
|
|
||||||
mold \
|
mold \
|
||||||
wget \
|
wget \
|
||||||
# Core system libraries
|
# Core system libraries
|
||||||
@@ -36,37 +35,69 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
libv4l-dev \
|
libv4l-dev \
|
||||||
libudev-dev \
|
libudev-dev \
|
||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
# Video/image processing
|
# Note: libjpeg-turbo is built from source below for static linking
|
||||||
libjpeg62-turbo-dev \
|
# Video codec libraries (dynamic, for software fallback)
|
||||||
libyuv-dev \
|
|
||||||
# Video codec libraries (for FFmpeg build)
|
|
||||||
libx264-dev \
|
libx264-dev \
|
||||||
libx265-dev \
|
libx265-dev \
|
||||||
libvpx-dev \
|
|
||||||
# Audio codec
|
# Audio codec
|
||||||
libopus-dev \
|
libopus-dev \
|
||||||
# Hardware acceleration
|
# Hardware acceleration
|
||||||
libva-dev \
|
libva-dev \
|
||||||
libdrm-dev \
|
libdrm-dev \
|
||||||
libmfx-dev \
|
libmfx-dev \
|
||||||
# X11 libraries
|
# X11 libraries (for VAAPI)
|
||||||
libx11-dev \
|
libx11-dev \
|
||||||
libxcb1-dev \
|
libxcb1-dev \
|
||||||
libxau-dev \
|
libxau-dev \
|
||||||
libxdmcp-dev \
|
libxdmcp-dev \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Build static libjpeg-turbo from source (needed by libyuv)
|
||||||
|
RUN git clone --depth 1 https://github.com/libjpeg-turbo/libjpeg-turbo /tmp/libjpeg-turbo \
|
||||||
|
&& cd /tmp/libjpeg-turbo \
|
||||||
|
&& cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||||
|
-DENABLE_SHARED=OFF -DENABLE_STATIC=ON \
|
||||||
|
&& cmake --build build -j$(nproc) \
|
||||||
|
&& cmake --install build \
|
||||||
|
&& rm -rf /tmp/libjpeg-turbo
|
||||||
|
|
||||||
|
# Build static libyuv from source (uses libjpeg-turbo headers)
|
||||||
|
RUN git clone --depth 1 https://github.com/lemenkov/libyuv /tmp/libyuv \
|
||||||
|
&& cd /tmp/libyuv \
|
||||||
|
&& mkdir build && cd build \
|
||||||
|
&& cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
|
||||||
|
-DCMAKE_PREFIX_PATH=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||||
|
&& make -j$(nproc) \
|
||||||
|
&& mkdir -p /opt/one-kvm-libs/x86_64-linux-gnu/lib \
|
||||||
|
&& cp libyuv.a /opt/one-kvm-libs/x86_64-linux-gnu/lib/ \
|
||||||
|
&& cp -r ../include /opt/one-kvm-libs/x86_64-linux-gnu/ \
|
||||||
|
&& rm -rf /tmp/libyuv
|
||||||
|
|
||||||
|
# Build static libvpx from source
|
||||||
|
RUN git clone --depth 1 https://github.com/webmproject/libvpx /tmp/libvpx \
|
||||||
|
&& cd /tmp/libvpx \
|
||||||
|
&& ./configure --prefix=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||||
|
--enable-static --disable-shared --enable-pic \
|
||||||
|
--disable-examples --disable-tools --disable-docs \
|
||||||
|
&& make -j$(nproc) \
|
||||||
|
&& make install \
|
||||||
|
&& rm -rf /tmp/libvpx
|
||||||
|
|
||||||
# Download and build FFmpeg with minimal configuration for encoding only
|
# Download and build FFmpeg with minimal configuration for encoding only
|
||||||
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
||||||
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
&& wget -q https://files.mofeng.run/src/image/other/ffmpeg.tar.gz \
|
||||||
&& tar -xzf ffmpeg.tar.gz \
|
&& tar -xzf ffmpeg.tar.gz \
|
||||||
&& cd ffmpeg/ffmpeg-rockchip \
|
&& cd ffmpeg/ffmpeg-rockchip \
|
||||||
|
&& export PKG_CONFIG_PATH="/opt/one-kvm-libs/x86_64-linux-gnu/lib/pkgconfig:$PKG_CONFIG_PATH" \
|
||||||
&& ./configure \
|
&& ./configure \
|
||||||
--prefix=/usr/local \
|
--prefix=/opt/one-kvm-libs/x86_64-linux-gnu \
|
||||||
--enable-gpl \
|
--enable-gpl \
|
||||||
--enable-version3 \
|
--enable-version3 \
|
||||||
--enable-shared \
|
--disable-shared \
|
||||||
--disable-static \
|
--enable-static \
|
||||||
|
--enable-pic \
|
||||||
# Hardware acceleration
|
# Hardware acceleration
|
||||||
--enable-libdrm \
|
--enable-libdrm \
|
||||||
--enable-vaapi \
|
--enable-vaapi \
|
||||||
@@ -128,13 +159,22 @@ RUN mkdir -p /tmp/ffmpeg-build && cd /tmp/ffmpeg-build \
|
|||||||
--disable-debug \
|
--disable-debug \
|
||||||
&& make -j$(nproc) \
|
&& make -j$(nproc) \
|
||||||
&& make install \
|
&& make install \
|
||||||
&& ldconfig \
|
|
||||||
&& cd / \
|
&& cd / \
|
||||||
&& rm -rf /tmp/ffmpeg-build
|
&& rm -rf /tmp/ffmpeg-build
|
||||||
|
|
||||||
# Add Rust target
|
# Add Rust target
|
||||||
RUN rustup target add x86_64-unknown-linux-gnu
|
RUN rustup target add x86_64-unknown-linux-gnu
|
||||||
|
|
||||||
# Configure mold as the linker and use custom FFmpeg
|
# Copy entrypoint script
|
||||||
|
COPY build/cross/entrypoint.sh /usr/local/bin/cross-entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/cross-entrypoint.sh
|
||||||
|
|
||||||
|
# Configure environment for static linking
|
||||||
ENV RUSTFLAGS="-C link-arg=-fuse-ld=mold" \
|
ENV RUSTFLAGS="-C link-arg=-fuse-ld=mold" \
|
||||||
PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
PKG_CONFIG_PATH="/opt/one-kvm-libs/x86_64-linux-gnu/lib/pkgconfig" \
|
||||||
|
LIBRARY_PATH="/opt/one-kvm-libs/x86_64-linux-gnu/lib" \
|
||||||
|
CPATH="/opt/one-kvm-libs/x86_64-linux-gnu/include" \
|
||||||
|
FFMPEG_STATIC=1 \
|
||||||
|
LIBYUV_STATIC=1
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/cross-entrypoint.sh"]
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ RustDesk 模块实现 RustDesk 协议集成,允许使用标准 RustDesk 客户
|
|||||||
- 视频/音频/HID 转换
|
- 视频/音频/HID 转换
|
||||||
- 端到端加密 (Curve25519 + XSalsa20-Poly1305)
|
- 端到端加密 (Curve25519 + XSalsa20-Poly1305)
|
||||||
- 签名认证 (Ed25519)
|
- 签名认证 (Ed25519)
|
||||||
- 公共服务器支持 (通过 secrets.toml)
|
|
||||||
- 动态编码器协商 (H264/H265/VP8/VP9)
|
- 动态编码器协商 (H264/H265/VP8/VP9)
|
||||||
- 输入节流 (防止 HID EAGAIN)
|
- 输入节流 (防止 HID EAGAIN)
|
||||||
- CapsLock 状态同步
|
- CapsLock 状态同步
|
||||||
@@ -660,7 +659,7 @@ pub struct RustDeskConfig {
|
|||||||
|
|
||||||
/// Rendezvous 服务器地址 (hbbs)
|
/// Rendezvous 服务器地址 (hbbs)
|
||||||
/// 格式: "rs.example.com" 或 "192.168.1.100:21116"
|
/// 格式: "rs.example.com" 或 "192.168.1.100:21116"
|
||||||
/// 如果为空,使用 secrets.toml 中配置的公共服务器
|
/// 必填项 - 不再提供公共服务器,需自行配置
|
||||||
pub rendezvous_server: String,
|
pub rendezvous_server: String,
|
||||||
|
|
||||||
/// 中继服务器地址 (hbbr),默认与 rendezvous 同主机
|
/// 中继服务器地址 (hbbr),默认与 rendezvous 同主机
|
||||||
@@ -703,15 +702,9 @@ impl RustDeskConfig {
|
|||||||
/// 检查配置是否有效
|
/// 检查配置是否有效
|
||||||
pub fn is_valid(&self) -> bool;
|
pub fn is_valid(&self) -> bool;
|
||||||
|
|
||||||
/// 检查是否使用公共服务器
|
|
||||||
pub fn is_using_public_server(&self) -> bool;
|
|
||||||
|
|
||||||
/// 获取有效的 Rendezvous 服务器地址
|
/// 获取有效的 Rendezvous 服务器地址
|
||||||
pub fn effective_rendezvous_server(&self) -> &str;
|
pub fn effective_rendezvous_server(&self) -> &str;
|
||||||
|
|
||||||
/// 获取公共服务器信息 (如果配置了)
|
|
||||||
pub fn public_server_info() -> Option<PublicServerInfo>;
|
|
||||||
|
|
||||||
/// 获取带默认端口的 Rendezvous 地址
|
/// 获取带默认端口的 Rendezvous 地址
|
||||||
pub fn rendezvous_addr(&self) -> String;
|
pub fn rendezvous_addr(&self) -> String;
|
||||||
|
|
||||||
@@ -721,14 +714,6 @@ impl RustDeskConfig {
|
|||||||
/// 确保 UUID 存在 (自动生成并标记需要保存)
|
/// 确保 UUID 存在 (自动生成并标记需要保存)
|
||||||
pub fn ensure_uuid(&mut self) -> ([u8; 16], bool);
|
pub fn ensure_uuid(&mut self) -> ([u8; 16], bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 公共服务器信息
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
#[typeshare]
|
|
||||||
pub struct PublicServerInfo {
|
|
||||||
pub server: String, // 服务器地址
|
|
||||||
pub public_key: String, // 公钥 (Base64)
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 配置文件示例
|
### 配置文件示例
|
||||||
@@ -744,23 +729,7 @@ device_password = "mypassword"
|
|||||||
# 密钥和 UUID 由程序自动生成和保存
|
# 密钥和 UUID 由程序自动生成和保存
|
||||||
```
|
```
|
||||||
|
|
||||||
**使用公共服务器:**
|
**注意**: 不再提供公共服务器,需自行配置 RustDesk 服务器。
|
||||||
```toml
|
|
||||||
[rustdesk]
|
|
||||||
enabled = true
|
|
||||||
rendezvous_server = "" # 留空使用 secrets.toml 中的公共服务器
|
|
||||||
device_id = "123456789"
|
|
||||||
device_password = "mypassword"
|
|
||||||
```
|
|
||||||
|
|
||||||
**secrets.toml 公共服务器配置:**
|
|
||||||
```toml
|
|
||||||
[rustdesk]
|
|
||||||
# 公共服务器配置 (可选)
|
|
||||||
public_server = "rs-ny.rustdesk.com"
|
|
||||||
public_key = "xxx...base64...xxx"
|
|
||||||
relay_key = "xxx...key...xxx"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -868,7 +837,7 @@ pub enum RustDeskError {
|
|||||||
```rust
|
```rust
|
||||||
let config = RustDeskConfig {
|
let config = RustDeskConfig {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
rendezvous_server: "".to_string(), // 使用公共服务器
|
rendezvous_server: "hbbs.example.com:21116".to_string(), // 必填 - 配置您的服务器
|
||||||
device_id: "123456789".to_string(),
|
device_id: "123456789".to_string(),
|
||||||
device_password: "mypassword".to_string(),
|
device_password: "mypassword".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -1271,7 +1240,7 @@ docker run -d --name hbbr \
|
|||||||
| 协议 | RustDesk Protocol | 同 |
|
| 协议 | RustDesk Protocol | 同 |
|
||||||
| P2P | 支持 | 支持 |
|
| P2P | 支持 | 支持 |
|
||||||
| 中继 | 支持 | 提供中继服务 |
|
| 中继 | 支持 | 提供中继服务 |
|
||||||
| 公共服务器 | 可配置 (secrets.toml) | N/A |
|
| 公共服务器 | 不提供,需自建 | N/A |
|
||||||
| 多连接 | 支持 | N/A |
|
| 多连接 | 支持 | N/A |
|
||||||
| 输入节流 | 60Hz 限流 | 无限制 |
|
| 输入节流 | 60Hz 限流 | 无限制 |
|
||||||
|
|
||||||
|
|||||||
@@ -97,10 +97,76 @@ mod ffmpeg {
|
|||||||
build_ffmpeg_ram(builder);
|
build_ffmpeg_ram(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Link system FFmpeg using pkg-config (for Linux development)
|
/// Link system FFmpeg using pkg-config or custom path
|
||||||
|
/// Supports both static and dynamic linking based on FFMPEG_STATIC env var
|
||||||
fn link_system_ffmpeg(builder: &mut Build) {
|
fn link_system_ffmpeg(builder: &mut Build) {
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
|
// Check if static linking is requested
|
||||||
|
let use_static = std::env::var("FFMPEG_STATIC").map(|v| v == "1").unwrap_or(false);
|
||||||
|
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
|
||||||
|
|
||||||
|
// Try custom library path first:
|
||||||
|
// 1. Check ONE_KVM_LIBS_PATH environment variable (explicit override)
|
||||||
|
// 2. Fall back to architecture-based detection
|
||||||
|
let custom_lib_path = if let Ok(path) = std::env::var("ONE_KVM_LIBS_PATH") {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
match target_arch.as_str() {
|
||||||
|
"x86_64" => "/opt/one-kvm-libs/x86_64-linux-gnu",
|
||||||
|
"aarch64" => "/opt/one-kvm-libs/aarch64-linux-gnu",
|
||||||
|
"arm" => "/opt/one-kvm-libs/armv7-linux-gnueabihf",
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if our custom FFmpeg exists
|
||||||
|
if !custom_lib_path.is_empty() {
|
||||||
|
let lib_dir = Path::new(&custom_lib_path).join("lib");
|
||||||
|
let include_dir = Path::new(&custom_lib_path).join("include");
|
||||||
|
let avcodec_lib = lib_dir.join("libavcodec.a");
|
||||||
|
|
||||||
|
if avcodec_lib.exists() {
|
||||||
|
println!("cargo:info=Using custom FFmpeg from {}", custom_lib_path);
|
||||||
|
println!("cargo:rustc-link-search=native={}", lib_dir.display());
|
||||||
|
builder.include(&include_dir);
|
||||||
|
|
||||||
|
// Static link FFmpeg core libraries
|
||||||
|
println!("cargo:rustc-link-lib=static=avcodec");
|
||||||
|
println!("cargo:rustc-link-lib=static=avutil");
|
||||||
|
|
||||||
|
// Link hardware acceleration dependencies (dynamic)
|
||||||
|
// These vary by architecture
|
||||||
|
if target_arch == "x86_64" {
|
||||||
|
// VAAPI for x86_64
|
||||||
|
println!("cargo:rustc-link-lib=va");
|
||||||
|
println!("cargo:rustc-link-lib=va-drm");
|
||||||
|
println!("cargo:rustc-link-lib=va-x11"); // Required for vaGetDisplay
|
||||||
|
println!("cargo:rustc-link-lib=mfx");
|
||||||
|
} else {
|
||||||
|
// RKMPP for ARM
|
||||||
|
println!("cargo:rustc-link-lib=rockchip_mpp");
|
||||||
|
println!("cargo:rustc-link-lib=rga");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Software codec dependencies (dynamic - GPL)
|
||||||
|
println!("cargo:rustc-link-lib=x264");
|
||||||
|
println!("cargo:rustc-link-lib=x265");
|
||||||
|
|
||||||
|
// VPX - check if static version exists in our custom path
|
||||||
|
let vpx_static = lib_dir.join("libvpx.a");
|
||||||
|
if vpx_static.exists() {
|
||||||
|
println!("cargo:rustc-link-lib=static=vpx");
|
||||||
|
} else {
|
||||||
|
println!("cargo:rustc-link-lib=vpx");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to pkg-config
|
||||||
// Only need libavcodec and libavutil for encoding
|
// Only need libavcodec and libavutil for encoding
|
||||||
let libs = ["libavcodec", "libavutil"];
|
let libs = ["libavcodec", "libavutil"];
|
||||||
|
|
||||||
@@ -120,9 +186,15 @@ mod ffmpeg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get libs - always use dynamic linking on Linux
|
// Get libs - use --static flag for static linking
|
||||||
|
let pkg_config_args = if use_static {
|
||||||
|
vec!["--static", "--libs", lib]
|
||||||
|
} else {
|
||||||
|
vec!["--libs", lib]
|
||||||
|
};
|
||||||
|
|
||||||
if let Ok(output) = Command::new("pkg-config")
|
if let Ok(output) = Command::new("pkg-config")
|
||||||
.args(["--libs", lib])
|
.args(&pkg_config_args)
|
||||||
.output()
|
.output()
|
||||||
{
|
{
|
||||||
if output.status.success() {
|
if output.status.success() {
|
||||||
@@ -131,7 +203,18 @@ mod ffmpeg {
|
|||||||
if flag.starts_with("-L") {
|
if flag.starts_with("-L") {
|
||||||
println!("cargo:rustc-link-search=native={}", &flag[2..]);
|
println!("cargo:rustc-link-search=native={}", &flag[2..]);
|
||||||
} else if flag.starts_with("-l") {
|
} else if flag.starts_with("-l") {
|
||||||
println!("cargo:rustc-link-lib={}", &flag[2..]);
|
let lib_name = &flag[2..];
|
||||||
|
if use_static {
|
||||||
|
// For static linking, link FFmpeg libs statically, others dynamically
|
||||||
|
if lib_name.starts_with("av") || lib_name == "swresample" {
|
||||||
|
println!("cargo:rustc-link-lib=static={}", lib_name);
|
||||||
|
} else {
|
||||||
|
// Runtime libraries (va, drm, etc.) must be dynamic
|
||||||
|
println!("cargo:rustc-link-lib={}", lib_name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("cargo:rustc-link-lib={}", lib_name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -142,7 +225,11 @@ mod ffmpeg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("cargo:info=Using system FFmpeg via pkg-config (dynamic linking)");
|
if use_static {
|
||||||
|
println!("cargo:info=Using system FFmpeg via pkg-config (static linking)");
|
||||||
|
} else {
|
||||||
|
println!("cargo:info=Using system FFmpeg via pkg-config (dynamic linking)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_vcpkg(builder: &mut Build, mut path: PathBuf) -> PathBuf {
|
fn link_vcpkg(builder: &mut Build, mut path: PathBuf) -> PathBuf {
|
||||||
@@ -203,14 +290,15 @@ mod ffmpeg {
|
|||||||
let dyn_libs: Vec<&str> = if target_os == "windows" {
|
let dyn_libs: Vec<&str> = if target_os == "windows" {
|
||||||
["User32", "bcrypt", "ole32", "advapi32"].to_vec()
|
["User32", "bcrypt", "ole32", "advapi32"].to_vec()
|
||||||
} else if target_os == "linux" {
|
} else if target_os == "linux" {
|
||||||
// Note: VA-API libraries (va, va-drm, va-x11) must remain dynamic
|
// Base libraries for all Linux platforms
|
||||||
// as they load drivers at runtime. Same for libmfx.
|
let mut v = vec!["drm", "stdc++"];
|
||||||
// All dependencies use dynamic linking on Linux.
|
|
||||||
let mut v = vec!["drm", "X11", "stdc++"];
|
|
||||||
|
|
||||||
|
// x86_64: needs X11 for VAAPI and zlib
|
||||||
if target_arch == "x86_64" {
|
if target_arch == "x86_64" {
|
||||||
|
v.push("X11");
|
||||||
v.push("z");
|
v.push("z");
|
||||||
}
|
}
|
||||||
|
// ARM (aarch64, arm): no X11 needed, uses RKMPP/V4L2
|
||||||
v
|
v
|
||||||
} else {
|
} else {
|
||||||
panic!("Unsupported OS: {}. Only Windows and Linux are supported.", target_os);
|
panic!("Unsupported OS: {}. Only Windows and Linux are supported.", target_os);
|
||||||
|
|||||||
@@ -148,40 +148,31 @@ fn link_vcpkg(mut path: PathBuf) -> bool {
|
|||||||
|
|
||||||
println!("cargo:rustc-link-search=native={}", lib_path.display());
|
println!("cargo:rustc-link-search=native={}", lib_path.display());
|
||||||
|
|
||||||
// For Linux: use dynamic linking
|
// Check if static linking is requested via environment variable
|
||||||
#[cfg(target_os = "linux")]
|
let use_static = env::var("LIBYUV_STATIC").map(|v| v == "1").unwrap_or(false);
|
||||||
{
|
|
||||||
println!("cargo:rustc-link-lib=yuv");
|
|
||||||
println!("cargo:rustc-link-lib=jpeg");
|
|
||||||
println!("cargo:rustc-link-lib=stdc++");
|
|
||||||
}
|
|
||||||
|
|
||||||
// For Windows/macOS: keep static linking
|
let static_lib = lib_path.join("libyuv.a");
|
||||||
#[cfg(not(target_os = "linux"))]
|
let jpeg_static = lib_path.join("libjpeg.a");
|
||||||
{
|
let turbojpeg_static = lib_path.join("libturbojpeg.a");
|
||||||
let static_lib = lib_path.join("libyuv.a");
|
|
||||||
let static_lib_win = lib_path.join("yuv.lib");
|
|
||||||
|
|
||||||
if static_lib.exists() || static_lib_win.exists() {
|
|
||||||
println!("cargo:rustc-link-lib=static=yuv");
|
|
||||||
} else {
|
|
||||||
println!("cargo:rustc-link-lib=yuv");
|
|
||||||
}
|
|
||||||
|
|
||||||
let jpeg_static = lib_path.join("libjpeg.a");
|
|
||||||
let jpeg_static_win = lib_path.join("jpeg.lib");
|
|
||||||
let turbojpeg_static = lib_path.join("libturbojpeg.a");
|
|
||||||
|
|
||||||
|
if use_static && static_lib.exists() {
|
||||||
|
// Static linking (for deb packaging)
|
||||||
|
println!("cargo:rustc-link-lib=static=yuv");
|
||||||
if turbojpeg_static.exists() {
|
if turbojpeg_static.exists() {
|
||||||
println!("cargo:rustc-link-lib=static=turbojpeg");
|
println!("cargo:rustc-link-lib=static=turbojpeg");
|
||||||
} else if jpeg_static.exists() || jpeg_static_win.exists() {
|
} else if jpeg_static.exists() {
|
||||||
println!("cargo:rustc-link-lib=static=jpeg");
|
println!("cargo:rustc-link-lib=static=jpeg");
|
||||||
} else {
|
} else {
|
||||||
println!("cargo:rustc-link-lib=jpeg");
|
println!("cargo:rustc-link-lib=jpeg");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
println!("cargo:rustc-link-lib=stdc++");
|
println!("cargo:rustc-link-lib=stdc++");
|
||||||
|
println!("cargo:info=Using libyuv from vcpkg (static linking)");
|
||||||
|
} else {
|
||||||
|
// Dynamic linking (default for development)
|
||||||
|
println!("cargo:rustc-link-lib=yuv");
|
||||||
|
println!("cargo:rustc-link-lib=jpeg");
|
||||||
|
println!("cargo:rustc-link-lib=stdc++");
|
||||||
|
println!("cargo:info=Using libyuv from vcpkg (dynamic linking)");
|
||||||
}
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
@@ -238,21 +229,72 @@ fn link_pkg_config() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn link_system() -> bool {
|
fn link_system() -> bool {
|
||||||
// Try common system library paths (dynamic linking only)
|
// Check if static linking is requested
|
||||||
let lib_paths = [
|
let use_static = env::var("LIBYUV_STATIC").map(|v| v == "1").unwrap_or(false);
|
||||||
|
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
|
||||||
|
|
||||||
|
// Build custom library paths based on target architecture:
|
||||||
|
// 1. Check ONE_KVM_LIBS_PATH environment variable (explicit override)
|
||||||
|
// 2. Fall back to architecture-based detection
|
||||||
|
let custom_lib_path = if let Ok(path) = env::var("ONE_KVM_LIBS_PATH") {
|
||||||
|
format!("{}/lib", path)
|
||||||
|
} else {
|
||||||
|
match target_arch.as_str() {
|
||||||
|
"x86_64" => "/opt/one-kvm-libs/x86_64-linux-gnu/lib",
|
||||||
|
"aarch64" => "/opt/one-kvm-libs/aarch64-linux-gnu/lib",
|
||||||
|
"arm" => "/opt/one-kvm-libs/armv7-linux-gnueabihf/lib",
|
||||||
|
_ => "",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try common system library paths (custom paths first)
|
||||||
|
let mut lib_paths: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
// Add custom build path first (highest priority)
|
||||||
|
if !custom_lib_path.is_empty() {
|
||||||
|
lib_paths.push(custom_lib_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then standard paths
|
||||||
|
lib_paths.extend([
|
||||||
|
"/usr/local/lib", // Custom builds
|
||||||
|
"/usr/local/lib64",
|
||||||
"/usr/lib",
|
"/usr/lib",
|
||||||
"/usr/lib64",
|
"/usr/lib64",
|
||||||
"/usr/local/lib",
|
|
||||||
"/usr/local/lib64",
|
|
||||||
"/usr/lib/x86_64-linux-gnu", // Debian/Ubuntu x86_64
|
"/usr/lib/x86_64-linux-gnu", // Debian/Ubuntu x86_64
|
||||||
"/usr/lib/aarch64-linux-gnu", // Debian/Ubuntu ARM64
|
"/usr/lib/aarch64-linux-gnu", // Debian/Ubuntu ARM64
|
||||||
"/usr/lib/arm-linux-gnueabihf", // Debian/Ubuntu ARMv7
|
"/usr/lib/arm-linux-gnueabihf", // Debian/Ubuntu ARMv7
|
||||||
];
|
].iter().map(|s| s.to_string()));
|
||||||
|
|
||||||
for path in &lib_paths {
|
for path in &lib_paths {
|
||||||
let lib_path = Path::new(path);
|
let lib_path = Path::new(path);
|
||||||
|
let libyuv_static = lib_path.join("libyuv.a");
|
||||||
let libyuv_so = lib_path.join("libyuv.so");
|
let libyuv_so = lib_path.join("libyuv.so");
|
||||||
|
|
||||||
|
// Prefer static library if LIBYUV_STATIC=1
|
||||||
|
if use_static && libyuv_static.exists() {
|
||||||
|
println!("cargo:rustc-link-search=native={}", path);
|
||||||
|
println!("cargo:rustc-link-lib=static=yuv");
|
||||||
|
|
||||||
|
// Check for static libjpeg-turbo in the same directory
|
||||||
|
let turbojpeg_static = lib_path.join("libturbojpeg.a");
|
||||||
|
let jpeg_static = lib_path.join("libjpeg.a");
|
||||||
|
if turbojpeg_static.exists() {
|
||||||
|
println!("cargo:rustc-link-lib=static=turbojpeg");
|
||||||
|
} else if jpeg_static.exists() {
|
||||||
|
println!("cargo:rustc-link-lib=static=jpeg");
|
||||||
|
} else {
|
||||||
|
// Fall back to dynamic jpeg
|
||||||
|
println!("cargo:rustc-link-lib=jpeg");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-lib=stdc++");
|
||||||
|
println!("cargo:info=Using system libyuv from {} (static linking)", path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to dynamic linking
|
||||||
if libyuv_so.exists() {
|
if libyuv_so.exists() {
|
||||||
println!("cargo:rustc-link-search=native={}", path);
|
println!("cargo:rustc-link-search=native={}", path);
|
||||||
println!("cargo:rustc-link-lib=yuv");
|
println!("cargo:rustc-link-lib=yuv");
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
use crate::secrets;
|
|
||||||
|
|
||||||
/// RustDesk configuration
|
/// RustDesk configuration
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@@ -15,9 +13,8 @@ pub struct RustDeskConfig {
|
|||||||
/// Enable RustDesk protocol
|
/// Enable RustDesk protocol
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
|
||||||
/// Rendezvous server address (hbbs), e.g., "rs.example.com" or "192.168.1.100"
|
/// Rendezvous server address (hbbs), e.g., "rs.example.com" or "192.168.1.100:21116"
|
||||||
/// Port defaults to 21116 if not specified
|
/// Required for RustDesk to function
|
||||||
/// If empty, uses the public server from secrets.toml
|
|
||||||
pub rendezvous_server: String,
|
pub rendezvous_server: String,
|
||||||
|
|
||||||
/// Relay server address (hbbr), if different from rendezvous server
|
/// Relay server address (hbbr), if different from rendezvous server
|
||||||
@@ -79,39 +76,17 @@ impl Default for RustDeskConfig {
|
|||||||
|
|
||||||
impl RustDeskConfig {
|
impl RustDeskConfig {
|
||||||
/// Check if the configuration is valid for starting the service
|
/// Check if the configuration is valid for starting the service
|
||||||
/// Returns true if enabled and has a valid server (user-configured or public)
|
/// Returns true if enabled and has a valid server
|
||||||
pub fn is_valid(&self) -> bool {
|
pub fn is_valid(&self) -> bool {
|
||||||
self.enabled
|
self.enabled
|
||||||
&& !self.effective_rendezvous_server().is_empty()
|
&& !self.rendezvous_server.is_empty()
|
||||||
&& !self.device_id.is_empty()
|
&& !self.device_id.is_empty()
|
||||||
&& !self.device_password.is_empty()
|
&& !self.device_password.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if using the public server (user left rendezvous_server empty)
|
/// Get the rendezvous server (user-configured)
|
||||||
pub fn is_using_public_server(&self) -> bool {
|
|
||||||
self.rendezvous_server.is_empty() && secrets::rustdesk::has_public_server()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the effective rendezvous server (user-configured or public fallback)
|
|
||||||
pub fn effective_rendezvous_server(&self) -> &str {
|
pub fn effective_rendezvous_server(&self) -> &str {
|
||||||
if self.rendezvous_server.is_empty() {
|
&self.rendezvous_server
|
||||||
secrets::rustdesk::PUBLIC_SERVER
|
|
||||||
} else {
|
|
||||||
&self.rendezvous_server
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get public server info for display (server address and public key)
|
|
||||||
/// Returns None if no public server is configured
|
|
||||||
pub fn public_server_info() -> Option<PublicServerInfo> {
|
|
||||||
if secrets::rustdesk::has_public_server() {
|
|
||||||
Some(PublicServerInfo {
|
|
||||||
server: secrets::rustdesk::PUBLIC_SERVER.to_string(),
|
|
||||||
public_key: secrets::rustdesk::PUBLIC_KEY.to_string(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a new random device ID
|
/// Generate a new random device ID
|
||||||
@@ -148,8 +123,10 @@ impl RustDeskConfig {
|
|||||||
|
|
||||||
/// Get the rendezvous server address with default port
|
/// Get the rendezvous server address with default port
|
||||||
pub fn rendezvous_addr(&self) -> String {
|
pub fn rendezvous_addr(&self) -> String {
|
||||||
let server = self.effective_rendezvous_server();
|
let server = &self.rendezvous_server;
|
||||||
if server.contains(':') {
|
if server.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else if server.contains(':') {
|
||||||
server.to_string()
|
server.to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("{}:21116", server)
|
format!("{}:21116", server)
|
||||||
@@ -165,8 +142,8 @@ impl RustDeskConfig {
|
|||||||
format!("{}:21117", s)
|
format!("{}:21117", s)
|
||||||
}
|
}
|
||||||
}).or_else(|| {
|
}).or_else(|| {
|
||||||
// Default: same host as effective rendezvous server
|
// Default: same host as rendezvous server
|
||||||
let server = self.effective_rendezvous_server();
|
let server = &self.rendezvous_server;
|
||||||
if !server.is_empty() {
|
if !server.is_empty() {
|
||||||
let host = server.split(':').next().unwrap_or("");
|
let host = server.split(':').next().unwrap_or("");
|
||||||
if !host.is_empty() {
|
if !host.is_empty() {
|
||||||
@@ -181,16 +158,6 @@ impl RustDeskConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Public server information for display to users
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
#[typeshare]
|
|
||||||
pub struct PublicServerInfo {
|
|
||||||
/// Public server address
|
|
||||||
pub server: String,
|
|
||||||
/// Public key for client connection
|
|
||||||
pub public_key: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a random 9-digit device ID
|
/// Generate a random 9-digit device ID
|
||||||
pub fn generate_device_id() -> String {
|
pub fn generate_device_id() -> String {
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -239,6 +206,10 @@ mod tests {
|
|||||||
|
|
||||||
config.rendezvous_server = "example.com:21116".to_string();
|
config.rendezvous_server = "example.com:21116".to_string();
|
||||||
assert_eq!(config.rendezvous_addr(), "example.com:21116");
|
assert_eq!(config.rendezvous_addr(), "example.com:21116");
|
||||||
|
|
||||||
|
// Empty server returns empty string
|
||||||
|
config.rendezvous_server = String::new();
|
||||||
|
assert_eq!(config.rendezvous_addr(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -252,6 +223,10 @@ mod tests {
|
|||||||
// Explicit relay server
|
// Explicit relay server
|
||||||
config.relay_server = Some("relay.example.com".to_string());
|
config.relay_server = Some("relay.example.com".to_string());
|
||||||
assert_eq!(config.relay_addr(), Some("relay.example.com:21117".to_string()));
|
assert_eq!(config.relay_addr(), Some("relay.example.com:21117".to_string()));
|
||||||
|
|
||||||
|
// No rendezvous server, relay is None
|
||||||
|
config.rendezvous_server = String::new();
|
||||||
|
assert_eq!(config.relay_addr(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -262,10 +237,8 @@ mod tests {
|
|||||||
config.rendezvous_server = "custom.example.com".to_string();
|
config.rendezvous_server = "custom.example.com".to_string();
|
||||||
assert_eq!(config.effective_rendezvous_server(), "custom.example.com");
|
assert_eq!(config.effective_rendezvous_server(), "custom.example.com");
|
||||||
|
|
||||||
// When empty, falls back to public server (if configured)
|
// When empty, returns empty
|
||||||
config.rendezvous_server = String::new();
|
config.rendezvous_server = String::new();
|
||||||
// This will return PUBLIC_SERVER from secrets
|
assert_eq!(config.effective_rendezvous_server(), "");
|
||||||
let effective = config.effective_rendezvous_server();
|
|
||||||
assert!(!effective.is_empty() || !secrets::rustdesk::has_public_server());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -239,16 +239,10 @@ impl RustDeskService {
|
|||||||
let config = service_config_punch.clone();
|
let config = service_config_punch.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
// Get relay_key from config, or use public server's relay_key if using public server
|
// Get relay_key from config (no public server fallback)
|
||||||
let relay_key = {
|
let relay_key = {
|
||||||
let cfg = config.read();
|
let cfg = config.read();
|
||||||
cfg.relay_key.clone().unwrap_or_else(|| {
|
cfg.relay_key.clone().unwrap_or_default()
|
||||||
if cfg.is_using_public_server() {
|
|
||||||
crate::secrets::rustdesk::RELAY_KEY.to_string()
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try P2P direct connection first
|
// Try P2P direct connection first
|
||||||
@@ -295,16 +289,10 @@ impl RustDeskService {
|
|||||||
let config = service_config.clone();
|
let config = service_config.clone();
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
// Get relay_key from config, or use public server's relay_key if using public server
|
// Get relay_key from config (no public server fallback)
|
||||||
let relay_key = {
|
let relay_key = {
|
||||||
let cfg = config.read();
|
let cfg = config.read();
|
||||||
cfg.relay_key.clone().unwrap_or_else(|| {
|
cfg.relay_key.clone().unwrap_or_default()
|
||||||
if cfg.is_using_public_server() {
|
|
||||||
crate::secrets::rustdesk::RELAY_KEY.to_string()
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = handle_relay_request(
|
if let Err(e) = handle_relay_request(
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use axum::{extract::State, Json};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::rustdesk::config::{PublicServerInfo, RustDeskConfig};
|
use crate::rustdesk::config::RustDeskConfig;
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
use super::apply::apply_rustdesk_config;
|
use super::apply::apply_rustdesk_config;
|
||||||
@@ -23,8 +23,6 @@ pub struct RustDeskConfigResponse {
|
|||||||
pub has_keypair: bool,
|
pub has_keypair: bool,
|
||||||
/// 是否已设置 relay key
|
/// 是否已设置 relay key
|
||||||
pub has_relay_key: bool,
|
pub has_relay_key: bool,
|
||||||
/// 是否使用公共服务器(用户留空时)
|
|
||||||
pub using_public_server: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&RustDeskConfig> for RustDeskConfigResponse {
|
impl From<&RustDeskConfig> for RustDeskConfigResponse {
|
||||||
@@ -37,7 +35,6 @@ impl From<&RustDeskConfig> for RustDeskConfigResponse {
|
|||||||
has_password: !config.device_password.is_empty(),
|
has_password: !config.device_password.is_empty(),
|
||||||
has_keypair: config.public_key.is_some() && config.private_key.is_some(),
|
has_keypair: config.public_key.is_some() && config.private_key.is_some(),
|
||||||
has_relay_key: config.relay_key.is_some(),
|
has_relay_key: config.relay_key.is_some(),
|
||||||
using_public_server: config.is_using_public_server(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,8 +45,6 @@ pub struct RustDeskStatusResponse {
|
|||||||
pub config: RustDeskConfigResponse,
|
pub config: RustDeskConfigResponse,
|
||||||
pub service_status: String,
|
pub service_status: String,
|
||||||
pub rendezvous_status: Option<String>,
|
pub rendezvous_status: Option<String>,
|
||||||
/// 公共服务器信息(仅当有公共服务器配置时返回)
|
|
||||||
pub public_server: Option<PublicServerInfo>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 获取 RustDesk 配置
|
/// 获取 RustDesk 配置
|
||||||
@@ -73,14 +68,10 @@ pub async fn get_rustdesk_status(State(state): State<Arc<AppState>>) -> Json<Rus
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取公共服务器信息
|
|
||||||
let public_server = RustDeskConfig::public_server_info();
|
|
||||||
|
|
||||||
Json(RustDeskStatusResponse {
|
Json(RustDeskStatusResponse {
|
||||||
config: RustDeskConfigResponse::from(&config),
|
config: RustDeskConfigResponse::from(&config),
|
||||||
service_status,
|
service_status,
|
||||||
rendezvous_status,
|
rendezvous_status,
|
||||||
public_server,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1814,35 +1814,62 @@ pub struct IceServerInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get ICE servers configuration for client-side WebRTC
|
/// Get ICE servers configuration for client-side WebRTC
|
||||||
|
/// Returns user-configured servers, or Google STUN as fallback if none configured
|
||||||
pub async fn webrtc_ice_servers(State(state): State<Arc<AppState>>) -> Json<IceServersResponse> {
|
pub async fn webrtc_ice_servers(State(state): State<Arc<AppState>>) -> Json<IceServersResponse> {
|
||||||
|
use crate::webrtc::config::public_ice;
|
||||||
|
|
||||||
let config = state.config.get();
|
let config = state.config.get();
|
||||||
let mut ice_servers = Vec::new();
|
let mut ice_servers = Vec::new();
|
||||||
|
|
||||||
// Add STUN server
|
// Check if user has configured custom ICE servers
|
||||||
if let Some(ref stun) = config.stream.stun_server {
|
let has_custom_stun = config
|
||||||
if !stun.is_empty() {
|
.stream
|
||||||
|
.stun_server
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| !s.is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
let has_custom_turn = config
|
||||||
|
.stream
|
||||||
|
.turn_server
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| !s.is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if has_custom_stun || has_custom_turn {
|
||||||
|
// Use user-configured ICE servers
|
||||||
|
if let Some(ref stun) = config.stream.stun_server {
|
||||||
|
if !stun.is_empty() {
|
||||||
|
ice_servers.push(IceServerInfo {
|
||||||
|
urls: vec![stun.clone()],
|
||||||
|
username: None,
|
||||||
|
credential: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref turn) = config.stream.turn_server {
|
||||||
|
if !turn.is_empty() {
|
||||||
|
let username = config.stream.turn_username.clone();
|
||||||
|
let credential = config.stream.turn_password.clone();
|
||||||
|
if username.is_some() && credential.is_some() {
|
||||||
|
ice_servers.push(IceServerInfo {
|
||||||
|
urls: vec![turn.clone()],
|
||||||
|
username,
|
||||||
|
credential,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No custom servers configured - use Google STUN as default
|
||||||
|
if let Some(stun) = public_ice::stun_server() {
|
||||||
ice_servers.push(IceServerInfo {
|
ice_servers.push(IceServerInfo {
|
||||||
urls: vec![stun.clone()],
|
urls: vec![stun],
|
||||||
username: None,
|
username: None,
|
||||||
credential: None,
|
credential: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
// Note: TURN servers are not provided - users must configure their own
|
||||||
|
|
||||||
// Add TURN server (with credentials)
|
|
||||||
if let Some(ref turn) = config.stream.turn_server {
|
|
||||||
if !turn.is_empty() {
|
|
||||||
let username = config.stream.turn_username.clone();
|
|
||||||
let credential = config.stream.turn_password.clone();
|
|
||||||
// Only add TURN if credentials are provided
|
|
||||||
if username.is_some() && credential.is_some() {
|
|
||||||
ice_servers.push(IceServerInfo {
|
|
||||||
urls: vec![turn.clone()],
|
|
||||||
username,
|
|
||||||
credential,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(IceServersResponse { ice_servers })
|
Json(IceServersResponse { ice_servers })
|
||||||
|
|||||||
@@ -4,21 +4,21 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::secrets;
|
use crate::secrets;
|
||||||
|
|
||||||
/// Public ICE server utilities
|
/// ICE server utilities - Only provides Google STUN, no TURN
|
||||||
pub mod public_ice {
|
pub mod public_ice {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Check if public ICE servers are configured (at compile time)
|
/// Always returns true (we have Google STUN)
|
||||||
pub fn is_configured() -> bool {
|
pub fn is_configured() -> bool {
|
||||||
secrets::ice::is_configured()
|
secrets::ice::is_configured()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if public TURN servers are configured (requires credentials)
|
/// Always returns false (TURN not provided)
|
||||||
pub fn has_turn() -> bool {
|
pub fn has_turn() -> bool {
|
||||||
secrets::ice::has_turn()
|
secrets::ice::has_turn()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the public STUN server URL
|
/// Get the Google STUN server URL
|
||||||
pub fn stun_server() -> Option<String> {
|
pub fn stun_server() -> Option<String> {
|
||||||
let server = secrets::ice::STUN_SERVER;
|
let server = secrets::ice::STUN_SERVER;
|
||||||
if server.is_empty() {
|
if server.is_empty() {
|
||||||
@@ -28,33 +28,15 @@ pub mod public_ice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get public TURN servers as TurnServer structs
|
/// Always returns empty vector (TURN not provided)
|
||||||
pub fn turn_servers() -> Vec<TurnServer> {
|
pub fn turn_servers() -> Vec<TurnServer> {
|
||||||
if !secrets::ice::has_turn() {
|
vec![]
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
|
|
||||||
let urls: Vec<String> = secrets::ice::TURN_URLS
|
|
||||||
.split(',')
|
|
||||||
.map(|s| s.trim().to_string())
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if urls.is_empty() {
|
|
||||||
return vec![];
|
|
||||||
}
|
|
||||||
|
|
||||||
vec![TurnServer {
|
|
||||||
urls,
|
|
||||||
username: secrets::ice::TURN_USERNAME.to_string(),
|
|
||||||
credential: secrets::ice::TURN_PASSWORD.to_string(),
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all public ICE servers (STUN + TURN) for WebRTC configuration
|
/// Get all public ICE servers (STUN only, no TURN)
|
||||||
pub fn get_all_servers() -> (Vec<String>, Vec<TurnServer>) {
|
pub fn get_all_servers() -> (Vec<String>, Vec<TurnServer>) {
|
||||||
let stun_servers = stun_server().into_iter().collect();
|
let stun_servers = stun_server().into_iter().collect();
|
||||||
let turn_servers = turn_servers();
|
let turn_servers = vec![];
|
||||||
(stun_servers, turn_servers)
|
(stun_servers, turn_servers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -199,46 +199,56 @@ impl PeerConnection {
|
|||||||
pc.on_data_channel(Box::new(move |dc: Arc<RTCDataChannel>| {
|
pc.on_data_channel(Box::new(move |dc: Arc<RTCDataChannel>| {
|
||||||
let data_channel = data_channel.clone();
|
let data_channel = data_channel.clone();
|
||||||
let hid = hid_clone.clone();
|
let hid = hid_clone.clone();
|
||||||
|
let label = dc.label().to_string();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
info!("Data channel opened with HID support: {}", dc.label());
|
// Handle both reliable (hid) and unreliable (hid-unreliable) channels
|
||||||
|
let is_hid_channel = label == "hid" || label == "hid-unreliable";
|
||||||
|
|
||||||
// Store data channel
|
if is_hid_channel {
|
||||||
*data_channel.write().await = Some(dc.clone());
|
info!("HID DataChannel opened: {} (unreliable: {})", label, label == "hid-unreliable");
|
||||||
|
|
||||||
// Set up message handler with HID processing
|
// Store the reliable data channel for sending responses
|
||||||
// Immediately spawn task in tokio runtime for low latency
|
if label == "hid" {
|
||||||
dc.on_message(Box::new(move |msg: DataChannelMessage| {
|
*data_channel.write().await = Some(dc.clone());
|
||||||
let hid = hid.clone();
|
}
|
||||||
|
|
||||||
tokio::spawn(async move {
|
// Set up message handler with HID processing
|
||||||
debug!("DataChannel HID message: {} bytes", msg.data.len());
|
// Both channels use the same HID processing logic
|
||||||
|
dc.on_message(Box::new(move |msg: DataChannelMessage| {
|
||||||
|
let hid = hid.clone();
|
||||||
|
|
||||||
// Parse and process HID message
|
tokio::spawn(async move {
|
||||||
if let Some(event) = parse_hid_message(&msg.data) {
|
debug!("DataChannel HID message: {} bytes", msg.data.len());
|
||||||
match event {
|
|
||||||
HidChannelEvent::Keyboard(kb_event) => {
|
// Parse and process HID message
|
||||||
if let Err(e) = hid.send_keyboard(kb_event).await {
|
if let Some(event) = parse_hid_message(&msg.data) {
|
||||||
debug!("Failed to send keyboard event: {}", e);
|
match event {
|
||||||
|
HidChannelEvent::Keyboard(kb_event) => {
|
||||||
|
if let Err(e) = hid.send_keyboard(kb_event).await {
|
||||||
|
debug!("Failed to send keyboard event: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
HidChannelEvent::Mouse(ms_event) => {
|
||||||
HidChannelEvent::Mouse(ms_event) => {
|
if let Err(e) = hid.send_mouse(ms_event).await {
|
||||||
if let Err(e) = hid.send_mouse(ms_event).await {
|
debug!("Failed to send mouse event: {}", e);
|
||||||
debug!("Failed to send mouse event: {}", e);
|
}
|
||||||
}
|
}
|
||||||
}
|
HidChannelEvent::Consumer(consumer_event) => {
|
||||||
HidChannelEvent::Consumer(consumer_event) => {
|
if let Err(e) = hid.send_consumer(consumer_event).await {
|
||||||
if let Err(e) = hid.send_consumer(consumer_event).await {
|
debug!("Failed to send consumer event: {}", e);
|
||||||
debug!("Failed to send consumer event: {}", e);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Return empty future (actual work is spawned above)
|
// Return empty future (actual work is spawned above)
|
||||||
Box::pin(async {})
|
Box::pin(async {})
|
||||||
}));
|
}));
|
||||||
|
} else {
|
||||||
|
info!("Non-HID DataChannel opened: {}", label);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -256,12 +256,6 @@ export const extensionsApi = {
|
|||||||
|
|
||||||
// ===== RustDesk 配置 API =====
|
// ===== RustDesk 配置 API =====
|
||||||
|
|
||||||
/** 公共服务器信息 */
|
|
||||||
export interface PublicServerInfo {
|
|
||||||
server: string
|
|
||||||
public_key: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/** RustDesk 配置响应 */
|
/** RustDesk 配置响应 */
|
||||||
export interface RustDeskConfigResponse {
|
export interface RustDeskConfigResponse {
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
@@ -271,7 +265,6 @@ export interface RustDeskConfigResponse {
|
|||||||
has_password: boolean
|
has_password: boolean
|
||||||
has_keypair: boolean
|
has_keypair: boolean
|
||||||
has_relay_key: boolean
|
has_relay_key: boolean
|
||||||
using_public_server: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** RustDesk 状态响应 */
|
/** RustDesk 状态响应 */
|
||||||
@@ -279,7 +272,6 @@ export interface RustDeskStatusResponse {
|
|||||||
config: RustDeskConfigResponse
|
config: RustDeskConfigResponse
|
||||||
service_status: string
|
service_status: string
|
||||||
rendezvous_status: string | null
|
rendezvous_status: string | null
|
||||||
public_server: PublicServerInfo | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** RustDesk 配置更新 */
|
/** RustDesk 配置更新 */
|
||||||
|
|||||||
@@ -570,18 +570,17 @@ export default {
|
|||||||
// WebRTC / ICE
|
// WebRTC / ICE
|
||||||
webrtcSettings: 'WebRTC Settings',
|
webrtcSettings: 'WebRTC Settings',
|
||||||
webrtcSettingsDesc: 'Configure STUN/TURN servers for NAT traversal',
|
webrtcSettingsDesc: 'Configure STUN/TURN servers for NAT traversal',
|
||||||
usingPublicIceServers: 'Using public ICE servers',
|
publicIceServersHint: 'Empty uses Google public STUN, configure your own TURN for production',
|
||||||
publicIceServersHint: 'Leave empty to use built-in public STUN/TURN servers for NAT traversal',
|
|
||||||
stunServer: 'STUN Server',
|
stunServer: 'STUN Server',
|
||||||
stunServerPlaceholder: 'stun:stun.l.google.com:19302',
|
stunServerPlaceholder: 'stun:stun.l.google.com:19302',
|
||||||
stunServerHint: 'Custom STUN server (leave empty to use public server)',
|
stunServerHint: 'Custom STUN server (leave empty to use Google public server)',
|
||||||
turnServer: 'TURN Server',
|
turnServer: 'TURN Server',
|
||||||
turnServerPlaceholder: 'turn:turn.example.com:3478',
|
turnServerPlaceholder: 'turn:turn.example.com:3478',
|
||||||
turnServerHint: 'Custom TURN relay server (leave empty to use public server)',
|
turnServerHint: 'Custom TURN relay server (required for production)',
|
||||||
turnUsername: 'TURN Username',
|
turnUsername: 'TURN Username',
|
||||||
turnPassword: 'TURN Password',
|
turnPassword: 'TURN Password',
|
||||||
turnPasswordConfigured: 'Password already configured. Leave empty to keep current password.',
|
turnPasswordConfigured: 'Password already configured. Leave empty to keep current password.',
|
||||||
turnCredentialsHint: 'Credentials for TURN server authentication (only needed for custom servers)',
|
turnCredentialsHint: 'Credentials for TURN server authentication',
|
||||||
iceConfigNote: 'Note: Changes require reconnecting the WebRTC session to take effect.',
|
iceConfigNote: 'Note: Changes require reconnecting the WebRTC session to take effect.',
|
||||||
},
|
},
|
||||||
virtualKeyboard: {
|
virtualKeyboard: {
|
||||||
@@ -703,7 +702,7 @@ export default {
|
|||||||
serverSettings: 'Server Settings',
|
serverSettings: 'Server Settings',
|
||||||
rendezvousServer: 'ID Server',
|
rendezvousServer: 'ID Server',
|
||||||
rendezvousServerPlaceholder: 'hbbs.example.com:21116',
|
rendezvousServerPlaceholder: 'hbbs.example.com:21116',
|
||||||
rendezvousServerHint: 'Leave empty to use public server',
|
rendezvousServerHint: 'Configure your RustDesk server address',
|
||||||
relayServer: 'Relay Server',
|
relayServer: 'Relay Server',
|
||||||
relayServerPlaceholder: 'hbbr.example.com:21117',
|
relayServerPlaceholder: 'hbbr.example.com:21117',
|
||||||
relayServerHint: 'Relay server address, auto-derived from ID server if empty',
|
relayServerHint: 'Relay server address, auto-derived from ID server if empty',
|
||||||
@@ -711,10 +710,6 @@ export default {
|
|||||||
relayKeyPlaceholder: 'Enter relay server key',
|
relayKeyPlaceholder: 'Enter relay server key',
|
||||||
relayKeySet: '••••••••',
|
relayKeySet: '••••••••',
|
||||||
relayKeyHint: 'Authentication key for relay server (if server uses -k option)',
|
relayKeyHint: 'Authentication key for relay server (if server uses -k option)',
|
||||||
publicServerInfo: 'Public Server Info',
|
|
||||||
publicServerAddress: 'Server Address',
|
|
||||||
publicServerKey: 'Connection Key',
|
|
||||||
usingPublicServer: 'Using public server',
|
|
||||||
deviceInfo: 'Device Info',
|
deviceInfo: 'Device Info',
|
||||||
deviceId: 'Device ID',
|
deviceId: 'Device ID',
|
||||||
deviceIdHint: 'Use this ID in RustDesk client to connect',
|
deviceIdHint: 'Use this ID in RustDesk client to connect',
|
||||||
|
|||||||
@@ -570,18 +570,17 @@ export default {
|
|||||||
// WebRTC / ICE
|
// WebRTC / ICE
|
||||||
webrtcSettings: 'WebRTC 设置',
|
webrtcSettings: 'WebRTC 设置',
|
||||||
webrtcSettingsDesc: '配置 STUN/TURN 服务器以实现 NAT 穿透',
|
webrtcSettingsDesc: '配置 STUN/TURN 服务器以实现 NAT 穿透',
|
||||||
usingPublicIceServers: '正在使用公共 ICE 服务器',
|
publicIceServersHint: '留空将使用 Google 公共 STUN 服务器,TURN 服务器需自行配置',
|
||||||
publicIceServersHint: '留空以使用内置的公共 STUN/TURN 服务器进行 NAT 穿透',
|
|
||||||
stunServer: 'STUN 服务器',
|
stunServer: 'STUN 服务器',
|
||||||
stunServerPlaceholder: 'stun:stun.l.google.com:19302',
|
stunServerPlaceholder: 'stun:stun.l.google.com:19302',
|
||||||
stunServerHint: '自定义 STUN 服务器(留空则使用公共服务器)',
|
stunServerHint: '自定义 STUN 服务器(留空则使用 Google 公共服务器)',
|
||||||
turnServer: 'TURN 服务器',
|
turnServer: 'TURN 服务器',
|
||||||
turnServerPlaceholder: 'turn:turn.example.com:3478',
|
turnServerPlaceholder: 'turn:turn.example.com:3478',
|
||||||
turnServerHint: '自定义 TURN 中继服务器(留空则使用公共服务器)',
|
turnServerHint: '自定义 TURN 中继服务器(生产环境必须配置)',
|
||||||
turnUsername: 'TURN 用户名',
|
turnUsername: 'TURN 用户名',
|
||||||
turnPassword: 'TURN 密码',
|
turnPassword: 'TURN 密码',
|
||||||
turnPasswordConfigured: '密码已配置。留空则保持当前密码。',
|
turnPasswordConfigured: '密码已配置。留空则保持当前密码。',
|
||||||
turnCredentialsHint: '用于 TURN 服务器认证的凭据(仅自定义服务器需要)',
|
turnCredentialsHint: '用于 TURN 服务器认证的凭据',
|
||||||
iceConfigNote: '注意:更改后需要重新连接 WebRTC 会话才能生效。',
|
iceConfigNote: '注意:更改后需要重新连接 WebRTC 会话才能生效。',
|
||||||
},
|
},
|
||||||
virtualKeyboard: {
|
virtualKeyboard: {
|
||||||
@@ -703,7 +702,7 @@ export default {
|
|||||||
serverSettings: '服务器设置',
|
serverSettings: '服务器设置',
|
||||||
rendezvousServer: 'ID 服务器',
|
rendezvousServer: 'ID 服务器',
|
||||||
rendezvousServerPlaceholder: 'hbbs.example.com:21116',
|
rendezvousServerPlaceholder: 'hbbs.example.com:21116',
|
||||||
rendezvousServerHint: '留空则使用公共服务器',
|
rendezvousServerHint: '请配置您的 RustDesk 服务器地址',
|
||||||
relayServer: '中继服务器',
|
relayServer: '中继服务器',
|
||||||
relayServerPlaceholder: 'hbbr.example.com:21117',
|
relayServerPlaceholder: 'hbbr.example.com:21117',
|
||||||
relayServerHint: '中继服务器地址,留空则自动从 ID 服务器推导',
|
relayServerHint: '中继服务器地址,留空则自动从 ID 服务器推导',
|
||||||
@@ -711,10 +710,6 @@ export default {
|
|||||||
relayKeyPlaceholder: '输入中继服务器密钥',
|
relayKeyPlaceholder: '输入中继服务器密钥',
|
||||||
relayKeySet: '••••••••',
|
relayKeySet: '••••••••',
|
||||||
relayKeyHint: '中继服务器认证密钥(如果服务器使用 -k 选项)',
|
relayKeyHint: '中继服务器认证密钥(如果服务器使用 -k 选项)',
|
||||||
publicServerInfo: '公共服务器信息',
|
|
||||||
publicServerAddress: '服务器地址',
|
|
||||||
publicServerKey: '连接密钥',
|
|
||||||
usingPublicServer: '正在使用公共服务器',
|
|
||||||
deviceInfo: '设备信息',
|
deviceInfo: '设备信息',
|
||||||
deviceId: '设备 ID',
|
deviceId: '设备 ID',
|
||||||
deviceIdHint: '此 ID 用于 RustDesk 客户端连接',
|
deviceIdHint: '此 ID 用于 RustDesk 客户端连接',
|
||||||
|
|||||||
@@ -314,9 +314,8 @@ export interface RustDeskConfig {
|
|||||||
/** Enable RustDesk protocol */
|
/** Enable RustDesk protocol */
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
/**
|
/**
|
||||||
* Rendezvous server address (hbbs), e.g., "rs.example.com" or "192.168.1.100"
|
* Rendezvous server address (hbbs), e.g., "rs.example.com" or "192.168.1.100:21116"
|
||||||
* Port defaults to 21116 if not specified
|
* Required for RustDesk to function
|
||||||
* If empty, uses the public server from secrets.toml
|
|
||||||
*/
|
*/
|
||||||
rendezvous_server: string;
|
rendezvous_server: string;
|
||||||
/**
|
/**
|
||||||
@@ -517,14 +516,6 @@ export interface MsdConfigUpdate {
|
|||||||
virtual_drive_size_mb?: number;
|
virtual_drive_size_mb?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Public server information for display to users */
|
|
||||||
export interface PublicServerInfo {
|
|
||||||
/** Public server address */
|
|
||||||
server: string;
|
|
||||||
/** Public key for client connection */
|
|
||||||
public_key: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RustDeskConfigUpdate {
|
export interface RustDeskConfigUpdate {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
rendezvous_server?: string;
|
rendezvous_server?: string;
|
||||||
|
|||||||
@@ -105,6 +105,12 @@ const mousePosition = ref({ x: 0, y: 0 })
|
|||||||
const lastMousePosition = ref({ x: 0, y: 0 }) // Track last position for relative mode
|
const lastMousePosition = ref({ x: 0, y: 0 }) // Track last position for relative mode
|
||||||
const isPointerLocked = ref(false) // Track pointer lock state
|
const isPointerLocked = ref(false) // Track pointer lock state
|
||||||
|
|
||||||
|
// Mouse move throttling (60 Hz = ~16.67ms interval)
|
||||||
|
const MOUSE_SEND_INTERVAL_MS = 16
|
||||||
|
let mouseSendTimer: ReturnType<typeof setInterval> | null = null
|
||||||
|
let pendingMouseMove: { type: 'move' | 'move_abs'; x: number; y: number } | null = null
|
||||||
|
let accumulatedDelta = { x: 0, y: 0 } // For relative mode: accumulate deltas between sends
|
||||||
|
|
||||||
// Cursor visibility (from localStorage, updated via storage event)
|
// Cursor visibility (from localStorage, updated via storage event)
|
||||||
const cursorVisible = ref(localStorage.getItem('hidShowCursor') !== 'false')
|
const cursorVisible = ref(localStorage.getItem('hidShowCursor') !== 'false')
|
||||||
|
|
||||||
@@ -1479,20 +1485,21 @@ function handleMouseMove(e: MouseEvent) {
|
|||||||
const y = Math.round((e.clientY - rect.top) / rect.height * 32767)
|
const y = Math.round((e.clientY - rect.top) / rect.height * 32767)
|
||||||
|
|
||||||
mousePosition.value = { x, y }
|
mousePosition.value = { x, y }
|
||||||
sendMouseEvent({ type: 'move_abs', x, y })
|
// Queue for throttled sending (absolute mode: just update pending position)
|
||||||
|
pendingMouseMove = { type: 'move_abs', x, y }
|
||||||
|
ensureMouseSendTimer()
|
||||||
} else {
|
} else {
|
||||||
// Relative mode: use movementX/Y when pointer is locked
|
// Relative mode: use movementX/Y when pointer is locked
|
||||||
if (isPointerLocked.value) {
|
if (isPointerLocked.value) {
|
||||||
const dx = e.movementX
|
const dx = e.movementX
|
||||||
const dy = e.movementY
|
const dy = e.movementY
|
||||||
|
|
||||||
// Only send if there's actual movement
|
// Only accumulate if there's actual movement
|
||||||
if (dx !== 0 || dy !== 0) {
|
if (dx !== 0 || dy !== 0) {
|
||||||
// Clamp to i8 range (-127 to 127)
|
// Accumulate deltas for throttled sending
|
||||||
const clampedDx = Math.max(-127, Math.min(127, dx))
|
accumulatedDelta.x += dx
|
||||||
const clampedDy = Math.max(-127, Math.min(127, dy))
|
accumulatedDelta.y += dy
|
||||||
|
ensureMouseSendTimer()
|
||||||
sendMouseEvent({ type: 'move', x: clampedDx, y: clampedDy })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update display position (accumulated delta for display only)
|
// Update display position (accumulated delta for display only)
|
||||||
@@ -1504,6 +1511,50 @@ function handleMouseMove(e: MouseEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start the mouse send timer if not already running
|
||||||
|
function ensureMouseSendTimer() {
|
||||||
|
if (mouseSendTimer !== null) return
|
||||||
|
|
||||||
|
// Send immediately on first event, then throttle
|
||||||
|
flushMouseMove()
|
||||||
|
|
||||||
|
mouseSendTimer = setInterval(() => {
|
||||||
|
if (!flushMouseMove()) {
|
||||||
|
// No pending data, stop the timer
|
||||||
|
if (mouseSendTimer !== null) {
|
||||||
|
clearInterval(mouseSendTimer)
|
||||||
|
mouseSendTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, MOUSE_SEND_INTERVAL_MS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush pending mouse move data, returns true if data was sent
|
||||||
|
function flushMouseMove(): boolean {
|
||||||
|
if (mouseMode.value === 'absolute') {
|
||||||
|
if (pendingMouseMove) {
|
||||||
|
sendMouseEvent(pendingMouseMove)
|
||||||
|
pendingMouseMove = null
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Relative mode: send accumulated delta
|
||||||
|
if (accumulatedDelta.x !== 0 || accumulatedDelta.y !== 0) {
|
||||||
|
// Clamp to i8 range (-127 to 127)
|
||||||
|
const clampedDx = Math.max(-127, Math.min(127, accumulatedDelta.x))
|
||||||
|
const clampedDy = Math.max(-127, Math.min(127, accumulatedDelta.y))
|
||||||
|
|
||||||
|
sendMouseEvent({ type: 'move', x: clampedDx, y: clampedDy })
|
||||||
|
|
||||||
|
// Subtract sent amount (keep remainder for next send if clamped)
|
||||||
|
accumulatedDelta.x -= clampedDx
|
||||||
|
accumulatedDelta.y -= clampedDy
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Track pressed mouse button for window-level mouseup handling
|
// Track pressed mouse button for window-level mouseup handling
|
||||||
const pressedMouseButton = ref<'left' | 'right' | 'middle' | null>(null)
|
const pressedMouseButton = ref<'left' | 'right' | 'middle' | null>(null)
|
||||||
|
|
||||||
@@ -1746,6 +1797,12 @@ onUnmounted(() => {
|
|||||||
// Reset initial device info flag
|
// Reset initial device info flag
|
||||||
initialDeviceInfoReceived = false
|
initialDeviceInfoReceived = false
|
||||||
|
|
||||||
|
// Clear mouse send timer
|
||||||
|
if (mouseSendTimer !== null) {
|
||||||
|
clearInterval(mouseSendTimer)
|
||||||
|
mouseSendTimer = null
|
||||||
|
}
|
||||||
|
|
||||||
// Clear ttyd poll interval
|
// Clear ttyd poll interval
|
||||||
if (ttydPollInterval) {
|
if (ttydPollInterval) {
|
||||||
clearInterval(ttydPollInterval)
|
clearInterval(ttydPollInterval)
|
||||||
|
|||||||
@@ -47,12 +47,6 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog'
|
} from '@/components/ui/dialog'
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from '@/components/ui/tooltip'
|
|
||||||
import {
|
import {
|
||||||
Monitor,
|
Monitor,
|
||||||
Keyboard,
|
Keyboard,
|
||||||
@@ -82,7 +76,6 @@ import {
|
|||||||
ExternalLink,
|
ExternalLink,
|
||||||
Copy,
|
Copy,
|
||||||
ScreenShare,
|
ScreenShare,
|
||||||
CircleHelp,
|
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
|
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n()
|
||||||
@@ -2094,28 +2087,7 @@ onMounted(async () => {
|
|||||||
v-model="rustdeskLocalConfig.rendezvous_server"
|
v-model="rustdeskLocalConfig.rendezvous_server"
|
||||||
:placeholder="t('extensions.rustdesk.rendezvousServerPlaceholder')"
|
:placeholder="t('extensions.rustdesk.rendezvousServerPlaceholder')"
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center gap-1">
|
<p class="text-xs text-muted-foreground">{{ t('extensions.rustdesk.rendezvousServerHint') }}</p>
|
||||||
<p class="text-xs text-muted-foreground">{{ t('extensions.rustdesk.rendezvousServerHint') }}</p>
|
|
||||||
<TooltipProvider v-if="rustdeskStatus?.public_server">
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger as-child>
|
|
||||||
<CircleHelp class="h-3.5 w-3.5 text-muted-foreground cursor-help" />
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="right" class="max-w-xs">
|
|
||||||
<div class="space-y-1.5 text-xs">
|
|
||||||
<p class="font-medium">{{ t('extensions.rustdesk.publicServerInfo') }}</p>
|
|
||||||
<div class="space-y-1">
|
|
||||||
<p><span class="text-muted-foreground">{{ t('extensions.rustdesk.publicServerAddress') }}:</span> {{ rustdeskStatus.public_server.server }}</p>
|
|
||||||
<p><span class="text-muted-foreground">{{ t('extensions.rustdesk.publicServerKey') }}:</span> <code class="text-[10px] break-all">{{ rustdeskStatus.public_server.public_key }}</code></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
</div>
|
|
||||||
<p v-if="rustdeskStatus?.config?.using_public_server" class="text-xs text-blue-500">
|
|
||||||
{{ t('extensions.rustdesk.usingPublicServer') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-4 items-center gap-4">
|
<div class="grid grid-cols-4 items-center gap-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user