Merge branch 'rustdesk:master' into master

This commit is contained in:
Andrzej Rudnik 2023-03-31 11:57:04 +02:00 committed by GitHub
commit 98234830a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1656 additions and 1651 deletions

View File

@ -16,808 +16,9 @@ on:
- "docs/**"
- "README.md"
env:
LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.7.5"
# vcpkg version: 2022.05.10
# for multiarch gcc compatibility
VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44"
VERSION: "1.2.0"
NDK_VERSION: "r23"
jobs:
build-for-windows:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: true
matrix:
job:
# - { target: i686-pc-windows-msvc , os: windows-2019 }
# - { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: x86_64-pc-windows-msvc, os: windows-2019 }
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Install LLVM and Clang
uses: KyleMayes/install-llvm-action@v1
with:
version: ${{ env.LLVM_VERSION }}
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Replace engine with rustdesk custom flutter engine
run: |
flutter doctor -v
flutter precache --windows
Invoke-WebRequest -Uri https://github.com/Kingtous/engine/releases/download/v3.7.0-rustdesk/windows-x64-release-flutter.zip -OutFile windows-x64-flutter-release.zip
Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine
mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
components: rustfmt
profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- name: Install flutter rust bridge deps
run: |
cargo install flutter_rust_bridge_codegen
Push-Location flutter ; flutter pub get ; Pop-Location
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v7
with:
setupOnly: true
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static
shell: bash
- name: Build rustdesk
run: python3 .\build.py --portable --hwcodec --flutter
build-for-macOS:
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}]
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: true
matrix:
job:
- {
target: x86_64-apple-darwin,
os: macos-latest,
extra-build-args: "",
}
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Install build runtime
run: |
brew install llvm create-dmg nasm yasm cmake gcc wget ninja pkg-config
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- name: Install flutter rust bridge deps
shell: bash
run: |
cargo install flutter_rust_bridge_codegen
pushd flutter && flutter pub get && popd
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v7
with:
setupOnly: true
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install libvpx libyuv opus
- name: Show version information (Rust, cargo, Clang)
shell: bash
run: |
clang --version || true
rustup -V
rustup toolchain list
rustup default
cargo -V
rustc -V
- name: Build rustdesk
run: |
# --hwcodec not supported on macos yet
./build.py --flutter ${{ matrix.job.extra-build-args }}
build-vcpkg-deps-linux:
uses: ./.github/workflows/vcpkg-deps-linux.yml
generate-bridge-linux:
uses: ./.github/workflows/bridge.yml
build-rustdesk-android:
needs: [generate-bridge-linux]
name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: true
matrix:
job:
- {
arch: x86_64,
target: aarch64-linux-android,
os: ubuntu-20.04,
extra-build-features: "",
openssl-arch: android-arm64
}
- {
arch: x86_64,
target: armv7-linux-androideabi,
os: ubuntu-18.04,
extra-build-features: "",
openssl-arch: android-arm
}
steps:
- name: Install dependencies
run: |
sudo apt update
sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless
- name: Checkout source code
uses: actions/checkout@v3
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
- uses: nttld/setup-ndk@v1
id: setup-ndk
with:
ndk-version: ${{ env.NDK_VERSION }}
add-to-path: true
- name: Clone deps
shell: bash
run: |
pushd /opt
git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2
with:
prefix-key: rustdesk-lib-cache
key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }}
- name: Disable rust bridge build
run: |
sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs
- name: Build rustdesk lib
env:
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg
run: |
rustup target add ${{ matrix.job.target }}
cargo install cargo-ndk
case ${{ matrix.job.target }} in
aarch64-linux-android)
./flutter/ndk_arm64.sh
mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
;;
armv7-linux-androideabi)
./flutter/ndk_arm.sh
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
;;
esac
- name: Build rustdesk
shell: bash
env:
JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64
run: |
export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH
# temporary use debug sign config
sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle
case ${{ matrix.job.target }} in
aarch64-linux-android)
mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/arm64-v8a/*.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
# build flutter
pushd flutter
flutter build apk --release --target-platform android-arm64 --split-per-abi
mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk
;;
armv7-linux-androideabi)
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/armeabi-v7a/*.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
# build flutter
pushd flutter
flutter build apk --release --target-platform android-arm --split-per-abi
mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk
;;
esac
popd
mkdir -p signed-apk; pushd signed-apk
mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk .
build-rustdesk-lib-linux-amd64:
needs: [generate-bridge-linux, build-vcpkg-deps-linux]
name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: true
matrix:
# use a high level qemu-user-static
job:
# - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
# - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- {
arch: x86_64,
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
extra-build-features: "",
}
- {
arch: x86_64,
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
extra-build-features: "flatpak",
}
- {
arch: x86_64,
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
extra-build-features: "appimage",
}
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
steps:
- name: Maximize build space
run: |
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet
sudo apt update -y
sudo apt install qemu-user-static
- name: Checkout source code
uses: actions/checkout@v3
- name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 12
- name: Free Space
run: |
df
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2
with:
prefix-key: rustdesk-lib-cache
key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }}
cache-directories: "/opt/rust-registry"
- name: Install local registry
run: |
mkdir -p /opt/rust-registry
cargo install cargo-local-registry
- name: Build local registry
uses: nick-fields/retry@v2
id: build-local-registry
continue-on-error: true
with:
max_attempts: 3
timeout_minutes: 15
retry_on: error
command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry
- name: Disable rust bridge build
run: |
sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs
# only build cdylib
sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Restore vcpkg files
uses: actions/download-artifact@master
with:
name: vcpkg-artifact-${{ matrix.job.arch }}
path: /opt/artifacts/vcpkg/installed
- uses: Kingtous/run-on-arch-action@amd64-support
name: Build rustdesk library for ${{ matrix.job.arch }}
id: vcpkg
with:
arch: ${{ matrix.job.arch }}
distro: ubuntu18.04
# not ready yet
# distro: ubuntu18.04-rustdesk
githubToken: ${{ github.token }}
setup: |
ls -l "${PWD}"
ls -l /opt/artifacts/vcpkg/installed
dockerRunArgs: |
--volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts"
--volume "/opt/rust-registry:/opt/rust-registry"
shell: /bin/bash
install: |
apt update -y
echo -e "installing deps"
apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null
# we have libopus compiled by us.
apt remove -y libopus-dev || true
# output devs
ls -l ./
tree -L 3 /opt/artifacts/vcpkg/installed
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
# rust
pushd /opt
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh
rm -rf rust-1.64.0-${{ matrix.job.target }}
# edit config
mkdir -p ~/.cargo/
echo """
[source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'local-registry'
[source.local-registry]
local-registry = '/opt/rust-registry/'
""" > ~/.cargo/config
cat ~/.cargo/config
# start build
pushd /workspace
# mock
case "${{ matrix.job.arch }}" in
x86_64)
# no need mock on x86_64
export VCPKG_ROOT=/opt/artifacts/vcpkg
cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release
;;
esac
- name: Upload Artifacts
uses: actions/upload-artifact@master
with:
name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so
path: target/release/liblibrustdesk.so
build-rustdesk-lib-linux-arm:
needs: [generate-bridge-linux, build-vcpkg-deps-linux]
name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: true
matrix:
# use a high level qemu-user-static
job:
- {
arch: aarch64,
target: aarch64-unknown-linux-gnu,
os: ubuntu-20.04,
use-cross: true,
extra-build-features: "",
}
- {
arch: aarch64,
target: aarch64-unknown-linux-gnu,
os: ubuntu-18.04, # just for naming package, not running host
use-cross: true,
extra-build-features: "appimage",
}
# - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" }
# - {
# arch: armv7,
# target: arm-unknown-linux-gnueabihf,
# os: ubuntu-20.04,
# use-cross: true,
# extra-build-features: "",
# }
# - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" }
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
steps:
- name: Maximize build space
run: |
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet
sudo apt update -y
sudo apt install qemu-user-static
- name: Checkout source code
uses: actions/checkout@v3
- name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 12
- name: Free Space
run: |
df
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2
with:
prefix-key: rustdesk-lib-cache
key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }}
cache-directories: "/opt/rust-registry"
- name: Install local registry
run: |
mkdir -p /opt/rust-registry
cargo install cargo-local-registry
- name: Build local registry
uses: nick-fields/retry@v2
id: build-local-registry
continue-on-error: true
with:
max_attempts: 3
timeout_minutes: 15
retry_on: error
command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry
- name: Disable rust bridge build
run: |
sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs
# only build cdylib
sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Restore vcpkg files
uses: actions/download-artifact@master
with:
name: vcpkg-artifact-${{ matrix.job.arch }}
path: /opt/artifacts/vcpkg/installed
- uses: Kingtous/run-on-arch-action@amd64-support
name: Build rustdesk library for ${{ matrix.job.arch }}
id: vcpkg
with:
arch: ${{ matrix.job.arch }}
distro: ubuntu18.04-rustdesk
githubToken: ${{ github.token }}
setup: |
ls -l "${PWD}"
ls -l /opt/artifacts/vcpkg/installed
dockerRunArgs: |
--volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts"
--volume "/opt/rust-registry:/opt/rust-registry"
shell: /bin/bash
install: |
apt update -y
echo -e "installing deps"
apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null
# we have libopus compiled by us.
apt remove -y libopus-dev || true
# output devs
ls -l ./
tree -L 3 /opt/artifacts/vcpkg/installed
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
# rust
pushd /opt
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh
rm -rf rust-1.64.0-${{ matrix.job.target }}
# edit config
mkdir -p ~/.cargo/
echo """
[source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'local-registry'
[source.local-registry]
local-registry = '/opt/rust-registry/'
""" > ~/.cargo/config
cat ~/.cargo/config
# start build
pushd /workspace
# mock
case "${{ matrix.job.arch }}" in
aarch64)
cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/aarch64-linux-gnu/
cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/
ls -l /opt/artifacts/vcpkg/installed/lib/
mkdir -p /vcpkg/installed/arm64-linux
ln -s /usr/lib/aarch64-linux-gnu /vcpkg/installed/arm64-linux/lib
ln -s /usr/include /vcpkg/installed/arm64-linux/include
export VCPKG_ROOT=/vcpkg
# disable hwcodec for compilation
cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release
;;
armv7)
cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/
cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/
mkdir -p /vcpkg/installed/arm-linux
ln -s /usr/lib/arm-linux-gnueabihf /vcpkg/installed/arm-linux/lib
ln -s /usr/include /vcpkg/installed/arm-linux/include
export VCPKG_ROOT=/vcpkg
# disable hwcodec for compilation
cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release
;;
esac
- name: Upload Artifacts
uses: actions/upload-artifact@master
with:
name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so
path: target/release/liblibrustdesk.so
build-rustdesk-linux-arm:
needs: [build-rustdesk-lib-linux-arm]
name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ubuntu-20.04 # 20.04 has more performance on arm build
strategy:
fail-fast: true
matrix:
job:
- {
arch: aarch64,
target: aarch64-unknown-linux-gnu,
os: ubuntu-18.04, # just for naming package, not running host
use-cross: true,
extra-build-features: "",
}
- {
arch: aarch64,
target: aarch64-unknown-linux-gnu,
os: ubuntu-18.04, # just for naming package, not running host
use-cross: true,
extra-build-features: "appimage",
}
# - {
# arch: aarch64,
# target: aarch64-unknown-linux-gnu,
# os: ubuntu-18.04, # just for naming package, not running host
# use-cross: true,
# extra-build-features: "flatpak",
# }
# - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" }
# - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" }
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Prepare env
run: |
sudo apt update -y
sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools
mkdir -p ./target/release/
- name: Restore the rustdesk lib file
uses: actions/download-artifact@master
with:
name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so
path: ./target/release/
- name: Download Flutter
shell: bash
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
pushd /opt
# clone repo and reset to flutter 3.7.0
git clone https://github.com/sony/flutter-elinux.git || true
pushd flutter-elinux
# reset to flutter 3.7.0
git fetch
git reset --hard 51a1d685901f79fbac51665a967c3a1a789ecee5
popd
- uses: Kingtous/run-on-arch-action@amd64-support
name: Build rustdesk binary for ${{ matrix.job.arch }}
id: vcpkg
with:
arch: ${{ matrix.job.arch }}
distro: ubuntu18.04-rustdesk
githubToken: ${{ github.token }}
setup: |
ls -l "${PWD}"
dockerRunArgs: |
--volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts"
--volume "/opt/flutter-elinux:/opt/flutter-elinux"
shell: /bin/bash
install: |
apt update -y
apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
pushd /workspace
# we use flutter-elinux to build our rustdesk
export PATH=/opt/flutter-elinux/bin:$PATH
sed -i "s/flutter build linux --release/flutter-elinux build linux/g" ./build.py
# Setup flutter-elinux. Run doctor to check if issues here.
flutter-elinux doctor -v
# Patch arm64 engine for flutter 3.6.0+
flutter-elinux precache --linux
pushd /tmp
curl -O https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.7.0-stable.tar.xz
tar -xvf flutter_linux_3.7.0-stable.tar.xz flutter/bin/cache/artifacts/engine/linux-x64/shader_lib
cp -R flutter/bin/cache/artifacts/engine/linux-x64/shader_lib /opt/flutter-elinux/flutter/bin/cache/artifacts/engine/linux-arm64
popd
case ${{ matrix.job.arch }} in
aarch64)
sed -i "s/Architecture: amd64/Architecture: arm64/g" ./build.py
sed -i "s/x64\/release/arm64\/release/g" ./build.py
;;
armv7)
sed -i "s/Architecture: amd64/Architecture: arm/g" ./build.py
sed -i "s/x64\/release/arm\/release/g" ./build.py
;;
esac
python3 ./build.py --flutter --hwcodec --skip-cargo
build-rustdesk-linux-amd64:
needs: [build-rustdesk-lib-linux-amd64]
name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ubuntu-20.04
strategy:
fail-fast: true
matrix:
job:
# - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true }
# - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
- {
arch: x86_64,
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
extra-build-features: "",
}
- {
arch: x86_64,
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
extra-build-features: "flatpak",
}
- {
arch: x86_64,
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
extra-build-features: "appimage",
}
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Prepare env
run: |
sudo apt update -y
sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools
mkdir -p ./target/release/
- name: Restore the rustdesk lib file
uses: actions/download-artifact@master
with:
name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so
path: ./target/release/
- uses: Kingtous/run-on-arch-action@amd64-support
name: Build rustdesk binary for ${{ matrix.job.arch }}
id: vcpkg
with:
arch: ${{ matrix.job.arch }}
distro: ubuntu18.04
githubToken: ${{ github.token }}
setup: |
ls -l "${PWD}"
dockerRunArgs: |
--volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts"
shell: /bin/bash
install: |
apt update -y
apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
# Setup Flutter
pushd /opt
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz
tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz
ls -l .
export PATH=/opt/flutter/bin:$PATH
flutter doctor -v
pushd /workspace
python3 ./build.py --flutter --hwcodec --skip-cargo
run-ci:
uses: ./.github/workflows/flutter-nightly.yml
with:
upload-artifact: false

View File

@ -5,6 +5,16 @@ on:
# schedule build every night
- cron: "0 0 * * *"
workflow_dispatch:
inputs:
upload-artifact:
description: "Upload the artifact produced by this workflow to the Release page."
type: boolean
default: true
workflow_call:
inputs:
upload-artifact:
type: boolean
default: true
env:
LLVM_VERSION: "15.0.6"
@ -21,9 +31,10 @@ env:
# To make a custom build with your own servers set the below secret values
RS_PUB_KEY: '${{ secrets.RS_PUB_KEY }}'
RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}'
UPLOAD_ARTIFACT: '${{ inputs.upload-artifact }}'
jobs:
build-for-windows:
build-for-windows-flutter:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
@ -33,7 +44,7 @@ jobs:
# - { target: i686-pc-windows-msvc , os: windows-2019 }
# - { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: x86_64-pc-windows-msvc, os: windows-2019 }
# - { target: aarch64-pc-windows-msvc, os: windows-2019 }
# - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 }
steps:
- name: Checkout source code
uses: actions/checkout@v3
@ -92,6 +103,7 @@ jobs:
- name: Sign rustdesk files
uses: GermanBluefox/code-sign-action@v7
if: env.UPLOAD_ARTIFACT == 'true'
with:
certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}'
password: '${{ secrets.WINDOWS_PFX_PASSWORD }}'
@ -102,6 +114,7 @@ jobs:
- name: Build self-extracted executable
shell: bash
if: env.UPLOAD_ARTIFACT == 'true'
run: |
pushd ./libs/portable
python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe
@ -118,6 +131,7 @@ jobs:
- name: Sign rustdesk self-extracted file
uses: GermanBluefox/code-sign-action@v7
if: env.UPLOAD_ARTIFACT == 'true'
with:
certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}'
password: '${{ secrets.WINDOWS_PFX_PASSWORD }}'
@ -128,6 +142,110 @@ jobs:
- name: Publish Release
uses: softprops/action-gh-release@v1
if: env.UPLOAD_ARTIFACT == 'true'
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
./SignOutput/rustdesk-*.exe
# The fallback for the flutter version, we use Sciter for 32bit Windows.
build-for-windows-sciter:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
# Temporarily disable this action due to additional test is needed.
if: false
strategy:
fail-fast: false
matrix:
job:
# - { target: i686-pc-windows-msvc , os: windows-2019 }
# - { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: i686-pc-windows-msvc, os: windows-2019 }
# - { target: aarch64-pc-windows-msvc, os: windows-2019 }
steps:
- name: Checkout source code
uses: actions/checkout@v3
- name: Install LLVM and Clang
uses: Kingtous/install-llvm-action-32bit@master
with:
version: ${{ env.LLVM_VERSION }}
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable-${{ matrix.job.target }}
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- name: Set Rust toolchain to the target
run: |
rustup default stable-${{ matrix.job.target }}
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}-sciter
- name: Restore from cache and install vcpkg
uses: lukka/run-vcpkg@v7
with:
setupOnly: true
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
- name: Install vcpkg dependencies
run: |
$VCPKG_ROOT/vcpkg install libvpx:x86-windows-static libyuv:x86-windows-static opus:x86-windows-static
shell: bash
- name: Build rustdesk
id: build
shell: bash
run: |
python3 res/inline-sciter.py
# Replace the link for the ico.
rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico
cargo build --features inline --release --bins
mkdir -p ./Release
mv ./target/release/rustdesk.exe ./Release/rustdesk.exe
curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll
echo "output_folder=./Release" >> $GITHUB_OUTPUT
- name: Sign rustdesk files
uses: GermanBluefox/code-sign-action@v7
if: env.UPLOAD_ARTIFACT == 'true'
with:
certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}'
password: '${{ secrets.WINDOWS_PFX_PASSWORD }}'
certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}'
# certificatename: '${{ secrets.CERTNAME }}'
folder: './Release/'
recursive: true
- name: Build self-extracted executable
shell: bash
run: |
pushd ./libs/portable
pip3 install -r requirements.txt
python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-sciter.exe
- name: Sign rustdesk self-extracted file
uses: GermanBluefox/code-sign-action@v7
with:
certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}'
password: '${{ secrets.WINDOWS_PFX_PASSWORD }}'
certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}'
# certificatename: '${{ secrets.WINDOWS_PFX_NAME }}'
folder: './SignOutput'
recursive: false
- name: Publish Release
uses: softprops/action-gh-release@v1
if: env.UPLOAD_ARTIFACT == 'true'
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
@ -146,11 +264,6 @@ jobs:
os: macos-latest,
extra-build-args: "",
}
- {
target: aarch64-apple-darwin,
os: macos-latest,
extra-build-args: "",
}
steps:
- name: Checkout source code
uses: actions/checkout@v3
@ -262,6 +375,7 @@ jobs:
done
- name: Publish DMG package
if: env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -411,14 +525,14 @@ jobs:
BUILD_TOOLS_VERSION: "30.0.2"
- name: Upload Artifacts
if: env.ANDROID_SIGNING_KEY != null
if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
uses: actions/upload-artifact@master
with:
name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk
path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}}
- name: Publish signed apk package
if: env.ANDROID_SIGNING_KEY != null
if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -427,7 +541,7 @@ jobs:
${{steps.sign-rustdesk.outputs.signedReleaseFile}}
- name: Publish unsigned apk package
if: env.ANDROID_SIGNING_KEY == null
if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -622,12 +736,12 @@ jobs:
# - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" }
# - {
# arch: armv7,
# target: arm-unknown-linux-gnueabihf,
# target: armv7-unknown-linux-gnueabihf,
# os: ubuntu-20.04,
# use-cross: true,
# extra-build-features: "",
# }
# - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" }
# - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" }
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
steps:
- name: Maximize build space
@ -743,30 +857,8 @@ jobs:
cat ~/.cargo/config
# start build
pushd /workspace
# mock
case "${{ matrix.job.arch }}" in
aarch64)
cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/aarch64-linux-gnu/
cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/
ls -l /opt/artifacts/vcpkg/installed/lib/
mkdir -p /vcpkg/installed/arm64-linux
ln -s /usr/lib/aarch64-linux-gnu /vcpkg/installed/arm64-linux/lib
ln -s /usr/include /vcpkg/installed/arm64-linux/include
export VCPKG_ROOT=/vcpkg
# disable hwcodec for compilation
cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release
;;
armv7)
cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/
cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/
mkdir -p /vcpkg/installed/arm-linux
ln -s /usr/lib/arm-linux-gnueabihf /vcpkg/installed/arm-linux/lib
ln -s /usr/include /vcpkg/installed/arm-linux/include
export VCPKG_ROOT=/vcpkg
# disable hwcodec for compilation
cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release
;;
esac
export VCPKG_ROOT=/opt/artifacts/vcpkg
cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release
- name: Upload Artifacts
uses: actions/upload-artifact@master
@ -774,6 +866,158 @@ jobs:
name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so
path: target/release/liblibrustdesk.so
build-rustdesk-sciter-arm:
needs: [build-vcpkg-deps-linux]
name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
# use a high level qemu-user-static
job:
- {
arch: armv7,
target: armv7-unknown-linux-gnueabihf,
deb-arch: armhf,
os: ubuntu-latest,
use-cross: true,
extra-build-features: "",
}
# - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" }
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
steps:
- name: Maximize build space
run: |
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet
sudo apt update -y
sudo apt install qemu-user-static
- name: Checkout source code
uses: actions/checkout@v3
- name: Set Swap Space
uses: pierotofy/set-swap-space@master
with:
swap-size-gb: 12
- name: Free Space
run: |
df
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2
with:
prefix-key: rustdesk-lib-cache
key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }}
cache-directories: "/opt/rust-registry"
- name: Install local registry
run: |
mkdir -p /opt/rust-registry
cargo install cargo-local-registry
- name: Build local registry
uses: nick-fields/retry@v2
id: build-local-registry
continue-on-error: true
with:
max_attempts: 3
timeout_minutes: 15
retry_on: error
command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry
- name: Restore vcpkg files
uses: actions/download-artifact@master
with:
name: vcpkg-artifact-${{ matrix.job.arch }}
path: /opt/artifacts/vcpkg/installed
- uses: Kingtous/run-on-arch-action@amd64-support
name: Build rustdesk sciter binary for ${{ matrix.job.arch }}
id: vcpkg
with:
arch: ${{ matrix.job.arch }}
distro: ubuntu18.04-rustdesk
githubToken: ${{ github.token }}
setup: |
ls -l "${PWD}"
dockerRunArgs: |
--volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts"
--volume "/opt/rust-registry:/opt/rust-registry"
shell: /bin/bash
install: |
apt update -y
apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm libclang-dev
apt-get -qq install -y libdbus-1-dev pkg-config nasm yasm libglib2.0-dev libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev
apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
# rust
pushd /opt
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh
rm -rf rust-1.64.0-${{ matrix.job.target }}
# edit config
mkdir -p ~/.cargo/
echo """
[source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'local-registry'
[source.local-registry]
local-registry = '/opt/rust-registry/'
""" > ~/.cargo/config
cat ~/.cargo/config
# build
pushd /workspace
python3 ./res/inline-sciter.py
export VCPKG_ROOT=/opt/artifacts/vcpkg
export ARCH=armhf
cargo build --features inline --release --bins
# package
mkdir -p ./Release
mv ./target/release/rustdesk ./Release/rustdesk
wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/arm32/libsciter-gtk.so
./build.py --package ./Release
- name: Rename rustdesk
shell: bash
run: |
for name in rustdesk*??.deb; do
# use cp to duplicate deb files to fit other packages.
cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb"
done
- name: Publish debian package
if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb
- name: Upload Artifact
uses: actions/upload-artifact@master
if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }}
with:
name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb
path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb
build-rustdesk-linux-arm:
needs: [build-rustdesk-lib-linux-arm]
name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
@ -803,8 +1047,8 @@ jobs:
# use-cross: true,
# extra-build-features: "flatpak",
# }
# - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" }
# - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" }
# - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" }
# - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" }
# - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
steps:
- name: Checkout source code
@ -932,7 +1176,7 @@ jobs:
done
- name: Publish debian package
if: ${{ matrix.job.extra-build-features == '' }}
if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -955,7 +1199,7 @@ jobs:
sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml
- name: Publish appimage package
if: ${{ matrix.job.extra-build-features == 'appimage' }}
if: ${{ matrix.job.extra-build-features == 'appimage' }} && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -1029,7 +1273,7 @@ jobs:
# res/rustdesk*.zst
- name: Publish fedora28/centos8 package
if: ${{ matrix.job.extra-build-features == '' }}
if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -1152,7 +1396,7 @@ jobs:
done
- name: Publish debian package
if: ${{ matrix.job.extra-build-features == '' }}
if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -1208,7 +1452,7 @@ jobs:
cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f
- name: Publish archlinux package
if: ${{ matrix.job.extra-build-features == '' }}
if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -1231,7 +1475,7 @@ jobs:
sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml
- name: Publish appimage package
if: ${{ matrix.job.extra-build-features == 'appimage' }}
if: ${{ matrix.job.extra-build-features == 'appimage' }} && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -1240,7 +1484,7 @@ jobs:
./appimage/rustdesk-${{ env.VERSION }}-*.AppImage
- name: Publish fedora28/centos8 package
if: ${{ matrix.job.extra-build-features == '' }}
if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1
with:
prerelease: true
@ -1369,6 +1613,7 @@ jobs:
- name: Publish flatpak package
uses: softprops/action-gh-release@v1
if: env.UPLOAD_ARTIFACT == 'true'
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}

View File

@ -10,7 +10,7 @@ jobs:
fail-fast: true
matrix:
job:
# - { arch: armv7, os: ubuntu-20.04 }
- { arch: armv7, os: ubuntu-20.04 }
- { arch: x86_64, os: ubuntu-20.04 }
- { arch: aarch64, os: ubuntu-20.04 }
steps:
@ -46,12 +46,12 @@ jobs:
wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
apt update -y
apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev
cmake --version
gcc -v
;;
aarch64|armv7)
apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev automake libtool
apt install -y curl zip unzip git
esac
cmake --version
gcc -v
run: |
# disable git safe.directory
git config --global --add safe.directory "*"
@ -65,25 +65,19 @@ jobs:
./bootstrap-vcpkg.sh
./vcpkg install libvpx libyuv opus
;;
aarch64|armv7)
aarch64)
pushd /artifacts
# libyuv
git clone https://chromium.googlesource.com/libyuv/libyuv || true
pushd libyuv
git pull
mkdir -p build
pushd build
rm -rf rustdesk_thirdparty_lib
git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
mkdir -p /artifacts/vcpkg/installed
cmake .. -DCMAKE_INSTALL_PREFIX=/artifacts/vcpkg/installed
make -j4 && make install
popd
popd
# libopus, ubuntu 18.04 prebuilt is not be compiled with -fPIC
wget -O opus.tar.gz http://archive.ubuntu.com/ubuntu/pool/main/o/opus/opus_1.1.2.orig.tar.gz
tar -zxvf opus.tar.gz; ls -l
pushd opus-1.1.2
./autogen.sh; ./configure --prefix=/artifacts/vcpkg/installed
make -j4; make install
mv ./rustdesk_thirdparty_lib/vcpkg/installed/arm64-linux /artifacts/vcpkg/installed/arm64-linux
;;
armv7)
pushd /artifacts
rm -rf rustdesk_thirdparty_lib
git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
mkdir -p /artifacts/vcpkg/installed
mv ./rustdesk_thirdparty_lib/vcpkg/installed/arm-linux /artifacts/vcpkg/installed/arm-linux
;;
esac
- name: Upload artifacts

1089
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -57,21 +57,22 @@ rpassword = "7.0"
base64 = "0.21"
num_cpus = "1.13"
bytes = { version = "1.2", features = ["serde"] }
default-net = "0.12.0"
default-net = "0.14"
wol-rs = "1.0"
flutter_rust_bridge = { version = "1.61.1", optional = true }
errno = "0.3"
rdev = { git = "https://github.com/fufesou/rdev" }
url = { version = "2.1", features = ["serde"] }
dlopen = "0.1"
hex = "0.4.3"
crossbeam-queue = "0.3"
hex = "0.4"
reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false }
chrono = "0.4.23"
cidr-utils = "0.5.9"
chrono = "0.4"
cidr-utils = "0.5"
[target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies]
cpal = "0.14"
ringbuf = "0.3"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
machine-uid = "0.2"
@ -93,8 +94,8 @@ winreg = "0.10"
windows-service = "0.4"
virtual_display = { path = "libs/virtual_display" }
impersonate_system = { git = "https://github.com/21pages/impersonate-system" }
shared_memory = "0.12.4"
shutdown_hooks = "0.1.0"
shared_memory = "0.12"
shutdown_hooks = "0.1"
[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2"
@ -102,10 +103,10 @@ cocoa = "0.24"
dispatch = "0.2"
core-foundation = "0.9"
core-graphics = "0.22"
include_dir = "0.7.2"
include_dir = "0.7"
dark-light = "1.0"
fruitbasket = "0.10.0"
objc_id = "0.1.1"
fruitbasket = "0.10"
objc_id = "0.1"
[target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies]
tray-icon = "0.4"

View File

@ -18,6 +18,12 @@ exe_path = 'target/release/' + hbb_name
flutter_win_target_dir = 'flutter/build/windows/runner/Release/'
skip_cargo = False
def get_arch() -> str:
custom_arch = os.environ.get("ARCH")
if custom_arch is None:
return "amd64"
return custom_arch
def system2(cmd):
err = os.system(cmd)
if err != 0:
@ -106,6 +112,10 @@ def make_parser():
action='store_true',
help='Skip cargo build process, only flutter version + Linux supported currently'
)
parser.add_argument(
"--package",
type=str
)
return parser
@ -251,13 +261,13 @@ def generate_control_file(version):
content = """Package: rustdesk
Version: %s
Architecture: amd64
Architecture: %s
Maintainer: open-trade <info@rustdesk.com>
Homepage: https://rustdesk.com
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0
Description: A remote control software.
""" % version
""" % (version, get_arch())
file = open(control_file_path, "w")
file.write(content)
file.close()
@ -307,6 +317,39 @@ def build_flutter_deb(version, features):
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
os.chdir("..")
def build_deb_from_folder(version, binary_folder):
os.chdir('flutter')
system2('mkdir -p tmpdeb/usr/bin/')
system2('mkdir -p tmpdeb/usr/lib/rustdesk')
system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
system2('mkdir -p tmpdeb/usr/share/applications/')
system2('mkdir -p tmpdeb/usr/share/polkit-1/actions')
system2('rm tmpdeb/usr/bin/rustdesk || true')
system2(
f'cp -r ../{binary_folder}/* tmpdeb/usr/lib/rustdesk/')
system2(
'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
system2(
'cp ../res/128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png')
system2(
'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
system2(
'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop')
system2(
'cp ../res/com.rustdesk.RustDesk.policy tmpdeb/usr/share/polkit-1/actions/')
system2(
"echo \"#!/bin/sh\" >> tmpdeb/usr/share/rustdesk/files/polkit && chmod a+x tmpdeb/usr/share/rustdesk/files/polkit")
system2('mkdir -p tmpdeb/DEBIAN')
generate_control_file(version)
system2('cp -a ../res/DEBIAN/* tmpdeb/DEBIAN/')
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service')
system2('dpkg-deb -b tmpdeb rustdesk.deb;')
system2('/bin/rm -rf tmpdeb/')
system2('/bin/rm -rf ../res/DEBIAN/control')
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
os.chdir("..")
def build_flutter_dmg(version, features):
if not skip_cargo:
@ -381,6 +424,10 @@ def main():
if args.skip_cargo:
skip_cargo = True
portable = args.portable
package = args.package
if package:
build_deb_from_folder(version, package)
return
if windows:
# build virtual display dynamic library
os.chdir('libs/virtual_display/dylib')

View File

@ -186,6 +186,71 @@ class MyTheme {
static const Color button = Color(0xFF2C8CFF);
static const Color hoverBorder = Color(0xFF999999);
// ListTile
static const ListTileThemeData listTileTheme = ListTileThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
);
// Checkbox
static const CheckboxThemeData checkboxTheme = CheckboxThemeData(
splashRadius: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
);
// TextButton
// Value is used to calculate "dialog.actionsPadding"
static const double mobileTextButtonPaddingLR = 20;
// TextButton on mobile needs a fixed padding, otherwise small buttons
// like "OK" has a larger left/right padding.
static TextButtonThemeData mobileTextButtonTheme = TextButtonThemeData(
style: TextButton.styleFrom(
padding: EdgeInsets.symmetric(horizontal: mobileTextButtonPaddingLR),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
);
// Dialogs
static const double dialogPadding = 24;
// padding bottom depends on content (some dialogs has no content)
static EdgeInsets dialogTitlePadding({bool content = true}) {
final double p = dialogPadding;
return EdgeInsets.fromLTRB(p, p, p, content ? 0 : p);
}
// padding bottom depends on actions (mobile has dialogs without actions)
static EdgeInsets dialogContentPadding({bool actions = true}) {
final double p = dialogPadding;
return isDesktop
? EdgeInsets.fromLTRB(p, p, p, actions ? (p - 4) : p)
: EdgeInsets.fromLTRB(p, p, p, actions ? (p / 2) : p);
}
static EdgeInsets dialogActionsPadding() {
final double p = dialogPadding;
return isDesktop
? EdgeInsets.fromLTRB(p, 0, p, (p - 4))
: EdgeInsets.fromLTRB(p, 0, (p - mobileTextButtonPaddingLR), (p / 2));
}
static EdgeInsets dialogButtonPadding = isDesktop
? EdgeInsets.only(left: dialogPadding)
: EdgeInsets.only(left: dialogPadding / 3);
static ThemeData lightTheme = ThemeData(
brightness: Brightness.light,
hoverColor: Color.fromARGB(255, 224, 224, 224),
@ -236,7 +301,7 @@ class MyTheme {
),
),
)
: null,
: mobileTextButtonTheme,
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: MyTheme.accent,
@ -254,21 +319,8 @@ class MyTheme {
),
),
),
checkboxTheme: const CheckboxThemeData(
splashRadius: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
listTileTheme: ListTileThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
checkboxTheme: checkboxTheme,
listTileTheme: listTileTheme,
menuBarTheme: MenuBarThemeData(
style:
MenuStyle(backgroundColor: MaterialStatePropertyAll(Colors.white))),
@ -334,7 +386,7 @@ class MyTheme {
),
),
)
: null,
: mobileTextButtonTheme,
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: MyTheme.accent,
@ -357,21 +409,8 @@ class MyTheme {
),
),
),
checkboxTheme: const CheckboxThemeData(
splashRadius: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
listTileTheme: ListTileThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
checkboxTheme: checkboxTheme,
listTileTheme: listTileTheme,
menuBarTheme: MenuBarThemeData(
style: MenuStyle(
backgroundColor: MaterialStatePropertyAll(Color(0xFF121212)))),
@ -771,6 +810,10 @@ void showToast(String text, {Duration timeout = const Duration(seconds: 2)}) {
});
}
// TODO
// - Remove argument "contentPadding", no need for it, all should look the same.
// - Remove "required" for argument "content". See simple confirm dialog "delete peer", only title and actions are used. No need to "content: SizedBox.shrink()".
// - Make dead code alive, transform arguments "onSubmit" and "onCancel" into correspondenting buttons "ConfirmOkButton", "CancelButton".
class CustomAlertDialog extends StatelessWidget {
const CustomAlertDialog(
{Key? key,
@ -798,8 +841,8 @@ class CustomAlertDialog extends StatelessWidget {
Future.delayed(Duration.zero, () {
if (!scopeNode.hasFocus) scopeNode.requestFocus();
});
const double padding = 30;
bool tabTapped = false;
return FocusScope(
node: scopeNode,
autofocus: true,
@ -824,22 +867,18 @@ class CustomAlertDialog extends StatelessWidget {
return KeyEventResult.ignored;
},
child: AlertDialog(
scrollable: true,
title: title,
titlePadding: EdgeInsets.fromLTRB(padding, 24, padding, 0),
contentPadding: EdgeInsets.fromLTRB(
contentPadding ?? padding,
25,
contentPadding ?? padding,
actions is List ? 10 : padding,
),
content: ConstrainedBox(
constraints: contentBoxConstraints,
child: content,
),
actions: actions,
actionsPadding: EdgeInsets.fromLTRB(padding, 0, padding, padding),
),
scrollable: true,
title: title,
content: ConstrainedBox(
constraints: contentBoxConstraints,
child: content,
),
actions: actions,
titlePadding: MyTheme.dialogTitlePadding(content: content != null),
contentPadding:
MyTheme.dialogContentPadding(actions: actions is List),
actionsPadding: MyTheme.dialogActionsPadding(),
buttonPadding: MyTheme.dialogButtonPadding),
);
}
}
@ -1115,25 +1154,32 @@ class AndroidPermissionManager {
}
}
// TODO move this to mobile/widgets.
// Used only for mobile, pages remote, settings, dialog
// TODO remove argument contentPadding, its not used, getToggle() has not
RadioListTile<T> getRadio<T>(
String name, T toValue, T curValue, void Function(T?) onChange,
{EdgeInsetsGeometry? contentPadding}) {
return RadioListTile<T>(
contentPadding: contentPadding,
contentPadding: contentPadding ?? EdgeInsets.zero,
visualDensity: VisualDensity.compact,
controlAffinity: ListTileControlAffinity.trailing,
title: Text(translate(name)),
value: toValue,
groupValue: curValue,
onChanged: onChange,
dense: true,
);
}
// TODO move this to mobile/widgets.
// Used only for mobile, pages remote, settings, dialog
CheckboxListTile getToggle(
String id, void Function(void Function()) setState, option, name,
{FFI? ffi}) {
final opt = bind.sessionGetToggleOptionSync(id: id, arg: option);
return CheckboxListTile(
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
value: opt,
onChanged: (v) {
setState(() {
@ -1143,7 +1189,6 @@ CheckboxListTile getToggle(
(ffi ?? gFFI).qualityMonitorModel.checkShowQualityMonitor(id);
}
},
dense: true,
title: Text(translate(name)));
}

View File

@ -802,7 +802,7 @@ class _FileManagerViewState extends State<FileManagerView> {
switchType: SwitchType.scheckbox,
text: translate("Show Hidden Files"),
getter: () async {
return controller.options.value.isWindows;
return controller.options.value.showHidden;
},
setter: (bool v) async {
controller.toggleShowHidden();

View File

@ -696,10 +696,8 @@ class _RemotePageState extends State<RemotePage> {
// return CustomAlertDialog(
// title: Text(translate('Physical Keyboard Input Mode')),
// content: Column(mainAxisSize: MainAxisSize.min, children: [
// getRadio('Legacy mode', 'legacy', current, setMode,
// contentPadding: EdgeInsets.zero),
// getRadio('Map mode', 'map', current, setMode,
// contentPadding: EdgeInsets.zero),
// getRadio('Legacy mode', 'legacy', current, setMode),
// getRadio('Map mode', 'map', current, setMode),
// ]));
// }, clickMaskDismiss: true);
// }
@ -1069,7 +1067,6 @@ void showOptions(
content: Column(
mainAxisSize: MainAxisSize.min,
children: displays + radios + toggles + more),
contentPadding: 0,
);
}, clickMaskDismiss: true, backDismiss: true);
}

View File

@ -502,19 +502,18 @@ void showLanguageSettings(OverlayDialogManager dialogManager) async {
}
return CustomAlertDialog(
title: SizedBox.shrink(),
content: Column(
children: [
getRadio('Default', '', lang, setLang),
Divider(color: MyTheme.border),
] +
langs.map((e) {
final key = e[0] as String;
final name = e[1] as String;
return getRadio(name, key, lang, setLang);
}).toList(),
),
actions: []);
content: Column(
children: [
getRadio('Default', '', lang, setLang),
Divider(color: MyTheme.border),
] +
langs.map((e) {
final key = e[0] as String;
final name = e[1] as String;
return getRadio(name, key, lang, setLang);
}).toList(),
),
);
}, backDismiss: true, clickMaskDismiss: true);
} catch (e) {
//
@ -536,14 +535,12 @@ void showThemeSettings(OverlayDialogManager dialogManager) async {
}
return CustomAlertDialog(
title: SizedBox.shrink(),
contentPadding: 10,
content: Column(children: [
getRadio('Light', ThemeMode.light, themeMode, setTheme),
getRadio('Dark', ThemeMode.dark, themeMode, setTheme),
getRadio('Follow System', ThemeMode.system, themeMode, setTheme)
]),
actions: []);
content: Column(children: [
getRadio('Light', ThemeMode.light, themeMode, setTheme),
getRadio('Dark', ThemeMode.dark, themeMode, setTheme),
getRadio('Follow System', ThemeMode.system, themeMode, setTheme)
]),
);
}, backDismiss: true, clickMaskDismiss: true);
}

View File

@ -128,12 +128,19 @@ void setTemporaryPasswordLengthDialog(
return CustomAlertDialog(
title: Text(translate("Set one-time password length")),
content: Column(
mainAxisSize: MainAxisSize.min,
children:
lengths.map((e) => getRadio(e, e, length, setLength)).toList()),
actions: [],
contentPadding: 14,
content: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: lengths
.map(
(value) => Row(
children: [
Text(value),
Radio(
value: value, groupValue: length, onChanged: setLength),
],
),
)
.toList()),
);
}, backDismiss: true, clickMaskDismiss: true);
}

View File

@ -59,7 +59,7 @@ def main():
b = toks[1].replace('ControlKey(ControlKey::', '').replace("Chr('", '').replace("' as _)),", '').replace(')),', '')
KEY_MAP[0] += ' "%s": "%s",\n'%(a, b)
print()
print('export function checkIfRetry(msgtype: string, title: string, text: string) {')
print('export function checkIfRetry(msgtype: string, title: string, text: string, retry_for_relay: boolean) {')
print(' return %s'%check_if_retry[0].replace('to_lowercase', 'toLowerCase').replace('contains', 'indexOf').replace('!', '').replace('")', '") < 0'))
print(';}')
print()

View File

@ -115,7 +115,7 @@ impl Enigo {
impl Default for Enigo {
fn default() -> Self {
let is_x11 = "x11" == hbb_common::platform::linux::get_display_server();
let is_x11 = hbb_common::platform::linux::is_x11_or_headless();
Self {
is_x11,
tfc: if is_x11 {

View File

@ -915,15 +915,12 @@ impl PeerConfig {
decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
config.password = password;
store = store || store2;
if let Some(v) = config.options.get_mut("rdp_password") {
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
*v = password;
store = store || store2;
}
if let Some(v) = config.options.get_mut("os-password") {
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
*v = password;
store = store || store2;
for opt in ["rdp_password", "os-password"] {
if let Some(v) = config.options.get_mut(opt) {
let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
*v = encrypted;
store = store || store2;
}
}
if store {
config.store(id);
@ -941,12 +938,11 @@ impl PeerConfig {
let _lock = CONFIG.read().unwrap();
let mut config = self.clone();
config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
if let Some(v) = config.options.get_mut("rdp_password") {
*v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)
for opt in ["rdp_password", "os-password"] {
if let Some(v) = config.options.get_mut(opt) {
*v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)
}
}
if let Some(v) = config.options.get_mut("os-password") {
*v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)
};
if let Err(err) = store_path(Self::path(id), config) {
log::error!("Failed to store config: {}", err);
}

View File

@ -5,6 +5,9 @@ lazy_static::lazy_static! {
pub static ref DISTRO: Distro = Distro::new();
}
pub const DISPLAY_SERVER_WAYLAND: &str = "wayland";
pub const DISPLAY_SERVER_X11: &str = "x11";
pub struct Distro {
pub name: String,
pub version_id: String,
@ -12,23 +15,41 @@ pub struct Distro {
impl Distro {
fn new() -> Self {
let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release".to_owned())
let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release")
.unwrap_or_default()
.trim()
.trim_matches('"')
.to_string();
let version_id = run_cmds("awk -F'=' '/^VERSION_ID=/ {print $2}' /etc/os-release")
.unwrap_or_default()
.trim()
.trim_matches('"')
.to_string();
let version_id =
run_cmds("awk -F'=' '/^VERSION_ID=/ {print $2}' /etc/os-release".to_owned())
.unwrap_or_default()
.trim()
.trim_matches('"')
.to_string();
Self { name, version_id }
}
}
#[inline]
pub fn is_gdm_user(username: &str) -> bool {
username == "gdm"
// || username == "lightgdm"
}
#[inline]
pub fn is_desktop_wayland() -> bool {
get_display_server() == DISPLAY_SERVER_WAYLAND
}
#[inline]
pub fn is_x11_or_headless() -> bool {
!is_desktop_wayland()
}
// -1
const INVALID_SESSION: &str = "4294967295";
pub fn get_display_server() -> String {
let mut session = get_values_of_seat0([0].to_vec())[0].clone();
let mut session = get_values_of_seat0(&[0])[0].clone();
if session.is_empty() {
// loginctl has not given the expected output. try something else.
if let Ok(sid) = std::env::var("XDG_SESSION_ID") {
@ -36,14 +57,20 @@ pub fn get_display_server() -> String {
session = sid;
}
if session.is_empty() {
session = run_cmds("cat /proc/self/sessionid".to_owned()).unwrap_or_default();
session = run_cmds("cat /proc/self/sessionid").unwrap_or_default();
if session == INVALID_SESSION {
session = "".to_owned();
}
}
}
get_display_server_of_session(&session)
if session.is_empty() {
"".to_owned()
} else {
get_display_server_of_session(&session)
}
}
fn get_display_server_of_session(session: &str) -> String {
pub fn get_display_server_of_session(session: &str) -> String {
let mut display_server = if let Ok(output) =
run_loginctl(Some(vec!["show-session", "-p", "Type", session]))
// Check session type of the session
@ -61,7 +88,7 @@ fn get_display_server_of_session(session: &str) -> String {
.replace("TTY=", "")
.trim_end()
.into();
if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{tty}.\\\\+Xorg\""))
if let Ok(xorg_results) = run_cmds(&format!("ps -e | grep \"{tty}.\\\\+Xorg\""))
// And check if Xorg is running on that tty
{
if xorg_results.trim_end() != "" {
@ -87,44 +114,68 @@ fn get_display_server_of_session(session: &str) -> String {
display_server.to_lowercase()
}
pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> {
#[inline]
fn line_values(indices: &[usize], line: &str) -> Vec<String> {
indices
.into_iter()
.map(|idx| line.split_whitespace().nth(*idx).unwrap_or("").to_owned())
.collect::<Vec<String>>()
}
#[inline]
pub fn get_values_of_seat0(indices: &[usize]) -> Vec<String> {
_get_values_of_seat0(indices, true)
}
#[inline]
pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec<String> {
_get_values_of_seat0(indices, false)
}
fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec<String> {
if let Ok(output) = run_loginctl(None) {
for line in String::from_utf8_lossy(&output.stdout).lines() {
if line.contains("seat0") {
if let Some(sid) = line.split_whitespace().next() {
if is_active(sid) {
return indices
.into_iter()
.map(|idx| line.split_whitespace().nth(idx).unwrap_or("").to_owned())
.collect::<Vec<String>>();
if ignore_gdm_wayland {
if is_gdm_user(line.split_whitespace().nth(2).unwrap_or(""))
&& get_display_server_of_session(sid) == DISPLAY_SERVER_WAYLAND
{
continue;
}
}
return line_values(indices, line);
}
}
}
}
}
// some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73
if let Ok(output) = run_loginctl(None) {
// some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73
for line in String::from_utf8_lossy(&output.stdout).lines() {
if let Some(sid) = line.split_whitespace().next() {
let d = get_display_server_of_session(sid);
if is_active(sid) && d != "tty" {
return indices
.into_iter()
.map(|idx| line.split_whitespace().nth(idx).unwrap_or("").to_owned())
.collect::<Vec<String>>();
if is_active(sid) {
let d = get_display_server_of_session(sid);
if ignore_gdm_wayland {
if is_gdm_user(line.split_whitespace().nth(2).unwrap_or(""))
&& d == DISPLAY_SERVER_WAYLAND
{
continue;
}
}
if d == "tty" {
continue;
}
return line_values(indices, line);
}
}
}
}
return indices
.iter()
.map(|_x| "".to_owned())
.collect::<Vec<String>>();
line_values(indices, "")
}
fn is_active(sid: &str) -> bool {
pub fn is_active(sid: &str) -> bool {
if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid])) {
String::from_utf8_lossy(&output.stdout).contains("active")
} else {
@ -132,9 +183,9 @@ fn is_active(sid: &str) -> bool {
}
}
pub fn run_cmds(cmds: String) -> ResultType<String> {
pub fn run_cmds(cmds: &str) -> ResultType<String> {
let output = std::process::Command::new("sh")
.args(vec!["-c", &cmds])
.args(vec!["-c", cmds])
.output()?;
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}

View File

@ -30,7 +30,7 @@ const CFG_KEY_DECODER: &str = "bestHwDecoders";
const DEFAULT_PIXFMT: AVPixelFormat = AVPixelFormat::AV_PIX_FMT_YUV420P;
pub const DEFAULT_TIME_BASE: [i32; 2] = [1, 30];
const DEFAULT_GOP: i32 = 60;
const DEFAULT_GOP: i32 = i32::MAX;
const DEFAULT_HW_QUALITY: Quality = Quality_Default;
const DEFAULT_RC: RateControl = RC_DEFAULT;

View File

@ -74,7 +74,7 @@ pub trait TraitCapturer {
#[cfg(x11)]
#[inline]
pub fn is_x11() -> bool {
"x11" == hbb_common::platform::linux::get_display_server()
hbb_common::platform::linux::is_x11_or_headless()
}
#[cfg(x11)]

View File

@ -13,7 +13,10 @@ use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait},
Device, Host, StreamConfig,
};
use crossbeam_queue::ArrayQueue;
use magnum_opus::{Channels::*, Decoder as AudioDecoder};
#[cfg(not(any(target_os = "android", target_os = "linux")))]
use ringbuf::{ring_buffer::RbBase, Rb};
use sha2::{Digest, Sha256};
use uuid::Uuid;
@ -65,6 +68,7 @@ pub mod io_loop;
pub const MILLI1: Duration = Duration::from_millis(1);
pub const SEC30: Duration = Duration::from_secs(30);
pub const VIDEO_QUEUE_SIZE: usize = 120;
/// Client of the remote desktop.
pub struct Client;
@ -701,11 +705,25 @@ pub struct AudioHandler {
#[cfg(target_os = "linux")]
simple: Option<psimple::Simple>,
#[cfg(not(any(target_os = "android", target_os = "linux")))]
audio_buffer: Arc<std::sync::Mutex<std::collections::vec_deque::VecDeque<f32>>>,
audio_buffer: AudioBuffer,
sample_rate: (u32, u32),
#[cfg(not(any(target_os = "android", target_os = "linux")))]
audio_stream: Option<Box<dyn StreamTrait>>,
channels: u16,
#[cfg(not(any(target_os = "android", target_os = "linux")))]
ready: Arc<std::sync::Mutex<bool>>,
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
struct AudioBuffer(pub Arc<std::sync::Mutex<ringbuf::HeapRb<f32>>>);
#[cfg(not(any(target_os = "android", target_os = "linux")))]
impl Default for AudioBuffer {
fn default() -> Self {
Self(Arc::new(std::sync::Mutex::new(
ringbuf::HeapRb::<f32>::new(48000 * 2), // 48000hz, 2 channel, 1 second
)))
}
}
impl AudioHandler {
@ -794,7 +812,7 @@ impl AudioHandler {
#[inline]
pub fn handle_frame(&mut self, frame: AudioFrame) {
#[cfg(not(any(target_os = "android", target_os = "linux")))]
if self.audio_stream.is_none() {
if self.audio_stream.is_none() || !self.ready.lock().unwrap().clone() {
return;
}
#[cfg(target_os = "linux")]
@ -814,11 +832,7 @@ impl AudioHandler {
{
let sample_rate0 = self.sample_rate.0;
let sample_rate = self.sample_rate.1;
let audio_buffer = self.audio_buffer.clone();
// avoiding memory overflow if audio_buffer consumer side has problem
if audio_buffer.lock().unwrap().len() as u32 > sample_rate * 120 {
*audio_buffer.lock().unwrap() = Default::default();
}
let audio_buffer = self.audio_buffer.0.clone();
if sample_rate != sample_rate0 {
let buffer = crate::resample_channels(
&buffer[0..n],
@ -826,12 +840,12 @@ impl AudioHandler {
sample_rate,
channels,
);
audio_buffer.lock().unwrap().extend(buffer);
audio_buffer.lock().unwrap().push_slice_overwrite(&buffer);
} else {
audio_buffer
.lock()
.unwrap()
.extend(buffer[0..n].iter().cloned());
.push_slice_overwrite(&buffer[0..n]);
}
}
#[cfg(target_os = "android")]
@ -859,16 +873,23 @@ impl AudioHandler {
// too many errors, will improve later
log::trace!("an error occurred on stream: {}", err);
};
let audio_buffer = self.audio_buffer.clone();
let audio_buffer = self.audio_buffer.0.clone();
let ready = self.ready.clone();
let stream = device.build_output_stream(
config,
move |data: &mut [T], _: &_| {
if !*ready.lock().unwrap() {
*ready.lock().unwrap() = true;
}
let mut lock = audio_buffer.lock().unwrap();
let mut n = data.len();
if lock.len() < n {
n = lock.len();
if lock.occupied_len() < n {
n = lock.occupied_len();
}
let mut input = lock.drain(0..n);
let mut elems = vec![0.0f32; n];
lock.pop_slice(&mut elems);
drop(lock);
let mut input = elems.into_iter();
for sample in data.iter_mut() {
*sample = match input.next() {
Some(x) => T::from(&x),
@ -1640,8 +1661,9 @@ impl LoginConfigHandler {
/// Media data.
pub enum MediaData {
VideoFrame(VideoFrame),
AudioFrame(AudioFrame),
VideoQueue,
VideoFrame(Box<VideoFrame>),
AudioFrame(Box<AudioFrame>),
AudioFormat(AudioFormat),
Reset,
RecordScreen(bool, i32, i32, String),
@ -1655,11 +1677,15 @@ pub type MediaSender = mpsc::Sender<MediaData>;
/// # Arguments
///
/// * `video_callback` - The callback for video frame. Being called when a video frame is ready.
pub fn start_video_audio_threads<F>(video_callback: F) -> (MediaSender, MediaSender)
pub fn start_video_audio_threads<F>(
video_callback: F,
) -> (MediaSender, MediaSender, Arc<ArrayQueue<VideoFrame>>)
where
F: 'static + FnMut(&mut Vec<u8>) + Send,
{
let (video_sender, video_receiver) = mpsc::channel::<MediaData>();
let video_queue = Arc::new(ArrayQueue::<VideoFrame>::new(VIDEO_QUEUE_SIZE));
let video_queue_cloned = video_queue.clone();
let mut video_callback = video_callback;
std::thread::spawn(move || {
@ -1668,10 +1694,17 @@ where
if let Ok(data) = video_receiver.recv() {
match data {
MediaData::VideoFrame(vf) => {
if let Ok(true) = video_handler.handle_frame(vf) {
if let Ok(true) = video_handler.handle_frame(*vf) {
video_callback(&mut video_handler.rgb);
}
}
MediaData::VideoQueue => {
if let Some(vf) = video_queue.pop() {
if let Ok(true) = video_handler.handle_frame(vf) {
video_callback(&mut video_handler.rgb);
}
}
}
MediaData::Reset => {
video_handler.reset();
}
@ -1687,7 +1720,7 @@ where
log::info!("Video decoder loop exits");
});
let audio_sender = start_audio_thread();
return (video_sender, audio_sender);
return (video_sender, audio_sender, video_queue_cloned);
}
/// Start an audio thread
@ -1700,7 +1733,7 @@ pub fn start_audio_thread() -> MediaSender {
if let Ok(data) = audio_receiver.recv() {
match data {
MediaData::AudioFrame(af) => {
audio_handler.handle_frame(af);
audio_handler.handle_frame(*af);
}
MediaData::AudioFormat(f) => {
log::debug!("recved audio format, sample rate={}", f.sample_rate);

View File

@ -9,6 +9,7 @@ use std::sync::{
#[cfg(windows)]
use clipboard::{cliprdr::CliprdrClientContext, ContextSend};
use crossbeam_queue::ArrayQueue;
use hbb_common::config::{PeerConfig, TransferSerde};
use hbb_common::fs::{
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
@ -42,6 +43,7 @@ use crate::{client::Data, client::Interface};
pub struct Remote<T: InvokeUiSession> {
handler: Session<T>,
video_queue: Arc<ArrayQueue<VideoFrame>>,
video_sender: MediaSender,
audio_sender: MediaSender,
receiver: mpsc::UnboundedReceiver<Data>,
@ -68,6 +70,7 @@ pub struct Remote<T: InvokeUiSession> {
impl<T: InvokeUiSession> Remote<T> {
pub fn new(
handler: Session<T>,
video_queue: Arc<ArrayQueue<VideoFrame>>,
video_sender: MediaSender,
audio_sender: MediaSender,
receiver: mpsc::UnboundedReceiver<Data>,
@ -76,6 +79,7 @@ impl<T: InvokeUiSession> Remote<T> {
) -> Self {
Self {
handler,
video_queue,
video_sender,
audio_sender,
receiver,
@ -812,6 +816,18 @@ impl<T: InvokeUiSession> Remote<T> {
}
}
fn contains_key_frame(vf: &VideoFrame) -> bool {
match &vf.union {
Some(vf) => match vf {
video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key),
video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key),
video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key),
_ => false,
},
None => false,
}
}
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
match msg_in.union {
@ -830,7 +846,15 @@ impl<T: InvokeUiSession> Remote<T> {
..Default::default()
})
};
self.video_sender.send(MediaData::VideoFrame(vf)).ok();
if Self::contains_key_frame(&vf) {
while let Some(_) = self.video_queue.pop() {}
self.video_sender
.send(MediaData::VideoFrame(Box::new(vf)))
.ok();
} else {
self.video_queue.force_push(vf);
self.video_sender.send(MediaData::VideoQueue).ok();
}
}
Some(message::Union::Hash(hash)) => {
self.handler
@ -1217,7 +1241,9 @@ impl<T: InvokeUiSession> Remote<T> {
}
Some(message::Union::AudioFrame(frame)) => {
if !self.handler.lc.read().unwrap().disable_audio.v {
self.audio_sender.send(MediaData::AudioFrame(frame)).ok();
self.audio_sender
.send(MediaData::AudioFrame(Box::new(frame)))
.ok();
}
}
Some(message::Union::FileAction(action)) => match action.union {

View File

@ -755,7 +755,7 @@ lazy_static::lazy_static! {
#[cfg(target_os = "linux")]
lazy_static::lazy_static! {
pub static ref IS_X11: bool = "x11" == hbb_common::platform::linux::get_display_server();
pub static ref IS_X11: bool = hbb_common::platform::linux::is_x11_or_headless();
}
pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> String {

View File

@ -843,7 +843,7 @@ pub fn map_keyboard_mode(_peer: &str, event: &Event, mut key_event: KeyEvent) ->
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
match &event.unicode {
Some(unicode_info) => {
if let Some(name) = &unicode_info.name {
@ -857,11 +857,13 @@ fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEve
None =>
{
#[cfg(target_os = "windows")]
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
if let Some(chr) = get_char_by_vk(event.platform_code as u32) {
let mut evt = key_event.clone();
evt.set_seq(chr.to_string());
events.push(evt);
if _peer == OS_LOWER_LINUX {
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
if let Some(chr) = get_char_by_vk(event.platform_code as u32) {
let mut evt = key_event.clone();
evt.set_seq(chr.to_string());
events.push(evt);
}
}
}
}
@ -886,7 +888,12 @@ fn is_hot_key_modifiers_down() -> bool {
#[cfg(target_os = "windows")]
pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> {
let mut key_event = map_keyboard_mode(peer, event, key_event)?;
key_event.set_chr((key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16));
let chr = if peer == OS_LOWER_WINDOWS {
(key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16)
} else {
key_event.chr()
};
key_event.set_chr(chr);
Some(key_event)
}
@ -962,7 +969,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
#[cfg(any(target_os = "linux", target_os = "windows"))]
if is_press(event) {
try_fill_unicode(event, &key_event, &mut events);
try_fill_unicode(peer, event, &key_event, &mut events);
}
#[cfg(target_os = "windows")]
@ -974,7 +981,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
#[cfg(target_os = "macos")]
if !unsafe { IS_LEFT_OPTION_DOWN } {
try_fill_unicode(event, &key_event, &mut events);
try_fill_unicode(peer, event, &key_event, &mut events);
}
if events.is_empty() {

View File

@ -1,4 +1,5 @@
use super::{CursorData, ResultType};
use desktop::Desktop;
pub use hbb_common::platform::linux::*;
use hbb_common::{
allow_err, bail,
@ -64,6 +65,11 @@ pub struct xcb_xfixes_get_cursor_image {
pub pixels: *const c_long,
}
#[inline]
fn sleep_millis(millis: u64) {
std::thread::sleep(Duration::from_millis(millis));
}
pub fn get_cursor_pos() -> Option<(i32, i32)> {
let mut res = None;
XDO.with(|xdo| {
@ -190,7 +196,7 @@ fn start_server(user: Option<(String, String)>, server: &mut Option<Child>) {
fn stop_server(server: &mut Option<Child>) {
if let Some(mut ps) = server.take() {
allow_err!(ps.kill());
std::thread::sleep(Duration::from_millis(30));
sleep_millis(30);
match ps.try_wait() {
Ok(Some(_status)) => {}
Ok(None) => {
@ -201,44 +207,20 @@ fn stop_server(server: &mut Option<Child>) {
}
}
fn set_x11_env(uid: &str) {
log::info!("uid of seat0: {}", uid);
let gdm = format!("/run/user/{}/gdm/Xauthority", uid);
let mut auth = get_env_tries("XAUTHORITY", uid, 10);
// auth is another user's when uid = 0, https://github.com/rustdesk/rustdesk/issues/2468
if auth.is_empty() || uid == "0" {
auth = if Path::new(&gdm).exists() {
gdm
} else {
let username = get_active_username();
if username == "root" {
format!("/{}/.Xauthority", username)
} else {
let tmp = format!("/home/{}/.Xauthority", username);
if Path::new(&tmp).exists() {
tmp
} else {
format!("/var/lib/{}/.Xauthority", username)
}
}
};
fn set_x11_env(desktop: &Desktop) {
log::info!("DISPLAY: {}", desktop.display);
log::info!("XAUTHORITY: {}", desktop.xauth);
if !desktop.display.is_empty() {
std::env::set_var("DISPLAY", &desktop.display);
}
let mut d = get_env("DISPLAY", uid);
if d.is_empty() {
d = get_display();
if !desktop.xauth.is_empty() {
std::env::set_var("XAUTHORITY", &desktop.xauth);
}
if d.is_empty() {
d = ":0".to_owned();
}
d = d.replace(&whoami::hostname(), "").replace("localhost", "");
log::info!("DISPLAY: {}", d);
log::info!("XAUTHORITY: {}", auth);
std::env::set_var("XAUTHORITY", auth);
std::env::set_var("DISPLAY", d);
}
#[inline]
fn stop_rustdesk_servers() {
let _ = run_cmds(format!(
let _ = run_cmds(&format!(
r##"ps -ef | grep -E 'rustdesk +--server' | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##,
));
}
@ -246,37 +228,49 @@ fn stop_rustdesk_servers() {
fn should_start_server(
try_x11: bool,
uid: &mut String,
cur_uid: String,
desktop: &Desktop,
cm0: &mut bool,
last_restart: &mut Instant,
server: &mut Option<Child>,
) -> bool {
let cm = get_cm();
let mut start_new = false;
if cur_uid != *uid && !cur_uid.is_empty() {
*uid = cur_uid;
let mut should_kill = false;
if desktop.is_headless() {
if !uid.is_empty() {
// From having a monitor to not having a monitor.
*uid = "".to_owned();
should_kill = true;
}
} else if desktop.uid != *uid && !desktop.uid.is_empty() {
*uid = desktop.uid.clone();
if try_x11 {
set_x11_env(&uid);
set_x11_env(&desktop);
}
if let Some(ps) = server.as_mut() {
allow_err!(ps.kill());
std::thread::sleep(Duration::from_millis(30));
*last_restart = Instant::now();
}
} else if !cm
should_kill = true;
}
if !should_kill
&& !cm
&& ((*cm0 && last_restart.elapsed().as_secs() > 60)
|| last_restart.elapsed().as_secs() > 3600)
{
// restart server if new connections all closed, or every one hour,
// as a workaround to resolve "SpotUdp" (dns resolve)
// and x server get displays failure issue
should_kill = true;
log::info!("restart server");
}
if should_kill {
if let Some(ps) = server.as_mut() {
allow_err!(ps.kill());
std::thread::sleep(Duration::from_millis(30));
sleep_millis(30);
*last_restart = Instant::now();
log::info!("restart server");
}
}
if let Some(ps) = server.as_mut() {
match ps.try_wait() {
Ok(Some(_)) => {
@ -296,7 +290,7 @@ fn should_start_server(
// stop_rustdesk_servers() is just a temp solution here.
fn force_stop_server() {
stop_rustdesk_servers();
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
sleep_millis(super::SERVICE_INTERVAL);
}
pub fn start_os_service() {
@ -305,6 +299,8 @@ pub fn start_os_service() {
let running = Arc::new(AtomicBool::new(true));
let r = running.clone();
let mut desktop = Desktop::default();
let mut sid = "".to_owned();
let mut uid = "".to_owned();
let mut server: Option<Child> = None;
let mut user_server: Option<Child> = None;
@ -317,31 +313,18 @@ pub fn start_os_service() {
let mut cm0 = false;
let mut last_restart = Instant::now();
while running.load(Ordering::SeqCst) {
let (cur_uid, cur_user) = get_active_user_id_name();
desktop.refresh();
// for fixing https://github.com/rustdesk/rustdesk/issues/3129 to avoid too much dbus calling,
// though duplicate logic here with should_start_server
if !(cur_uid != *uid && !cur_uid.is_empty()) {
let cm = get_cm();
if !(!cm
&& ((cm0 && last_restart.elapsed().as_secs() > 60)
|| last_restart.elapsed().as_secs() > 3600))
{
std::thread::sleep(Duration::from_millis(500));
continue;
}
}
let is_wayland = current_is_wayland();
if cur_user == "root" || !is_wayland {
// Duplicate logic here with should_start_server
// Login wayland will try to start a headless --server.
if desktop.username == "root" || !desktop.is_wayland() || desktop.is_login_wayland() {
// try kill subprocess "--server"
stop_server(&mut user_server);
// try start subprocess "--server"
if should_start_server(
true,
&mut uid,
cur_uid,
&desktop,
&mut cm0,
&mut last_restart,
&mut server,
@ -349,30 +332,42 @@ pub fn start_os_service() {
force_stop_server();
start_server(None, &mut server);
}
} else if cur_user != "" {
if cur_user != "gdm" {
// try kill subprocess "--server"
stop_server(&mut server);
} else if desktop.username != "" {
// try kill subprocess "--server"
stop_server(&mut server);
// try start subprocess "--server"
if should_start_server(
false,
&mut uid,
cur_uid.clone(),
&mut cm0,
&mut last_restart,
// try start subprocess "--server"
if should_start_server(
false,
&mut uid,
&desktop,
&mut cm0,
&mut last_restart,
&mut user_server,
) {
force_stop_server();
start_server(
Some((desktop.uid.clone(), desktop.username.clone())),
&mut user_server,
) {
force_stop_server();
start_server(Some((cur_uid, cur_user)), &mut user_server);
}
);
}
} else {
force_stop_server();
stop_server(&mut user_server);
stop_server(&mut server);
}
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
let keeps_headless = sid.is_empty() && desktop.is_headless();
let keeps_session = sid == desktop.sid;
if keeps_headless || keeps_session {
// for fixing https://github.com/rustdesk/rustdesk/issues/3129 to avoid too much dbus calling,
sleep_millis(500);
} else {
sleep_millis(super::SERVICE_INTERVAL);
}
if !desktop.is_headless() {
sid = desktop.sid.clone();
}
}
if let Some(ps) = user_server.take().as_mut() {
@ -384,13 +379,15 @@ pub fn start_os_service() {
log::info!("Exit");
}
#[inline]
pub fn get_active_user_id_name() -> (String, String) {
let vec_id_name = get_values_of_seat0([1, 2].to_vec());
let vec_id_name = get_values_of_seat0(&[1, 2]);
(vec_id_name[0].clone(), vec_id_name[1].clone())
}
#[inline]
pub fn get_active_userid() -> String {
get_values_of_seat0([1].to_vec())[0].clone()
get_values_of_seat0(&[1])[0].clone()
}
fn get_cm() -> bool {
@ -409,45 +406,6 @@ fn get_cm() -> bool {
false
}
fn get_display() -> String {
let user = get_active_username();
log::debug!("w {}", &user);
if let Ok(output) = Command::new("w").arg(&user).output() {
for line in String::from_utf8_lossy(&output.stdout).lines() {
log::debug!(" {}", line);
let mut iter = line.split_whitespace();
let b = iter.nth(2);
if let Some(b) = b {
if b.starts_with(":") {
return b.to_owned();
}
}
}
}
// above not work for gdm user
log::debug!("ls -l /tmp/.X11-unix/");
let mut last = "".to_owned();
if let Ok(output) = Command::new("ls")
.args(vec!["-l", "/tmp/.X11-unix/"])
.output()
{
for line in String::from_utf8_lossy(&output.stdout).lines() {
log::debug!(" {}", line);
let mut iter = line.split_whitespace();
let user_field = iter.nth(2);
if let Some(x) = iter.last() {
if x.starts_with("X") {
last = x.replace("X", ":").to_owned();
if user_field == Some(&user) {
return last;
}
}
}
}
}
last
}
pub fn is_login_wayland() -> bool {
if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") {
contents.contains("#WaylandEnable=false") || contents.contains("WaylandEnable=true")
@ -458,9 +416,9 @@ pub fn is_login_wayland() -> bool {
}
}
#[inline]
pub fn current_is_wayland() -> bool {
let dtype = get_display_server();
return "wayland" == dtype && unsafe { UNMODIFIED };
return is_desktop_wayland() && unsafe { UNMODIFIED };
}
// to-do: test the other display manager
@ -473,8 +431,9 @@ fn _get_display_manager() -> String {
"gdm3".to_owned()
}
#[inline]
pub fn get_active_username() -> String {
get_values_of_seat0([2].to_vec())[0].clone()
get_values_of_seat0(&[2])[0].clone()
}
pub fn get_active_user_home() -> Option<PathBuf> {
@ -488,9 +447,16 @@ pub fn get_active_user_home() -> Option<PathBuf> {
None
}
pub fn get_env_var(k: &str) -> String {
match std::env::var(k) {
Ok(v) => v,
Err(_e) => "".to_owned(),
}
}
pub fn is_prelogin() -> bool {
let n = get_active_userid().len();
n < 4 && n > 1
let (uid, uname) = get_active_user_id_name();
uid.len() >= 4 || uname == "root"
}
pub fn is_root() -> bool {
@ -498,7 +464,7 @@ pub fn is_root() -> bool {
}
fn is_opensuse() -> bool {
if let Ok(res) = run_cmds("cat /etc/os-release | grep opensuse".to_owned()) {
if let Ok(res) = run_cmds("cat /etc/os-release | grep opensuse") {
if !res.is_empty() {
return true;
}
@ -512,6 +478,9 @@ pub fn run_as_user(arg: Vec<&str>, user: Option<(String, String)>) -> ResultType
None => get_active_user_id_name(),
};
let cmd = std::env::current_exe()?;
if uid.is_empty() {
bail!("No valid uid");
}
let xdg = &format!("XDG_RUNTIME_DIR=/run/user/{}", uid) as &str;
let mut args = vec![xdg, "-u", &username, cmd.to_str().unwrap_or("")];
args.append(&mut arg.clone());
@ -597,21 +566,31 @@ pub fn is_installed() -> bool {
true
}
fn get_env_tries(name: &str, uid: &str, n: usize) -> String {
pub(super) fn get_env_tries(name: &str, uid: &str, process: &str, n: usize) -> String {
for _ in 0..n {
let x = get_env(name, uid);
let x = get_env(name, uid, process);
if !x.is_empty() {
return x;
}
std::thread::sleep(Duration::from_millis(300));
sleep_millis(300);
}
"".to_owned()
}
fn get_env(name: &str, uid: &str) -> String {
let cmd = format!("ps -u {} -o pid= | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, name, name);
log::debug!("Run: {}", &cmd);
if let Ok(x) = run_cmds(cmd) {
#[inline]
fn get_env(name: &str, uid: &str, process: &str) -> String {
let cmd = format!("ps -u {} -f | grep '{}' | grep -v 'grep' | tail -1 | awk '{{print $2}}' | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, process, name, name);
if let Ok(x) = run_cmds(&cmd) {
x.trim_end().to_string()
} else {
"".to_owned()
}
}
#[inline]
fn get_env_from_pid(name: &str, pid: &str) -> String {
let cmd = format!("cat /proc/{}/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", pid, name, name);
if let Ok(x) = run_cmds(&cmd) {
x.trim_end().to_string()
} else {
"".to_owned()
@ -701,7 +680,7 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
let connected_pat = get_xrandr_conn_pat(name);
let mut v = vec![];
if let Ok(re) = Regex::new(&format!("{}{}", connected_pat, resolutions_pat)) {
match run_cmds("xrandr --query | tr -s ' '".to_owned()) {
match run_cmds("xrandr --query | tr -s ' '") {
Ok(xrandr_output) => {
// There'are different kinds of xrandr output.
/*
@ -750,7 +729,7 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
}
pub fn current_resolution(name: &str) -> ResultType<Resolution> {
let xrandr_output = run_cmds("xrandr --query | tr -s ' '".to_owned())?;
let xrandr_output = run_cmds("xrandr --query | tr -s ' '")?;
let re = Regex::new(&get_xrandr_conn_pat(name))?;
if let Some(caps) = re.captures(&xrandr_output) {
if let Some((width, height)) = get_width_height_from_captures(&caps) {
@ -775,3 +754,176 @@ pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<
.spawn()?;
Ok(())
}
mod desktop {
use super::*;
pub const XFCE4_PANEL: &str = "xfce4-panel";
pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary";
#[derive(Debug, Clone, Default)]
pub struct Desktop {
pub sid: String,
pub username: String,
pub uid: String,
pub protocal: String,
pub display: String,
pub xauth: String,
}
impl Desktop {
#[inline]
pub fn is_wayland(&self) -> bool {
self.protocal == DISPLAY_SERVER_WAYLAND
}
#[inline]
pub fn is_login_wayland(&self) -> bool {
super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND
}
#[inline]
pub fn is_headless(&self) -> bool {
self.sid.is_empty()
}
fn get_display(&mut self) {
self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10);
if self.display.is_empty() {
self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10);
}
if self.display.is_empty() {
self.display = Self::get_display_by_user(&self.username);
}
if self.display.is_empty() {
self.display = ":0".to_owned();
}
self.display = self
.display
.replace(&whoami::hostname(), "")
.replace("localhost", "");
}
fn get_xauth_from_xorg(&mut self) {
if let Ok(output) = run_cmds(&format!(
"ps -u {} -f | grep 'Xorg' | grep -v 'grep'",
&self.uid
)) {
for line in output.lines() {
let mut auth_found = false;
for v in line.split_whitespace() {
if v == "-auth" {
auth_found = true;
} else if auth_found {
if std::path::Path::new(v).is_absolute() {
self.xauth = v.to_string();
} else {
if let Some(pid) = line.split_whitespace().nth(1) {
let home_dir = get_env_from_pid("HOME", pid);
if home_dir.is_empty() {
self.xauth = format!("/home/{}/{}", self.username, v);
} else {
self.xauth = format!("{}/{}", home_dir, v);
}
} else {
// unreachable!
}
}
return;
}
}
}
}
}
fn get_xauth(&mut self) {
self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10);
if self.xauth.is_empty() {
get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10);
}
if self.xauth.is_empty() {
self.get_xauth_from_xorg();
}
let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid);
if self.xauth.is_empty() {
self.xauth = if std::path::Path::new(&gdm).exists() {
gdm
} else {
let username = &self.username;
if username == "root" {
format!("/{}/.Xauthority", username)
} else {
let tmp = format!("/home/{}/.Xauthority", username);
if std::path::Path::new(&tmp).exists() {
tmp
} else {
format!("/var/lib/{}/.Xauthority", username)
}
}
};
}
}
fn get_display_by_user(user: &str) -> String {
// log::debug!("w {}", &user);
if let Ok(output) = std::process::Command::new("w").arg(&user).output() {
for line in String::from_utf8_lossy(&output.stdout).lines() {
let mut iter = line.split_whitespace();
let b = iter.nth(2);
if let Some(b) = b {
if b.starts_with(":") {
return b.to_owned();
}
}
}
}
// above not work for gdm user
//log::debug!("ls -l /tmp/.X11-unix/");
let mut last = "".to_owned();
if let Ok(output) = std::process::Command::new("ls")
.args(vec!["-l", "/tmp/.X11-unix/"])
.output()
{
for line in String::from_utf8_lossy(&output.stdout).lines() {
let mut iter = line.split_whitespace();
let user_field = iter.nth(2);
if let Some(x) = iter.last() {
if x.starts_with("X") {
last = x.replace("X", ":").to_owned();
if user_field == Some(&user) {
return last;
}
}
}
}
}
last
}
pub fn refresh(&mut self) {
if !self.sid.is_empty() && is_active(&self.sid) {
return;
}
let seat0_values = get_values_of_seat0(&[0, 1, 2]);
if seat0_values[0].is_empty() {
*self = Self::default();
return;
}
self.sid = seat0_values[0].clone();
self.uid = seat0_values[1].clone();
self.username = seat0_values[2].clone();
self.protocal = get_display_server_of_session(&self.sid).into();
if self.is_login_wayland() {
self.display = "".to_owned();
self.xauth = "".to_owned();
return;
}
self.get_display();
self.get_xauth();
}
}
}

View File

@ -883,7 +883,7 @@ impl Connection {
let dtype = crate::platform::linux::get_display_server();
if dtype != "x11" && dtype != "wayland" {
res.set_error(format!(
"Unsupported display server type {}, x11 or wayland expected",
"Unsupported display server type \"{}\", x11 or wayland expected",
dtype
));
let mut msg_out = Message::new();
@ -1669,7 +1669,7 @@ impl Connection {
Some(message::Union::AudioFrame(frame)) => {
if !self.disable_audio {
if let Some(sender) = &self.audio_sender {
allow_err!(sender.send(MediaData::AudioFrame(frame)));
allow_err!(sender.send(MediaData::AudioFrame(Box::new(frame))));
} else {
log::warn!(
"Processing audio frame without the voice call audio sender."

View File

@ -1193,7 +1193,9 @@ fn is_function_key(ck: &EnumOrUnknown<ControlKey>) -> bool {
});
res = true;
} else if ck.value() == ControlKey::LockScreen.value() {
lock_screen_2();
std::thread::spawn(|| {
lock_screen_2();
});
res = true;
}
return res;

View File

@ -189,6 +189,17 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
}
}
#[inline]
fn wait_prelogin(&self) {
#[cfg(target_os = "linux")]
while self.active() {
if crate::platform::linux::is_prelogin() {
break;
}
thread::sleep(time::Duration::from_millis(300));
}
}
pub fn repeat<S, F>(&self, interval_ms: u64, callback: F)
where
F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send,
@ -198,6 +209,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
let mut callback = callback;
let sp = self.clone();
let thread = thread::spawn(move || {
sp.wait_prelogin();
let mut state = S::default();
let mut may_reset = false;
while sp.active() {
@ -232,6 +245,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
let sp = self.clone();
let mut callback = callback;
let thread = thread::spawn(move || {
sp.wait_prelogin();
let mut error_timeout = HIBERNATE_TIMEOUT;
while sp.active() {
if sp.has_subscribes() {

View File

@ -54,6 +54,18 @@ pub fn start(args: &mut [String]) {
let dir = "/usr";
sciter::set_library(&(prefix + dir + "/lib/rustdesk/libsciter-gtk.so")).ok();
}
#[cfg(windows)]
// Check if there is a sciter.dll nearby.
if let Ok(exe) = std::env::current_exe() {
if let Some(parent) = exe.parent() {
let sciter_dll_path = parent.join("sciter.dll");
if sciter_dll_path.exists() {
// Try to set the sciter dll.
let p = sciter_dll_path.to_string_lossy().to_string();
log::debug!("Found dll:{}, \n {:?}", p, sciter::set_library(&p));
}
}
}
// https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-types.h
// https://github.com/rustdesk/rustdesk/issues/132#issuecomment-886069737
#[cfg(windows)]

View File

@ -1149,13 +1149,15 @@ pub async fn io_loop<T: InvokeUiSession>(handler: Session<T>) {
let frame_count = Arc::new(AtomicUsize::new(0));
let frame_count_cl = frame_count.clone();
let ui_handler = handler.ui_handler.clone();
let (video_sender, audio_sender) = start_video_audio_threads(move |data: &mut Vec<u8>| {
frame_count_cl.fetch_add(1, Ordering::Relaxed);
ui_handler.on_rgba(data);
});
let (video_sender, audio_sender, video_queue) =
start_video_audio_threads(move |data: &mut Vec<u8>| {
frame_count_cl.fetch_add(1, Ordering::Relaxed);
ui_handler.on_rgba(data);
});
let mut remote = Remote::new(
handler,
video_queue,
video_sender,
audio_sender,
receiver,