Merge branch 'rustdesk:master' into master
This commit is contained in:
commit
98234830a3
809
.github/workflows/flutter-ci.yml
vendored
809
.github/workflows/flutter-ci.yml
vendored
@ -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
|
|
335
.github/workflows/flutter-nightly.yml
vendored
335
.github/workflows/flutter-nightly.yml
vendored
@ -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 }}
|
||||||
|
36
.github/workflows/vcpkg-deps-linux.yml
vendored
36
.github/workflows/vcpkg-deps-linux.yml
vendored
@ -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
1089
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
21
Cargo.toml
@ -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"
|
||||||
|
51
build.py
51
build.py
@ -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')
|
||||||
|
@ -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, it’s 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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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)]
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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() {
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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."
|
||||||
|
@ -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;
|
||||||
|
@ -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() {
|
||||||
|
12
src/ui.rs
12
src/ui.rs
@ -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)]
|
||||||
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user