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/**" - "docs/**"
- "README.md" - "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: jobs:
build-for-windows: run-ci:
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) uses: ./.github/workflows/flutter-nightly.yml
runs-on: ${{ matrix.job.os }} with:
strategy: upload-artifact: false
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

View File

@ -5,6 +5,16 @@ on:
# schedule build every night # schedule build every night
- cron: "0 0 * * *" - cron: "0 0 * * *"
workflow_dispatch: 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: env:
LLVM_VERSION: "15.0.6" LLVM_VERSION: "15.0.6"
@ -21,9 +31,10 @@ env:
# To make a custom build with your own servers set the below secret values # To make a custom build with your own servers set the below secret values
RS_PUB_KEY: '${{ secrets.RS_PUB_KEY }}' RS_PUB_KEY: '${{ secrets.RS_PUB_KEY }}'
RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}' RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}'
UPLOAD_ARTIFACT: '${{ inputs.upload-artifact }}'
jobs: jobs:
build-for-windows: build-for-windows-flutter:
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
strategy: strategy:
@ -33,7 +44,7 @@ jobs:
# - { target: i686-pc-windows-msvc , os: windows-2019 } # - { target: i686-pc-windows-msvc , os: windows-2019 }
# - { target: x86_64-pc-windows-gnu , os: windows-2019 } # - { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: x86_64-pc-windows-msvc, 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: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -92,6 +103,7 @@ jobs:
- name: Sign rustdesk files - name: Sign rustdesk files
uses: GermanBluefox/code-sign-action@v7 uses: GermanBluefox/code-sign-action@v7
if: env.UPLOAD_ARTIFACT == 'true'
with: with:
certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}'
password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' password: '${{ secrets.WINDOWS_PFX_PASSWORD }}'
@ -102,6 +114,7 @@ jobs:
- name: Build self-extracted executable - name: Build self-extracted executable
shell: bash shell: bash
if: env.UPLOAD_ARTIFACT == 'true'
run: | run: |
pushd ./libs/portable pushd ./libs/portable
python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe 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 - name: Sign rustdesk self-extracted file
uses: GermanBluefox/code-sign-action@v7 uses: GermanBluefox/code-sign-action@v7
if: env.UPLOAD_ARTIFACT == 'true'
with: with:
certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}'
password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' password: '${{ secrets.WINDOWS_PFX_PASSWORD }}'
@ -128,6 +142,110 @@ jobs:
- name: Publish Release - name: Publish Release
uses: softprops/action-gh-release@v1 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: with:
prerelease: true prerelease: true
tag_name: ${{ env.TAG_NAME }} tag_name: ${{ env.TAG_NAME }}
@ -146,11 +264,6 @@ jobs:
os: macos-latest, os: macos-latest,
extra-build-args: "", extra-build-args: "",
} }
- {
target: aarch64-apple-darwin,
os: macos-latest,
extra-build-args: "",
}
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v3 uses: actions/checkout@v3
@ -262,6 +375,7 @@ jobs:
done done
- name: Publish DMG package - name: Publish DMG package
if: env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -411,14 +525,14 @@ jobs:
BUILD_TOOLS_VERSION: "30.0.2" BUILD_TOOLS_VERSION: "30.0.2"
- name: Upload Artifacts - name: Upload Artifacts
if: env.ANDROID_SIGNING_KEY != null if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true'
uses: actions/upload-artifact@master uses: actions/upload-artifact@master
with: with:
name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk
path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}}
- name: Publish signed apk package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -427,7 +541,7 @@ jobs:
${{steps.sign-rustdesk.outputs.signedReleaseFile}} ${{steps.sign-rustdesk.outputs.signedReleaseFile}}
- name: Publish unsigned apk package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true 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: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" }
# - { # - {
# arch: armv7, # arch: armv7,
# target: arm-unknown-linux-gnueabihf, # target: armv7-unknown-linux-gnueabihf,
# os: ubuntu-20.04, # os: ubuntu-20.04,
# use-cross: true, # use-cross: true,
# extra-build-features: "", # 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 } # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
steps: steps:
- name: Maximize build space - name: Maximize build space
@ -743,30 +857,8 @@ jobs:
cat ~/.cargo/config cat ~/.cargo/config
# start build # start build
pushd /workspace pushd /workspace
# mock export VCPKG_ROOT=/opt/artifacts/vcpkg
case "${{ matrix.job.arch }}" in cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release
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
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@master uses: actions/upload-artifact@master
@ -774,6 +866,158 @@ jobs:
name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so
path: target/release/liblibrustdesk.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: build-rustdesk-linux-arm:
needs: [build-rustdesk-lib-linux-arm] needs: [build-rustdesk-lib-linux-arm]
name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
@ -803,8 +1047,8 @@ jobs:
# use-cross: true, # use-cross: true,
# extra-build-features: "flatpak", # extra-build-features: "flatpak",
# } # }
# - { arch: armv7, target: arm-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: "" }
# - { 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 } # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true }
steps: steps:
- name: Checkout source code - name: Checkout source code
@ -932,7 +1176,7 @@ jobs:
done done
- name: Publish debian package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -955,7 +1199,7 @@ jobs:
sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml
- name: Publish appimage package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -1029,7 +1273,7 @@ jobs:
# res/rustdesk*.zst # res/rustdesk*.zst
- name: Publish fedora28/centos8 package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -1152,7 +1396,7 @@ jobs:
done done
- name: Publish debian package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -1208,7 +1452,7 @@ jobs:
cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f
- name: Publish archlinux package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -1231,7 +1475,7 @@ jobs:
sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml
- name: Publish appimage package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -1240,7 +1484,7 @@ jobs:
./appimage/rustdesk-${{ env.VERSION }}-*.AppImage ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage
- name: Publish fedora28/centos8 package - 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 uses: softprops/action-gh-release@v1
with: with:
prerelease: true prerelease: true
@ -1369,6 +1613,7 @@ jobs:
- name: Publish flatpak package - name: Publish flatpak package
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
if: env.UPLOAD_ARTIFACT == 'true'
with: with:
prerelease: true prerelease: true
tag_name: ${{ env.TAG_NAME }} tag_name: ${{ env.TAG_NAME }}

View File

@ -10,7 +10,7 @@ jobs:
fail-fast: true fail-fast: true
matrix: matrix:
job: job:
# - { arch: armv7, os: ubuntu-20.04 } - { arch: armv7, os: ubuntu-20.04 }
- { arch: x86_64, os: ubuntu-20.04 } - { arch: x86_64, os: ubuntu-20.04 }
- { arch: aarch64, os: ubuntu-20.04 } - { arch: aarch64, os: ubuntu-20.04 }
steps: 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 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 update -y
apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev 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) 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 esac
cmake --version
gcc -v
run: | run: |
# disable git safe.directory # disable git safe.directory
git config --global --add safe.directory "*" git config --global --add safe.directory "*"
@ -65,25 +65,19 @@ jobs:
./bootstrap-vcpkg.sh ./bootstrap-vcpkg.sh
./vcpkg install libvpx libyuv opus ./vcpkg install libvpx libyuv opus
;; ;;
aarch64|armv7) aarch64)
pushd /artifacts pushd /artifacts
# libyuv rm -rf rustdesk_thirdparty_lib
git clone https://chromium.googlesource.com/libyuv/libyuv || true git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
pushd libyuv
git pull
mkdir -p build
pushd build
mkdir -p /artifacts/vcpkg/installed mkdir -p /artifacts/vcpkg/installed
cmake .. -DCMAKE_INSTALL_PREFIX=/artifacts/vcpkg/installed mv ./rustdesk_thirdparty_lib/vcpkg/installed/arm64-linux /artifacts/vcpkg/installed/arm64-linux
make -j4 && make install ;;
popd armv7)
popd pushd /artifacts
# libopus, ubuntu 18.04 prebuilt is not be compiled with -fPIC rm -rf rustdesk_thirdparty_lib
wget -O opus.tar.gz http://archive.ubuntu.com/ubuntu/pool/main/o/opus/opus_1.1.2.orig.tar.gz git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
tar -zxvf opus.tar.gz; ls -l mkdir -p /artifacts/vcpkg/installed
pushd opus-1.1.2 mv ./rustdesk_thirdparty_lib/vcpkg/installed/arm-linux /artifacts/vcpkg/installed/arm-linux
./autogen.sh; ./configure --prefix=/artifacts/vcpkg/installed
make -j4; make install
;; ;;
esac esac
- name: Upload artifacts - 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" base64 = "0.21"
num_cpus = "1.13" num_cpus = "1.13"
bytes = { version = "1.2", features = ["serde"] } bytes = { version = "1.2", features = ["serde"] }
default-net = "0.12.0" default-net = "0.14"
wol-rs = "1.0" wol-rs = "1.0"
flutter_rust_bridge = { version = "1.61.1", optional = true } flutter_rust_bridge = { version = "1.61.1", optional = true }
errno = "0.3" errno = "0.3"
rdev = { git = "https://github.com/fufesou/rdev" } rdev = { git = "https://github.com/fufesou/rdev" }
url = { version = "2.1", features = ["serde"] } url = { version = "2.1", features = ["serde"] }
dlopen = "0.1" 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 } reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false }
chrono = "0.4.23" chrono = "0.4"
cidr-utils = "0.5.9" cidr-utils = "0.5"
[target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies]
cpal = "0.14" cpal = "0.14"
ringbuf = "0.3"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
machine-uid = "0.2" machine-uid = "0.2"
@ -93,8 +94,8 @@ winreg = "0.10"
windows-service = "0.4" windows-service = "0.4"
virtual_display = { path = "libs/virtual_display" } virtual_display = { path = "libs/virtual_display" }
impersonate_system = { git = "https://github.com/21pages/impersonate-system" } impersonate_system = { git = "https://github.com/21pages/impersonate-system" }
shared_memory = "0.12.4" shared_memory = "0.12"
shutdown_hooks = "0.1.0" shutdown_hooks = "0.1"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2" objc = "0.2"
@ -102,10 +103,10 @@ cocoa = "0.24"
dispatch = "0.2" dispatch = "0.2"
core-foundation = "0.9" core-foundation = "0.9"
core-graphics = "0.22" core-graphics = "0.22"
include_dir = "0.7.2" include_dir = "0.7"
dark-light = "1.0" dark-light = "1.0"
fruitbasket = "0.10.0" fruitbasket = "0.10"
objc_id = "0.1.1" objc_id = "0.1"
[target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies]
tray-icon = "0.4" 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/' flutter_win_target_dir = 'flutter/build/windows/runner/Release/'
skip_cargo = False 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): def system2(cmd):
err = os.system(cmd) err = os.system(cmd)
if err != 0: if err != 0:
@ -106,6 +112,10 @@ def make_parser():
action='store_true', action='store_true',
help='Skip cargo build process, only flutter version + Linux supported currently' help='Skip cargo build process, only flutter version + Linux supported currently'
) )
parser.add_argument(
"--package",
type=str
)
return parser return parser
@ -251,13 +261,13 @@ def generate_control_file(version):
content = """Package: rustdesk content = """Package: rustdesk
Version: %s Version: %s
Architecture: amd64 Architecture: %s
Maintainer: open-trade <info@rustdesk.com> Maintainer: open-trade <info@rustdesk.com>
Homepage: https://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 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. Description: A remote control software.
""" % version """ % (version, get_arch())
file = open(control_file_path, "w") file = open(control_file_path, "w")
file.write(content) file.write(content)
file.close() file.close()
@ -307,6 +317,39 @@ def build_flutter_deb(version, features):
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version) os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
os.chdir("..") 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): def build_flutter_dmg(version, features):
if not skip_cargo: if not skip_cargo:
@ -381,6 +424,10 @@ def main():
if args.skip_cargo: if args.skip_cargo:
skip_cargo = True skip_cargo = True
portable = args.portable portable = args.portable
package = args.package
if package:
build_deb_from_folder(version, package)
return
if windows: if windows:
# build virtual display dynamic library # build virtual display dynamic library
os.chdir('libs/virtual_display/dylib') os.chdir('libs/virtual_display/dylib')

View File

@ -186,6 +186,71 @@ class MyTheme {
static const Color button = Color(0xFF2C8CFF); static const Color button = Color(0xFF2C8CFF);
static const Color hoverBorder = Color(0xFF999999); 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( static ThemeData lightTheme = ThemeData(
brightness: Brightness.light, brightness: Brightness.light,
hoverColor: Color.fromARGB(255, 224, 224, 224), hoverColor: Color.fromARGB(255, 224, 224, 224),
@ -236,7 +301,7 @@ class MyTheme {
), ),
), ),
) )
: null, : mobileTextButtonTheme,
elevatedButtonTheme: ElevatedButtonThemeData( elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: MyTheme.accent, backgroundColor: MyTheme.accent,
@ -254,21 +319,8 @@ class MyTheme {
), ),
), ),
), ),
checkboxTheme: const CheckboxThemeData( checkboxTheme: checkboxTheme,
splashRadius: 0, listTileTheme: listTileTheme,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
listTileTheme: ListTileThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
menuBarTheme: MenuBarThemeData( menuBarTheme: MenuBarThemeData(
style: style:
MenuStyle(backgroundColor: MaterialStatePropertyAll(Colors.white))), MenuStyle(backgroundColor: MaterialStatePropertyAll(Colors.white))),
@ -334,7 +386,7 @@ class MyTheme {
), ),
), ),
) )
: null, : mobileTextButtonTheme,
elevatedButtonTheme: ElevatedButtonThemeData( elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: MyTheme.accent, backgroundColor: MyTheme.accent,
@ -357,21 +409,8 @@ class MyTheme {
), ),
), ),
), ),
checkboxTheme: const CheckboxThemeData( checkboxTheme: checkboxTheme,
splashRadius: 0, listTileTheme: listTileTheme,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
listTileTheme: ListTileThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(5),
),
),
),
menuBarTheme: MenuBarThemeData( menuBarTheme: MenuBarThemeData(
style: MenuStyle( style: MenuStyle(
backgroundColor: MaterialStatePropertyAll(Color(0xFF121212)))), 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 { class CustomAlertDialog extends StatelessWidget {
const CustomAlertDialog( const CustomAlertDialog(
{Key? key, {Key? key,
@ -798,8 +841,8 @@ class CustomAlertDialog extends StatelessWidget {
Future.delayed(Duration.zero, () { Future.delayed(Duration.zero, () {
if (!scopeNode.hasFocus) scopeNode.requestFocus(); if (!scopeNode.hasFocus) scopeNode.requestFocus();
}); });
const double padding = 30;
bool tabTapped = false; bool tabTapped = false;
return FocusScope( return FocusScope(
node: scopeNode, node: scopeNode,
autofocus: true, autofocus: true,
@ -824,22 +867,18 @@ class CustomAlertDialog extends StatelessWidget {
return KeyEventResult.ignored; return KeyEventResult.ignored;
}, },
child: AlertDialog( child: AlertDialog(
scrollable: true, scrollable: true,
title: title, title: title,
titlePadding: EdgeInsets.fromLTRB(padding, 24, padding, 0), content: ConstrainedBox(
contentPadding: EdgeInsets.fromLTRB( constraints: contentBoxConstraints,
contentPadding ?? padding, child: content,
25, ),
contentPadding ?? padding, actions: actions,
actions is List ? 10 : padding, titlePadding: MyTheme.dialogTitlePadding(content: content != null),
), contentPadding:
content: ConstrainedBox( MyTheme.dialogContentPadding(actions: actions is List),
constraints: contentBoxConstraints, actionsPadding: MyTheme.dialogActionsPadding(),
child: content, buttonPadding: MyTheme.dialogButtonPadding),
),
actions: actions,
actionsPadding: EdgeInsets.fromLTRB(padding, 0, padding, padding),
),
); );
} }
} }
@ -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>( RadioListTile<T> getRadio<T>(
String name, T toValue, T curValue, void Function(T?) onChange, String name, T toValue, T curValue, void Function(T?) onChange,
{EdgeInsetsGeometry? contentPadding}) { {EdgeInsetsGeometry? contentPadding}) {
return RadioListTile<T>( return RadioListTile<T>(
contentPadding: contentPadding, contentPadding: contentPadding ?? EdgeInsets.zero,
visualDensity: VisualDensity.compact,
controlAffinity: ListTileControlAffinity.trailing, controlAffinity: ListTileControlAffinity.trailing,
title: Text(translate(name)), title: Text(translate(name)),
value: toValue, value: toValue,
groupValue: curValue, groupValue: curValue,
onChanged: onChange, onChanged: onChange,
dense: true,
); );
} }
// TODO move this to mobile/widgets.
// Used only for mobile, pages remote, settings, dialog
CheckboxListTile getToggle( CheckboxListTile getToggle(
String id, void Function(void Function()) setState, option, name, String id, void Function(void Function()) setState, option, name,
{FFI? ffi}) { {FFI? ffi}) {
final opt = bind.sessionGetToggleOptionSync(id: id, arg: option); final opt = bind.sessionGetToggleOptionSync(id: id, arg: option);
return CheckboxListTile( return CheckboxListTile(
contentPadding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
value: opt, value: opt,
onChanged: (v) { onChanged: (v) {
setState(() { setState(() {
@ -1143,7 +1189,6 @@ CheckboxListTile getToggle(
(ffi ?? gFFI).qualityMonitorModel.checkShowQualityMonitor(id); (ffi ?? gFFI).qualityMonitorModel.checkShowQualityMonitor(id);
} }
}, },
dense: true,
title: Text(translate(name))); title: Text(translate(name)));
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -59,7 +59,7 @@ def main():
b = toks[1].replace('ControlKey(ControlKey::', '').replace("Chr('", '').replace("' as _)),", '').replace(')),', '') b = toks[1].replace('ControlKey(ControlKey::', '').replace("Chr('", '').replace("' as _)),", '').replace(')),', '')
KEY_MAP[0] += ' "%s": "%s",\n'%(a, b) KEY_MAP[0] += ' "%s": "%s",\n'%(a, b)
print() 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(' return %s'%check_if_retry[0].replace('to_lowercase', 'toLowerCase').replace('contains', 'indexOf').replace('!', '').replace('")', '") < 0'))
print(';}') print(';}')
print() print()

View File

@ -115,7 +115,7 @@ impl Enigo {
impl Default for Enigo { impl Default for Enigo {
fn default() -> Self { 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 { Self {
is_x11, is_x11,
tfc: if is_x11 { tfc: if is_x11 {

View File

@ -915,15 +915,12 @@ impl PeerConfig {
decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
config.password = password; config.password = password;
store = store || store2; store = store || store2;
if let Some(v) = config.options.get_mut("rdp_password") { for opt in ["rdp_password", "os-password"] {
let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); if let Some(v) = config.options.get_mut(opt) {
*v = password; let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
store = store || store2; *v = encrypted;
} 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;
} }
if store { if store {
config.store(id); config.store(id);
@ -941,12 +938,11 @@ impl PeerConfig {
let _lock = CONFIG.read().unwrap(); let _lock = CONFIG.read().unwrap();
let mut config = self.clone(); let mut config = self.clone();
config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
if let Some(v) = config.options.get_mut("rdp_password") { for opt in ["rdp_password", "os-password"] {
*v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION) 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) { if let Err(err) = store_path(Self::path(id), config) {
log::error!("Failed to store config: {}", err); 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 static ref DISTRO: Distro = Distro::new();
} }
pub const DISPLAY_SERVER_WAYLAND: &str = "wayland";
pub const DISPLAY_SERVER_X11: &str = "x11";
pub struct Distro { pub struct Distro {
pub name: String, pub name: String,
pub version_id: String, pub version_id: String,
@ -12,23 +15,41 @@ pub struct Distro {
impl Distro { impl Distro {
fn new() -> Self { 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() .unwrap_or_default()
.trim() .trim()
.trim_matches('"') .trim_matches('"')
.to_string(); .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 } 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 { 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() { if session.is_empty() {
// loginctl has not given the expected output. try something else. // loginctl has not given the expected output. try something else.
if let Ok(sid) = std::env::var("XDG_SESSION_ID") { if let Ok(sid) = std::env::var("XDG_SESSION_ID") {
@ -36,14 +57,20 @@ pub fn get_display_server() -> String {
session = sid; session = sid;
} }
if session.is_empty() { 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();
}
} }
} }
if session.is_empty() {
get_display_server_of_session(&session) "".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) = let mut display_server = if let Ok(output) =
run_loginctl(Some(vec!["show-session", "-p", "Type", session])) run_loginctl(Some(vec!["show-session", "-p", "Type", session]))
// Check session type of the session // Check session type of the session
@ -61,7 +88,7 @@ fn get_display_server_of_session(session: &str) -> String {
.replace("TTY=", "") .replace("TTY=", "")
.trim_end() .trim_end()
.into(); .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 // And check if Xorg is running on that tty
{ {
if xorg_results.trim_end() != "" { if xorg_results.trim_end() != "" {
@ -87,44 +114,68 @@ fn get_display_server_of_session(session: &str) -> String {
display_server.to_lowercase() 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) { if let Ok(output) = run_loginctl(None) {
for line in String::from_utf8_lossy(&output.stdout).lines() { for line in String::from_utf8_lossy(&output.stdout).lines() {
if line.contains("seat0") { if line.contains("seat0") {
if let Some(sid) = line.split_whitespace().next() { if let Some(sid) = line.split_whitespace().next() {
if is_active(sid) { if is_active(sid) {
return indices if ignore_gdm_wayland {
.into_iter() if is_gdm_user(line.split_whitespace().nth(2).unwrap_or(""))
.map(|idx| line.split_whitespace().nth(idx).unwrap_or("").to_owned()) && get_display_server_of_session(sid) == DISPLAY_SERVER_WAYLAND
.collect::<Vec<String>>(); {
continue;
}
}
return line_values(indices, line);
} }
} }
} }
} }
}
// some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73
if let Ok(output) = run_loginctl(None) {
for line in String::from_utf8_lossy(&output.stdout).lines() { for line in String::from_utf8_lossy(&output.stdout).lines() {
if let Some(sid) = line.split_whitespace().next() { if let Some(sid) = line.split_whitespace().next() {
let d = get_display_server_of_session(sid); if is_active(sid) {
if is_active(sid) && d != "tty" { let d = get_display_server_of_session(sid);
return indices if ignore_gdm_wayland {
.into_iter() if is_gdm_user(line.split_whitespace().nth(2).unwrap_or(""))
.map(|idx| line.split_whitespace().nth(idx).unwrap_or("").to_owned()) && d == DISPLAY_SERVER_WAYLAND
.collect::<Vec<String>>(); {
continue;
}
}
if d == "tty" {
continue;
}
return line_values(indices, line);
} }
} }
} }
} }
return indices line_values(indices, "")
.iter()
.map(|_x| "".to_owned())
.collect::<Vec<String>>();
} }
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])) { if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid])) {
String::from_utf8_lossy(&output.stdout).contains("active") String::from_utf8_lossy(&output.stdout).contains("active")
} else { } 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") let output = std::process::Command::new("sh")
.args(vec!["-c", &cmds]) .args(vec!["-c", cmds])
.output()?; .output()?;
Ok(String::from_utf8_lossy(&output.stdout).to_string()) 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; const DEFAULT_PIXFMT: AVPixelFormat = AVPixelFormat::AV_PIX_FMT_YUV420P;
pub const DEFAULT_TIME_BASE: [i32; 2] = [1, 30]; 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_HW_QUALITY: Quality = Quality_Default;
const DEFAULT_RC: RateControl = RC_DEFAULT; const DEFAULT_RC: RateControl = RC_DEFAULT;

View File

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

View File

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

View File

@ -9,6 +9,7 @@ use std::sync::{
#[cfg(windows)] #[cfg(windows)]
use clipboard::{cliprdr::CliprdrClientContext, ContextSend}; use clipboard::{cliprdr::CliprdrClientContext, ContextSend};
use crossbeam_queue::ArrayQueue;
use hbb_common::config::{PeerConfig, TransferSerde}; use hbb_common::config::{PeerConfig, TransferSerde};
use hbb_common::fs::{ use hbb_common::fs::{
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, 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> { pub struct Remote<T: InvokeUiSession> {
handler: Session<T>, handler: Session<T>,
video_queue: Arc<ArrayQueue<VideoFrame>>,
video_sender: MediaSender, video_sender: MediaSender,
audio_sender: MediaSender, audio_sender: MediaSender,
receiver: mpsc::UnboundedReceiver<Data>, receiver: mpsc::UnboundedReceiver<Data>,
@ -68,6 +70,7 @@ pub struct Remote<T: InvokeUiSession> {
impl<T: InvokeUiSession> Remote<T> { impl<T: InvokeUiSession> Remote<T> {
pub fn new( pub fn new(
handler: Session<T>, handler: Session<T>,
video_queue: Arc<ArrayQueue<VideoFrame>>,
video_sender: MediaSender, video_sender: MediaSender,
audio_sender: MediaSender, audio_sender: MediaSender,
receiver: mpsc::UnboundedReceiver<Data>, receiver: mpsc::UnboundedReceiver<Data>,
@ -76,6 +79,7 @@ impl<T: InvokeUiSession> Remote<T> {
) -> Self { ) -> Self {
Self { Self {
handler, handler,
video_queue,
video_sender, video_sender,
audio_sender, audio_sender,
receiver, 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 { async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
if let Ok(msg_in) = Message::parse_from_bytes(&data) { if let Ok(msg_in) = Message::parse_from_bytes(&data) {
match msg_in.union { match msg_in.union {
@ -830,7 +846,15 @@ impl<T: InvokeUiSession> Remote<T> {
..Default::default() ..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)) => { Some(message::Union::Hash(hash)) => {
self.handler self.handler
@ -1217,7 +1241,9 @@ impl<T: InvokeUiSession> Remote<T> {
} }
Some(message::Union::AudioFrame(frame)) => { Some(message::Union::AudioFrame(frame)) => {
if !self.handler.lc.read().unwrap().disable_audio.v { 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 { Some(message::Union::FileAction(action)) => match action.union {

View File

@ -755,7 +755,7 @@ lazy_static::lazy_static! {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
lazy_static::lazy_static! { 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 { 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")))] #[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 { match &event.unicode {
Some(unicode_info) => { Some(unicode_info) => {
if let Some(name) = &unicode_info.name { 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 => None =>
{ {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { if _peer == OS_LOWER_LINUX {
if let Some(chr) = get_char_by_vk(event.platform_code as u32) { if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
let mut evt = key_event.clone(); if let Some(chr) = get_char_by_vk(event.platform_code as u32) {
evt.set_seq(chr.to_string()); let mut evt = key_event.clone();
events.push(evt); evt.set_seq(chr.to_string());
events.push(evt);
}
} }
} }
} }
@ -886,7 +888,12 @@ fn is_hot_key_modifiers_down() -> bool {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> { 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)?; 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) 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"))] #[cfg(any(target_os = "linux", target_os = "windows"))]
if is_press(event) { 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")] #[cfg(target_os = "windows")]
@ -974,7 +981,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
if !unsafe { IS_LEFT_OPTION_DOWN } { 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() { if events.is_empty() {

View File

@ -1,4 +1,5 @@
use super::{CursorData, ResultType}; use super::{CursorData, ResultType};
use desktop::Desktop;
pub use hbb_common::platform::linux::*; pub use hbb_common::platform::linux::*;
use hbb_common::{ use hbb_common::{
allow_err, bail, allow_err, bail,
@ -64,6 +65,11 @@ pub struct xcb_xfixes_get_cursor_image {
pub pixels: *const c_long, 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)> { pub fn get_cursor_pos() -> Option<(i32, i32)> {
let mut res = None; let mut res = None;
XDO.with(|xdo| { 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>) { fn stop_server(server: &mut Option<Child>) {
if let Some(mut ps) = server.take() { if let Some(mut ps) = server.take() {
allow_err!(ps.kill()); allow_err!(ps.kill());
std::thread::sleep(Duration::from_millis(30)); sleep_millis(30);
match ps.try_wait() { match ps.try_wait() {
Ok(Some(_status)) => {} Ok(Some(_status)) => {}
Ok(None) => { Ok(None) => {
@ -201,44 +207,20 @@ fn stop_server(server: &mut Option<Child>) {
} }
} }
fn set_x11_env(uid: &str) { fn set_x11_env(desktop: &Desktop) {
log::info!("uid of seat0: {}", uid); log::info!("DISPLAY: {}", desktop.display);
let gdm = format!("/run/user/{}/gdm/Xauthority", uid); log::info!("XAUTHORITY: {}", desktop.xauth);
let mut auth = get_env_tries("XAUTHORITY", uid, 10); if !desktop.display.is_empty() {
// auth is another user's when uid = 0, https://github.com/rustdesk/rustdesk/issues/2468 std::env::set_var("DISPLAY", &desktop.display);
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)
}
}
};
} }
let mut d = get_env("DISPLAY", uid); if !desktop.xauth.is_empty() {
if d.is_empty() { std::env::set_var("XAUTHORITY", &desktop.xauth);
d = get_display();
} }
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() { 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"##, 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( fn should_start_server(
try_x11: bool, try_x11: bool,
uid: &mut String, uid: &mut String,
cur_uid: String, desktop: &Desktop,
cm0: &mut bool, cm0: &mut bool,
last_restart: &mut Instant, last_restart: &mut Instant,
server: &mut Option<Child>, server: &mut Option<Child>,
) -> bool { ) -> bool {
let cm = get_cm(); let cm = get_cm();
let mut start_new = false; let mut start_new = false;
if cur_uid != *uid && !cur_uid.is_empty() { let mut should_kill = false;
*uid = cur_uid;
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 { if try_x11 {
set_x11_env(&uid); set_x11_env(&desktop);
} }
if let Some(ps) = server.as_mut() { should_kill = true;
allow_err!(ps.kill()); }
std::thread::sleep(Duration::from_millis(30));
*last_restart = Instant::now(); if !should_kill
} && !cm
} else if !cm
&& ((*cm0 && last_restart.elapsed().as_secs() > 60) && ((*cm0 && last_restart.elapsed().as_secs() > 60)
|| last_restart.elapsed().as_secs() > 3600) || last_restart.elapsed().as_secs() > 3600)
{ {
// restart server if new connections all closed, or every one hour, // restart server if new connections all closed, or every one hour,
// as a workaround to resolve "SpotUdp" (dns resolve) // as a workaround to resolve "SpotUdp" (dns resolve)
// and x server get displays failure issue // and x server get displays failure issue
should_kill = true;
log::info!("restart server");
}
if should_kill {
if let Some(ps) = server.as_mut() { if let Some(ps) = server.as_mut() {
allow_err!(ps.kill()); allow_err!(ps.kill());
std::thread::sleep(Duration::from_millis(30)); sleep_millis(30);
*last_restart = Instant::now(); *last_restart = Instant::now();
log::info!("restart server");
} }
} }
if let Some(ps) = server.as_mut() { if let Some(ps) = server.as_mut() {
match ps.try_wait() { match ps.try_wait() {
Ok(Some(_)) => { Ok(Some(_)) => {
@ -296,7 +290,7 @@ fn should_start_server(
// stop_rustdesk_servers() is just a temp solution here. // stop_rustdesk_servers() is just a temp solution here.
fn force_stop_server() { fn force_stop_server() {
stop_rustdesk_servers(); stop_rustdesk_servers();
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); sleep_millis(super::SERVICE_INTERVAL);
} }
pub fn start_os_service() { pub fn start_os_service() {
@ -305,6 +299,8 @@ pub fn start_os_service() {
let running = Arc::new(AtomicBool::new(true)); let running = Arc::new(AtomicBool::new(true));
let r = running.clone(); let r = running.clone();
let mut desktop = Desktop::default();
let mut sid = "".to_owned();
let mut uid = "".to_owned(); let mut uid = "".to_owned();
let mut server: Option<Child> = None; let mut server: Option<Child> = None;
let mut user_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 cm0 = false;
let mut last_restart = Instant::now(); let mut last_restart = Instant::now();
while running.load(Ordering::SeqCst) { 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, // Duplicate logic here with should_start_server
// though duplicate logic here with should_start_server // Login wayland will try to start a headless --server.
if !(cur_uid != *uid && !cur_uid.is_empty()) { if desktop.username == "root" || !desktop.is_wayland() || desktop.is_login_wayland() {
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 {
// try kill subprocess "--server" // try kill subprocess "--server"
stop_server(&mut user_server); stop_server(&mut user_server);
// try start subprocess "--server" // try start subprocess "--server"
if should_start_server( if should_start_server(
true, true,
&mut uid, &mut uid,
cur_uid, &desktop,
&mut cm0, &mut cm0,
&mut last_restart, &mut last_restart,
&mut server, &mut server,
@ -349,30 +332,42 @@ pub fn start_os_service() {
force_stop_server(); force_stop_server();
start_server(None, &mut server); start_server(None, &mut server);
} }
} else if cur_user != "" { } else if desktop.username != "" {
if cur_user != "gdm" { // try kill subprocess "--server"
// try kill subprocess "--server" stop_server(&mut server);
stop_server(&mut server);
// try start subprocess "--server" // try start subprocess "--server"
if should_start_server( if should_start_server(
false, false,
&mut uid, &mut uid,
cur_uid.clone(), &desktop,
&mut cm0, &mut cm0,
&mut last_restart, &mut last_restart,
&mut user_server,
) {
force_stop_server();
start_server(
Some((desktop.uid.clone(), desktop.username.clone())),
&mut user_server, &mut user_server,
) { );
force_stop_server();
start_server(Some((cur_uid, cur_user)), &mut user_server);
}
} }
} else { } else {
force_stop_server(); force_stop_server();
stop_server(&mut user_server); stop_server(&mut user_server);
stop_server(&mut 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() { if let Some(ps) = user_server.take().as_mut() {
@ -384,13 +379,15 @@ pub fn start_os_service() {
log::info!("Exit"); log::info!("Exit");
} }
#[inline]
pub fn get_active_user_id_name() -> (String, String) { 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()) (vec_id_name[0].clone(), vec_id_name[1].clone())
} }
#[inline]
pub fn get_active_userid() -> String { 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 { fn get_cm() -> bool {
@ -409,45 +406,6 @@ fn get_cm() -> bool {
false 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 { pub fn is_login_wayland() -> bool {
if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") { if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") {
contents.contains("#WaylandEnable=false") || contents.contains("WaylandEnable=true") 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 { pub fn current_is_wayland() -> bool {
let dtype = get_display_server(); return is_desktop_wayland() && unsafe { UNMODIFIED };
return "wayland" == dtype && unsafe { UNMODIFIED };
} }
// to-do: test the other display manager // to-do: test the other display manager
@ -473,8 +431,9 @@ fn _get_display_manager() -> String {
"gdm3".to_owned() "gdm3".to_owned()
} }
#[inline]
pub fn get_active_username() -> String { 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> { pub fn get_active_user_home() -> Option<PathBuf> {
@ -488,9 +447,16 @@ pub fn get_active_user_home() -> Option<PathBuf> {
None 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 { pub fn is_prelogin() -> bool {
let n = get_active_userid().len(); let (uid, uname) = get_active_user_id_name();
n < 4 && n > 1 uid.len() >= 4 || uname == "root"
} }
pub fn is_root() -> bool { pub fn is_root() -> bool {
@ -498,7 +464,7 @@ pub fn is_root() -> bool {
} }
fn is_opensuse() -> 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() { if !res.is_empty() {
return true; 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(), None => get_active_user_id_name(),
}; };
let cmd = std::env::current_exe()?; 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 xdg = &format!("XDG_RUNTIME_DIR=/run/user/{}", uid) as &str;
let mut args = vec![xdg, "-u", &username, cmd.to_str().unwrap_or("")]; let mut args = vec![xdg, "-u", &username, cmd.to_str().unwrap_or("")];
args.append(&mut arg.clone()); args.append(&mut arg.clone());
@ -597,21 +566,31 @@ pub fn is_installed() -> bool {
true 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 { for _ in 0..n {
let x = get_env(name, uid); let x = get_env(name, uid, process);
if !x.is_empty() { if !x.is_empty() {
return x; return x;
} }
std::thread::sleep(Duration::from_millis(300)); sleep_millis(300);
} }
"".to_owned() "".to_owned()
} }
fn get_env(name: &str, uid: &str) -> String { #[inline]
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); fn get_env(name: &str, uid: &str, process: &str) -> String {
log::debug!("Run: {}", &cmd); 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) { 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() x.trim_end().to_string()
} else { } else {
"".to_owned() "".to_owned()
@ -701,7 +680,7 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
let connected_pat = get_xrandr_conn_pat(name); let connected_pat = get_xrandr_conn_pat(name);
let mut v = vec![]; let mut v = vec![];
if let Ok(re) = Regex::new(&format!("{}{}", connected_pat, resolutions_pat)) { 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) => { Ok(xrandr_output) => {
// There'are different kinds of 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> { 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))?; let re = Regex::new(&get_xrandr_conn_pat(name))?;
if let Some(caps) = re.captures(&xrandr_output) { if let Some(caps) = re.captures(&xrandr_output) {
if let Some((width, height)) = get_width_height_from_captures(&caps) { 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()?; .spawn()?;
Ok(()) 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(); let dtype = crate::platform::linux::get_display_server();
if dtype != "x11" && dtype != "wayland" { if dtype != "x11" && dtype != "wayland" {
res.set_error(format!( res.set_error(format!(
"Unsupported display server type {}, x11 or wayland expected", "Unsupported display server type \"{}\", x11 or wayland expected",
dtype dtype
)); ));
let mut msg_out = Message::new(); let mut msg_out = Message::new();
@ -1669,7 +1669,7 @@ impl Connection {
Some(message::Union::AudioFrame(frame)) => { Some(message::Union::AudioFrame(frame)) => {
if !self.disable_audio { if !self.disable_audio {
if let Some(sender) = &self.audio_sender { if let Some(sender) = &self.audio_sender {
allow_err!(sender.send(MediaData::AudioFrame(frame))); allow_err!(sender.send(MediaData::AudioFrame(Box::new(frame))));
} else { } else {
log::warn!( log::warn!(
"Processing audio frame without the voice call audio sender." "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; res = true;
} else if ck.value() == ControlKey::LockScreen.value() { } else if ck.value() == ControlKey::LockScreen.value() {
lock_screen_2(); std::thread::spawn(|| {
lock_screen_2();
});
res = true; res = true;
} }
return res; 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) pub fn repeat<S, F>(&self, interval_ms: u64, callback: F)
where where
F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send, F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send,
@ -198,6 +209,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
let mut callback = callback; let mut callback = callback;
let sp = self.clone(); let sp = self.clone();
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
sp.wait_prelogin();
let mut state = S::default(); let mut state = S::default();
let mut may_reset = false; let mut may_reset = false;
while sp.active() { while sp.active() {
@ -232,6 +245,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
let sp = self.clone(); let sp = self.clone();
let mut callback = callback; let mut callback = callback;
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
sp.wait_prelogin();
let mut error_timeout = HIBERNATE_TIMEOUT; let mut error_timeout = HIBERNATE_TIMEOUT;
while sp.active() { while sp.active() {
if sp.has_subscribes() { if sp.has_subscribes() {

View File

@ -54,6 +54,18 @@ pub fn start(args: &mut [String]) {
let dir = "/usr"; let dir = "/usr";
sciter::set_library(&(prefix + dir + "/lib/rustdesk/libsciter-gtk.so")).ok(); 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/c-smile/sciter-sdk/blob/master/include/sciter-x-types.h
// https://github.com/rustdesk/rustdesk/issues/132#issuecomment-886069737 // https://github.com/rustdesk/rustdesk/issues/132#issuecomment-886069737
#[cfg(windows)] #[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 = Arc::new(AtomicUsize::new(0));
let frame_count_cl = frame_count.clone(); let frame_count_cl = frame_count.clone();
let ui_handler = handler.ui_handler.clone(); let ui_handler = handler.ui_handler.clone();
let (video_sender, audio_sender) = start_video_audio_threads(move |data: &mut Vec<u8>| { let (video_sender, audio_sender, video_queue) =
frame_count_cl.fetch_add(1, Ordering::Relaxed); start_video_audio_threads(move |data: &mut Vec<u8>| {
ui_handler.on_rgba(data); frame_count_cl.fetch_add(1, Ordering::Relaxed);
}); ui_handler.on_rgba(data);
});
let mut remote = Remote::new( let mut remote = Remote::new(
handler, handler,
video_queue,
video_sender, video_sender,
audio_sender, audio_sender,
receiver, receiver,