566 Commits
1.2.1 ... 1.2.2

265 changed files with 9209 additions and 4864 deletions

View File

@@ -6,7 +6,7 @@ on:
workflow_call: workflow_call:
env: env:
FLUTTER_VERSION: "3.10.5" FLUTTER_VERSION: "3.10.6"
FLUTTER_RUST_BRIDGE_VERSION: "1.75.3" FLUTTER_RUST_BRIDGE_VERSION: "1.75.3"
jobs: jobs:
@@ -42,7 +42,6 @@ jobs:
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
with: with:
prefix-key: bridge-${{ matrix.job.os }} prefix-key: bridge-${{ matrix.job.os }}
workspace: "/tmp/flutter_rust_bridge/frb_codegen"
- name: Cache Bridge - name: Cache Bridge
id: cache-bridge id: cache-bridge

View File

@@ -124,7 +124,12 @@ jobs:
with: with:
use-cross: ${{ matrix.job.use-cross }} use-cross: ${{ matrix.job.use-cross }}
command: build command: build
args: --locked --release --target=${{ matrix.job.target }} args: --locked --target=${{ matrix.job.target }}
- name: clean
shell: bash
run: |
cargo clean
# - name: Strip debug information from executable # - name: Strip debug information from executable
# id: strip # id: strip

View File

@@ -13,16 +13,16 @@ on:
env: env:
CARGO_NDK_VERSION: "3.1.2" CARGO_NDK_VERSION: "3.1.2"
LLVM_VERSION: "15.0.6" LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.10.5" FLUTTER_VERSION: "3.10.6"
FLUTTER_RUST_BRIDGE_VERSION: "1.75.3" FLUTTER_RUST_BRIDGE_VERSION: "1.75.3"
# for arm64 linux # for arm64 linux
FLUTTER_ELINUX_VERSION: "3.10.5" FLUTTER_ELINUX_VERSION: "3.10.6"
FLUTTER_ELINUX_COMMIT_ID: "410b3ca42f2cd0c485edf517a1666652bab442d4" FLUTTER_ELINUX_COMMIT_ID: "410b3ca42f2cd0c485edf517a1666652bab442d4"
TAG_NAME: "${{ inputs.upload-tag }}" TAG_NAME: "${{ inputs.upload-tag }}"
# vcpkg version: 2023.04.15 # vcpkg version: 2023.04.15
# for multiarch gcc compatibility # for multiarch gcc compatibility
VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1" VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1"
VERSION: "1.2.1" VERSION: "1.2.2"
NDK_VERSION: "r25c" NDK_VERSION: "r25c"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}' ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}'
@@ -377,7 +377,7 @@ jobs:
uses: ./.github/workflows/bridge.yml uses: ./.github/workflows/bridge.yml
build-rustdesk-ios: build-rustdesk-ios:
if: ${{ inputs.upload-artifact == 'true' }} if: ${{ inputs.upload-artifact }}
needs: [generate-bridge-linux] needs: [generate-bridge-linux]
name: build rustdesk ios ipa ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] name: build rustdesk ios ipa ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
@@ -654,7 +654,7 @@ jobs:
sudo rm -rf /usr/local/lib/android sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet sudo rm -rf /usr/share/dotnet
sudo apt update -y sudo apt update -y
sudo apt install qemu-user-static sudo apt install qemu-user-static -y
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -666,7 +666,8 @@ jobs:
- name: Free Space - name: Free Space
run: | run: |
df df -h
free -m
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -675,27 +676,11 @@ jobs:
target: ${{ matrix.job.target }} target: ${{ matrix.job.target }}
override: true override: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2 - name: Save Rust toolchain version
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: | run: |
mkdir -p /opt/rust-registry RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
cargo install cargo-local-registry echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV
- 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 - name: Disable rust bridge build
run: | run: |
@@ -729,7 +714,6 @@ jobs:
dockerRunArgs: | dockerRunArgs: |
--volume "${PWD}:/workspace" --volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts" --volume "/opt/artifacts:/opt/artifacts"
--volume "/opt/rust-registry:/opt/rust-registry"
shell: /bin/bash shell: /bin/bash
install: | install: |
apt update -y apt update -y
@@ -746,19 +730,15 @@ jobs:
# rust # rust
pushd /opt pushd /opt
# do not use rustup, because memory overflow in qemu # do not use rustup, because memory overflow in qemu
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.70.0-${{ matrix.job.target }}.tar.gz wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
cd rust-1.70.0-${{ matrix.job.target }} && ./install.sh cd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }} && ./install.sh
rm -rf rust-1.70.0-${{ matrix.job.target }} rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
# edit config # edit config
mkdir -p ~/.cargo/ mkdir -p ~/.cargo/
echo """ echo """
[source.crates-io] [source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index' registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'local-registry'
[source.local-registry]
local-registry = '/opt/rust-registry/'
""" > ~/.cargo/config """ > ~/.cargo/config
cat ~/.cargo/config cat ~/.cargo/config
# start build # start build
@@ -783,10 +763,10 @@ jobs:
path: target/release/liblibrustdesk.so path: target/release/liblibrustdesk.so
build-rustdesk-lib-linux-arm: build-rustdesk-lib-linux-arm:
if: ${{ inputs.upload-artifact == 'true' }} if: ${{ inputs.upload-artifact }}
needs: [generate-bridge-linux, build-vcpkg-deps-linux] needs: [generate-bridge-linux, build-vcpkg-deps-linux]
name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }} runs-on: [self-hosted, Linux, ARM64]
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -819,25 +799,26 @@ jobs:
# - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } # - { 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
run: | # run: |
sudo rm -rf /opt/ghc # sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/lib/android # sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet # sudo rm -rf /usr/share/dotnet
sudo apt update -y # sudo apt update -y
sudo apt install qemu-user-static # sudo apt install qemu-user-static -y
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Set Swap Space #- name: Set Swap Space
uses: pierotofy/set-swap-space@master # uses: pierotofy/set-swap-space@master
with: # with:
swap-size-gb: 12 # swap-size-gb: 12
- name: Free Space - name: Free Space
run: | run: |
df df -h
free -m
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -846,27 +827,11 @@ jobs:
target: ${{ matrix.job.target }} target: ${{ matrix.job.target }}
override: true override: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2 - name: Save Rust toolchain version
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: | run: |
mkdir -p /opt/rust-registry RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
cargo install cargo-local-registry echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV
- 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 - name: Disable rust bridge build
run: | run: |
@@ -898,7 +863,6 @@ jobs:
dockerRunArgs: | dockerRunArgs: |
--volume "${PWD}:/workspace" --volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts" --volume "/opt/artifacts:/opt/artifacts"
--volume "/opt/rust-registry:/opt/rust-registry"
shell: /bin/bash shell: /bin/bash
install: | install: |
apt update -y apt update -y
@@ -915,19 +879,15 @@ jobs:
# rust # rust
pushd /opt pushd /opt
# do not use rustup, because memory overflow in qemu # do not use rustup, because memory overflow in qemu
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.70.0-${{ matrix.job.target }}.tar.gz wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
cd rust-1.70.0-${{ matrix.job.target }} && ./install.sh cd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }} && ./install.sh
rm -rf rust-1.70.0-${{ matrix.job.target }} rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
# edit config # edit config
mkdir -p ~/.cargo/ mkdir -p ~/.cargo/
echo """ echo """
[source.crates-io] [source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index' registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'local-registry'
[source.local-registry]
local-registry = '/opt/rust-registry/'
""" > ~/.cargo/config """ > ~/.cargo/config
cat ~/.cargo/config cat ~/.cargo/config
# start build # start build
@@ -937,6 +897,7 @@ jobs:
if ${{ matrix.job.enable-headless }}; then if ${{ matrix.job.enable-headless }}; then
export DEFAULT_FEAT=linux_headless export DEFAULT_FEAT=linux_headless
fi fi
export CARGO_INCREMENTAL=0
cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release
- name: Upload Artifacts - name: Upload Artifacts
@@ -946,10 +907,10 @@ jobs:
path: target/release/liblibrustdesk.so path: target/release/liblibrustdesk.so
build-rustdesk-sciter-arm: build-rustdesk-sciter-arm:
if: ${{ inputs.upload-artifact == 'true' }} if: ${{ inputs.upload-artifact }}
needs: [build-vcpkg-deps-linux] needs: [build-vcpkg-deps-linux]
name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
runs-on: ${{ matrix.job.os }} runs-on: [self-hosted, Linux, ARM64]
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -968,25 +929,26 @@ jobs:
# - { 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
run: | # run: |
sudo rm -rf /opt/ghc # sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/lib/android # sudo rm -rf /usr/local/lib/android
sudo rm -rf /usr/share/dotnet # sudo rm -rf /usr/share/dotnet
sudo apt update -y # sudo apt update -y
sudo apt install qemu-user-static # sudo apt install qemu-user-static -y
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: Set Swap Space #- name: Set Swap Space
uses: pierotofy/set-swap-space@master # uses: pierotofy/set-swap-space@master
with: # with:
swap-size-gb: 12 # swap-size-gb: 12
- name: Free Space - name: Free Space
run: | run: |
df df -h
free -m
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
@@ -995,27 +957,11 @@ jobs:
target: ${{ matrix.job.target }} target: ${{ matrix.job.target }}
override: true override: true
profile: minimal # minimal component installation (ie, no documentation) profile: minimal # minimal component installation (ie, no documentation)
- uses: Swatinem/rust-cache@v2 - name: Save Rust toolchain version
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: | run: |
mkdir -p /opt/rust-registry RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
cargo install cargo-local-registry echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV
- 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 - name: Restore vcpkg files
uses: actions/download-artifact@master uses: actions/download-artifact@master
@@ -1035,7 +981,6 @@ jobs:
dockerRunArgs: | dockerRunArgs: |
--volume "${PWD}:/workspace" --volume "${PWD}:/workspace"
--volume "/opt/artifacts:/opt/artifacts" --volume "/opt/artifacts:/opt/artifacts"
--volume "/opt/rust-registry:/opt/rust-registry"
shell: /bin/bash shell: /bin/bash
install: | install: |
apt update -y apt update -y
@@ -1048,19 +993,15 @@ jobs:
# rust # rust
pushd /opt pushd /opt
# do not use rustup, because memory overflow in qemu # do not use rustup, because memory overflow in qemu
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.70.0-${{ matrix.job.target }}.tar.gz wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
cd rust-1.70.0-${{ matrix.job.target }} && ./install.sh cd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }} && ./install.sh
rm -rf rust-1.70.0-${{ matrix.job.target }} rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
# edit config # edit config
mkdir -p ~/.cargo/ mkdir -p ~/.cargo/
echo """ echo """
[source.crates-io] [source.crates-io]
registry = 'https://github.com/rust-lang/crates.io-index' registry = 'https://github.com/rust-lang/crates.io-index'
replace-with = 'local-registry'
[source.local-registry]
local-registry = '/opt/rust-registry/'
""" > ~/.cargo/config """ > ~/.cargo/config
cat ~/.cargo/config cat ~/.cargo/config
@@ -1070,6 +1011,7 @@ jobs:
export VCPKG_ROOT=/opt/artifacts/vcpkg export VCPKG_ROOT=/opt/artifacts/vcpkg
export ARCH=armhf export ARCH=armhf
export DEFAULT_FEAT="" export DEFAULT_FEAT=""
export CARGO_INCREMENTAL=0
if ${{ matrix.job.enable-headless }}; then if ${{ matrix.job.enable-headless }}; then
export DEFAULT_FEAT=linux_headless export DEFAULT_FEAT=linux_headless
fi fi
@@ -1105,7 +1047,7 @@ jobs:
path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb
build-rustdesk-linux-arm: build-rustdesk-linux-arm:
if: ${{ inputs.upload-artifact == 'true' }} if: ${{ inputs.upload-artifact }}
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 }}]
runs-on: ubuntu-20.04 # 20.04 has more performance on arm build runs-on: ubuntu-20.04 # 20.04 has more performance on arm build
@@ -1216,6 +1158,7 @@ jobs:
sed -i "s/x64\/release/arm\/release/g" ./build.py sed -i "s/x64\/release/arm\/release/g" ./build.py
;; ;;
esac esac
export CARGO_INCREMENTAL=0
python3 ./build.py --flutter --hwcodec --skip-cargo python3 ./build.py --flutter --hwcodec --skip-cargo
# rpm package # rpm package
echo -e "start packaging fedora package" echo -e "start packaging fedora package"
@@ -1233,7 +1176,7 @@ jobs:
pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }}
mkdir -p /opt/artifacts/rpm mkdir -p /opt/artifacts/rpm
for name in rustdesk*??.rpm; do for name in rustdesk*??.rpm; do
mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" mv "$name" "/opt/artifacts/rpm/${name%%.rpm}.rpm"
done done
# rpm suse package # rpm suse package
echo -e "start packaging suse package" echo -e "start packaging suse package"
@@ -1358,7 +1301,7 @@ jobs:
# files: | # files: |
# res/rustdesk*.zst # res/rustdesk*.zst
- name: Publish fedora28/centos8 package - name: Publish fedora package
if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
@@ -1457,7 +1400,7 @@ jobs:
pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }}
mkdir -p /opt/artifacts/rpm mkdir -p /opt/artifacts/rpm
for name in rustdesk*??.rpm; do for name in rustdesk*??.rpm; do
mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" mv "$name" "/opt/artifacts/rpm/${name%%.rpm}.rpm"
done done
# rpm suse package # rpm suse package
pushd /workspace pushd /workspace
@@ -1572,7 +1515,7 @@ jobs:
files: | files: |
./appimage/rustdesk-${{ env.VERSION }}-*.AppImage ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage
- name: Publish fedora28/centos8 package - name: Publish fedora package
if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true'
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:

View File

@@ -15,4 +15,4 @@ jobs:
secrets: inherit secrets: inherit
with: with:
upload-artifact: true upload-artifact: true
upload-tag: "1.2.1" upload-tag: "1.2.2"

View File

@@ -4,30 +4,28 @@ on: [workflow_dispatch]
env: env:
LLVM_VERSION: "10.0" LLVM_VERSION: "10.0"
# Note: currently 3.0.5 does not support arm64 officially, we use latest stable version first. FLUTTER_VERSION: "3.10.6"
FLUTTER_VERSION: "3.0.5"
TAG_NAME: "tmp" TAG_NAME: "tmp"
FLUTTER_RUST_BRIDGE_VERSION: "1.75.3"
# vcpkg version: 2022.05.10 # vcpkg version: 2022.05.10
# for multiarch gcc compatibility # for multiarch gcc compatibility
VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44" VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1"
VERSION: "1.2.1" VERSION: "1.2.2"
jobs: jobs:
build-for-windows-2022-12-05: build-for-history-windows:
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) name: ${{ matrix.job.date }}
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
job: job:
# - { target: i686-pc-windows-msvc , os: windows-2019 } - { target: x86_64-pc-windows-msvc, os: windows-2019, arch: x86_64, date: 2023-08-04, ref: 72c198a1e94cc1e0242fce88f92b3f3caedcd0c3 }
# - { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: x86_64-pc-windows-msvc, os: windows-2019 }
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
ref: '8d1254cf14b69f545c9cefa026c5eeb0e7dd3e7c' ref: ${{ matrix.job.ref }}
- name: Install LLVM and Clang - name: Install LLVM and Clang
uses: KyleMayes/install-llvm-action@v1 uses: KyleMayes/install-llvm-action@v1
@@ -41,49 +39,30 @@ jobs:
flutter-version: ${{ env.FLUTTER_VERSION }} flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true 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.0.5-rustdesk.2/windows-x64-flutter-release.zip -OutFile windows-x64-flutter-release.zip
Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine
mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-3.0.5-x64/bin/cache/artifacts/engine/windows-x64-release/
- name: Install Rust toolchain - name: Install Rust toolchain
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: "1.62" toolchain: stable
target: ${{ matrix.job.target }} target: ${{ matrix.job.target }}
override: true override: true
components: rustfmt components: rustfmt
profile: minimal # minimal component installation (ie, no documentation) 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 - name: Install flutter rust bridge deps
run: | run: |
dart pub global activate ffigen --version 5.0.1 cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid"
$exists = Test-Path ~/.cargo/bin/flutter_rust_bridge_codegen.exe
Push-Location ..
git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1
Push-Location flutter_rust_bridge/frb_codegen ; cargo install --path . ; Pop-Location
Pop-Location
Push-Location flutter ; flutter pub get ; Pop-Location 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 ~/.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 - name: Install vcpkg dependencies
run: | run: |
$VCPKG_ROOT/vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static cd C:\
shell: bash git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1
- name: Build rustdesk - name: Build rustdesk
run: python3 .\build.py --portable --hwcodec --flutter env:
VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg
run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver
- name: Build self-extracted executable - name: Build self-extracted executable
shell: bash shell: bash
@@ -92,274 +71,7 @@ jobs:
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
popd popd
mkdir -p ./SignOutput mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-2022-12-05-${{ matrix.job.target }}.exe mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ matrix.job.date }}-${{ matrix.job.target }}.exe
- name: Publish Release
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
./SignOutput/rustdesk-*.exe
build-for-windows-2022-12-12:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
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
with:
ref: '3dd43b79ec0409fc38103bed0c7eb0bc3cd993d5'
- 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.0.5-rustdesk.2/windows-x64-flutter-release.zip -OutFile windows-x64-flutter-release.zip
Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine
mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-3.0.5-x64/bin/cache/artifacts/engine/windows-x64-release/
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: "1.62"
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: |
dart pub global activate ffigen --version 5.0.1
$exists = Test-Path ~/.cargo/bin/flutter_rust_bridge_codegen.exe
Push-Location ..
git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1
Push-Location flutter_rust_bridge/frb_codegen ; cargo install --path . ; Pop-Location
Pop-Location
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
- name: Build self-extracted executable
shell: bash
run: |
pushd ./libs/portable
python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-2022-12-12-${{ matrix.job.target }}.exe
- name: Publish Release
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
./SignOutput/rustdesk-*.exe
build-for-windows-2022-12-19:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
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
with:
ref: '1054715891c4e73ad9b164acec6dadecfc599a65'
- 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.0.5-rustdesk.2/windows-x64-flutter-release.zip -OutFile windows-x64-flutter-release.zip
Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine
mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-3.0.5-x64/bin/cache/artifacts/engine/windows-x64-release/
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: "1.62"
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: |
dart pub global activate ffigen --version 5.0.1
$exists = Test-Path ~/.cargo/bin/flutter_rust_bridge_codegen.exe
Push-Location ..
git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1
Push-Location flutter_rust_bridge/frb_codegen ; cargo install --path . ; Pop-Location
Pop-Location
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
- name: Build self-extracted executable
shell: bash
run: |
pushd ./libs/portable
python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-2022-12-19-${{ matrix.job.target }}.exe
- name: Publish Release
uses: softprops/action-gh-release@v1
with:
prerelease: true
tag_name: ${{ env.TAG_NAME }}
files: |
./SignOutput/rustdesk-*.exe
build-for-windows-2022-12-26:
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
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
with:
ref: 'b241925fe093dc4da804a5aac419375f4ca7653f'
- 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.0.5-rustdesk.2/windows-x64-flutter-release.zip -OutFile windows-x64-flutter-release.zip
Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine
mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-3.0.5-x64/bin/cache/artifacts/engine/windows-x64-release/
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: "1.62"
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: |
dart pub global activate ffigen --version 5.0.1
$exists = Test-Path ~/.cargo/bin/flutter_rust_bridge_codegen.exe
Push-Location ..
git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1
Push-Location flutter_rust_bridge/frb_codegen ; cargo install --path . ; Pop-Location
Pop-Location
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
- name: Build self-extracted executable
shell: bash
run: |
pushd ./libs/portable
python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-2022-12-26-${{ matrix.job.target }}.exe
- name: Publish Release - name: Publish Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1

72
Cargo.lock generated
View File

@@ -1197,7 +1197,7 @@ dependencies = [
"js-sys", "js-sys",
"libc", "libc",
"mach2", "mach2",
"ndk 0.7.0", "ndk",
"ndk-context", "ndk-context",
"oboe", "oboe",
"once_cell", "once_cell",
@@ -2987,8 +2987,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hwcodec" name = "hwcodec"
version = "0.1.0" version = "0.1.1"
source = "git+https://github.com/21pages/hwcodec?branch=stable#3ea79865a10387b7e1b7630c2ae068bd2081f680" source = "git+https://github.com/21pages/hwcodec?branch=stable#82cdc15457e42feaf14e1b38622506b2d54baf76"
dependencies = [ dependencies = [
"bindgen 0.59.2", "bindgen 0.59.2",
"cc", "cc",
@@ -3764,19 +3764,6 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "ndk"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4"
dependencies = [
"bitflags",
"jni-sys",
"ndk-sys 0.3.0",
"num_enum",
"thiserror",
]
[[package]] [[package]]
name = "ndk" name = "ndk"
version = "0.7.0" version = "0.7.0"
@@ -3785,7 +3772,7 @@ checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"jni-sys", "jni-sys",
"ndk-sys 0.4.1+23.1.7779620", "ndk-sys",
"num_enum", "num_enum",
"raw-window-handle", "raw-window-handle",
"thiserror", "thiserror",
@@ -3797,15 +3784,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
[[package]]
name = "ndk-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97"
dependencies = [
"jni-sys",
]
[[package]] [[package]]
name = "ndk-sys" name = "ndk-sys"
version = "0.4.1+23.1.7779620" version = "0.4.1+23.1.7779620"
@@ -4116,7 +4094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0" checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0"
dependencies = [ dependencies = [
"jni 0.20.0", "jni 0.20.0",
"ndk 0.7.0", "ndk",
"ndk-context", "ndk-context",
"num-derive", "num-derive",
"num-traits 0.2.15", "num-traits 0.2.15",
@@ -4894,7 +4872,7 @@ dependencies = [
[[package]] [[package]]
name = "rdev" name = "rdev"
version = "0.5.0-2" version = "0.5.0-2"
source = "git+https://github.com/fufesou/rdev#ab48d5798c86303b9398727684509b1b43ecfdab" source = "git+https://github.com/fufesou/rdev#ee3057bd97c91529e8b9daf2ca133a5c49f0c0eb"
dependencies = [ dependencies = [
"cocoa", "cocoa",
"core-foundation", "core-foundation",
@@ -5146,7 +5124,7 @@ dependencies = [
[[package]] [[package]]
name = "rustdesk" name = "rustdesk"
version = "1.2.1" version = "1.2.2"
dependencies = [ dependencies = [
"android_logger", "android_logger",
"arboard", "arboard",
@@ -5409,7 +5387,7 @@ dependencies = [
"jni 0.21.1", "jni 0.21.1",
"lazy_static", "lazy_static",
"log", "log",
"ndk 0.7.0", "ndk",
"num_cpus", "num_cpus",
"pkg-config", "pkg-config",
"quest", "quest",
@@ -5841,9 +5819,9 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.29.0" version = "0.29.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02f1dc6930a439cc5d154221b5387d153f8183529b07c19aca24ea31e0a167e1" checksum = "c7cb97a5a85a136d84e75d5c3cf89655090602efb1be0d8d5337b7e386af2908"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"core-foundation-sys 0.8.4", "core-foundation-sys 0.8.4",
@@ -5915,8 +5893,8 @@ dependencies = [
[[package]] [[package]]
name = "tao" name = "tao"
version = "0.19.1" version = "0.22.2"
source = "git+https://github.com/rustdesk-org/tao?branch=muda#173f128608d282dc4036f213c1c42137464ff096" source = "git+https://github.com/rustdesk-org/tao?branch=dev#1e5b97258cf42a30f80f85a6aa0b1a4aece1977e"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"cairo-rs", "cairo-rs",
@@ -5937,13 +5915,13 @@ dependencies = [
"gtk", "gtk",
"image", "image",
"instant", "instant",
"jni 0.20.0", "jni 0.21.1",
"lazy_static", "lazy_static",
"libc", "libc",
"log", "log",
"ndk 0.6.0", "ndk",
"ndk-context", "ndk-context",
"ndk-sys 0.3.0", "ndk-sys",
"objc", "objc",
"once_cell", "once_cell",
"parking_lot", "parking_lot",
@@ -5952,16 +5930,18 @@ dependencies = [
"scopeguard", "scopeguard",
"tao-macros", "tao-macros",
"unicode-segmentation", "unicode-segmentation",
"url",
"uuid", "uuid",
"windows 0.44.0", "windows 0.48.0",
"windows-implement", "windows-implement",
"x11-dl", "x11-dl",
"zbus",
] ]
[[package]] [[package]]
name = "tao-macros" name = "tao-macros"
version = "0.1.1" version = "0.1.2"
source = "git+https://github.com/rustdesk-org/tao?branch=muda#173f128608d282dc4036f213c1c42137464ff096" source = "git+https://github.com/rustdesk-org/tao?branch=dev#1e5b97258cf42a30f80f85a6aa0b1a4aece1977e"
dependencies = [ dependencies = [
"proc-macro2 1.0.63", "proc-macro2 1.0.63",
"quote 1.0.27", "quote 1.0.27",
@@ -6850,8 +6830,6 @@ version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b"
dependencies = [ dependencies = [
"windows-implement",
"windows-interface",
"windows-targets 0.42.2", "windows-targets 0.42.2",
] ]
@@ -6870,14 +6848,16 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [ dependencies = [
"windows-implement",
"windows-interface",
"windows-targets 0.48.0", "windows-targets 0.48.0",
] ]
[[package]] [[package]]
name = "windows-implement" name = "windows-implement"
version = "0.44.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6" checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c"
dependencies = [ dependencies = [
"proc-macro2 1.0.63", "proc-macro2 1.0.63",
"quote 1.0.27", "quote 1.0.27",
@@ -6886,9 +6866,9 @@ dependencies = [
[[package]] [[package]]
name = "windows-interface" name = "windows-interface"
version = "0.44.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f" checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7"
dependencies = [ dependencies = [
"proc-macro2 1.0.63", "proc-macro2 1.0.63",
"quote 1.0.27", "quote 1.0.27",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rustdesk" name = "rustdesk"
version = "1.2.1" version = "1.2.2"
authors = ["rustdesk <info@rustdesk.com>"] authors = ["rustdesk <info@rustdesk.com>"]
edition = "2021" edition = "2021"
build= "build.rs" build= "build.rs"
@@ -48,7 +48,7 @@ lazy_static = "1.4"
sha2 = "0.10" sha2 = "0.10"
repng = "0.2" repng = "0.2"
parity-tokio-ipc = { git = "https://github.com/open-trade/parity-tokio-ipc" } parity-tokio-ipc = { git = "https://github.com/open-trade/parity-tokio-ipc" }
runas = "1.0" runas = "=1.0" # https://github.com/mitsuhiko/rust-runas/issues/13
magnum-opus = { git = "https://github.com/rustdesk/magnum-opus" } magnum-opus = { git = "https://github.com/rustdesk/magnum-opus" }
dasp = { version = "0.11", features = ["signal", "interpolate-linear", "interpolate"], optional = true } dasp = { version = "0.11", features = ["signal", "interpolate-linear", "interpolate"], optional = true }
rubato = { version = "0.12", optional = true } rubato = { version = "0.12", optional = true }
@@ -111,7 +111,7 @@ objc_id = "0.1"
[target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
tray-icon = { git = "https://github.com/rustdesk-org/tray-icon" } tray-icon = { git = "https://github.com/rustdesk-org/tray-icon" }
tao = { git = "https://github.com/rustdesk-org/tao", branch = "muda" } tao = { git = "https://github.com/rustdesk-org/tao", branch = "dev" }
image = "0.24" image = "0.24"
[target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies] [target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies]

View File

@@ -1,15 +1,15 @@
FROM debian FROM debian
WORKDIR / WORKDIR /
RUN apt update -y && apt install -y g++ gcc 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 unzip zip sudo libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev cmake ninja-build RUN apt update -y && apt install -y g++ gcc git curl nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake unzip zip sudo libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev cmake ninja-build && rm -rf /var/lib/apt/lists/*
RUN git clone https://github.com/microsoft/vcpkg && cd vcpkg && git checkout 2023.04.15 RUN git clone --branch 2023.04.15 --depth=1 https://github.com/microsoft/vcpkg
RUN /vcpkg/bootstrap-vcpkg.sh -disableMetrics RUN /vcpkg/bootstrap-vcpkg.sh -disableMetrics
RUN /vcpkg/vcpkg --disable-metrics install libvpx libyuv opus aom RUN /vcpkg/vcpkg --disable-metrics install libvpx libyuv opus aom
RUN groupadd -r user && useradd -r -g user user --home /home/user && mkdir -p /home/user && chown user /home/user && echo "user ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/user RUN groupadd -r user && useradd -r -g user user --home /home/user && mkdir -p /home/user && chown user /home/user && echo "user ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/user
WORKDIR /home/user WORKDIR /home/user
RUN wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so RUN curl -LO https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
USER user USER user
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh
RUN chmod +x rustup.sh RUN chmod +x rustup.sh

View File

@@ -35,7 +35,6 @@ Below are the servers you are using for free, they may change over time. If you
| Location | Vendor | Specification | | Location | Vendor | Specification |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4 GB RAM | | Germany | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4 GB RAM |
| Germany | [Codext](https://codext.de) | 4 vCPU / 8 GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM | | Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Dev Container ## Dev Container

View File

@@ -2,12 +2,13 @@
version: 1 version: 1
script: script:
- rm -rf ./AppDir || true - rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.1.deb - bsdtar -zxvf ../rustdesk-1.2.2.deb
- tar -xvf ./data.tar.xz - tar -xvf ./data.tar.xz
- mkdir ./AppDir - mkdir ./AppDir
- mv ./usr ./AppDir/usr - mv ./usr ./AppDir/usr
# 32x32 icon # 32x32 icon
- for i in {32,64,128}; do mkdir -p ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/; cp ../res/$i\x$i.png ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/rustdesk.png; done - for i in {32,64,128}; do mkdir -p ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/; cp ../res/$i\x$i.png ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/rustdesk.png; done
- mkdir -p ./AppDir/usr/share/icons/hicolor/scalable/apps/; cp ../res/scalable.svg ./AppDir/usr/share/icons/hicolor/scalable/apps/rustdesk.svg
# desktop file # desktop file
# - sed -i "s/Icon=\/usr\/share\/rustdesk\/files\/rustdesk.png/Icon=rustdesk/g" ./AppDir/usr/share/applications/rustdesk.desktop # - sed -i "s/Icon=\/usr\/share\/rustdesk\/files\/rustdesk.png/Icon=rustdesk/g" ./AppDir/usr/share/applications/rustdesk.desktop
- rm -rf ./AppDir/usr/share/applications - rm -rf ./AppDir/usr/share/applications
@@ -17,7 +18,7 @@ AppDir:
id: rustdesk id: rustdesk
name: rustdesk name: rustdesk
icon: rustdesk icon: rustdesk
version: 1.2.1 version: 1.2.2
exec: usr/lib/rustdesk/rustdesk exec: usr/lib/rustdesk/rustdesk
exec_args: $@ exec_args: $@
apt: apt:
@@ -50,7 +51,6 @@ AppDir:
- libva-x11-2 - libva-x11-2
- libvdpau1 - libvdpau1
- libgstreamer-plugins-base1.0-0 - libgstreamer-plugins-base1.0-0
- gstreamer1.0-pipewire
- libwayland-cursor0 - libwayland-cursor0
- libwayland-egl1 - libwayland-egl1
- libpulse0 - libpulse0

View File

@@ -2,12 +2,13 @@
version: 1 version: 1
script: script:
- rm -rf ./AppDir || true - rm -rf ./AppDir || true
- bsdtar -zxvf ../rustdesk-1.2.1.deb - bsdtar -zxvf ../rustdesk-1.2.2.deb
- tar -xvf ./data.tar.xz - tar -xvf ./data.tar.xz
- mkdir ./AppDir - mkdir ./AppDir
- mv ./usr ./AppDir/usr - mv ./usr ./AppDir/usr
# 32x32 icon # 32x32 icon
- for i in {32,64,128}; do mkdir -p ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/; cp ../res/$i\x$i.png ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/rustdesk.png; done - for i in {32,64,128}; do mkdir -p ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/; cp ../res/$i\x$i.png ./AppDir/usr/share/icons/hicolor/$i\x$i/apps/rustdesk.png; done
- mkdir -p ./AppDir/usr/share/icons/hicolor/scalable/apps/; cp ../res/scalable.svg ./AppDir/usr/share/icons/hicolor/scalable/apps/rustdesk.svg
# desktop file # desktop file
# - sed -i "s/Icon=\/usr\/share\/rustdesk\/files\/rustdesk.png/Icon=rustdesk/g" ./AppDir/usr/share/applications/rustdesk.desktop # - sed -i "s/Icon=\/usr\/share\/rustdesk\/files\/rustdesk.png/Icon=rustdesk/g" ./AppDir/usr/share/applications/rustdesk.desktop
- rm -rf ./AppDir/usr/share/applications - rm -rf ./AppDir/usr/share/applications
@@ -17,7 +18,7 @@ AppDir:
id: rustdesk id: rustdesk
name: rustdesk name: rustdesk
icon: rustdesk icon: rustdesk
version: 1.2.1 version: 1.2.2
exec: usr/lib/rustdesk/rustdesk exec: usr/lib/rustdesk/rustdesk
exec_args: $@ exec_args: $@
apt: apt:

View File

@@ -71,14 +71,14 @@ def parse_rc_features(feature):
return 'osx' in platforms return 'osx' in platforms
else: else:
return 'linux' in platforms return 'linux' in platforms
def get_all_features(): def get_all_features():
features = [] features = []
for (feat, feat_info) in available_features.items(): for (feat, feat_info) in available_features.items():
if platform_check(feat_info['platform']): if platform_check(feat_info['platform']):
features.append(feat) features.append(feat)
return features return features
if isinstance(feature, str) and feature.upper() == 'ALL': if isinstance(feature, str) and feature.upper() == 'ALL':
return get_all_features() return get_all_features()
elif isinstance(feature, list): elif isinstance(feature, list):
@@ -311,6 +311,8 @@ def build_flutter_deb(version, features):
system2('mkdir -p tmpdeb/etc/rustdesk/') system2('mkdir -p tmpdeb/etc/rustdesk/')
system2('mkdir -p tmpdeb/etc/pam.d/') system2('mkdir -p tmpdeb/etc/pam.d/')
system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/') system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
system2('mkdir -p tmpdeb/usr/share/icons/hicolor/256x256/apps/')
system2('mkdir -p tmpdeb/usr/share/icons/hicolor/scalable/apps/')
system2('mkdir -p tmpdeb/usr/share/applications/') system2('mkdir -p tmpdeb/usr/share/applications/')
system2('mkdir -p tmpdeb/usr/share/polkit-1/actions') system2('mkdir -p tmpdeb/usr/share/polkit-1/actions')
system2('rm tmpdeb/usr/bin/rustdesk || true') system2('rm tmpdeb/usr/bin/rustdesk || true')
@@ -319,7 +321,9 @@ def build_flutter_deb(version, features):
system2( system2(
'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/') 'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
system2( system2(
'cp ../res/128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png') 'cp ../res/128x128@2x.png tmpdeb/usr/share/icons/hicolor/256x256/apps/rustdesk.png')
system2(
'cp ../res/scalable.svg tmpdeb/usr/share/icons/hicolor/scalable/apps/rustdesk.svg')
system2( system2(
'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop') 'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
system2( system2(
@@ -351,6 +355,8 @@ def build_deb_from_folder(version, binary_folder):
system2('mkdir -p tmpdeb/usr/bin/') system2('mkdir -p tmpdeb/usr/bin/')
system2('mkdir -p tmpdeb/usr/lib/rustdesk') system2('mkdir -p tmpdeb/usr/lib/rustdesk')
system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/') system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
system2('mkdir -p tmpdeb/usr/share/icons/hicolor/256x256/apps/')
system2('mkdir -p tmpdeb/usr/share/icons/hicolor/scalable/apps/')
system2('mkdir -p tmpdeb/usr/share/applications/') system2('mkdir -p tmpdeb/usr/share/applications/')
system2('mkdir -p tmpdeb/usr/share/polkit-1/actions') system2('mkdir -p tmpdeb/usr/share/polkit-1/actions')
system2('rm tmpdeb/usr/bin/rustdesk || true') system2('rm tmpdeb/usr/bin/rustdesk || true')
@@ -359,7 +365,9 @@ def build_deb_from_folder(version, binary_folder):
system2( system2(
'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/') 'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
system2( system2(
'cp ../res/128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png') 'cp ../res/128x128@2x.png tmpdeb/usr/share/icons/hicolor/256x256/apps/rustdesk.png')
system2(
'cp ../res/scalable.svg tmpdeb/usr/share/icons/hicolor/scalable/apps/rustdesk.svg')
system2( system2(
'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop') 'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
system2( system2(
@@ -556,7 +564,7 @@ def main():
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/* codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/*
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app
'''.format(pa)) '''.format(pa))
system2('create-dmg target/release/bundle/osx/RustDesk.app') system2('create-dmg "RustDesk %s.dmg" "target/release/bundle/osx/RustDesk.app"' % version)
os.rename('RustDesk %s.dmg' % os.rename('RustDesk %s.dmg' %
version, 'rustdesk-%s.dmg' % version) version, 'rustdesk-%s.dmg' % version)
if pa: if pa:
@@ -581,10 +589,14 @@ def main():
'mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb') 'mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb')
system2('dpkg-deb -R rustdesk.deb tmpdeb') system2('dpkg-deb -R rustdesk.deb tmpdeb')
system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/') system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
system2('mkdir -p tmpdeb/usr/share/icons/hicolor/256x256/apps/')
system2('mkdir -p tmpdeb/usr/share/icons/hicolor/scalable/apps/')
system2( system2(
'cp res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/') 'cp res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
system2( system2(
'cp res/128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png') 'cp res/128x128@2x.png tmpdeb/usr/share/icons/hicolor/256x256/apps/rustdesk.png')
system2(
'cp res/scalable.svg tmpdeb/usr/share/icons/hicolor/scalable/apps/rustdesk.svg')
system2( system2(
'cp res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop') 'cp res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
system2( system2(

37
docs/CONTRIBUTING-IT.md Normal file
View File

@@ -0,0 +1,37 @@
# Contribuzione a RustDesk
RustDesk accoglie con favore il contributo di tutti.
Ecco le linee guida se stai pensando di aiutarci.
## Contribuzione
I contributi a RustDesk o alle sue dipendenze dovrebbero essere forniti sotto forma di richieste pull GitHub.
Ogni richiesta pull verrà esaminata da un collaboratore principale (qualcuno con il permesso di applicare) ed è abilitato all'uso dell'albero principale o dare un feedback per le modifiche che sarebbero necessarie.
Tutti i contributi dovrebbero seguire questo formato, anche quelli dei contributori principali.
Se desideri lavorare su un problema, rivendicalo prima commentando
il problema di GitHub su cui vuoi lavorare.
Questo per evitare duplicati sforzi dei contributori sullo stesso problema.
## Elenco di controllo delle richieste pull
- Branch del master branch e, se necessario, rebase al master attuale branch prima di inviare la richiesta pull.
Se l'unione non è in mod pulito con il master ti potrebbe essere chiesto di effettuare il rebase delle modifiche.
- Le modifiche dovrebbero essere le più piccole possibile, assicurando al tempo stesso che ogni modifica sia corretta in modo indipendente (ovvero, ogni modifica dovrebbe essere compilabile e superare i test).
- Le modifiche devono essere accompagnati da un certificato di origine per sviluppatori firmato (http://developercertificate.org), che indica che tu (e il tuo datore di lavoro se applicabile) accetti di essere vincolato dai termini della [licenza progetto](../LICENCE). In git, questa è l'opzione `-s` di `git commit`
- Se la tua patch non viene esaminata o hai bisogno che una persona specifica la esamini, puoi @-rispondere ad un revisore chiedendo una revisione nella richiesta pull o un commento, oppure puoi chiedere una revisione tramite [email](mailto:info@rustdesk.com).
- Aggiungi test relativi al bug corretto o alla nuova funzionalità.
Per istruzioni specifiche su git, vedi [Workflow GitHub - 101](https://github.com/servo/servo/wiki/GitHub-workflow).
## Condotta
https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT-IT.md
## Comunicazioni
I contributori di RustDesk frequentano [Discord](https://discord.gg/nDceKgxnkV).

14
docs/DEVCONTAINER-IT.md Normal file
View File

@@ -0,0 +1,14 @@
Dopo l'avvio di devcontainer nel contenitore docker, viene creato un binario linux in modalità debug.
Attualmente devcontainer consente creazione build Linux e Android sia in modalità debug che in modalità rilascio.
Di seguito è riportata la tabella dei comandi da eseguire dalla root del progetto per la creazione di build specifiche.
Comando|Tipo build|Modo
-|-|-|
`.devcontainer/build.sh --debug linux`|Linux|debug
`.devcontainer/build.sh --release linux`|Linux|release
`.devcontainer/build.sh --debug android`|android-arm64|debug
`.devcontainer/build.sh --release android`|android-arm64|release

View File

@@ -33,7 +33,6 @@
| الموقع | المورد | المواصفات | | الموقع | المورد | المواصفات |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM | | Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## التبعيات ## التبعيات

View File

@@ -28,7 +28,6 @@ Níže jsou uvedeny servery zdarma k vašemu použití (údaje se mohou v čase
| umístění | dodavatel | parametry | | umístění | dodavatel | parametry |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM | | Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Softwarové součásti, na kterých závisí ## Softwarové součásti, na kterých závisí

View File

@@ -26,8 +26,6 @@ Nedenfor er de servere, du bruger gratis, det kan ændre sig med tiden. Hvis du
| Beliggenhed | Udbyder | Specifikation | | Beliggenhed | Udbyder | Specifikation |
| ---------- | ------------- | ------------------ | | ---------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
## Afhængigheder ## Afhængigheder

View File

@@ -35,7 +35,6 @@ Nachfolgend sind die Server gelistet, die Sie kostenlos nutzen können. Es kann
| Standort | Anbieter | Spezifikation | | Standort | Anbieter | Spezifikation |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Deutschland | [Hetzner](https://www.hetzner.com/de/) | 2 vCPU / 4 GB RAM | | Deutschland | [Hetzner](https://www.hetzner.com/de/) | 2 vCPU / 4 GB RAM |
| Deutschland | [Codext](https://codext.de/) | 4 vCPU / 8 GB RAM |
| Ukraine (Kiew) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM | | Ukraine (Kiew) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Dev-Container ## Dev-Container

View File

@@ -25,7 +25,6 @@ Malsupre estas la serviloj, kiuj vi uzas senpage, ĝi povas ŝanĝi laŭlonge de
| Situo | Vendanto | Detaloj | | Situo | Vendanto | Detaloj |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | | Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dependantaĵoj ## Dependantaĵoj

View File

@@ -32,7 +32,6 @@ A continuación se muestran los servidores gratuitos, pueden cambiar a medida qu
| Ubicación | Compañía | Especificación | | Ubicación | Compañía | Especificación |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | | Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dependencias ## Dependencias

View File

@@ -31,7 +31,6 @@
| موقعیت | سرویس دهنده | مشخصات | | موقعیت | سرویس دهنده | مشخصات |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| آلمان | Hetzner | 2 vCPU / 4GB RAM | | آلمان | Hetzner | 2 vCPU / 4GB RAM |
| آلمان | Codext | 4 vCPU / 8GB RAM |
## وابستگی ها ## وابستگی ها

View File

@@ -25,7 +25,6 @@ Alla on palvelimia, joita voit käyttää ilmaiseksi, ne saattavat muuttua ajan
| Sijainti | Myyjä | Määrittely | | Sijainti | Myyjä | Määrittely |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | | Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Riippuvuudet ## Riippuvuudet

View File

@@ -26,7 +26,6 @@ Ci-dessous se trouvent les serveurs que vous utilisez gratuitement, cela peut ch
| Location | Vendor | Specification | | Location | Vendor | Specification |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
## Dépendances ## Dépendances

View File

@@ -35,7 +35,6 @@
| Περιοχή | Πάροχος | Προδιαγραφές | | Περιοχή | Πάροχος | Προδιαγραφές |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Γερμανία | Hetzner | 2 vCPU / 4GB RAM | | Γερμανία | Hetzner | 2 vCPU / 4GB RAM |
| Γερμανία | Codext | 4 vCPU / 8GB RAM |
| Ουκρανία (Κίεβο) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | | Ουκρανία (Κίεβο) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dev Container ## Dev Container

View File

@@ -33,7 +33,6 @@ Ezalatt az üzenet alatt találhatóak azok a publikus szerverek, amelyeket ingy
| Hely | Host | Specifikáció | | Hely | Host | Specifikáció |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | | Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dependencies ## Dependencies

View File

@@ -6,53 +6,64 @@
<a href="#file-structure">Structure</a> • <a href="#file-structure">Structure</a> •
<a href="#snapshot">Snapshot</a><br> <a href="#snapshot">Snapshot</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br> [<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
<b>Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> ke bahasa asli anda</b> <b>Kami membutuhkan bantuanmu untuk menterjemahkan file README dan <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> ke Bahasa Indonesia</b>
</p> </p>
Birbincang bersama kami: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) Mari mengobrol bersama kami: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
Perangkat lunak desktop jarak jauh lainnya, ditulis dengan Rust. Bekerja begitu saja, tidak memerlukan konfigurasi. Anda memiliki kendali penuh atas data Anda, tanpa khawatir tentang keamanan. Anda dapat menggunakan server rendezvous/relay kami, [konfigurasi server sendiri](https://rustdesk.com/server), or [tulis rendezvous/relay server anda sendiri](https://github.com/rustdesk/rustdesk-server-demo). Merupakan perangkat lunak Remote Desktop yang baru, dibangun dengan Rust. kamu bisa langsung menggunakannya tanpa perlu konfigurasi tambahan. Serta ,emiliki kontrol penuh terhadap semua data, tanpa perlu merasa was-was tentang isu keamanan, dan yang lebih menarik adalah memiliki opsi untuk menggunakan server rendezvous/relay milik kami, [konfigurasi server sendiri](https://rustdesk.com/server), atau [tulis rendezvous/relay server anda sendiri](https://github.com/rustdesk/rustdesk-server-demo).
RustDesk menyambut baik kontribusi dari semua orang. Lihat [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) untuk membantu sebelum memulai. RustDesk mengajak semua orang untuk ikut berkontribusi. Lihat [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) untuk melihat panduan.
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) [**UNDUH BINARY**](https://github.com/rustdesk/rustdesk/releases)
## Publik Server Gratis ## Server Publik Gratis
Di bawah ini adalah server yang bisa Anda gunakan secara gratis, dapat berubah seiring waktu. Jika Anda tidak dekat dengan salah satu dari ini, jaringan Anda mungkin lambat. Di bawah ini merupakan server gratis yang bisa kamu gunakan, seiring waktu kemungkinan akan terjadi perubahan spesifikasi pada setiap server. Jika lokasi kamu berada jauh dengan salah satu server yang tersedia, kemungkinan koneksi akan terasa lambat ketika melakukan proses remote.
| Lokasi | Vendor | Spesifikasi | | Lokasi | Penyedia | Spesifikasi |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Jerman | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM | | Ukraina (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dependencies ## Dev Container
Versi desktop menggunakan [sciter](https://sciter.com/) untuk GUI, silahkan download sendiri sciter dynamic library. [![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
Apabila kamu sudah menginstall VS Code dan Docker, kamu bisa mengklik badge yang ada diatas untuk memulainya. Dengan mengklik badge tersebut secara otomatis akan menginstal ekstensi pada VS Code, lakukan kloning (clone) source code kedalam container volume, dan aktifkan dev container untuk menggunakannya.
## Dependensi
Pada versi desktop, antarmuka pengguna (GUI) menggunakan [Sciter](https://sciter.com/) atau flutter, tutorial ini hanya berlaku untuk Sciter
Kamu bisa mengunduh Sciter dynamic library disini.
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) | [Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) | [Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib) [MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
## Langkah untuk RAW Build ## Langkah awal untuk memulai
- Siapkan env pengembangan Rust dan C++ build env - Siapkan env development Rust dan env build C++
- Install [vcpkg](https://github.com/microsoft/vcpkg), dan arahkan `VCPKG_ROOT` env variable dengan benar - Install [vcpkg](https://github.com/microsoft/vcpkg), dan atur variabel env `VCPKG_ROOT` dengan benar
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static - Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
- Linux/MacOS: vcpkg install libvpx libyuv opus aom - Linux/MacOS: vcpkg install libvpx libyuv opus aom
- jalankan `cargo run` - jalankan `cargo run`
## Bagaimana Build di Linux ## [Build](https://rustdesk.com/docs/en/dev/build/)
## Cara Build di Linux
### Ubuntu 18 (Debian 10) ### Ubuntu 18 (Debian 10)
```sh ```sh
sudo apt install -y g++ gcc 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 sudo apt install -y zip g++ gcc 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 make \
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
``` ```
### Fedora 28 (CentOS 8) ### Fedora 28 (CentOS 8)
@@ -79,7 +90,7 @@ export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus aom vcpkg/vcpkg install libvpx libyuv opus aom
``` ```
### Perbaiki libvpx (Untuk Fedora) ### Mengatasi masalah libvpx (Untuk Fedora)
```sh ```sh
cd vcpkg/buildtrees/libvpx/src cd vcpkg/buildtrees/libvpx/src
@@ -105,13 +116,40 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run VCPKG_ROOT=$HOME/vcpkg cargo run
``` ```
### Ubah Wayland menjadi X11 (Xorg) ### Mengubah Wayland ke X11 (Xorg)
RustDesk tidak mendukung Wayland. Cek [ini](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) untuk mengonfigurasi Xorg sebagai sesi GNOME default. RustDesk tidak mendukung Wayland. Cek [ini](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) untuk mengonfigurasi Xorg sebagai sesi standar di GNOME.
## Bagaimana build dengan Docker ## Kompatibilitas dengan Wayland
Mulailah dengan mengkloning repositori dan build dengan docker container: Sepertinya Wayland tidak memiliki API untuk mengirimkan ketukan tombol ke jendela lain. Maka dari itu, RustDesk menggunakan API dari level yang lebih rendah, lebih tepatnya perangkat `/dev/uinput` (linux kernel level)
Saat Wayland menjadi sisi yang dikendalikan atau sisi yang sedang diremote, kamu harus memulai dengan cara ini
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Harap Diperhatikan**: Saat Perekaman layar menggunakan Wayland antarmuka (UI) yang ditampilkan akan berbeda. Untuk saat ini RustDesk hanya mendukung org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## Cara Build dengan Docker
Mulailah dengan melakukan kloning (clone) repositori dan build dengan docker container:
```sh ```sh
git clone https://github.com/rustdesk/rustdesk git clone https://github.com/rustdesk/rustdesk
@@ -119,25 +157,25 @@ cd rustdesk
docker build -t "rustdesk-builder" . docker build -t "rustdesk-builder" .
``` ```
Kemudian, setiap kali Anda perlu build aplikasi, jalankan perintah berikut: Selanjutnya, setiap kali ketika kamu akan melakukan build aplikasi, jalankan perintah berikut:
```sh ```sh
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
``` ```
Perhatikan bahwa build pertama mungkin memerlukan waktu lebih lama sebelum dependensi di-cache, build berikutnya akan lebih cepat. Selain itu, jika Anda perlu menentukan argumen yang berbeda untuk perintah build, Anda dapat melakukannya di akhir perintah di posisi `<OPTIONAL-ARGS>`. Misalnya, jika Anda ingin membangun versi rilis yang dioptimalkan, Anda akan menjalankan perintah di atas diikuti oleh `--release`. Hasil eksekusi akan tersedia pada target folder di sistem anda, dan dapat dijalankan dengan: Perlu diingat bahwa pada saat build pertama kali, mungkin memerlukan waktu lebih lama sebelum dependensi di-cache, build berikutnya akan lebih cepat. Selain itu, jika perlu menentukan argumen yang berbeda untuk perintah build, kamu dapat melakukannya di akhir perintah di posisi `<OPTIONAL-ARGS>`. Misalnya, jika ingin membangun versi rilis yang dioptimalkan, jalankan perintah di atas dan tambahkan `--release`. Hasil eksekusi perintah tersebut akan tersimpan pada target folder di sistem kamu, dan dapat dijalankan dengan:
```sh ```sh
target/debug/rustdesk target/debug/rustdesk
``` ```
Atau, jika Anda menjalankan rilis yang dapat dieksekusi: Atau, jika kamu menjalankan rilis yang dapat dieksekusi:
```sh ```sh
target/release/rustdesk target/release/rustdesk
``` ```
Harap pastikan bahwa Anda menjalankan perintah ini dari root repositori RustDesk, jika tidak, aplikasi mungkin tidak dapat menemukan sumber daya yang diperlukan. Perhatikan juga perintah cargo seperti `install` atau `run` saat ini tidak didukung melalui metode ini karena mereka akan menginstal atau menjalankan program di dalam container bukan pada host. Harap pastikan bahwa kamu menjalankan perintah ini dari repositori root RustDesk, jika tidak demikian, aplikasi mungkin tidak dapat menemukan sumber yang diperlukan. Dan juga, perintah cargo seperti `install` atau `run` saat ini tidak didukung melalui metode ini karena, proses menginstal atau menjalankan program terjadi di dalam container bukan pada host.
## Struktur File ## Struktur File

View File

@@ -1,36 +1,40 @@
<p align="center"> <p align="center">
<img src="../res/logo-header.svg" alt="RustDesk - Your remote desktop"><br> <img src="../res/logo-header.svg" alt="RustDesk - il tuo desktop remoto"><br>
<a href="#server-pubblici-gratuiti">Servers</a> • <a href="#server-pubblici-gratuiti">Server</a> •
<a href="#passaggi-per-la-compilazione">Compilazione</a> • <a href="#passaggi-per-la-compilazione">Compilazione</a> •
<a href="#come-compilare-con-docker">Docker</a> • <a href="#come-compilare-con-docker">Docker</a> •
<a href="#struttura-dei-file">Struttura</a> • <a href="#struttura-dei-file">Struttura</a> •
<a href="#screenshots">Screenshots</a><br> <a href="#screenshots">Schermate</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br> [<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
<b>Abbiamo bisogno del tuo aiuto per tradurre questo README e la <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> nella tua lingua nativa</b> <b>Abbiamo bisogno del tuo aiuto per tradurre questo file README e la <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">UI RustDesk</a> nella tua lingua nativa</b>
</p> </p>
Chatta con noi: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) Chatta con noi su: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09)
Ancora un altro software per il controllo remoto del desktop, scritto in Rust. Funziona immediatamente, nessuna configurazione richiesta. Hai il pieno controllo dei tuoi dati, senza preoccupazioni per la sicurezza. Puoi utilizzare il nostro server rendezvous/relay, [configurare il tuo](https://rustdesk.com/server) o [scrivere il tuo rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). Ancora un altro software per il controllo remoto del desktop, scritto in Rust.
Funziona immediatamente, nessuna configurazione richiesta. Hai il pieno controllo dei tuoi dati, senza preoccupazioni per la sicurezza.
Puoi usare il nostro server rendezvous/relay, [configurare il tuo server](https://rustdesk.com/server) o [realizzare il tuo server rendezvous/relay](https://github.com/rustdesk/rustdesk-server-demo).
RustDesk accoglie il contributo di tutti. Per ulteriori informazioni su come inizare a contribuire, vedere [`docs/CONTRIBUTING.md`](CONTRIBUTING.md). RustDesk accoglie il contributo di tutti.
Per ulteriori informazioni su come iniziare a contribuire, vedi [`docs/CONTRIBUTING-IT.md`](CONTRIBUTING.md).
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) [**DOWNLOAD PROGRAMMA**](https://github.com/rustdesk/rustdesk/releases)
## Server pubblici gratuiti ## Server pubblici gratuiti
Qui sotto trovate i server che possono essere usati gratuitamente, la lista potrebbe cambiare nel tempo. Se non si è vicini a uno di questi server, la vostra connessione potrebbe essere lenta. Qui sotto trovi i server che possono essere usati gratuitamente, la lista potrebbe cambiare nel tempo.
| Posizione | Vendor | Specifiche | Se non sei vicino a uno di questi server, la connessione potrebbe essere lenta.
| Posizione | Venditore | Specifiche |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germania | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM | | Ucraina (Kyev) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dipendenze ## Dipendenze
La versione Desktop utilizza [sciter](https://sciter.com/) per la GUI, per favore scarica sciter dynamic library. La versione Desktop usa per la GUI [sciter](https://sciter.com/), per favore scarica la libreria dinamica sciter.
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) | [Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) | [Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
@@ -47,7 +51,7 @@ La versione Desktop utilizza [sciter](https://sciter.com/) per la GUI, per favor
- Esegui `cargo run` - Esegui `cargo run`
## Come compilare su Linux ## Come compilare in Linux
### Ubuntu 18 (Debian 10) ### Ubuntu 18 (Debian 10)
@@ -67,7 +71,7 @@ sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pipewire sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pipewire
``` ```
### Installare vcpkg ### Installa vcpkg
```sh ```sh
git clone https://github.com/microsoft/vcpkg git clone https://github.com/microsoft/vcpkg
@@ -79,7 +83,7 @@ export VCPKG_ROOT=$HOME/vcpkg
vcpkg/vcpkg install libvpx libyuv opus aom vcpkg/vcpkg install libvpx libyuv opus aom
``` ```
### Fix libvpx (Per Fedora) ### Correzione libvpx (per Fedora)
```sh ```sh
cd vcpkg/buildtrees/libvpx/src cd vcpkg/buildtrees/libvpx/src
@@ -105,13 +109,14 @@ mv libsciter-gtk.so target/debug
VCPKG_ROOT=$HOME/vcpkg cargo run VCPKG_ROOT=$HOME/vcpkg cargo run
``` ```
### Cambiare Wayland a X11 (Xorg) ### Cambiare Wayland in X11 (Xorg)
RustDesk non supporta Wayland. Controlla [questo](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) per configurare Xorg come sessione di default di GNOME. RustDesk non supporta Wayland.
Controlla [qui](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) per configurare Xorg come sessione predefinita di GNOME.
## Come compilare con Docker ## Come compilare con Docker
Cominciare clonando il repository e compilare i container docker: Clona il repository e compila i container docker:
```sh ```sh
git clone https://github.com/rustdesk/rustdesk git clone https://github.com/rustdesk/rustdesk
@@ -119,38 +124,42 @@ cd rustdesk
docker build -t "rustdesk-builder" . docker build -t "rustdesk-builder" .
``` ```
Quindi, ogni volta che devi compilare l'applicazione, esegui il comando seguente: Quindi, ogni volta che devi compilare l'applicazione, esegui il seguente comando:
```sh ```sh
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
``` ```
Tieni presente che la prima build potrebbe richiedere più tempo prima che le dipendenze vengano memorizzate nella cache, le build successive saranno più veloci. Inoltre, se hai bisogno di specificare argomenti diversi per il comando build, puoi farlo alla fine del comando nella posizione `<OPTIONAL-ARGS>`. Ad esempio, se si desidera creare una versione di rilascio ottimizzata, eseguire il comando sopra seguito da `--release`. L'eseguibile generato sarà creato nella cartella di destinazione del proprio sistema e può essere eseguito con: Tieni presente che la prima build potrebbe richiedere più tempo prima che le dipendenze vengano memorizzate nella cache, le build successive saranno più veloci.
Inoltre, se hai bisogno di specificare argomenti diversi per il comando build, puoi farlo alla fine del comando nella posizione `<OPTIONAL-ARGS>`.
Ad esempio, se vuoi creare una versione di rilascio ottimizzata, esegui il comando precedentemente indicato seguito da `--release`.
L'eseguibile generato sarà creato nella cartella destinazione del sistema e può essere eseguito con:
```sh ```sh
target/debug/rustdesk target/debug/rustdesk
``` ```
Oppure, se si sta eseguendo un eseguibile di rilascio: Oppure, se stai avviando un eseguibile di rilascio:
```sh ```sh
target/release/rustdesk target/release/rustdesk
``` ```
Assicurati di eseguire questi comandi dalla radice del repository RustDesk, altrimenti l'applicazione potrebbe non essere in grado di trovare le risorse richieste. Notare inoltre che altri sottocomandi cargo come `install` o `run` non sono attualmente supportati tramite questo metodo poiché installerebbero o eseguirebbero il programma all'interno del container anziché nell'host. Assicurati di eseguire questi comandi dalla radice del repository RustDesk, altrimenti l'applicazione potrebbe non essere in grado di trovare le risorse richieste.
Nota inoltre che altri sottocomandi cargo come `install` o `run` non sono attualmente supportati tramite questo metodo poiché installerebbero o eseguirebbero il programma all'interno del container anziché nell'host.
## Struttura dei file ## Struttura dei file
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs funzioni per il trasferimento file, e altre funzioni utili. - **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: codec video, config, wrapper tcp/udp, protobuf, funzioni per il trasferimento file, e altre funzioni utili.
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: cattura dello schermo - **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: cattura dello schermo
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: controllo tastiera/mouse specifico della piattaforma - **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: controllo tastiera/mouse specifico della piattaforma
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI - **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: servizi audio/appunti/input/video e connessioni di rete - **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: servizi audio/appunti/input/video e connessioni di rete
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: avviare una connessione peer - **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: avvio di una connessione peer
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Comunica con [rustdesk-server](https://github.com/rustdesk/rustdesk-server), attende la connessione remota diretta (TCP hole punching) oppure indiretta (relayed) - **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Comunica con [rustdesk-server](https://github.com/rustdesk/rustdesk-server), attende la connessione remota diretta (TCP hole punching) oppure indiretta (relayed)
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: codice specifico della piattaforma - **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: codice specifico della piattaforma
## Screenshots ## Schermate
![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png) ![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png)

View File

@@ -30,7 +30,6 @@ RustDeskは誰からの貢献も歓迎します。 貢献するには [`docs/CON
| Location | Vendor | Specification | | Location | Vendor | Specification |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
## 依存関係 ## 依存関係

View File

@@ -30,7 +30,6 @@ RustDesk는 모든 기여를 환영합니다. 기여하고자 한다면 [`docs/C
| Location | Vendor | Specification | | Location | Vendor | Specification |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
## 의존관계 ## 의존관계

View File

@@ -25,7 +25,6 @@
| സ്ഥാനം | കച്ചവടക്കാരൻ | വിവരണം | | സ്ഥാനം | കച്ചവടക്കാരൻ | വിവരണം |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
## ഡിപെൻഡൻസികൾ ## ഡിപെൻഡൻസികൾ

View File

@@ -33,7 +33,6 @@ Hieronder staan de servers die u gratis gebruikt, ze kunnen in de loop van de ti
| Locatie | Aanbieder | Specificaties | | Locatie | Aanbieder | Specificaties |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Duitsland | Hetzner | 2 vCPU / 4GB RAM | | Duitsland | Hetzner | 2 vCPU / 4GB RAM |
| Duitsland | Codext | 4 vCPU / 8GB RAM |
| Oekraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | | Oekraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dev Container ## Dev Container

View File

@@ -35,7 +35,6 @@ Poniżej znajdują się serwery, z których można korzystać za darmo, może si
| Lokalizacja | Dostawca | Specyfikacja | | Lokalizacja | Dostawca | Specyfikacja |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Niemcy | Hetzner | 2 vCPU / 4GB RAM | | Niemcy | Hetzner | 2 vCPU / 4GB RAM |
| Niemcy | Codext | 4 vCPU / 8GB RAM |
| Ukraina (Kijów) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | | Ukraina (Kijów) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Konterner Programisty (Dev Container) ## Konterner Programisty (Dev Container)

View File

@@ -26,7 +26,6 @@ Abaixo estão os servidores que você está utilizando de graça, ele pode mudar
| Localização | Fornecedor | Especificações | | Localização | Fornecedor | Especificações |
| ----------- | ------------- | ------------------ | | ----------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
## Dependências ## Dependências

View File

@@ -34,8 +34,6 @@ RustDesk приветствует вклад каждого. Ознакомьт
| Расположение | Поставщик | Технические характеристики | | Расположение | Поставщик | Технические характеристики |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Германия | Hetzner | 2 vCPU / 4GB RAM | | Германия | Hetzner | 2 vCPU / 4GB RAM |
| Германия | Codext | 4 vCPU / 8GB RAM |
| Россия (Москва) | [nt-vps](https://nt-vps.ru) | 8 vCPU / 8GB RAM |
## Зависимости ## Зависимости

View File

@@ -35,7 +35,6 @@ RustDesk вітає внесок кожного. Дивіться [`docs/CONTRIB
| Місцезнаходження | Постачальник | Технічні характеристики | | Місцезнаходження | Постачальник | Технічні характеристики |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Німеччина | Hetzner | 2 VCPU / 4GB RAM | | Німеччина | Hetzner | 2 VCPU / 4GB RAM |
| Німеччина | Codext | 4 vCPU / 8GB RAM |
| Україна (Київ) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | | Україна (Київ) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
## Dev Container ## Dev Container

View File

@@ -34,7 +34,6 @@ Dưới đây là những máy chủ mà bạn có thể sử dụng mà không
| Địa điểm | Nhà cung cấp | Cấu hình | | Địa điểm | Nhà cung cấp | Cấu hình |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | Hetzner | 2 vCPU / 4GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM |
## Dependencies ## Dependencies

View File

@@ -1,8 +1,8 @@
<p align="center"> <p align="center">
<img src="../res/logo-header.svg" alt="RustDesk - Your remote desktop"><br> <img src="../res/logo-header.svg" alt="RustDesk - Your remote desktop"><br>
<a href="#免费公共服务器">服务器</a> • <a href="#免费公共服务器">服务器</a> •
<a href="#基本构建步骤">编译</a> • <a href="#基本构建步骤">编译</a> •
<a href="#使用Docker编译">Docker</a> • <a href="#使用-Docker-编译">Docker</a> •
<a href="#文件结构">结构</a> • <a href="#文件结构">结构</a> •
<a href="#截图">截图</a><br> <a href="#截图">截图</a><br>
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br> [<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
@@ -16,9 +16,19 @@ Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https:
或者[自己设置](https://rustdesk.com/server) 或者[自己设置](https://rustdesk.com/server)
亦或者[开发您的版本](https://github.com/rustdesk/rustdesk-server-demo)。 亦或者[开发您的版本](https://github.com/rustdesk/rustdesk-server-demo)。
欢迎大家贡献代码, 请看 [`docs/CONTRIBUTING.md`](CONTRIBUTING.md). ![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png)
[**可执行程序下载**](https://github.com/rustdesk/rustdesk/releases) RustDesk 期待各位的贡献. 如何参与开发? 详情请看 [CONTRIBUTING.md](docs/CONTRIBUTING.md).
[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
[**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases)
[**NIGHTLY BUILD**](https://github.com/rustdesk/rustdesk/releases/tag/nightly)
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
## 免费的公共服务器 ## 免费的公共服务器
@@ -26,8 +36,16 @@ Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https:
| Location | Vendor | Specification | | Location | Vendor | Specification |
| --------- | ------------- | ------------------ | | --------- | ------------- | ------------------ |
| Germany | Hetzner | 2 vCPU / 4GB RAM | | Germany | [Hetzner](https://www.hetzner.com) | 2 vCPU / 4 GB RAM |
| Germany | Codext | 4 vCPU / 8GB RAM | | Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
## Dev Container
[![在 Dev Containers 中打开](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
如果你已经安装了 VS Code 和 Docker, 你可以点击上面的徽章开始使用. 点击后, VS Code 将自动安装 Dev Containers 扩展(如果需要),将源代码克隆到容器卷中, 并启动一个 Dev 容器供使用.
Go through [DEVCONTAINER.md](docs/DEVCONTAINER.md) for more info.
## 依赖 ## 依赖
@@ -37,16 +55,14 @@ Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https:
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) | [Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
[macOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib) [macOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
移动版本使用Flutter未来会将桌面版本从Sciter迁移到Flutter。
## 基本构建步骤 ## 基本构建步骤
- 请准备好 Rust 开发环境和 C++编译环境 - 请准备好 Rust 开发环境和 C++ 编译环境
- 安装[vcpkg](https://github.com/microsoft/vcpkg), 正确设置`VCPKG_ROOT`环境变量 - 安装 [vcpkg](https://github.com/microsoft/vcpkg), 正确设置 `VCPKG_ROOT` 环境变量
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static - Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static aom:x64-windows-static
- Linux/Osx: vcpkg install libvpx libyuv opus aom - Linux/macOS: vcpkg install libvpx libyuv opus aom
- 运行 `cargo run` - 运行 `cargo run`
@@ -57,7 +73,15 @@ Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https:
### Ubuntu 18 (Debian 10) ### Ubuntu 18 (Debian 10)
```sh ```sh
sudo apt install -y g++ gcc 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 sudo apt install -y zip g++ gcc 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 make \
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
```
### openSUSE Tumbleweed
```sh
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
``` ```
### Fedora 28 (CentOS 8) ### Fedora 28 (CentOS 8)
@@ -107,24 +131,52 @@ cd rustdesk
mkdir -p target/debug mkdir -p target/debug
wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
mv libsciter-gtk.so target/debug mv libsciter-gtk.so target/debug
cargo run VCPKG_ROOT=$HOME/vcpkg cargo run
``` ```
### 把 Wayland 修改成 X11 (Xorg) ### 把 Wayland 修改成 X11 (Xorg)
RustDesk 暂时不支持 Wayland不过正在积极开发中。 RustDesk 暂时不支持 Wayland不过正在积极开发中。
> [点我](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) > [点我](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/)
查看 如何将Xorg设置成默认的GNOME session 查看如何将 Xorg 设置成默认的 GNOME session.
## Wayland 支持
Wayland 似乎没有提供任何将按键发送到其他窗口的 API. 因此, RustDesk 使用较低级别的 API, 即 `/dev/uinput` devices (Linux kernal level).
当 Wayland 是受控方时,您必须以下列方式开始操作:
```bash
# Start uinput service
$ sudo rustdesk --service
$ rustdesk
```
**Notice**: Wayland 屏幕录制使用不同的接口. RustDesk 目前只支持 org.freedesktop.portal.ScreenCast.
```bash
$ dbus-send --session --print-reply \
--dest=org.freedesktop.portal.Desktop \
/org/freedesktop/portal/desktop \
org.freedesktop.DBus.Properties.Get \
string:org.freedesktop.portal.ScreenCast string:version
# Not support
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
# Support
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
variant uint32 4
```
## 使用 Docker 编译 ## 使用 Docker 编译
### 构建Docker容器 克隆版本库并构建 Docker 容器:
```sh ```sh
git clone https://github.com/rustdesk/rustdesk # 克隆Github存储库 git clone https://github.com/rustdesk/rustdesk # 克隆Github存储库
cd rustdesk # 进入文件夹 cd rustdesk # 进入文件夹
docker build -t "rustdesk-builder" . # 构建容器 docker build -t "rustdesk-builder" . # 构建容器
``` ```
请注意: 请注意:
* 针对国内网络访问问题,可以做以下几点优化: * 针对国内网络访问问题,可以做以下几点优化:
1. Dockerfile 中修改系统的源到国内镜像 1. Dockerfile 中修改系统的源到国内镜像
@@ -163,8 +215,9 @@ docker build -t "rustdesk-builder" . # 构建容器
docker build -t "rustdesk-builder" . --build-arg http_proxy=http://host:port --build-arg https_proxy=http://host:port docker build -t "rustdesk-builder" . --build-arg http_proxy=http://host:port --build-arg https_proxy=http://host:port
``` ```
### 构建RustDesk程序 ### 构建 RustDesk 程序
容器构建完成后运行下列指令以完成对RustDesk应用程序的构建
然后, 每次需要构建应用程序时, 运行以下命令:
```sh ```sh
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
@@ -179,25 +232,25 @@ docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user
groupmod: Permission denied. groupmod: Permission denied.
groupmod: cannot lock /etc/group; try again later. groupmod: cannot lock /etc/group; try again later.
``` ```
> **原因:** 容器的entrypoint脚本会检测UIDGID在度判和给定的环境变量的不一致时会强行修改user的UID和GID并重新运行。但在重启后读不到环境中的UIDGID然后再次进入判错重启环节 > **原因:** 容器的 entrypoint 脚本会检测 UIDGID在度判和给定的环境变量的不一致时会强行修改 user 的 UID 和 GID 并重新运行。但在重启后读不到环境中的 UIDGID然后再次进入判错重启环节
### 运行RustDesk程序 ### 运行 RustDesk 程序
生成的可执行程序在target目录下可直接通过指令运行调试(Debug)版本的RustDesk: 生成的可执行程序在 target 目录下,可直接通过指令运行调试 (Debug) 版本的 RustDesk:
```sh ```sh
target/debug/rustdesk target/debug/rustdesk
``` ```
或者您想运行发行(Release)版本: 或者您想运行发行 (Release) 版本:
```sh ```sh
target/release/rustdesk target/release/rustdesk
``` ```
请注意: 请注意:
* 请保证您运行的目录是在RustDesk库的根目录内否则软件会读不到文件。 * 请保证您运行的目录是在 RustDesk 库的根目录内,否则软件会读不到文件。
* `install`、`run`等Cargo的子指令在容器内不可用宿主机才行。 * `install`、`run`等 Cargo 的子指令在容器内不可用,宿主机才行。
## 文件结构 ## 文件结构

11
docs/SECURITY-IT.md Normal file
View File

@@ -0,0 +1,11 @@
# Policy sicurezza
## Segnalazione di una vulnerabilità
Attribuiamo grande importanza alla sicurezza del progetto.
Incoraggiamo tutti gli utenti a segnalare eventuali vulnerabilità di sicurezza che ci scoprono.
Se trovi una vulnerabilità nel progetto RustDesk, segnalala responsabilmente inviando un'email a info@rustdesk.com.
Al momento non abbiamo un programma di taglia sui bug.
Siamo una piccola squadra che cerca di risolvere un grosso problema.
Ti esortiamo a segnalare responsabilmente tutte le vulnerabilità in modo da poter continuare a sviluppare un'applicazione sicura per l'intera comunità.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 422 KiB

After

Width:  |  Height:  |  Size: 419 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 452 KiB

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 379 KiB

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

After

Width:  |  Height:  |  Size: 266 KiB

View File

@@ -4,7 +4,7 @@
"runtime-version": "21.08", "runtime-version": "21.08",
"sdk": "org.freedesktop.Sdk", "sdk": "org.freedesktop.Sdk",
"command": "rustdesk", "command": "rustdesk",
"icon": "share/rustdesk/files/rustdesk.png", "icon": "share/icons/hicolor/scalable/apps/rustdesk.svg",
"modules": [ "modules": [
"shared-modules/libappindicator/libappindicator-gtk3-12.10.json", "shared-modules/libappindicator/libappindicator-gtk3-12.10.json",
"xdotool.json", "xdotool.json",
@@ -12,20 +12,21 @@
"name": "rustdesk", "name": "rustdesk",
"buildsystem": "simple", "buildsystem": "simple",
"build-commands": [ "build-commands": [
"bsdtar -zxvf rustdesk-1.2.1.deb", "bsdtar -zxvf rustdesk-1.2.2.deb",
"tar -xvf ./data.tar.xz", "tar -xvf ./data.tar.xz",
"cp -r ./usr/* /app/", "cp -r ./usr/* /app/",
"mkdir -p /app/bin && ln -s /app/lib/rustdesk/rustdesk /app/bin/rustdesk", "mkdir -p /app/bin && ln -s /app/lib/rustdesk/rustdesk /app/bin/rustdesk",
"mv /app/share/applications/rustdesk.desktop /app/share/applications/com.rustdesk.RustDesk.desktop", "mv /app/share/applications/rustdesk.desktop /app/share/applications/com.rustdesk.RustDesk.desktop",
"sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/com.rustdesk.RustDesk.desktop", "sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/com.rustdesk.RustDesk.desktop",
"sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/rustdesk-link.desktop", "sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/rustdesk-link.desktop",
"mv /app/share/icons/hicolor/scalable/apps/rustdesk.svg /app/share/icons/hicolor/scalable/apps/com.rustdesk.RustDesk.svg",
"for size in 16 24 32 48 64 128 256 512; do\n rsvg-convert -w $size -h $size -f png -o $size.png logo.svg\n install -Dm644 $size.png /app/share/icons/hicolor/${size}x${size}/apps/com.rustdesk.RustDesk.png\n done" "for size in 16 24 32 48 64 128 256 512; do\n rsvg-convert -w $size -h $size -f png -o $size.png logo.svg\n install -Dm644 $size.png /app/share/icons/hicolor/${size}x${size}/apps/com.rustdesk.RustDesk.png\n done"
], ],
"cleanup": ["/include", "/lib/pkgconfig", "/share/gtk-doc"], "cleanup": ["/include", "/lib/pkgconfig", "/share/gtk-doc"],
"sources": [ "sources": [
{ {
"type": "file", "type": "file",
"path": "../rustdesk-1.2.1.deb" "path": "../rustdesk-1.2.2.deb"
}, },
{ {
"type": "file", "type": "file",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 487 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 715 B

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 719 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"><path fill="#fff" d="M12 0C5.374 0 0 5.373 0 12c0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23A11.509 11.509 0 0 1 12 5.803c1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576C20.566 21.797 24 17.3 24 12c0-6.627-5.373-12-12-12z"/></svg>

Before

Width:  |  Height:  |  Size: 792 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><circle cx="512" cy="512" r="512" fill="#999"/><path fill="#fff" d="M407.2 722.1c-10.1-6.7-19-15-26.5-24.5-8.2-9.9-15.7-20.3-22.7-31-16.3-23.9-29.1-50-38-77.5-10.7-32-15.8-62.7-15.8-92.7 0-33.5 7.2-62.7 21.4-87.2 10.4-19.2 26-35.2 44.8-46.5 18.1-11.3 39.2-17.5 60.6-17.9 7.5 0 15.6 1.1 24.1 3.2 6.2 1.7 13.6 4.5 22.8 7.9 11.7 4.5 18.1 7.2 20.3 7.9 6.8 2.6 12.6 3.6 17.1 3.6 3.4 0 8.3-1.1 13.8-2.8 3.1-1.1 9-3 17.3-6.6 8.2-3 14.8-5.5 19.9-7.5 7.9-2.3 15.5-4.5 22.4-5.5 8.3-1.3 16.6-1.7 24.5-1.1 15.1 1.1 29 4.3 41.4 9 21.7 8.7 39.3 22.4 52.4 41.8-5.5 3.4-10.7 7.4-15.5 11.7-10.4 9.2-19.2 20-26.2 32.1-9.2 16.4-13.9 35-13.7 53.7.3 23.1 6.2 43.4 17.9 61 8.3 12.8 19.3 23.8 32.7 32.7 6.6 4.5 12.4 7.6 17.9 9.6-2.6 8-5.4 15.8-8.6 23.5-7.4 17.2-16.2 33.7-26.7 49.3-9.2 13.4-16.5 23.5-22 30.1-8.6 10.2-16.8 17.9-25.2 23.4-9.2 6.1-19.9 9.3-31 9.3-7.5.3-14.9-.6-22-2.7-6.2-2-12.3-4.3-18.3-6.9-6.2-2.9-12.7-5.3-19.3-7.2-8.1-2.1-16.4-3.2-24.8-3.1-8.5 0-16.8 1.1-24.7 3.1-6.6 1.9-13 4.2-19.3 6.9-9 3.7-14.8 6.2-18.2 7.2-6.9 2-14 3.3-21.1 3.7-11.1 0-21.4-3.2-31.7-9.6zm146.1-393.6c-14.5 7.2-28.3 10.3-42.1 9.3-2.1-13.8 0-27.9 5.8-43.4 5.1-13.2 11.9-25.2 21.3-35.8 9.8-11.1 21.5-20.3 34.8-26.9 14.1-7.2 27.5-11.1 40.3-11.7 1.7 14.5 0 28.8-5.3 44.1-4.9 13.6-12.1 26.2-21.3 37.5-9.3 11.1-20.8 20.3-33.8 26.9z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg width="32" height="32"><path d="M29.307 9.932 26.161 0H5.796L2.692 9.932c-1.802 5.75.042 12.271 5.089 16.021L16.01 32l8.208-6.068c5.005-3.75 6.911-10.25 5.089-16.021l-8.214 6.104 3.12 9.938-8.208-6.13-8.208 6.104 3.141-9.911-8.25-6.063 10.177-.063 3.146-9.891 3.141 9.87z"/></svg>

After

Width:  |  Height:  |  Size: 285 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="199"><path fill="#0089d6" d="M118.432 187.698c32.89-5.81 60.055-10.618 60.367-10.684l.568-.12-31.052-36.935c-17.078-20.314-31.051-37.014-31.051-37.11 0-.182 32.063-88.477 32.243-88.792.06-.105 21.88 37.567 52.893 91.32 29.035 50.323 52.973 91.815 53.195 92.203l.405.707-98.684-.012-98.684-.013 59.8-10.564zM0 176.435c0-.052 14.631-25.451 32.514-56.442l32.514-56.347 37.891-31.799C123.76 14.358 140.867.027 140.935.001c.069-.026-.205.664-.609 1.534s-18.919 40.582-41.145 88.25l-40.41 86.67-29.386.037c-16.162.02-29.385-.005-29.385-.057z"/></svg>

After

Width:  |  Height:  |  Size: 604 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120"><path d="M142.554 52.81c0-4.113 1.078-6.374 5.369-11.26 17.207-19.593 57.193-19.593 74.4 0 4.291 4.886 5.37 7.147 5.37 11.26v5.145h-85.14zm71.239-42.863 6.676-6.692 10.462 10.74 25.49-25.453 6.133 6.543-31.536 32.356-17.225-17.494Zm-34.474 3.377c-15.027-5.337-19.348-22.264-8.57-33.575 10.85-11.387 29.85-6.099 34.149 9.503 2.523 9.161-4.38 21.951-12.951 23.995-4.39 1.58-8.73 1.433-12.628.077z" style="fill:#024eff;fill-opacity:1;stroke-width:.999998" transform="translate(-142.554 44.365)"/></svg>

After

Width:  |  Height:  |  Size: 564 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><rect width="512" height="512" fill="#1877f2" rx="76.8"/><path fill="#fff" d="m355.6 330 11.4-74h-71v-48c0-20.2 9.9-40 41.7-40H370v-63s-29.3-5-57.3-5c-58.5 0-96.7 35.4-96.7 99.6V256h-65v74h65v182h80V330z"/></svg>

After

Width:  |  Height:  |  Size: 274 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24"><path fill="#231f20" d="M12 1A10.89 10.89 0 0 0 1 11.77 10.79 10.79 0 0 0 8.52 22c.55.1.75-.23.75-.52v-1.83c-3.06.65-3.71-1.44-3.71-1.44a2.86 2.86 0 0 0-1.22-1.58c-1-.66.08-.65.08-.65a2.31 2.31 0 0 1 1.68 1.11 2.37 2.37 0 0 0 3.2.89 2.33 2.33 0 0 1 .7-1.44c-2.44-.27-5-1.19-5-5.32a4.15 4.15 0 0 1 1.11-2.91 3.78 3.78 0 0 1 .11-2.84s.93-.29 3 1.1a10.68 10.68 0 0 1 5.5 0c2.1-1.39 3-1.1 3-1.1a3.78 3.78 0 0 1 .11 2.84A4.15 4.15 0 0 1 19 11.2c0 4.14-2.58 5.05-5 5.32a2.5 2.5 0 0 1 .75 2v2.95s.2.63.75.52A10.8 10.8 0 0 0 23 11.77 10.89 10.89 0 0 0 12 1"/></svg>

After

Width:  |  Height:  |  Size: 582 B

View File

@@ -0,0 +1 @@
<svg width="48" height="48"><path fill="#ffc107" d="M43.611 20.083H42V20H24v8h11.303c-1.649 4.657-6.08 8-11.303 8-6.627 0-12-5.373-12-12s5.373-12 12-12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 12.955 4 4 12.955 4 24s8.955 20 20 20 20-8.955 20-20c0-1.341-.138-2.65-.389-3.917z"/><path fill="#ff3d00" d="m6.306 14.691 6.571 4.819C14.655 15.108 18.961 12 24 12c3.059 0 5.842 1.154 7.961 3.039l5.657-5.657C34.046 6.053 29.268 4 24 4 16.318 4 9.656 8.337 6.306 14.691z"/><path fill="#4caf50" d="M24 44c5.166 0 9.86-1.977 13.409-5.192l-6.19-5.238A11.91 11.91 0 0 1 24 36c-5.202 0-9.619-3.317-11.283-7.946l-6.522 5.025C9.505 39.556 16.227 44 24 44z"/><path fill="#1976d2" d="M43.611 20.083H42V20H24v8h11.303a12.04 12.04 0 0 1-4.087 5.571l.003-.002 6.19 5.238C36.971 39.205 44 34 44 24c0-1.341-.138-2.65-.389-3.917z"/></svg>

After

Width:  |  Height:  |  Size: 846 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
flutter/assets/checkbox.ttf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

After

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -11,15 +11,16 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:flutter_hbb/desktop/widgets/refresh_wrapper.dart'; import 'package:flutter_hbb/desktop/widgets/refresh_wrapper.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/main.dart'; import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/models/peer_model.dart'; import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:flutter_hbb/utils/platform_channel.dart'; import 'package:flutter_hbb/utils/platform_channel.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:texture_rgba_renderer/texture_rgba_renderer.dart';
import 'package:uni_links/uni_links.dart'; import 'package:uni_links/uni_links.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
@@ -47,11 +48,6 @@ var isMobile = isAndroid || isIOS;
var version = ""; var version = "";
int androidVersion = 0; int androidVersion = 0;
/// Incriment count for textureId.
int _textureId = 0;
int get newTextureId => _textureId++;
final textureRenderer = TextureRgbaRenderer();
/// only available for Windows target /// only available for Windows target
int windowsBuildNumber = 0; int windowsBuildNumber = 0;
DesktopType? desktopType; DesktopType? desktopType;
@@ -95,6 +91,7 @@ class IconFont {
static const IconData roundClose = IconData(0xe6ed, fontFamily: _family2); static const IconData roundClose = IconData(0xe6ed, fontFamily: _family2);
static const IconData addressBook = static const IconData addressBook =
IconData(0xe602, fontFamily: "AddressBook"); IconData(0xe602, fontFamily: "AddressBook");
static const IconData checkbox = IconData(0xe7d6, fontFamily: "CheckBox");
} }
class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> { class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
@@ -223,6 +220,13 @@ class MyTheme {
), ),
); );
//tooltip
static TooltipThemeData tooltipTheme() {
return TooltipThemeData(
waitDuration: Duration(seconds: 1, milliseconds: 500),
);
}
// Dialogs // Dialogs
static const double dialogPadding = 24; static const double dialogPadding = 24;
@@ -292,6 +296,7 @@ class MyTheme {
tabBarTheme: const TabBarTheme( tabBarTheme: const TabBarTheme(
labelColor: Colors.black87, labelColor: Colors.black87,
), ),
tooltipTheme: tooltipTheme(),
splashColor: isDesktop ? Colors.transparent : null, splashColor: isDesktop ? Colors.transparent : null,
highlightColor: isDesktop ? Colors.transparent : null, highlightColor: isDesktop ? Colors.transparent : null,
splashFactory: isDesktop ? NoSplash.splashFactory : null, splashFactory: isDesktop ? NoSplash.splashFactory : null,
@@ -381,6 +386,7 @@ class MyTheme {
scrollbarTheme: ScrollbarThemeData( scrollbarTheme: ScrollbarThemeData(
thumbColor: MaterialStateProperty.all(Colors.grey[500]), thumbColor: MaterialStateProperty.all(Colors.grey[500]),
), ),
tooltipTheme: tooltipTheme(),
splashColor: isDesktop ? Colors.transparent : null, splashColor: isDesktop ? Colors.transparent : null,
highlightColor: isDesktop ? Colors.transparent : null, highlightColor: isDesktop ? Colors.transparent : null,
splashFactory: isDesktop ? NoSplash.splashFactory : null, splashFactory: isDesktop ? NoSplash.splashFactory : null,
@@ -549,17 +555,19 @@ closeConnection({String? id}) {
} }
} }
void window_on_top(int? id) async { void windowOnTop(int? id) async {
if (!isDesktop) { if (!isDesktop) {
return; return;
} }
print("Bring window '$id' on top");
if (id == null) { if (id == null) {
print("Bring window on top");
// main window // main window
windowManager.restore(); if (stateGlobal.isMinimized) {
windowManager.show(); await windowManager.restore();
windowManager.focus(); }
rustDeskWinManager.registerActiveWindow(kWindowMainId); await windowManager.show();
await windowManager.focus();
await rustDeskWinManager.registerActiveWindow(kWindowMainId);
} else { } else {
WindowController.fromWindowId(id) WindowController.fromWindowId(id)
..focus() ..focus()
@@ -689,9 +697,12 @@ class OverlayDialogManager {
String showLoading(String text, String showLoading(String text,
{bool clickMaskDismiss = false, {bool clickMaskDismiss = false,
bool showCancel = true, bool showCancel = true,
VoidCallback? onCancel}) { VoidCallback? onCancel,
final tag = _tagCount.toString(); String? tag}) {
_tagCount++; if (tag == null) {
tag = _tagCount.toString();
_tagCount++;
}
show((setState, close, context) { show((setState, close, context) {
cancel() { cancel() {
dismissAll(); dismissAll();
@@ -1066,6 +1077,37 @@ Color str2color(String str, [alpha = 0xFF]) {
return Color((hash & 0xFF7FFF) | (alpha << 24)); return Color((hash & 0xFF7FFF) | (alpha << 24));
} }
Color str2color2(String str, [alpha = 0xFF]) {
Map<String, Color> colorMap = {
"red": Colors.red,
"green": Colors.green,
"blue": Colors.blue,
"orange": Colors.orange,
"purple": Colors.purple,
"grey": Colors.grey,
"cyan": Colors.cyan,
"lime": Colors.lime,
"teal": Colors.teal,
"pink": Colors.pink[200]!,
"indigo": Colors.indigo,
"brown": Colors.brown,
};
final color = colorMap[str.toLowerCase()];
if (color != null) {
return color.withAlpha(alpha);
}
if (str.toLowerCase() == 'yellow') {
return Colors.yellow.withAlpha(alpha);
}
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash += str.codeUnitAt(i);
}
List<Color> colorList = colorMap.values.toList();
hash = hash % colorList.length;
return colorList[hash].withAlpha(alpha);
}
const K = 1024; const K = 1024;
const M = K * K; const M = K * K;
const G = M * K; const G = M * K;
@@ -1222,7 +1264,7 @@ FFI get gFFI => _globalFFI;
Future<void> initGlobalFFI() async { Future<void> initGlobalFFI() async {
debugPrint("_globalFFI init"); debugPrint("_globalFFI init");
_globalFFI = FFI(); _globalFFI = FFI(null);
debugPrint("_globalFFI init end"); debugPrint("_globalFFI init end");
// after `put`, can also be globally found by Get.find<FFI>(); // after `put`, can also be globally found by Get.find<FFI>();
Get.put(_globalFFI, permanent: true); Get.put(_globalFFI, permanent: true);
@@ -1243,7 +1285,7 @@ bool option2bool(String option, String value) {
option == "stop-service" || option == "stop-service" ||
option == "direct-server" || option == "direct-server" ||
option == "stop-rendezvous-service" || option == "stop-rendezvous-service" ||
option == "force-always-relay") { option == kOptionForceAlwaysRelay) {
res = value == "Y"; res = value == "Y";
} else { } else {
assert(false); assert(false);
@@ -1260,7 +1302,7 @@ String bool2option(String option, bool b) {
option == "stop-service" || option == "stop-service" ||
option == "direct-server" || option == "direct-server" ||
option == "stop-rendezvous-service" || option == "stop-rendezvous-service" ||
option == "force-always-relay") { option == kOptionForceAlwaysRelay) {
res = b ? 'Y' : ''; res = b ? 'Y' : '';
} else { } else {
assert(false); assert(false);
@@ -1269,6 +1311,36 @@ String bool2option(String option, bool b) {
return res; return res;
} }
mainSetBoolOption(String key, bool value) async {
String v = bool2option(key, value);
await bind.mainSetOption(key: key, value: v);
}
Future<bool> mainGetBoolOption(String key) async {
return option2bool(key, await bind.mainGetOption(key: key));
}
bool mainGetBoolOptionSync(String key) {
return option2bool(key, bind.mainGetOptionSync(key: key));
}
mainSetLocalBoolOption(String key, bool value) async {
String v = bool2option(key, value);
await bind.mainSetLocalOption(key: key, value: v);
}
bool mainGetLocalBoolOptionSync(String key) {
return option2bool(key, bind.mainGetLocalOption(key: key));
}
bool mainGetPeerBoolOptionSync(String id, String key) {
return option2bool(key, bind.mainGetPeerOptionSync(id: id, key: key));
}
mainSetPeerBoolOptionSync(String id, String key, bool v) {
bind.mainSetPeerOptionSync(id: id, key: key, value: bool2option(key, v));
}
Future<bool> matchPeer(String searchText, Peer peer) async { Future<bool> matchPeer(String searchText, Peer peer) async {
if (searchText.isEmpty) { if (searchText.isEmpty) {
return true; return true;
@@ -1280,7 +1352,7 @@ Future<bool> matchPeer(String searchText, Peer peer) async {
peer.username.toLowerCase().contains(searchText)) { peer.username.toLowerCase().contains(searchText)) {
return true; return true;
} }
final alias = await bind.mainGetPeerOption(id: peer.id, key: 'alias'); final alias = peer.alias;
if (alias.isEmpty) { if (alias.isEmpty) {
return false; return false;
} }
@@ -1337,7 +1409,9 @@ class LastWindowPosition {
return LastWindowPosition(m["width"], m["height"], m["offsetWidth"], return LastWindowPosition(m["width"], m["height"], m["offsetWidth"],
m["offsetHeight"], m["isMaximized"]); m["offsetHeight"], m["isMaximized"]);
} catch (e) { } catch (e) {
debugPrintStack(label: e.toString()); debugPrintStack(
label:
'Failed to load LastWindowPosition "$content" ${e.toString()}');
return null; return null;
} }
} }
@@ -1350,18 +1424,30 @@ Future<void> saveWindowPosition(WindowType type, {int? windowId}) async {
debugPrint( debugPrint(
"Error: windowId cannot be null when saving positions for sub window"); "Error: windowId cannot be null when saving positions for sub window");
} }
late Offset position;
late Size sz;
late bool isMaximized;
setFrameIfMaximized() {
if (isMaximized) {
final pos = bind.getLocalFlutterOption(k: kWindowPrefix + type.name);
var lpos = LastWindowPosition.loadFromString(pos);
position = Offset(
lpos?.offsetWidth ?? position.dx, lpos?.offsetHeight ?? position.dy);
sz = Size(lpos?.width ?? sz.width, lpos?.height ?? sz.height);
}
}
switch (type) { switch (type) {
case WindowType.Main: case WindowType.Main:
final position = await windowManager.getPosition(); isMaximized = await windowManager.isMaximized();
final sz = await windowManager.getSize(); position = await windowManager.getPosition();
final isMaximized = await windowManager.isMaximized(); sz = await windowManager.getSize();
final pos = LastWindowPosition( setFrameIfMaximized();
sz.width, sz.height, position.dx, position.dy, isMaximized);
await bind.setLocalFlutterConfig(
k: kWindowPrefix + type.name, v: pos.toString());
break; break;
default: default:
final wc = WindowController.fromWindowId(windowId!); final wc = WindowController.fromWindowId(windowId!);
isMaximized = await wc.isMaximized();
final Rect frame; final Rect frame;
try { try {
frame = await wc.getFrame(); frame = await wc.getFrame();
@@ -1369,38 +1455,72 @@ Future<void> saveWindowPosition(WindowType type, {int? windowId}) async {
debugPrint("Failed to get frame of window $windowId, it may be hidden"); debugPrint("Failed to get frame of window $windowId, it may be hidden");
return; return;
} }
final position = frame.topLeft; position = frame.topLeft;
final sz = frame.size; sz = frame.size;
final isMaximized = await wc.isMaximized(); setFrameIfMaximized();
final pos = LastWindowPosition(
sz.width, sz.height, position.dx, position.dy, isMaximized);
debugPrint(
"saving frame: $windowId: ${pos.width}/${pos.height}, offset:${pos.offsetWidth}/${pos.offsetHeight}");
await bind.setLocalFlutterConfig(
k: kWindowPrefix + type.name, v: pos.toString());
break; break;
} }
if (Platform.isWindows) {
const kMinOffset = -10000;
const kMaxOffset = 10000;
if (position.dx < kMinOffset ||
position.dy < kMinOffset ||
position.dx > kMaxOffset ||
position.dy > kMaxOffset) {
debugPrint("Invalid position: $position, ignore saving position");
return;
}
}
final pos = LastWindowPosition(
sz.width, sz.height, position.dx, position.dy, isMaximized);
debugPrint(
"Saving frame: $windowId: ${pos.width}/${pos.height}, offset:${pos.offsetWidth}/${pos.offsetHeight}, isMaximized:${pos.isMaximized}");
await bind.setLocalFlutterOption(
k: kWindowPrefix + type.name, v: pos.toString());
if (type == WindowType.RemoteDesktop && windowId != null) {
await _saveSessionWindowPosition(type, windowId, isMaximized, pos);
}
}
Future _saveSessionWindowPosition(WindowType windowType, int windowId,
bool isMaximized, LastWindowPosition pos) async {
final remoteList = await DesktopMultiWindow.invokeMethod(
windowId, kWindowEventGetRemoteList, null);
getPeerPos(String peerId) {
if (isMaximized) {
final peerPos = bind.mainGetPeerFlutterOptionSync(
id: peerId, k: kWindowPrefix + windowType.name);
var lpos = LastWindowPosition.loadFromString(peerPos);
return LastWindowPosition(
lpos?.width ?? pos.offsetWidth,
lpos?.height ?? pos.offsetHeight,
lpos?.offsetWidth ?? pos.offsetWidth,
lpos?.offsetHeight ?? pos.offsetHeight,
isMaximized)
.toString();
} else {
return pos.toString();
}
}
if (remoteList != null) {
for (final peerId in remoteList.split(',')) {
bind.mainSetPeerFlutterOptionSync(
id: peerId,
k: kWindowPrefix + windowType.name,
v: getPeerPos(peerId));
}
}
} }
Future<Size> _adjustRestoreMainWindowSize(double? width, double? height) async { Future<Size> _adjustRestoreMainWindowSize(double? width, double? height) async {
const double minWidth = 600; const double minWidth = 1;
const double minHeight = 100; const double minHeight = 1;
double maxWidth = (((isDesktop || isWebDesktop) const double maxWidth = 6480;
? kDesktopMaxDisplayWidth const double maxHeight = 6480;
: kMobileMaxDisplayWidth))
.toDouble();
double maxHeight = ((isDesktop || isWebDesktop)
? kDesktopMaxDisplayHeight
: kMobileMaxDisplayHeight)
.toDouble();
if (isDesktop || isWebDesktop) {
final screen = (await window_size.getWindowInfo()).screen;
if (screen != null) {
maxWidth = screen.visibleFrame.width;
maxHeight = screen.visibleFrame.height;
}
}
final defaultWidth = final defaultWidth =
((isDesktop || isWebDesktop) ? 1280 : kMobileDefaultDisplayWidth) ((isDesktop || isWebDesktop) ? 1280 : kMobileDefaultDisplayWidth)
@@ -1412,64 +1532,79 @@ Future<Size> _adjustRestoreMainWindowSize(double? width, double? height) async {
double restoreHeight = height ?? defaultHeight; double restoreHeight = height ?? defaultHeight;
if (restoreWidth < minWidth) { if (restoreWidth < minWidth) {
restoreWidth = minWidth; restoreWidth = defaultWidth;
} }
if (restoreHeight < minHeight) { if (restoreHeight < minHeight) {
restoreHeight = minHeight; restoreHeight = defaultHeight;
} }
if (restoreWidth > maxWidth) { if (restoreWidth > maxWidth) {
restoreWidth = maxWidth; restoreWidth = defaultWidth;
} }
if (restoreHeight > maxHeight) { if (restoreHeight > maxHeight) {
restoreHeight = maxHeight; restoreHeight = defaultHeight;
} }
return Size(restoreWidth, restoreHeight); return Size(restoreWidth, restoreHeight);
} }
/// return null means center /// return null means center
Future<Offset?> _adjustRestoreMainWindowOffset( Future<Offset?> _adjustRestoreMainWindowOffset(
double? left, double? top) async { double? left,
if (left == null || top == null) { double? top,
await windowManager.center(); double? width,
} else { double? height,
double windowLeft = max(0.0, left); ) async {
double windowTop = max(0.0, top); if (left == null || top == null || width == null || height == null) {
return null;
}
double frameLeft = double.infinity; double? frameLeft;
double frameTop = double.infinity; double? frameTop;
double frameRight = ((isDesktop || isWebDesktop) double? frameRight;
? kDesktopMaxDisplayWidth double? frameBottom;
: kMobileMaxDisplayWidth)
.toDouble();
double frameBottom = ((isDesktop || isWebDesktop)
? kDesktopMaxDisplayHeight
: kMobileMaxDisplayHeight)
.toDouble();
if (isDesktop || isWebDesktop) { if (isDesktop || isWebDesktop) {
for (final screen in await window_size.getScreenList()) { for (final screen in await window_size.getScreenList()) {
frameLeft = min(screen.visibleFrame.left, frameLeft); frameLeft = frameLeft == null
frameTop = min(screen.visibleFrame.top, frameTop); ? screen.visibleFrame.left
frameRight = max(screen.visibleFrame.right, frameRight); : min(screen.visibleFrame.left, frameLeft);
frameBottom = max(screen.visibleFrame.bottom, frameBottom); frameTop = frameTop == null
} ? screen.visibleFrame.top
} : min(screen.visibleFrame.top, frameTop);
frameRight = frameRight == null
if (windowLeft < frameLeft || ? screen.visibleFrame.right
windowLeft > frameRight || : max(screen.visibleFrame.right, frameRight);
windowTop < frameTop || frameBottom = frameBottom == null
windowTop > frameBottom) { ? screen.visibleFrame.bottom
return null; : max(screen.visibleFrame.bottom, frameBottom);
} else {
return Offset(windowLeft, windowTop);
} }
} }
return null; if (frameLeft == null) {
frameLeft = 0.0;
frameTop = 0.0;
frameRight = ((isDesktop || isWebDesktop)
? kDesktopMaxDisplaySize
: kMobileMaxDisplaySize)
.toDouble();
frameBottom = ((isDesktop || isWebDesktop)
? kDesktopMaxDisplaySize
: kMobileMaxDisplaySize)
.toDouble();
}
final minWidth = 10.0;
if ((left + minWidth) > frameRight! ||
(top + minWidth) > frameBottom! ||
(left + width - minWidth) < frameLeft ||
top < frameTop!) {
return null;
} else {
return Offset(left, top);
}
} }
/// Restore window position and size on start /// Restore window position and size on start
/// Note that windowId must be provided if it's subwindow /// Note that windowId must be provided if it's subwindow
Future<bool> restoreWindowPosition(WindowType type, {int? windowId}) async { Future<bool> restoreWindowPosition(WindowType type,
{int? windowId, String? peerId}) async {
if (bind if (bind
.mainGetEnv(key: "DISABLE_RUSTDESK_RESTORE_WINDOW_POSITION") .mainGetEnv(key: "DISABLE_RUSTDESK_RESTORE_WINDOW_POSITION")
.isNotEmpty) { .isNotEmpty) {
@@ -1478,42 +1613,74 @@ Future<bool> restoreWindowPosition(WindowType type, {int? windowId}) async {
if (type != WindowType.Main && windowId == null) { if (type != WindowType.Main && windowId == null) {
debugPrint( debugPrint(
"Error: windowId cannot be null when saving positions for sub window"); "Error: windowId cannot be null when saving positions for sub window");
return false;
} }
final pos = bind.getLocalFlutterConfig(k: kWindowPrefix + type.name);
bool isRemotePeerPos = false;
String? pos;
// No need to check mainGetLocalBoolOptionSync(kOptionOpenNewConnInTabs)
// Though "open in tabs" is true and the new window restore peer position, it's ok.
if (type == WindowType.RemoteDesktop && windowId != null && peerId != null) {
// If the restore position is called by main window, and the peer id is not null
// then we may need to get the position by reading the peer config.
// Because the session may not be read at this time.
if (desktopType == DesktopType.main) {
pos = bind.mainGetPeerFlutterOptionSync(
id: peerId, k: kWindowPrefix + type.name);
} else {
pos = await bind.sessionGetFlutterOptionByPeerId(
id: peerId, k: kWindowPrefix + type.name);
}
isRemotePeerPos = pos != null;
}
pos ??= bind.getLocalFlutterOption(k: kWindowPrefix + type.name);
var lpos = LastWindowPosition.loadFromString(pos); var lpos = LastWindowPosition.loadFromString(pos);
if (lpos == null) { if (lpos == null) {
debugPrint("no window position saved, ignoring position restoration"); debugPrint("no window position saved, ignoring position restoration");
return false; return false;
} }
if (type == WindowType.RemoteDesktop &&
!isRemotePeerPos &&
windowId != null) {
if (lpos.offsetWidth != null) {
lpos.offsetWidth = lpos.offsetWidth! + windowId * 20;
}
if (lpos.offsetHeight != null) {
lpos.offsetHeight = lpos.offsetHeight! + windowId * 20;
}
}
final size = await _adjustRestoreMainWindowSize(lpos.width, lpos.height);
final offset = await _adjustRestoreMainWindowOffset(
lpos.offsetWidth,
lpos.offsetHeight,
size.width,
size.height,
);
debugPrint(
"restore lpos: ${size.width}/${size.height}, offset:${offset?.dx}/${offset?.dy}");
switch (type) { switch (type) {
case WindowType.Main: case WindowType.Main:
if (lpos.isMaximized == true) { restorePos() async {
await windowManager.maximize();
} else {
final size =
await _adjustRestoreMainWindowSize(lpos.width, lpos.height);
final offset = await _adjustRestoreMainWindowOffset(
lpos.offsetWidth, lpos.offsetHeight);
await windowManager.setSize(size);
if (offset == null) { if (offset == null) {
await windowManager.center(); await windowManager.center();
} else { } else {
await windowManager.setPosition(offset); await windowManager.setPosition(offset);
} }
} }
if (lpos.isMaximized == true) {
await restorePos();
await windowManager.maximize();
} else {
await windowManager.setSize(size);
await restorePos();
}
return true; return true;
default: default:
final wc = WindowController.fromWindowId(windowId!); final wc = WindowController.fromWindowId(windowId!);
if (lpos.isMaximized == true) { restoreFrame() async {
await wc.maximize();
} else {
final size =
await _adjustRestoreMainWindowSize(lpos.width, lpos.height);
final offset = await _adjustRestoreMainWindowOffset(
lpos.offsetWidth, lpos.offsetHeight);
debugPrint(
"restore lpos: ${size.width}/${size.height}, offset:${offset?.dx}/${offset?.dy}");
if (offset == null) { if (offset == null) {
await wc.center(); await wc.center();
} else { } else {
@@ -1522,6 +1689,12 @@ Future<bool> restoreWindowPosition(WindowType type, {int? windowId}) async {
await wc.setFrame(frame); await wc.setFrame(frame);
} }
} }
if (lpos.isMaximized == true) {
await restoreFrame();
await wc.maximize();
} else {
await restoreFrame();
}
break; break;
} }
return false; return false;
@@ -1560,7 +1733,7 @@ StreamSubscription? listenUniLinks({handleByFlutter = true}) {
} }
final sub = uriLinkStream.listen((Uri? uri) { final sub = uriLinkStream.listen((Uri? uri) {
debugPrint("A uri was received: $uri."); debugPrint("A uri was received: $uri. handleByFlutter $handleByFlutter");
if (uri != null) { if (uri != null) {
if (handleByFlutter) { if (handleByFlutter) {
handleUriLink(uri: uri); handleUriLink(uri: uri);
@@ -1603,7 +1776,14 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
args = urlLinkToCmdArgs(uri); args = urlLinkToCmdArgs(uri);
} }
} }
if (args == null) return false; if (args == null) {
return false;
}
if (args.isEmpty) {
windowOnTop(null);
return true;
}
UriLinkType? type; UriLinkType? type;
String? id; String? id;
@@ -1654,7 +1834,7 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
Future.delayed(Duration.zero, () { Future.delayed(Duration.zero, () {
rustDeskWinManager.newRemoteDesktop(id!, rustDeskWinManager.newRemoteDesktop(id!,
password: password, password: password,
switch_uuid: switchUuid, switchUuid: switchUuid,
forceRelay: forceRelay); forceRelay: forceRelay);
}); });
break; break;
@@ -1687,7 +1867,10 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
List<String>? urlLinkToCmdArgs(Uri uri) { List<String>? urlLinkToCmdArgs(Uri uri) {
String? command; String? command;
String? id; String? id;
if (uri.authority == "connection" && uri.path.startsWith("/new/")) { if (uri.authority.isEmpty &&
uri.path.split('').every((char) => char == '/')) {
return [];
} else if (uri.authority == "connection" && uri.path.startsWith("/new/")) {
// For compatibility // For compatibility
command = '--connect'; command = '--connect';
id = uri.path.substring("/new/".length); id = uri.path.substring("/new/".length);
@@ -1719,11 +1902,13 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
return null; return null;
} }
connectMainDesktop(String id, connectMainDesktop(
{required bool isFileTransfer, String id, {
required bool isTcpTunneling, required bool isFileTransfer,
required bool isRDP, required bool isTcpTunneling,
bool? forceRelay}) async { required bool isRDP,
bool? forceRelay,
}) async {
if (isFileTransfer) { if (isFileTransfer) {
await rustDeskWinManager.newFileTransfer(id, forceRelay: forceRelay); await rustDeskWinManager.newFileTransfer(id, forceRelay: forceRelay);
} else if (isTcpTunneling || isRDP) { } else if (isTcpTunneling || isRDP) {
@@ -1737,11 +1922,22 @@ connectMainDesktop(String id,
/// If [isFileTransfer], starts a session only for file transfer. /// If [isFileTransfer], starts a session only for file transfer.
/// If [isTcpTunneling], starts a session only for tcp tunneling. /// If [isTcpTunneling], starts a session only for tcp tunneling.
/// If [isRDP], starts a session only for rdp. /// If [isRDP], starts a session only for rdp.
connect(BuildContext context, String id, connect(
{bool isFileTransfer = false, BuildContext context,
bool isTcpTunneling = false, String id, {
bool isRDP = false}) async { bool isFileTransfer = false,
bool isTcpTunneling = false,
bool isRDP = false,
}) async {
if (id == '') return; if (id == '') return;
if (!isDesktop || desktopType == DesktopType.main) {
try {
if (Get.isRegistered<IDTextEditingController>()) {
final idController = Get.find<IDTextEditingController>();
idController.text = formatID(id);
}
} catch (_) {}
}
id = id.replaceAll(' ', ''); id = id.replaceAll(' ', '');
final oldId = id; final oldId = id;
id = await bind.mainHandleRelayId(id: id); id = await bind.mainHandleRelayId(id: id);
@@ -1751,18 +1947,20 @@ connect(BuildContext context, String id,
if (isDesktop) { if (isDesktop) {
if (desktopType == DesktopType.main) { if (desktopType == DesktopType.main) {
await connectMainDesktop(id, await connectMainDesktop(
isFileTransfer: isFileTransfer, id,
isTcpTunneling: isTcpTunneling, isFileTransfer: isFileTransfer,
isRDP: isRDP, isTcpTunneling: isTcpTunneling,
forceRelay: forceRelay); isRDP: isRDP,
forceRelay: forceRelay,
);
} else { } else {
await rustDeskWinManager.call(WindowType.Main, kWindowConnect, { await rustDeskWinManager.call(WindowType.Main, kWindowConnect, {
'id': id, 'id': id,
'isFileTransfer': isFileTransfer, 'isFileTransfer': isFileTransfer,
'isTcpTunneling': isTcpTunneling, 'isTcpTunneling': isTcpTunneling,
'isRDP': isRDP, 'isRDP': isRDP,
"forceRelay": forceRelay, 'forceRelay': forceRelay,
}); });
} }
} else { } else {
@@ -1851,10 +2049,14 @@ Future<void> onActiveWindowChanged() async {
if (rustDeskWinManager.getActiveWindows().isEmpty) { if (rustDeskWinManager.getActiveWindows().isEmpty) {
// close all sub windows // close all sub windows
try { try {
await Future.wait([ if (Platform.isLinux) {
saveWindowPosition(WindowType.Main), await Future.wait([
rustDeskWinManager.closeAllSubWindows() saveWindowPosition(WindowType.Main),
]); rustDeskWinManager.closeAllSubWindows()
]);
} else {
await rustDeskWinManager.closeAllSubWindows();
}
} catch (err) { } catch (err) {
debugPrintStack(label: "$err"); debugPrintStack(label: "$err");
} finally { } finally {
@@ -2154,10 +2356,18 @@ void onCopyFingerprint(String value) {
} }
} }
Future<bool> callMainCheckSuperUserPermission() async {
bool checked = await bind.mainCheckSuperUserPermission();
if (Platform.isMacOS) {
await windowManager.show();
}
return checked;
}
Future<void> start_service(bool is_start) async { Future<void> start_service(bool is_start) async {
bool checked = !bind.mainIsInstalled() || bool checked = !bind.mainIsInstalled() ||
!Platform.isMacOS || !Platform.isMacOS ||
await bind.mainCheckSuperUserPermission(); await callMainCheckSuperUserPermission();
if (checked) { if (checked) {
bind.mainSetOption(key: "stop-service", value: is_start ? "" : "Y"); bind.mainSetOption(key: "stop-service", value: is_start ? "" : "Y");
} }
@@ -2223,3 +2433,10 @@ Widget unreadTopRightBuilder(RxInt? count, {Widget? icon}) {
], ],
); );
} }
String toCapitalized(String s) {
if (s.isEmpty) {
return s;
}
return s.substring(0, 1).toUpperCase() + s.substring(1);
}

View File

@@ -26,6 +26,8 @@ class IDTextInputFormatter extends TextInputFormatter {
selection: TextSelection.collapsed( selection: TextSelection.collapsed(
offset: newID.length - selectionIndexFromTheRight, offset: newID.length - selectionIndexFromTheRight,
), ),
// https://github.com/flutter/flutter/issues/78066#issuecomment-797869906
composing: newValue.composing,
); );
} }
} }
@@ -33,6 +35,11 @@ class IDTextInputFormatter extends TextInputFormatter {
String formatID(String id) { String formatID(String id) {
String id2 = id.replaceAll(' ', ''); String id2 = id.replaceAll(' ', '');
String suffix = '';
if (id2.endsWith(r'\r') || id2.endsWith(r'/r')) {
suffix = id2.substring(id2.length - 2, id2.length);
id2 = id2.substring(0, id2.length - 2);
}
if (int.tryParse(id2) == null) return id; if (int.tryParse(id2) == null) return id;
String newID = ''; String newID = '';
if (id2.length <= 3) { if (id2.length <= 3) {
@@ -45,7 +52,7 @@ String formatID(String id) {
newID += " ${id2.substring(i, i + 3)}"; newID += " ${id2.substring(i, i + 3)}";
} }
} }
return newID; return newID + suffix;
} }
String trimID(String id) { String trimID(String id) {

View File

@@ -1,4 +1,5 @@
import 'dart:io'; import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/models/peer_model.dart'; import 'package:flutter_hbb/models/peer_model.dart';
@@ -70,16 +71,6 @@ class PeerPayload {
} }
} }
class DeviceInfo {
static Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['os'] = Platform.operatingSystem;
data['type'] = "client";
data['name'] = bind.mainGetHostname();
return data;
}
}
class LoginRequest { class LoginRequest {
String? username; String? username;
String? password; String? password;
@@ -88,7 +79,6 @@ class LoginRequest {
bool? autoLogin; bool? autoLogin;
String? type; String? type;
String? verificationCode; String? verificationCode;
Map<String, dynamic> deviceInfo = DeviceInfo.toJson();
LoginRequest( LoginRequest(
{this.username, {this.username,
@@ -110,6 +100,13 @@ class LoginRequest {
if (verificationCode != null) { if (verificationCode != null) {
data['verificationCode'] = verificationCode; data['verificationCode'] = verificationCode;
} }
Map<String, dynamic> deviceInfo = {};
try {
deviceInfo = jsonDecode(bind.mainGetLoginDeviceInfo());
} catch (e) {
debugPrint('Failed to decode get device info: $e');
}
data['deviceInfo'] = deviceInfo; data['deviceInfo'] = deviceInfo;
return data; return data;
} }

View File

@@ -3,11 +3,13 @@ import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:flutter_hbb/common/widgets/peer_card.dart'; import 'package:flutter_hbb/common/widgets/peer_card.dart';
import 'package:flutter_hbb/common/widgets/peers_view.dart'; import 'package:flutter_hbb/common/widgets/peers_view.dart';
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart'; import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
import '../../consts.dart'; import 'package:flutter_hbb/models/ab_model.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../common.dart'; import '../../common.dart';
import 'dialog.dart';
import 'login.dart'; import 'login.dart';
final hideAbTagsPanel = false.obs; final hideAbTagsPanel = false.obs;
@@ -37,63 +39,115 @@ class _AddressBookState extends State<AddressBook> {
child: ElevatedButton( child: ElevatedButton(
onPressed: loginDialog, child: Text(translate("Login")))); onPressed: loginDialog, child: Text(translate("Login"))));
} else { } else {
if (gFFI.abModel.abLoading.value) { if (gFFI.abModel.abLoading.value && gFFI.abModel.emtpy) {
return const Center( return const Center(
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
); );
} }
if (gFFI.abModel.abError.isNotEmpty) { return Column(
return _buildShowError(gFFI.abModel.abError.value); children: [
} // NOT use Offstage to wrap LinearProgressIndicator
return isDesktop if (gFFI.abModel.retrying.value) LinearProgressIndicator(),
? _buildAddressBookDesktop() _buildErrorBanner(
: _buildAddressBookMobile(); err: gFFI.abModel.pullError,
retry: null,
close: () => gFFI.abModel.pullError.value = ''),
_buildErrorBanner(
err: gFFI.abModel.pushError,
retry: () => gFFI.abModel.pushAb(isRetry: true),
close: () => gFFI.abModel.pushError.value = ''),
Expanded(
child: isDesktop
? _buildAddressBookDesktop()
: _buildAddressBookMobile())
],
);
} }
}); });
Widget _buildShowError(String error) { Widget _buildErrorBanner(
return Center( {required RxString err,
child: Column( required Function? retry,
mainAxisAlignment: MainAxisAlignment.center, required Function close}) {
children: [ const double height = 25;
Text(translate(error)), return Obx(() => Offstage(
TextButton( offstage: !(!gFFI.abModel.abLoading.value && err.value.isNotEmpty),
onPressed: () { child: Center(
gFFI.abModel.pullAb(); child: Container(
}, height: height,
child: Text(translate("Retry"))) color: Color.fromARGB(255, 253, 238, 235),
], child: Row(
)); mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FittedBox(
child: Icon(
Icons.info,
color: Color.fromARGB(255, 249, 81, 81),
),
).marginAll(4),
Flexible(
child: Align(
alignment: Alignment.centerLeft,
child: Tooltip(
message: translate(err.value),
child: Text(
translate(err.value),
overflow: TextOverflow.ellipsis,
),
)).marginSymmetric(vertical: 2),
),
if (retry != null)
InkWell(
onTap: () {
retry.call();
},
child: Text(
translate("Retry"),
style: TextStyle(color: MyTheme.accent),
)).marginSymmetric(horizontal: 5),
FittedBox(
child: InkWell(
onTap: () {
close.call();
},
child: Icon(Icons.close).marginSymmetric(horizontal: 5),
),
).marginAll(4)
],
),
)).marginOnly(bottom: 14),
));
} }
Widget _buildAddressBookDesktop() { Widget _buildAddressBookDesktop() {
return Row( return Row(
children: [ children: [
Offstage( Offstage(
offstage: hideAbTagsPanel.value, offstage: hideAbTagsPanel.value,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: border: Border.all(
Border.all(color: Theme.of(context).colorScheme.background)), color: Theme.of(context).colorScheme.background)),
child: Container( child: Container(
width: 150, width: 150,
height: double.infinity, height: double.infinity,
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
children: [ children: [
_buildTagHeader().marginOnly(left: 8.0, right: 0), _buildTagHeader().marginOnly(left: 8.0, right: 0),
Expanded( Expanded(
child: Container( child: Container(
width: double.infinity, width: double.infinity,
height: double.infinity, height: double.infinity,
child: _buildTags(), child: _buildTags(),
), ),
) )
], ],
), ),
), ),
).marginOnly(right: 12.0)), ).marginOnly(right: 12.0)),
_buildPeersViews() _buildPeersViews()
], ],
); );
@@ -102,25 +156,27 @@ class _AddressBookState extends State<AddressBook> {
Widget _buildAddressBookMobile() { Widget _buildAddressBookMobile() {
return Column( return Column(
children: [ children: [
Container( Offstage(
decoration: BoxDecoration( offstage: hideAbTagsPanel.value,
borderRadius: BorderRadius.circular(6), child: Container(
border: decoration: BoxDecoration(
Border.all(color: Theme.of(context).colorScheme.background)), borderRadius: BorderRadius.circular(6),
child: Container( border: Border.all(
padding: const EdgeInsets.all(8.0), color: Theme.of(context).colorScheme.background)),
child: Column( child: Container(
mainAxisSize: MainAxisSize.min, padding: const EdgeInsets.all(8.0),
children: [ child: Column(
_buildTagHeader().marginOnly(left: 8.0, right: 0), mainAxisSize: MainAxisSize.min,
Container( children: [
width: double.infinity, _buildTagHeader().marginOnly(left: 8.0, right: 0),
child: _buildTags(), Container(
width: double.infinity,
child: _buildTags(),
),
],
), ),
], ),
), ).marginOnly(bottom: 12.0)),
),
).marginOnly(bottom: 12.0),
_buildPeersViews() _buildPeersViews()
], ],
); );
@@ -144,9 +200,16 @@ class _AddressBookState extends State<AddressBook> {
} }
Widget _buildTags() { Widget _buildTags() {
return Obx( return Obx(() {
() => Wrap( final List tags;
children: gFFI.abModel.tags if (gFFI.abModel.sortTags.value) {
tags = gFFI.abModel.tags.toList();
tags.sort();
} else {
tags = gFFI.abModel.tags;
}
return Wrap(
children: tags
.map((e) => AddressBookTag( .map((e) => AddressBookTag(
name: e, name: e,
tags: gFFI.abModel.selectedTags, tags: gFFI.abModel.selectedTags,
@@ -158,8 +221,8 @@ class _AddressBookState extends State<AddressBook> {
} }
})) }))
.toList(), .toList(),
), );
); });
} }
Widget _buildPeersViews() { Widget _buildPeersViews() {
@@ -174,11 +237,44 @@ class _AddressBookState extends State<AddressBook> {
); );
} }
@protected
MenuEntryBase<String> syncMenuItem() {
return MenuEntrySwitch<String>(
switchType: SwitchType.scheckbox,
text: translate('Sync with recent sessions'),
getter: () async {
return shouldSyncAb();
},
setter: (bool v) async {
bind.mainSetLocalOption(key: syncAbOption, value: v ? 'Y' : '');
},
dismissOnClicked: true,
);
}
@protected
MenuEntryBase<String> sortMenuItem() {
return MenuEntrySwitch<String>(
switchType: SwitchType.scheckbox,
text: translate('Sort tags'),
getter: () async {
return shouldSortTags();
},
setter: (bool v) async {
bind.mainSetLocalOption(key: sortAbTagsOption, value: v ? 'Y' : '');
gFFI.abModel.sortTags.value = v;
},
dismissOnClicked: true,
);
}
void _showMenu(RelativeRect pos) { void _showMenu(RelativeRect pos) {
final items = [ final items = [
getEntry(translate("Add ID"), abAddId), getEntry(translate("Add ID"), abAddId),
getEntry(translate("Add Tag"), abAddTag), getEntry(translate("Add Tag"), abAddTag),
getEntry(translate("Unselect all tags"), gFFI.abModel.unsetSelectedTags), getEntry(translate("Unselect all tags"), gFFI.abModel.unsetSelectedTags),
sortMenuItem(),
syncMenuItem(),
]; ];
mod_menu.showMenu( mod_menu.showMenu(
@@ -198,6 +294,9 @@ class _AddressBookState extends State<AddressBook> {
} }
void abAddId() async { void abAddId() async {
if (gFFI.abModel.isFull(true)) {
return;
}
var isInProgress = false; var isInProgress = false;
IDTextEditingController idController = IDTextEditingController(text: ''); IDTextEditingController idController = IDTextEditingController(text: '');
TextEditingController aliasController = TextEditingController(text: ''); TextEditingController aliasController = TextEditingController(text: '');
@@ -224,13 +323,14 @@ class _AddressBookState extends State<AddressBook> {
return; return;
} }
gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag); gFFI.abModel.addId(id, aliasController.text.trim(), selectedTag);
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
this.setState(() {}); this.setState(() {});
// final currentPeers // final currentPeers
} }
close(); close();
} }
double marginBottom = 4;
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Add ID")), title: Text(translate("Add ID")),
content: Column( content: Column(
@@ -252,7 +352,7 @@ class _AddressBookState extends State<AddressBook> {
), ),
], ],
), ),
), ).marginOnly(bottom: marginBottom),
TextField( TextField(
controller: idController, controller: idController,
inputFormatters: [IDTextInputFormatter()], inputFormatters: [IDTextInputFormatter()],
@@ -264,7 +364,7 @@ class _AddressBookState extends State<AddressBook> {
translate('Alias'), translate('Alias'),
style: style, style: style,
), ),
).marginOnly(top: 8, bottom: 2), ).marginOnly(top: 8, bottom: marginBottom),
TextField( TextField(
controller: aliasController, controller: aliasController,
), ),
@@ -274,8 +374,9 @@ class _AddressBookState extends State<AddressBook> {
translate('Tags'), translate('Tags'),
style: style, style: style,
), ),
).marginOnly(top: 8), ).marginOnly(top: 8, bottom: marginBottom),
Container( Align(
alignment: Alignment.centerLeft,
child: Wrap( child: Wrap(
children: tags children: tags
.map((e) => AddressBookTag( .map((e) => AddressBookTag(
@@ -297,8 +398,8 @@ class _AddressBookState extends State<AddressBook> {
const SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage( // NOT use Offstage to wrap LinearProgressIndicator
offstage: !isInProgress, child: const LinearProgressIndicator()) if (isInProgress) const LinearProgressIndicator(),
], ],
), ),
actions: [ actions: [
@@ -331,7 +432,7 @@ class _AddressBookState extends State<AddressBook> {
for (final tag in tags) { for (final tag in tags) {
gFFI.abModel.addTag(tag); gFFI.abModel.addTag(tag);
} }
await gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
// final currentPeers // final currentPeers
} }
close(); close();
@@ -363,8 +464,8 @@ class _AddressBookState extends State<AddressBook> {
const SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage( // NOT use Offstage to wrap LinearProgressIndicator
offstage: !isInProgress, child: const LinearProgressIndicator()) if (isInProgress) const LinearProgressIndicator(),
], ],
), ),
actions: [ actions: [
@@ -402,31 +503,71 @@ class AddressBookTag extends StatelessWidget {
pos = RelativeRect.fromLTRB(x, y, x, y); pos = RelativeRect.fromLTRB(x, y, x, y);
} }
const double radius = 8;
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,
onTapDown: showActionMenu ? setPosition : null, onTapDown: showActionMenu ? setPosition : null,
onSecondaryTapDown: showActionMenu ? setPosition : null, onSecondaryTapDown: showActionMenu ? setPosition : null,
onSecondaryTap: showActionMenu ? () => _showMenu(context, pos) : null, onSecondaryTap: showActionMenu ? () => _showMenu(context, pos) : null,
onLongPress: showActionMenu ? () => _showMenu(context, pos) : null, onLongPress: showActionMenu ? () => _showMenu(context, pos) : null,
child: Obx( child: Obx(() => Container(
() => Container( decoration: BoxDecoration(
decoration: BoxDecoration( color: tags.contains(name)
color: tags.contains(name) ? str2color2(name, 0xFF)
? Colors.blue : Theme.of(context).colorScheme.background,
: Theme.of(context).colorScheme.background, borderRadius: BorderRadius.circular(4)),
borderRadius: BorderRadius.circular(6)), margin: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0),
margin: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 8.0), padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 6.0),
padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 8.0), child: IntrinsicWidth(
child: Text(name, child: Row(
style: children: [
TextStyle(color: tags.contains(name) ? Colors.white : null)), Container(
), width: radius,
), height: radius,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: tags.contains(name)
? Colors.white
: str2color2(name)),
).marginOnly(right: radius / 2),
Expanded(
child: Text(name,
style: TextStyle(
overflow: TextOverflow.ellipsis,
color: tags.contains(name) ? Colors.white : null)),
),
],
),
),
)),
); );
} }
void _showMenu(BuildContext context, RelativeRect pos) { void _showMenu(BuildContext context, RelativeRect pos) {
final items = [ final items = [
getEntry(translate("Rename"), () {
renameDialog(
oldName: name,
validator: (String? newName) {
if (newName == null || newName.isEmpty) {
return translate('Can not be empty');
}
if (newName != name && gFFI.abModel.tags.contains(newName)) {
return translate('Already exists');
}
return null;
},
onSubmit: (String newName) {
if (name != newName) {
gFFI.abModel.renameTag(name, newName);
gFFI.abModel.pushAb();
}
Future.delayed(Duration.zero, () => Get.back());
},
onCancel: () {
Future.delayed(Duration.zero, () => Get.back());
});
}),
getEntry(translate("Delete"), () { getEntry(translate("Delete"), () {
gFFI.abModel.deleteTag(name); gFFI.abModel.deleteTag(name);
gFFI.abModel.pushAb(); gFFI.abModel.pushAb();
@@ -458,7 +599,6 @@ MenuEntryButton<String> getEntry(String title, VoidCallback proc) {
style: style, style: style,
), ),
proc: proc, proc: proc,
padding: kDesktopMenuPadding,
dismissOnClicked: true, dismissOnClicked: true,
); );
} }

View File

@@ -1,11 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
class AnimatedRotationWidget extends StatefulWidget { class AnimatedRotationWidget extends StatefulWidget {
final VoidCallback onPressed; final VoidCallback onPressed;
final ValueChanged<bool>? onHover; final ValueChanged<bool>? onHover;
final Widget child; final Widget child;
final RxBool? spinning;
const AnimatedRotationWidget( const AnimatedRotationWidget(
{super.key, required this.onPressed, required this.child, this.onHover}); {super.key,
required this.onPressed,
required this.child,
this.spinning,
this.onHover});
@override @override
State<AnimatedRotationWidget> createState() => AnimatedRotationWidgetState(); State<AnimatedRotationWidget> createState() => AnimatedRotationWidgetState();
@@ -14,14 +20,31 @@ class AnimatedRotationWidget extends StatefulWidget {
class AnimatedRotationWidgetState extends State<AnimatedRotationWidget> { class AnimatedRotationWidgetState extends State<AnimatedRotationWidget> {
double turns = 0.0; double turns = 0.0;
@override
void initState() {
super.initState();
widget.spinning?.listen((v) {
if (v && mounted) {
setState(() {
turns += 1;
});
}
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedRotation( return AnimatedRotation(
turns: turns, turns: turns,
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
onEnd: () {
if (widget.spinning?.value == true && mounted) {
setState(() => turns += 1.0);
}
},
child: InkWell( child: InkWell(
onTap: () { onTap: () {
setState(() => turns += 1.0); if (mounted) setState(() => turns += 1.0);
widget.onPressed(); widget.onPressed();
}, },
onHover: widget.onHover, onHover: widget.onHover,

View File

@@ -9,6 +9,7 @@ import 'package:get/get.dart';
import '../../common.dart'; import '../../common.dart';
import '../../models/model.dart'; import '../../models/model.dart';
import '../../models/platform_model.dart'; import '../../models/platform_model.dart';
import 'address_book.dart';
void clientClose(SessionID sessionId, OverlayDialogManager dialogManager) { void clientClose(SessionID sessionId, OverlayDialogManager dialogManager) {
msgBox(sessionId, 'info', 'Close', 'Are you sure to close the connection?', msgBox(sessionId, 'info', 'Close', 'Are you sure to close the connection?',
@@ -155,8 +156,8 @@ void changeIdDialog() {
}).toList(), }).toList(),
)).marginOnly(bottom: 8) )).marginOnly(bottom: 8)
: SizedBox.shrink(), : SizedBox.shrink(),
Offstage( // NOT use Offstage to wrap LinearProgressIndicator
offstage: !isInProgress, child: const LinearProgressIndicator()) if (isInProgress) const LinearProgressIndicator(),
], ],
), ),
actions: [ actions: [
@@ -201,8 +202,8 @@ void changeWhiteList({Function()? callback}) async {
const SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage( // NOT use Offstage to wrap LinearProgressIndicator
offstage: !isInProgress, child: const LinearProgressIndicator()) if (isInProgress) const LinearProgressIndicator(),
], ],
), ),
actions: [ actions: [
@@ -947,7 +948,9 @@ showSetOSPassword(
Function()? closeCallback, Function()? closeCallback,
) async { ) async {
final controller = TextEditingController(); final controller = TextEditingController();
osPassword ??= await bind.sessionGetOption(sessionId: sessionId, arg: 'os-password') ?? ''; osPassword ??=
await bind.sessionGetOption(sessionId: sessionId, arg: 'os-password') ??
'';
var autoLogin = var autoLogin =
await bind.sessionGetOption(sessionId: sessionId, arg: 'auto-login') != await bind.sessionGetOption(sessionId: sessionId, arg: 'auto-login') !=
''; '';
@@ -957,6 +960,7 @@ showSetOSPassword(
close(); close();
if (closeCallback != null) closeCallback(); if (closeCallback != null) closeCallback();
} }
submit() { submit() {
var text = controller.text.trim(); var text = controller.text.trim();
bind.sessionPeerOption( bind.sessionPeerOption(
@@ -1220,7 +1224,8 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
qualityInitValue = qualityInitValue =
quality != null && quality.isNotEmpty ? quality[0].toDouble() : 50.0; quality != null && quality.isNotEmpty ? quality[0].toDouble() : 50.0;
const qualityMinValue = 10.0; const qualityMinValue = 10.0;
const qualityMaxValue = 100.0; const qualityMoreThresholdValue = 100.0;
const qualityMaxValue = 2000.0;
if (qualityInitValue < qualityMinValue) { if (qualityInitValue < qualityMinValue) {
qualityInitValue = qualityMinValue; qualityInitValue = qualityMinValue;
} }
@@ -1228,6 +1233,8 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
qualityInitValue = qualityMaxValue; qualityInitValue = qualityMaxValue;
} }
final RxDouble qualitySliderValue = RxDouble(qualityInitValue); final RxDouble qualitySliderValue = RxDouble(qualityInitValue);
final moreQualityInitValue = qualityInitValue > qualityMoreThresholdValue;
final RxBool moreQualityChecked = RxBool(moreQualityInitValue);
final debouncerQuality = Debouncer<double>( final debouncerQuality = Debouncer<double>(
Duration(milliseconds: 1000), Duration(milliseconds: 1000),
onChanged: (double v) { onChanged: (double v) {
@@ -1242,7 +1249,9 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
child: Slider( child: Slider(
value: qualitySliderValue.value, value: qualitySliderValue.value,
min: qualityMinValue, min: qualityMinValue,
max: qualityMaxValue, max: moreQualityChecked.value
? qualityMaxValue
: qualityMoreThresholdValue,
divisions: 18, divisions: 18,
onChanged: (double value) { onChanged: (double value) {
qualitySliderValue.value = value; qualitySliderValue.value = value;
@@ -1256,11 +1265,32 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
style: const TextStyle(fontSize: 15), style: const TextStyle(fontSize: 15),
)), )),
Expanded( Expanded(
flex: 2, flex: 1,
child: Text( child: Text(
translate('Bitrate'), translate('Bitrate'),
style: const TextStyle(fontSize: 15), style: const TextStyle(fontSize: 15),
)), )),
Expanded(
flex: 1,
child: Row(
children: [
Checkbox(
value: moreQualityChecked.value,
onChanged: (bool? value) {
moreQualityChecked.value = value!;
if (!value &&
qualitySliderValue.value >
qualityMoreThresholdValue) {
qualitySliderValue.value = qualityMoreThresholdValue;
debouncerQuality.value = qualityMoreThresholdValue;
}
},
).marginOnly(right: 5),
Expanded(
child: Text(translate('More')),
)
],
)),
], ],
)); ));
// fps // fps
@@ -1321,3 +1351,171 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async {
); );
msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]); msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]);
} }
void deletePeerConfirmDialog(Function onSubmit, String title) async {
gFFI.dialogManager.show(
(setState, close, context) {
submit() async {
await onSubmit();
close();
}
return CustomAlertDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.delete_rounded,
color: Colors.red,
),
Expanded(
child: Text(title, overflow: TextOverflow.ellipsis).paddingOnly(
left: 10,
),
),
],
),
content: SizedBox.shrink(),
actions: [
dialogButton(
"Cancel",
icon: Icon(Icons.close_rounded),
onPressed: close,
isOutline: true,
),
dialogButton(
"OK",
icon: Icon(Icons.done_rounded),
onPressed: submit,
),
],
onSubmit: submit,
onCancel: close,
);
},
);
}
void editAbTagDialog(
List<dynamic> currentTags, Function(List<dynamic>) onSubmit) {
var isInProgress = false;
final tags = List.of(gFFI.abModel.tags);
var selectedTag = currentTags.obs;
gFFI.dialogManager.show((setState, close, context) {
submit() async {
setState(() {
isInProgress = true;
});
await onSubmit(selectedTag);
close();
}
return CustomAlertDialog(
title: Text(translate("Edit Tag")),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Wrap(
children: tags
.map((e) => AddressBookTag(
name: e,
tags: selectedTag,
onTap: () {
if (selectedTag.contains(e)) {
selectedTag.remove(e);
} else {
selectedTag.add(e);
}
},
showActionMenu: false))
.toList(growable: false),
),
),
// NOT use Offstage to wrap LinearProgressIndicator
if (isInProgress) const LinearProgressIndicator(),
],
),
actions: [
dialogButton("Cancel", onPressed: close, isOutline: true),
dialogButton("OK", onPressed: submit),
],
onSubmit: submit,
onCancel: close,
);
});
}
void renameDialog(
{required String oldName,
FormFieldValidator<String>? validator,
required ValueChanged<String> onSubmit,
Function? onCancel}) async {
RxBool isInProgress = false.obs;
var controller = TextEditingController(text: oldName);
final formKey = GlobalKey<FormState>();
gFFI.dialogManager.show((setState, close, context) {
submit() async {
String text = controller.text.trim();
if (validator != null && formKey.currentState?.validate() == false) {
return;
}
isInProgress.value = true;
onSubmit(text);
close();
isInProgress.value = false;
}
cancel() {
onCancel?.call();
close();
}
return CustomAlertDialog(
title: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.edit_rounded, color: MyTheme.accent),
Text(translate('Rename')).paddingOnly(left: 10),
],
),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: Form(
key: formKey,
child: TextFormField(
controller: controller,
autofocus: true,
decoration: InputDecoration(labelText: translate('Name')),
validator: validator,
),
),
),
// NOT use Offstage to wrap LinearProgressIndicator
Obx(() =>
isInProgress.value ? const LinearProgressIndicator() : Offstage())
],
),
actions: [
dialogButton(
"Cancel",
icon: Icon(Icons.close_rounded),
onPressed: cancel,
isOutline: true,
),
dialogButton(
"OK",
icon: Icon(Icons.done_rounded),
onPressed: submit,
),
],
onSubmit: submit,
onCancel: cancel,
);
});
}

View File

@@ -113,13 +113,14 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
} }
void onOneFingerStartDebounce(ScaleUpdateDetails d) { void onOneFingerStartDebounce(ScaleUpdateDetails d) {
final start = (ScaleUpdateDetails d) { start(ScaleUpdateDetails d) {
_currentState = GestureState.oneFingerPan; _currentState = GestureState.oneFingerPan;
if (onOneFingerPanStart != null) { if (onOneFingerPanStart != null) {
onOneFingerPanStart!(DragStartDetails( onOneFingerPanStart!(DragStartDetails(
localPosition: d.localFocalPoint, globalPosition: d.focalPoint)); localPosition: d.localFocalPoint, globalPosition: d.focalPoint));
} }
}; }
if (_currentState != GestureState.none) { if (_currentState != GestureState.none) {
_debounceTimer = Timer(Duration(milliseconds: 200), () { _debounceTimer = Timer(Duration(milliseconds: 200), () {
start(d); start(d);
@@ -132,13 +133,14 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
} }
void onTwoFingerStartDebounce(ScaleUpdateDetails d) { void onTwoFingerStartDebounce(ScaleUpdateDetails d) {
final start = (ScaleUpdateDetails d) { start(ScaleUpdateDetails d) {
_currentState = GestureState.twoFingerScale; _currentState = GestureState.twoFingerScale;
if (onTwoFingerScaleStart != null) { if (onTwoFingerScaleStart != null) {
onTwoFingerScaleStart!(ScaleStartDetails( onTwoFingerScaleStart!(ScaleStartDetails(
localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint)); localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint));
} }
}; }
if (_currentState == GestureState.threeFingerVerticalDrag) { if (_currentState == GestureState.threeFingerVerticalDrag) {
_debounceTimer = Timer(Duration(milliseconds: 200), () { _debounceTimer = Timer(Duration(milliseconds: 200), () {
start(d); start(d);
@@ -182,6 +184,8 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
_TapTracker? _firstTap; _TapTracker? _firstTap;
_TapTracker? _secondTap; _TapTracker? _secondTap;
PointerDownEvent? _lastPointerDownEvent;
final Map<int, _TapTracker> _trackers = <int, _TapTracker>{}; final Map<int, _TapTracker> _trackers = <int, _TapTracker>{};
@override @override
@@ -236,6 +240,7 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
gestureSettings: gestureSettings, gestureSettings: gestureSettings,
); );
_trackers[event.pointer] = tracker; _trackers[event.pointer] = tracker;
_lastPointerDownEvent = event;
tracker.startTrackingPointer(_handleEvent, event.transform); tracker.startTrackingPointer(_handleEvent, event.transform);
} }
@@ -246,7 +251,11 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
_registerFirstTap(tracker); _registerFirstTap(tracker);
} else if (_secondTap != null) { } else if (_secondTap != null) {
if (event.pointer == _secondTap!.pointer) { if (event.pointer == _secondTap!.pointer) {
if (onHoldDragEnd != null) onHoldDragEnd!(DragEndDetails()); if (onHoldDragEnd != null) {
onHoldDragEnd!(DragEndDetails());
_secondTap = null;
_isStart = false;
}
} }
} else { } else {
_reject(tracker); _reject(tracker);
@@ -266,11 +275,12 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
if (!_isStart) { if (!_isStart) {
_resolve(); _resolve();
} }
if (onHoldDragUpdate != null) if (onHoldDragUpdate != null) {
onHoldDragUpdate!(DragUpdateDetails( onHoldDragUpdate!(DragUpdateDetails(
globalPosition: event.position, globalPosition: event.position,
localPosition: event.localPosition, localPosition: event.localPosition,
delta: event.delta)); delta: event.delta));
}
} }
} }
} else if (event is PointerCancelEvent) { } else if (event is PointerCancelEvent) {
@@ -300,7 +310,11 @@ class HoldTapMoveGestureRecognizer extends GestureRecognizer {
_secondTap?.entry.resolve(GestureDisposition.accepted); _secondTap?.entry.resolve(GestureDisposition.accepted);
_isStart = true; _isStart = true;
// TODO start details // TODO start details
if (onHoldDragStart != null) onHoldDragStart!(DragStartDetails()); if (onHoldDragStart != null) {
onHoldDragStart!(DragStartDetails(
kind: _lastPointerDownEvent?.kind,
));
}
} }
void _reject(_TapTracker tracker) { void _reject(_TapTracker tracker) {
@@ -432,6 +446,8 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
Timer? _firstTapTimer; Timer? _firstTapTimer;
_TapTracker? _firstTap; _TapTracker? _firstTap;
PointerDownEvent? _lastPointerDownEvent;
var _isStart = false; var _isStart = false;
final Set<int> _upTap = {}; final Set<int> _upTap = {};
@@ -473,6 +489,7 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
} else { } else {
// first tap // first tap
_isStart = true; _isStart = true;
_lastPointerDownEvent = event;
_startFirstTapDownTimer(); _startFirstTapDownTimer();
} }
_trackTap(event); _trackTap(event);
@@ -498,8 +515,9 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
debugPrint("PointerUpEvent"); debugPrint("PointerUpEvent");
_upTap.add(tracker.pointer); _upTap.add(tracker.pointer);
} else if (event is PointerMoveEvent) { } else if (event is PointerMoveEvent) {
if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop)) if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop)) {
_reject(tracker); _reject(tracker);
}
} else if (event is PointerCancelEvent) { } else if (event is PointerCancelEvent) {
_reject(tracker); _reject(tracker);
} }
@@ -587,7 +605,11 @@ class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
void _resolve() { void _resolve() {
// TODO tap down details // TODO tap down details
if (onDoubleFinerTap != null) onDoubleFinerTap!(TapDownDetails()); if (onDoubleFinerTap != null) {
onDoubleFinerTap!(TapDownDetails(
kind: _lastPointerDownEvent?.kind,
));
}
_trackers.forEach((key, value) { _trackers.forEach((key, value) {
value.entry.resolve(GestureDisposition.accepted); value.entry.resolve(GestureDisposition.accepted);
}); });

View File

@@ -12,25 +12,42 @@ import 'package:url_launcher/url_launcher.dart';
import '../../common.dart'; import '../../common.dart';
import './dialog.dart'; import './dialog.dart';
const kOpSvgList = [
'github',
'google',
'apple',
'okta',
'facebook',
'azure',
'auth0'
];
class _IconOP extends StatelessWidget { class _IconOP extends StatelessWidget {
final String icon; final String op;
final double iconWidth; final String? icon;
final EdgeInsets margin; final EdgeInsets margin;
const _IconOP( const _IconOP(
{Key? key, {Key? key,
required this.op,
required this.icon, required this.icon,
required this.iconWidth,
this.margin = const EdgeInsets.symmetric(horizontal: 4.0)}) this.margin = const EdgeInsets.symmetric(horizontal: 4.0)})
: super(key: key); : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final svgFile =
kOpSvgList.contains(op.toLowerCase()) ? op.toLowerCase() : 'default';
return Container( return Container(
margin: margin, margin: margin,
child: SvgPicture.asset( child: icon == null
'assets/$icon.svg', ? SvgPicture.asset(
width: iconWidth, 'assets/auth-$svgFile.svg',
), width: 20,
)
: SvgPicture.string(
icon!,
width: 20,
),
); );
} }
} }
@@ -38,7 +55,7 @@ class _IconOP extends StatelessWidget {
class ButtonOP extends StatelessWidget { class ButtonOP extends StatelessWidget {
final String op; final String op;
final RxString curOP; final RxString curOP;
final double iconWidth; final String? icon;
final Color primaryColor; final Color primaryColor;
final double height; final double height;
final Function() onTap; final Function() onTap;
@@ -47,7 +64,7 @@ class ButtonOP extends StatelessWidget {
Key? key, Key? key,
required this.op, required this.op,
required this.curOP, required this.curOP,
required this.iconWidth, required this.icon,
required this.primaryColor, required this.primaryColor,
required this.height, required this.height,
required this.onTap, required this.onTap,
@@ -61,7 +78,7 @@ class ButtonOP extends StatelessWidget {
width: 200, width: 200,
child: Obx(() => ElevatedButton( child: Obx(() => ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
primary: curOP.value.isEmpty || curOP.value == op backgroundColor: curOP.value.isEmpty || curOP.value == op
? primaryColor ? primaryColor
: Colors.grey, : Colors.grey,
).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)), ).copyWith(elevation: ButtonStyleButton.allOrNull(0.0)),
@@ -69,17 +86,21 @@ class ButtonOP extends StatelessWidget {
child: Row( child: Row(
children: [ children: [
SizedBox( SizedBox(
width: 30, width: 30,
child: _IconOP( child: _IconOP(
icon: op, op: op,
iconWidth: iconWidth, icon: icon,
margin: EdgeInsets.only(right: 5), margin: EdgeInsets.only(right: 5),
)), ),
),
Expanded( Expanded(
child: FittedBox( child: FittedBox(
fit: BoxFit.scaleDown, fit: BoxFit.scaleDown,
child: Center( child: Center(
child: Text('${translate("Continue with")} $op')))), child: Text(
'${translate("Continue with")} ${op.toLowerCase() == "github" ? "GitHub" : toCapitalized(op)}')),
),
),
], ],
))), ))),
), ),
@@ -89,8 +110,8 @@ class ButtonOP extends StatelessWidget {
class ConfigOP { class ConfigOP {
final String op; final String op;
final double iconWidth; final String? icon;
ConfigOP({required this.op, required this.iconWidth}); ConfigOP({required this.op, required this.icon});
} }
class WidgetOP extends StatefulWidget { class WidgetOP extends StatefulWidget {
@@ -182,7 +203,7 @@ class _WidgetOPState extends State<WidgetOP> {
ButtonOP( ButtonOP(
op: widget.config.op, op: widget.config.op,
curOP: widget.curOP, curOP: widget.curOP,
iconWidth: widget.config.iconWidth, icon: widget.config.icon,
primaryColor: str2color(widget.config.op, 0x7f), primaryColor: str2color(widget.config.op, 0x7f),
height: 36, height: 36,
onTap: () async { onTap: () async {
@@ -333,9 +354,8 @@ class LoginWidgetUserPass extends StatelessWidget {
autoFocus: false, autoFocus: false,
errorText: passMsg, errorText: passMsg,
), ),
Offstage( // NOT use Offstage to wrap LinearProgressIndicator
offstage: !isInProgress, if (isInProgress) const LinearProgressIndicator(),
child: const LinearProgressIndicator()),
const SizedBox(height: 12.0), const SizedBox(height: 12.0),
FittedBox( FittedBox(
child: child:
@@ -380,7 +400,7 @@ Future<bool?> loginDialog() async {
final loginOptions = [].obs; final loginOptions = [].obs;
Future.delayed(Duration.zero, () async { Future.delayed(Duration.zero, () async {
loginOptions.value = await UserModel.queryLoginOptions(); loginOptions.value = await UserModel.queryOidcLoginOptions();
}); });
final res = await gFFI.dialogManager.show<bool>((setState, close, context) { final res = await gFFI.dialogManager.show<bool>((setState, close, context) {
@@ -460,12 +480,8 @@ Future<bool?> loginDialog() async {
} }
thirdAuthWidget() => Obx(() { thirdAuthWidget() => Obx(() {
final oidcOptions = loginOptions
.where((opt) => opt.startsWith(kAuthReqTypeOidc))
.map((opt) => opt.substring(kAuthReqTypeOidc.length))
.toList();
return Offstage( return Offstage(
offstage: oidcOptions.isEmpty, offstage: loginOptions.isEmpty,
child: Column( child: Column(
children: [ children: [
const SizedBox( const SizedBox(
@@ -480,12 +496,8 @@ Future<bool?> loginDialog() async {
height: 8.0, height: 8.0,
), ),
LoginWidgetOP( LoginWidgetOP(
ops: [ ops: loginOptions
ConfigOP(op: 'GitHub', iconWidth: 20), .map((e) => ConfigOP(op: e['name'], icon: e['icon']))
ConfigOP(op: 'Google', iconWidth: 20),
ConfigOP(op: 'Okta', iconWidth: 38),
]
.where((op) => oidcOptions.contains(op.op.toLowerCase()))
.toList(), .toList(),
curOP: curOP, curOP: curOP,
cbLogin: (Map<String, dynamic> authBody) { cbLogin: (Map<String, dynamic> authBody) {
@@ -660,9 +672,8 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
}, },
), ),
*/ */
Offstage( // NOT use Offstage to wrap LinearProgressIndicator
offstage: !isInProgress, if (isInProgress) const LinearProgressIndicator(),
child: const LinearProgressIndicator()),
], ],
), ),
onCancel: close, onCancel: close,
@@ -675,3 +686,22 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
return res; return res;
} }
void logOutConfirmDialog() {
gFFI.dialogManager.show((setState, close, context) {
submit() {
close();
gFFI.userModel.logOut();
}
return CustomAlertDialog(
content: Text(translate("logout_tip")),
actions: [
dialogButton(translate("Cancel"), onPressed: close, isOutline: true),
dialogButton(translate("OK"), onPressed: submit),
],
onSubmit: submit,
onCancel: close,
);
});
}

Some files were not shown because too many files have changed in this diff Show More