From 0c004861c54ec6f5b5a40d1210b5f3d8b34fdbde Mon Sep 17 00:00:00 2001 From: 2mao Date: Tue, 3 Jan 2023 17:39:06 +0800 Subject: [PATCH 001/366] Create history.yml --- .github/workflows/history.yml | 103 ++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 .github/workflows/history.yml diff --git a/.github/workflows/history.yml b/.github/workflows/history.yml new file mode 100644 index 000000000..9b7e80fd6 --- /dev/null +++ b/.github/workflows/history.yml @@ -0,0 +1,103 @@ +name: Flutter Windows History Build + +on: [workflow_dispatch] + +env: + LLVM_VERSION: "10.0" + # Note: currently 3.0.5 does not support arm64 officially, we use latest stable version first. + FLUTTER_VERSION: "3.0.5" + TAG_NAME: "tmp" + # vcpkg version: 2022.05.10 + # for multiarch gcc compatibility + VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44" + VERSION: "1.2.0" + +jobs: + build-for-windows: + 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: 'master@{2022-12-05 00:00:00}' + + - 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-05-${{ 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 From dbb3d7e4e918fc659fef27a0a7735147248c4c5e Mon Sep 17 00:00:00 2001 From: 2mao Date: Tue, 3 Jan 2023 17:44:09 +0800 Subject: [PATCH 002/366] Update history.yml --- .github/workflows/history.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/history.yml b/.github/workflows/history.yml index 9b7e80fd6..bc2c99b60 100644 --- a/.github/workflows/history.yml +++ b/.github/workflows/history.yml @@ -27,7 +27,7 @@ jobs: - name: Checkout source code uses: actions/checkout@v3 with: - ref: 'master@{2022-12-05 00:00:00}' + ref: '8d1254cf14b69f545c9cefa026c5eeb0e7dd3e7c' - name: Install LLVM and Clang uses: KyleMayes/install-llvm-action@v1 From 8c11a363a2875c59b6bd55fcb882559ab2084d1a Mon Sep 17 00:00:00 2001 From: 2mao Date: Tue, 3 Jan 2023 17:54:50 +0800 Subject: [PATCH 003/366] Update history.yml --- .github/workflows/history.yml | 269 +++++++++++++++++++++++++++++++++- 1 file changed, 268 insertions(+), 1 deletion(-) diff --git a/.github/workflows/history.yml b/.github/workflows/history.yml index bc2c99b60..b1ca74721 100644 --- a/.github/workflows/history.yml +++ b/.github/workflows/history.yml @@ -13,7 +13,7 @@ env: VERSION: "1.2.0" jobs: - build-for-windows: + build-for-windows-2022-12-05: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) runs-on: ${{ matrix.job.os }} strategy: @@ -101,3 +101,270 @@ jobs: 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 + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./SignOutput/rustdesk-*.exe From c19e46acddaa7bfa1a8b7410c26b245abacb9861 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 9 Mar 2023 18:28:27 +0800 Subject: [PATCH 004/366] add trackpad speed factor Signed-off-by: fufesou --- flutter/lib/models/input_model.dart | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 9366d5b46..38402216d 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -45,7 +45,10 @@ class InputModel { var command = false; // trackpad + final _trackpadSpeed = 0.01; var trackpadScrollDistance = Offset.zero; + var _trackpadScrollUnsent = Offset.zero; + var _newScroll = true; Timer? _flingTimer; // mouse @@ -331,10 +334,22 @@ class InputModel { void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) { var delta = e.panDelta; trackpadScrollDistance += delta; - bind.sessionSendMouse( - id: id, - msg: - '{"type": "trackpad", "x": "${delta.dx.toInt()}", "y": "${delta.dy.toInt()}"}'); + _trackpadScrollUnsent += (delta * _trackpadSpeed); + final x = _trackpadScrollUnsent.dx.truncate(); + final y = _trackpadScrollUnsent.dy.truncate(); + _trackpadScrollUnsent -= Offset(_trackpadScrollUnsent.dx - x.toDouble(), + _trackpadScrollUnsent.dy - y.toDouble()); + + var sendMsg = x != 0 || y != 0; + if (_newScroll) { + sendMsg = true; + _newScroll = false; + } + + if (sendMsg) { + bind.sessionSendMouse( + id: id, msg: '{"type": "trackpad", "x": "$x", "y": "$y"}'); + } } // Simple simulation for fling. @@ -358,6 +373,8 @@ class InputModel { } void onPointerPanZoomEnd(PointerPanZoomEndEvent e) { + _newScroll = true; + _trackpadScrollUnsent = Offset.zero; var x = _signOrZero(trackpadScrollDistance.dx); var y = _signOrZero(trackpadScrollDistance.dy); var dx = trackpadScrollDistance.dx.abs() ~/ 40; From 28f41d6c938a9d228e12c89f92f21ecaa7af51b0 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 20 Mar 2023 18:42:03 +0800 Subject: [PATCH 005/366] maybe better touchpad control Signed-off-by: fufesou --- flutter/lib/models/input_model.dart | 101 +++++++++++++++++++++------- 1 file changed, 77 insertions(+), 24 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 38402216d..310a3cadb 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -45,11 +45,12 @@ class InputModel { var command = false; // trackpad - final _trackpadSpeed = 0.01; - var trackpadScrollDistance = Offset.zero; + final _trackpadSpeed = 0.02; + var _trackpadLastDelta = Offset.zero; var _trackpadScrollUnsent = Offset.zero; - var _newScroll = true; + var _stopFling = true; Timer? _flingTimer; + final _flingBaseDelay = 10; // mouse final isPhysicalMouse = false.obs; @@ -310,6 +311,7 @@ class InputModel { } void onPointHoverImage(PointerHoverEvent e) { + _stopFling = true; if (e.kind != ui.PointerDeviceKind.mouse) return; if (!isPhysicalMouse.value) { isPhysicalMouse.value = true; @@ -327,29 +329,33 @@ class InputModel { } } - void onPointerPanZoomStart(PointerPanZoomStartEvent e) {} + void onPointerPanZoomStart(PointerPanZoomStartEvent e) { + _stopFling = true; + } // https://docs.flutter.dev/release/breaking-changes/trackpad-gestures // TODO(support zoom in/out) void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) { var delta = e.panDelta; - trackpadScrollDistance += delta; + _trackpadLastDelta = delta; _trackpadScrollUnsent += (delta * _trackpadSpeed); - final x = _trackpadScrollUnsent.dx.truncate(); - final y = _trackpadScrollUnsent.dy.truncate(); + var x = _trackpadScrollUnsent.dx.truncate(); + var y = _trackpadScrollUnsent.dy.truncate(); _trackpadScrollUnsent -= Offset(_trackpadScrollUnsent.dx - x.toDouble(), _trackpadScrollUnsent.dy - y.toDouble()); - var sendMsg = x != 0 || y != 0; - if (_newScroll) { - sendMsg = true; - _newScroll = false; + if (x == 0 && y == 0) { + x = delta.dx > 1 ? 1 : (delta.dx < -1 ? -1 : 0); + y = delta.dy > 1 ? 1 : (delta.dy < -1 ? -1 : 0); + if (x.abs() > y.abs()) { + y = 0; + } else { + x = 0; + } } - if (sendMsg) { - bind.sessionSendMouse( - id: id, msg: '{"type": "trackpad", "x": "$x", "y": "$y"}'); - } + bind.sessionSendMouse( + id: id, msg: '{"type": "trackpad", "x": "$x", "y": "$y"}'); } // Simple simulation for fling. @@ -372,20 +378,67 @@ class InputModel { }); } - void onPointerPanZoomEnd(PointerPanZoomEndEvent e) { - _newScroll = true; - _trackpadScrollUnsent = Offset.zero; - var x = _signOrZero(trackpadScrollDistance.dx); - var y = _signOrZero(trackpadScrollDistance.dy); - var dx = trackpadScrollDistance.dx.abs() ~/ 40; - var dy = trackpadScrollDistance.dy.abs() ~/ 40; - _scheduleFling(x, y, dx, dy); + void _scheduleFling2(double x, double y, int delay) { + if ((x ==0 && y == 0) || _stopFling) { + return; + } - trackpadScrollDistance = Offset.zero; + _flingTimer = Timer(Duration(milliseconds: delay), () { + if (_stopFling) { + return; + } + + final d = 0.95; + x *= d; + y *= d; + final dx0 = x * _trackpadSpeed * 2; + final dy0 = y * _trackpadSpeed * 2; + + // Try set delta (x,y) and delay. + var dx = dx0.toInt(); + var dy = dy0.toInt(); + var delay = _flingBaseDelay; + + // Try set min delta (x,y), and increase delay. + if (dx == 0 && dy == 0) { + final thr = 25; + var vx = thr; + var vy = thr; + if (dx0 != 0) { + vx = 1.0 ~/ dx0.abs(); + } + if (dy0 != 0) { + vy = 1.0 ~/ dy0.abs(); + } + if (vx < vy && vx < thr) { + delay *= vx; + dx = dx0 > 0 ? 1 : (dx0 < 0 ? -1 : 0); + } else if (vy < thr) { + delay *= vy; + dy = dy0 > 0 ? 1 : (dy0 < 0 ? -1 : 0); + } + } + + if (dx == 0 && dy == 0) { + return; + } + + bind.sessionSendMouse( + id: id, msg: '{"type": "trackpad", "x": "$dx", "y": "$dy"}'); + _scheduleFling2(x, y, delay); + }); + } + + void onPointerPanZoomEnd(PointerPanZoomEndEvent e) { + _stopFling = false; + _trackpadScrollUnsent = Offset.zero; + _scheduleFling2(_trackpadLastDelta.dx, _trackpadLastDelta.dy, _flingBaseDelay); + _trackpadLastDelta = Offset.zero; } void onPointDownImage(PointerDownEvent e) { debugPrint("onPointDownImage"); + _stopFling = true; if (e.kind != ui.PointerDeviceKind.mouse) { if (isPhysicalMouse.value) { isPhysicalMouse.value = false; From be0b54dd7dca8364286f55a246029329b085a017 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Mon, 20 Mar 2023 23:10:55 +0100 Subject: [PATCH 006/366] Update de.rs --- src/lang/de.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 58ea77d6d..876c6dd46 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -278,13 +278,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Input Control", "Eingabesteuerung"), ("Audio Capture", "Audioaufnahme"), ("File Connection", "Dateiverbindung"), - ("Screen Connection", "Bildschirmanschluss"), + ("Screen Connection", "Bildschirmverbindung"), ("Do you accept?", "Verbindung zulassen?"), ("Open System Setting", "Systemeinstellung öffnen"), ("How to get Android input permission?", "Wie erhalte ich eine Android-Eingabeberechtigung?"), ("android_input_permission_tip1", "Damit ein entferntes Gerät Ihr Android-Gerät steuern kann, müssen Sie RustDesk erlauben, den Dienst \"Barrierefreiheit\" zu verwenden."), ("android_input_permission_tip2", "Bitte gehen Sie zur nächsten Systemeinstellungsseite, suchen Sie \"Installierte Dienste\" und schalten Sie den Dienst \"RustDesk Input\" ein."), - ("android_new_connection_tip", "möchte ihr Gerät steuern."), + ("android_new_connection_tip", "möchte Ihr Gerät steuern."), ("android_service_will_start_tip", "Durch das Aktivieren der Bildschirmfreigabe wird der Dienst automatisch gestartet, sodass andere Geräte dieses Android-Gerät steuern können."), ("android_stop_service_tip", "Durch das Deaktivieren des Dienstes werden automatisch alle hergestellten Verbindungen getrennt."), ("android_version_audio_tip", "Ihre Android-Version unterstützt keine Audioaufnahme, bitte aktualisieren Sie auf Android 10 oder höher, falls möglich."), @@ -428,7 +428,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ask the remote user for authentication", "Den entfernten Benutzer zur Authentifizierung auffordern"), ("Choose this if the remote account is administrator", "Wählen Sie dies, wenn das entfernte Konto Administrator ist."), ("Transmit the username and password of administrator", "Übermitteln Sie den Benutzernamen und das Passwort des Administrators"), - ("still_click_uac_tip", "Der entfernte Benutzer muss immer noch im UAC-Fenster von RustDesk auf OK klicken."), + ("still_click_uac_tip", "Der entfernte Benutzer muss immer noch im UAC-Fenster von RustDesk auf \"Ja\" klicken."), ("Request Elevation", "Erhöhte Rechte anfordern"), ("wait_accept_uac_tip", "Bitte warten Sie, bis der entfernte Benutzer den UAC-Dialog akzeptiert hat."), ("Elevate successfully", "Erhöhung der Rechte erfolgreich"), @@ -477,8 +477,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Username", "Leerer Benutzername"), ("Empty Password", "Leeres Passwort"), ("Me", "Ich"), - ("identical_file_tip", ""), - ("show_monitors_tip", ""), - ("View Mode", ""), + ("identical_file_tip", "Diese Datei ist identisch mit der Datei der Gegenstelle."), + ("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"), + ("View Mode", "Ansichtsmodus"), ].iter().cloned().collect(); } From 1d122b5a73e8946053ee689f598b448d192919f6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 21 Mar 2023 11:27:30 +0800 Subject: [PATCH 007/366] platform additions Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_tab_page.dart | 4 +++- flutter/lib/desktop/widgets/remote_toolbar.dart | 3 +++ flutter/lib/mobile/pages/remote_page.dart | 2 +- flutter/lib/models/model.dart | 8 ++++++++ libs/hbb_common/protos/message.proto | 2 ++ src/flutter.rs | 1 + src/server/connection.rs | 10 ++++++++++ 7 files changed, 28 insertions(+), 2 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index f7bb85a37..53774e36d 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -259,7 +259,9 @@ class _ConnectionTabPageState extends State { ), ]); - if (!ffi.canvasModel.cursorEmbedded && !ffi.ffiModel.viewOnly) { + if (!ffi.canvasModel.cursorEmbedded && + !ffi.ffiModel.viewOnly && + !pi.is_wayland) { menu.add(MenuEntryDivider()); menu.add(RemoteMenuEntry.showRemoteCursor( key, diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 30dc09a6e..d85fe779e 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1684,6 +1684,9 @@ class _KeyboardMenu extends StatelessWidget { for (KeyboardModeMenu mode in modes) { if (bind.sessionIsKeyboardModeSupported(id: id, mode: mode.key)) { + if (pi.is_wayland && mode.key != _kKeyMapMode) { + continue; + } if (mode.key == _kKeyTranslateMode) { if (Platform.isLinux) { continue; diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 951d63faf..4d94dc669 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -1059,7 +1059,7 @@ void showOptions( final toggles = [ getToggle(id, setState, 'show-quality-monitor', 'Show quality monitor'), ]; - if (!gFFI.canvasModel.cursorEmbedded) { + if (!gFFI.canvasModel.cursorEmbedded && !pi.is_wayland) { toggles.insert(0, getToggle(id, setState, 'show-remote-cursor', 'Show remote cursor')); } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 1cc059ce0..5c09b1cdb 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -452,6 +452,11 @@ class FfiModel with ChangeNotifier { setViewOnly(peerId, bind.sessionGetToggleOptionSync(id: peerId, arg: 'view-only')); } + if (connType == ConnType.defaultConn) { + try { + _pi.platform_additions = json.decode(evt['platform_additions']); + } catch (e) {} + } notifyListeners(); } @@ -1687,6 +1692,9 @@ class PeerInfo { List displays = []; Features features = Features(); List resolutions = []; + Map platform_additions = {}; + + bool get is_wayland => platform_additions['is_wayland'] == true; } const canvasKey = 'canvas'; diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index ed704f91e..bb4492b74 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -91,6 +91,8 @@ message PeerInfo { Features features = 9; SupportedEncoding encoding = 10; SupportedResolutions resolutions = 11; + // Use JSON's key-value format which is friendly for peer to handle. + string platform_additions = 12; } message LoginResponse { diff --git a/src/flutter.rs b/src/flutter.rs index 089daff5c..d2498bc7a 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -509,6 +509,7 @@ impl InvokeUiSession for FlutterHandler { ("features", &features), ("current_display", &pi.current_display.to_string()), ("resolutions", &resolutions), + ("platform_additions": &pi.platform_additions), ], ); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 5e6fbe50e..85ebc9926 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -843,6 +843,16 @@ impl Connection { pi.hostname = DEVICE_NAME.lock().unwrap().clone(); pi.platform = "Android".into(); } + + #[cfg(target_os = "linux")] + { + pi.platform_additions = format!(r#" + {{ + "is_wayland": {}, + }} + "#, crate::platform::current_is_wayland()); + } + #[cfg(feature = "hwcodec")] { let (h264, h265) = scrap::codec::Encoder::supported_encoding(); From bea1eacf72241b620e0f874bc88bc27c61452246 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 21 Mar 2023 12:25:47 +0800 Subject: [PATCH 008/366] debug done Signed-off-by: fufesou --- flutter/lib/desktop/widgets/remote_toolbar.dart | 3 ++- flutter/lib/models/model.dart | 11 ++++++++--- src/flutter.rs | 2 +- src/server/connection.rs | 2 +- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index d85fe779e..ec039b67c 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1464,7 +1464,8 @@ class _DisplayMenuState extends State<_DisplayMenu> { return Offstage(); } final ffiModel = widget.ffi.ffiModel; - final visible = !widget.ffi.canvasModel.cursorEmbedded; + final visible = + !widget.ffi.canvasModel.cursorEmbedded && !ffiModel.pi.is_wayland; if (!visible) return Offstage(); final enabled = !ffiModel.viewOnly; final state = ShowRemoteCursorState.find(widget.id); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 5c09b1cdb..b8d4f2ebc 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -453,9 +453,14 @@ class FfiModel with ChangeNotifier { bind.sessionGetToggleOptionSync(id: peerId, arg: 'view-only')); } if (connType == ConnType.defaultConn) { - try { - _pi.platform_additions = json.decode(evt['platform_additions']); - } catch (e) {} + final platform_additions = evt['platform_additions']; + if (platform_additions != null && platform_additions != '') { + try { + _pi.platform_additions = json.decode(platform_additions); + } catch (e) { + debugPrint('Failed to decode platform_additions $e'); + } + } } notifyListeners(); } diff --git a/src/flutter.rs b/src/flutter.rs index d2498bc7a..fde9ce7cd 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -509,7 +509,7 @@ impl InvokeUiSession for FlutterHandler { ("features", &features), ("current_display", &pi.current_display.to_string()), ("resolutions", &resolutions), - ("platform_additions": &pi.platform_additions), + ("platform_additions", &pi.platform_additions), ], ); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 85ebc9926..07c37b12c 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -848,7 +848,7 @@ impl Connection { { pi.platform_additions = format!(r#" {{ - "is_wayland": {}, + "is_wayland": {} }} "#, crate::platform::current_is_wayland()); } From 172221192f675102bb2ad67b1cc576ed45e570bd Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 21 Mar 2023 13:21:00 +0800 Subject: [PATCH 009/366] better peer info Signed-off-by: fufesou --- src/server/connection.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 07c37b12c..4b65013a3 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -843,14 +843,15 @@ impl Connection { pi.hostname = DEVICE_NAME.lock().unwrap().clone(); pi.platform = "Android".into(); } - + let mut platform_additions = serde_json::Map::new(); #[cfg(target_os = "linux")] { - pi.platform_additions = format!(r#" - {{ - "is_wayland": {} - }} - "#, crate::platform::current_is_wayland()); + if crate::platform::current_is_wayland() { + platform_additions.insert("is_wayland".into(), json!(true)); + } + } + if !platform_additions.is_empty() { + pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into()); } #[cfg(feature = "hwcodec")] From 904f75bbba13315babbc389518c22c8c5f6914ac Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 21 Mar 2023 21:30:10 +0800 Subject: [PATCH 010/366] prototype --- vdi/host/.cargo/config.toml | 2 + vdi/host/.devcontainer/Dockerfile | 16 -- vdi/host/.devcontainer/devcontainer.json | 28 -- vdi/host/Cargo.lock | 155 +++++++++-- vdi/host/Cargo.toml | 7 +- vdi/host/README.md | 4 + vdi/host/src/lib.rs | 1 + vdi/host/src/main.rs | 4 + vdi/host/src/server.rs | 335 +++++++++++++++++++++++ 9 files changed, 480 insertions(+), 72 deletions(-) create mode 100644 vdi/host/.cargo/config.toml delete mode 100644 vdi/host/.devcontainer/Dockerfile delete mode 100644 vdi/host/.devcontainer/devcontainer.json create mode 100644 vdi/host/src/lib.rs create mode 100644 vdi/host/src/server.rs diff --git a/vdi/host/.cargo/config.toml b/vdi/host/.cargo/config.toml new file mode 100644 index 000000000..70f9eaeb2 --- /dev/null +++ b/vdi/host/.cargo/config.toml @@ -0,0 +1,2 @@ +[registries.crates-io] +protocol = "sparse" diff --git a/vdi/host/.devcontainer/Dockerfile b/vdi/host/.devcontainer/Dockerfile deleted file mode 100644 index f02042771..000000000 --- a/vdi/host/.devcontainer/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM rockylinux:9.1 -ENV HOME=/home/vscode -ENV WORKDIR=$HOME/rustdesk/vdi/host - -# https://ciq.co/blog/top-10-things-to-do-after-rocky-linux-9-install/ also gpu driver install -WORKDIR $HOME -RUN dnf -y install epel-release -RUN dnf config-manager --set-enabled crb -RUN dnf -y install cargo libvpx-devel opus-devel usbredir-devel git cmake gcc-c++ pkg-config nasm yasm ninja-build automake libtool libva-devel libvdpau-devel llvm-devel -WORKDIR / - -RUN git clone https://chromium.googlesource.com/libyuv/libyuv -WORKDIR /libyuv -RUN cmake . -DCMAKE_INSTALL_PREFIX=/user -RUN make -j4 && make install -WORKDIR / \ No newline at end of file diff --git a/vdi/host/.devcontainer/devcontainer.json b/vdi/host/.devcontainer/devcontainer.json deleted file mode 100644 index f0016b5b1..000000000 --- a/vdi/host/.devcontainer/devcontainer.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "rustdesk", - "build": { - "dockerfile": "./Dockerfile", - "context": "." - }, - "workspaceMount": "source=${localWorkspaceFolder}/../..,target=/home/vscode/rustdesk,type=bind,consistency=cache", - "workspaceFolder": "/home/vscode/rustdesk/vdi/host", - "customizations": { - "vscode": { - "extensions": [ - "vadimcn.vscode-lldb", - "mutantdino.resourcemonitor", - "rust-lang.rust-analyzer", - "tamasfe.even-better-toml", - "serayuzgur.crates", - "mhutchie.git-graph", - "formulahendry.terminal", - "eamodio.gitlens" - ], - "settings": { - "files.watcherExclude": { - "**/target/**": true - } - } - } - } -} \ No newline at end of file diff --git a/vdi/host/Cargo.lock b/vdi/host/Cargo.lock index 7b7cf26bd..52783ceef 100644 --- a/vdi/host/Cargo.lock +++ b/vdi/host/Cargo.lock @@ -157,17 +157,6 @@ dependencies = [ "syn", ] -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - [[package]] name = "autocfg" version = "1.1.0" @@ -316,6 +305,16 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.14" @@ -475,17 +474,38 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.9.3" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" dependencies = [ - "atty", "humantime", + "is-terminal", "log", "regex", "termcolor", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "event-listener" version = "2.5.3" @@ -513,6 +533,24 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "flexi_logger" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eae57842a8221ef13f1f207632d786a175dd13bd8fbdc8be9d852f7c9cf1046" +dependencies = [ + "chrono", + "crossbeam-channel", + "crossbeam-queue", + "glob", + "is-terminal", + "lazy_static", + "log", + "nu-ansi-term", + "regex", + "thiserror", +] + [[package]] name = "futures" version = "0.3.26" @@ -634,6 +672,12 @@ version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "hashbrown" version = "0.12.3" @@ -656,6 +700,7 @@ dependencies = [ "dirs-next", "env_logger", "filetime", + "flexi_logger", "futures", "futures-util", "lazy_static", @@ -680,15 +725,6 @@ dependencies = [ "zstd", ] -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - [[package]] name = "hermit-abi" version = "0.2.6" @@ -698,6 +734,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -753,6 +795,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "itoa" version = "1.0.5" @@ -822,6 +887,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -941,13 +1012,23 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.3.7" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1013,6 +1094,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking" version = "2.0.0" @@ -1328,6 +1415,20 @@ version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +[[package]] +name = "rustix" +version = "0.36.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + [[package]] name = "ryu" version = "1.0.12" @@ -1515,9 +1616,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.24.7" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54cb4ebf3d49308b99e6e9dc95e989e2fdbdc210e4f67c39db0bb89ba927001c" +checksum = "f69e0d827cce279e61c2f3399eb789271a8f136d8245edef70f06e3c9601a670" dependencies = [ "cfg-if", "core-foundation-sys", diff --git a/vdi/host/Cargo.toml b/vdi/host/Cargo.toml index 6a67813a2..a1fd1e68e 100644 --- a/vdi/host/Cargo.toml +++ b/vdi/host/Cargo.toml @@ -5,5 +5,10 @@ authors = ["rustdesk "] edition = "2021" [dependencies] -qemu-display = { git = "https://gitlab.com/marcandre.lureau/qemu-display" } +qemu-display = { git = "https://github.com/rustdesk/qemu-display" } hbb_common = { path = "../../libs/hbb_common" } +clap = { version = "4.1", features = ["derive"] } +zbus = { version = "3.0" } +derivative = "2.2" +image = "0.24" +async-trait = "0.1" diff --git a/vdi/host/README.md b/vdi/host/README.md index 3b29a10e3..0283266bf 100644 --- a/vdi/host/README.md +++ b/vdi/host/README.md @@ -1 +1,5 @@ # RustDesk protocol on QEMU D-Bus display + +``` +sudo apt install libusbredirparser-dev libusbredirhost-dev libusb-1.0-0-dev +``` diff --git a/vdi/host/src/lib.rs b/vdi/host/src/lib.rs new file mode 100644 index 000000000..74f47ad34 --- /dev/null +++ b/vdi/host/src/lib.rs @@ -0,0 +1 @@ +pub mod server; diff --git a/vdi/host/src/main.rs b/vdi/host/src/main.rs index f79c691f0..ea32a028a 100644 --- a/vdi/host/src/main.rs +++ b/vdi/host/src/main.rs @@ -1,2 +1,6 @@ fn main() { + hbb_common::init_log(false, ""); + if let Err(err) = qemu_rustdesk::server::run() { + hbb_common::log::error!("{err}"); + } } diff --git a/vdi/host/src/server.rs b/vdi/host/src/server.rs new file mode 100644 index 000000000..0171741df --- /dev/null +++ b/vdi/host/src/server.rs @@ -0,0 +1,335 @@ +use clap::Parser; +use hbb_common::{ + anyhow::{anyhow, Context}, + log, + message_proto::*, + tokio, ResultType, +}; +use image::GenericImage; +use qemu_display::{Console, ConsoleListenerHandler, MouseButton, VMProxy}; +use std::{ + borrow::Borrow, + collections::HashSet, + error::Error, + io, + iter::FromIterator, + net::{TcpListener, TcpStream}, + sync::{mpsc, Arc, Mutex}, + thread, time, +}; + +#[derive(Parser, Debug)] +pub struct SocketAddrArgs { + /// IP address + #[clap(short, long, default_value = "0.0.0.0")] + address: std::net::IpAddr, + /// IP port number + #[clap(short, long, default_value = "21116")] + port: u16, +} + +impl From for std::net::SocketAddr { + fn from(args: SocketAddrArgs) -> Self { + (args.address, args.port).into() + } +} + +#[derive(Parser, Debug)] +struct Cli { + #[clap(flatten)] + address: SocketAddrArgs, + #[clap(short, long)] + dbus_address: Option, +} + +#[derive(Debug)] +enum Event { + ConsoleUpdate((i32, i32, i32, i32)), + Disconnected, +} + +const PIXMAN_X8R8G8B8: u32 = 0x20020888; +type BgraImage = image::ImageBuffer, Vec>; + +#[derive(derivative::Derivative)] +#[derivative(Debug)] +struct Client { + #[derivative(Debug = "ignore")] + server: Server, + share: bool, + last_update: Option, + has_update: bool, + req_update: bool, + last_buttons: HashSet, + dimensions: (u16, u16), +} + +impl Client { + fn new(server: Server, share: bool) -> Self { + Self { + server, + share, + last_update: None, + has_update: false, + req_update: false, + last_buttons: HashSet::new(), + dimensions: (0, 0), + } + } + + fn update_pending(&self) -> bool { + self.has_update && self.req_update + } + + async fn key_event(&self, qnum: u32, down: bool) -> ResultType<()> { + let inner = self.server.inner.lock().unwrap(); + if down { + inner.console.keyboard.press(qnum).await?; + } else { + inner.console.keyboard.release(qnum).await?; + } + Ok(()) + } + + fn desktop_resize(&mut self) -> ResultType<()> { + let (width, height) = self.server.dimensions(); + if (width, height) == self.dimensions { + return Ok(()); + } + self.dimensions = (width, height); + Ok(()) + } + + fn send_framebuffer_update(&mut self) -> ResultType<()> { + self.desktop_resize()?; + if self.has_update && self.req_update { + if let Some(last_update) = self.last_update { + if last_update.elapsed().as_millis() < 10 { + println!("TODO: <10ms, could delay update..") + } + } + // self.server.send_framebuffer_update(&self.vnc_server)?; + self.last_update = Some(time::Instant::now()); + self.has_update = false; + self.req_update = false; + } + Ok(()) + } + + async fn handle_event(&mut self, event: Option) -> ResultType { + match event { + Some(Event::ConsoleUpdate(_)) => { + self.has_update = true; + } + Some(Event::Disconnected) => { + return Ok(false); + } + None => { + self.send_framebuffer_update()?; + } + } + + Ok(true) + } +} + +#[derive(Debug)] +struct ConsoleListener { + server: Server, +} + +#[async_trait::async_trait] +impl ConsoleListenerHandler for ConsoleListener { + async fn scanout(&mut self, s: qemu_display::Scanout) { + let mut inner = self.server.inner.lock().unwrap(); + inner.image = image_from_vec(s.format, s.width, s.height, s.stride, s.data); + } + + async fn update(&mut self, u: qemu_display::Update) { + let mut inner = self.server.inner.lock().unwrap(); + let update = image_from_vec(u.format, u.w as _, u.h as _, u.stride, u.data); + if (u.x, u.y) == (0, 0) && update.dimensions() == inner.image.dimensions() { + inner.image = update; + } else { + inner.image.copy_from(&update, u.x as _, u.y as _).unwrap(); + } + inner + .tx + .send(Event::ConsoleUpdate((u.x, u.y, u.w, u.h))) + .unwrap(); + } + + async fn scanout_dmabuf(&mut self, _scanout: qemu_display::ScanoutDMABUF) { + unimplemented!() + } + + async fn update_dmabuf(&mut self, _update: qemu_display::UpdateDMABUF) { + unimplemented!() + } + + async fn mouse_set(&mut self, set: qemu_display::MouseSet) { + dbg!(set); + } + + async fn cursor_define(&mut self, cursor: qemu_display::Cursor) { + dbg!(cursor); + } + + fn disconnected(&mut self) { + dbg!(); + } +} + +#[derive(Debug)] +struct ServerInner { + console: Console, + image: BgraImage, + tx: mpsc::Sender, +} + +#[derive(Clone, Debug)] +struct Server { + vm_name: String, + rx: Arc>>, + inner: Arc>, +} + +impl Server { + async fn new(vm_name: String, console: Console) -> ResultType { + let width = console.width().await?; + let height = console.height().await?; + let image = BgraImage::new(width as _, height as _); + let (tx, rx) = mpsc::channel(); + Ok(Self { + vm_name, + rx: Arc::new(Mutex::new(rx)), + inner: Arc::new(Mutex::new(ServerInner { console, image, tx })), + }) + } + + fn stop_console(&self) -> ResultType<()> { + let mut inner = self.inner.lock().unwrap(); + inner.console.unregister_listener(); + Ok(()) + } + + async fn run_console(&self) -> ResultType<()> { + let inner = self.inner.lock().unwrap(); + inner + .console + .register_listener(ConsoleListener { + server: self.clone(), + }) + .await?; + Ok(()) + } + + fn dimensions(&self) -> (u16, u16) { + let inner = self.inner.lock().unwrap(); + (inner.image.width() as u16, inner.image.height() as u16) + } + + async fn handle_connection(&self, stream: TcpStream) -> ResultType<()> { + let (width, height) = self.dimensions(); + + let tx = self.inner.lock().unwrap().tx.clone(); + let _client_thread = thread::spawn(move || loop {}); + + self.run_console().await?; + let rx = self.rx.lock().unwrap(); + self.stop_console()?; + Ok(()) + } +} + +fn button_mask_to_set(mask: u8) -> HashSet { + let mut set = HashSet::new(); + if mask & 0b0000_0001 != 0 { + set.insert(MouseButton::Left); + } + if mask & 0b0000_0010 != 0 { + set.insert(MouseButton::Middle); + } + if mask & 0b0000_0100 != 0 { + set.insert(MouseButton::Right); + } + if mask & 0b0000_1000 != 0 { + set.insert(MouseButton::WheelUp); + } + if mask & 0b0001_0000 != 0 { + set.insert(MouseButton::WheelDown); + } + set +} + +fn image_from_vec(format: u32, width: u32, height: u32, stride: u32, data: Vec) -> BgraImage { + if format != PIXMAN_X8R8G8B8 { + todo!("unhandled pixman format: {}", format) + } + if cfg!(target_endian = "big") { + todo!("pixman/image in big endian") + } + let layout = image::flat::SampleLayout { + channels: 4, + channel_stride: 1, + width, + width_stride: 4, + height, + height_stride: stride as _, + }; + let samples = image::flat::FlatSamples { + samples: data, + layout, + color_hint: None, + }; + samples + .try_into_buffer::>() + .or_else::<&str, _>(|(_err, samples)| { + let view = samples.as_view::>().unwrap(); + let mut img = BgraImage::new(width, height); + img.copy_from(&view, 0, 0).unwrap(); + Ok(img) + }) + .unwrap() +} + +#[tokio::main] +pub async fn run() -> ResultType<()> { + let args = Cli::parse(); + + let listener = TcpListener::bind::(args.address.into()).unwrap(); + let dbus = if let Some(addr) = args.dbus_address { + zbus::ConnectionBuilder::address(addr.borrow())? + .build() + .await + } else { + zbus::Connection::session().await + } + .context("Failed to connect to DBus")?; + + let vm_name = VMProxy::new(&dbus).await?.name().await?; + + let console = Console::new(&dbus.into(), 0) + .await + .context("Failed to get the console")?; + let server = Server::new(format!("qemu-rustdesk ({})", vm_name), console).await?; + for stream in listener.incoming() { + match stream { + Ok(stream) => { + tokio::spawn(async { + /* + if let Err(err) = server.handle_connection(stream).await { + log::error!("Connection closed: {err}"); + } + */ + }); + } + Err(err) => { + log::error!("Failed to accept connection: {}", err); + continue; + } + } + } + + Ok(()) +} From 7ac58f8875d2ec0a36bc5f3f8f1d7427f5d135ea Mon Sep 17 00:00:00 2001 From: NicKoehler <53040044+NicKoehler@users.noreply.github.com> Date: Tue, 21 Mar 2023 16:12:28 +0100 Subject: [PATCH 011/366] Update it.rs --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 7dc3b70ef..5b3e4d92f 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -479,6 +479,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", "Io"), ("identical_file_tip", "Questo file è identico a quello del peer."), ("show_monitors_tip", "Mostra schermi nella barra degli strumenti"), - ("View Mode", ""), + ("View Mode", "Modalità di visualizzazione"), ].iter().cloned().collect(); } From c14619ef1f2aa81ca18bfa4782556b7aa143d053 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 22 Mar 2023 01:04:26 +0800 Subject: [PATCH 012/366] no concurrent connection --- vdi/host/Cargo.lock | 310 ++++++++++++++++++++++++++++++++++++++++- vdi/host/src/server.rs | 37 +++-- 2 files changed, 325 insertions(+), 22 deletions(-) diff --git a/vdi/host/Cargo.lock b/vdi/host/Cargo.lock index 52783ceef..161664625 100644 --- a/vdi/host/Cargo.lock +++ b/vdi/host/Cargo.lock @@ -178,18 +178,36 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" + [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "byteorder" version = "1.4.3" @@ -235,6 +253,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "clap" +version = "4.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098" +dependencies = [ + "bitflags 2.0.2", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -245,6 +300,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "concurrent-queue" version = "2.1.0" @@ -271,6 +332,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -324,6 +394,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "cxx" version = "1.0.91" @@ -512,6 +588,22 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "exr" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -533,6 +625,16 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "flexi_logger" version = "0.25.3" @@ -551,6 +653,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + [[package]] name = "futures" version = "0.3.26" @@ -662,8 +777,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", ] [[package]] @@ -678,6 +805,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -725,6 +861,12 @@ dependencies = [ "zstd", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.2.6" @@ -776,6 +918,25 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "image" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "scoped_threadpool", + "tiff", +] + [[package]] name = "indexmap" version = "1.9.2" @@ -833,6 +994,15 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + [[package]] name = "js-sys" version = "0.3.61" @@ -848,6 +1018,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.139" @@ -976,13 +1152,22 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + [[package]] name = "nix" version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", "cfg-if", "libc", @@ -995,7 +1180,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.6.5", @@ -1039,6 +1224,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -1083,6 +1279,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_str_bytes" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" + [[package]] name = "osascript" version = "0.3.0" @@ -1167,6 +1369,18 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "png" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "flate2", + "miniz_oxide", +] + [[package]] name = "polling" version = "2.5.2" @@ -1197,6 +1411,30 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.51" @@ -1261,7 +1499,7 @@ dependencies = [ [[package]] name = "qemu-display" version = "0.1.0" -source = "git+https://gitlab.com/marcandre.lureau/qemu-display#544a4075615702abf414cd2d63bbb6a9ca10d0ea" +source = "git+https://github.com/rustdesk/qemu-display#e8a0925c2e804aa1eb07ee3027deaf8dd1c71b1d" dependencies = [ "async-broadcast 0.3.4", "async-lock", @@ -1288,8 +1526,13 @@ dependencies = [ name = "qemu-rustdesk" version = "0.1.0" dependencies = [ + "async-trait", + "clap", + "derivative", "hbb_common", + "image", "qemu-display", + "zbus", ] [[package]] @@ -1343,9 +1586,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1359,7 +1602,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1421,7 +1664,7 @@ version = "0.36.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -1444,6 +1687,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1549,6 +1798,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "slab" version = "0.4.8" @@ -1597,12 +1852,27 @@ dependencies = [ "serde", ] +[[package]] +name = "spin" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5d6e0250b93c8427a177b849d144a96d5acc57006149479403d7861ab721e34" +dependencies = [ + "lock_api", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.109" @@ -1672,6 +1942,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tiff" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.1.45" @@ -1965,6 +2246,12 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -2220,6 +2507,15 @@ dependencies = [ "libc", ] +[[package]] +name = "zune-inflate" +version = "0.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01728b79fb9b7e28a8c11f715e1cd8dc2cda7416a007d66cac55cebb3a8ac6b" +dependencies = [ + "simd-adler32", +] + [[package]] name = "zvariant" version = "3.11.0" diff --git a/vdi/host/src/server.rs b/vdi/host/src/server.rs index 0171741df..12f4526c9 100644 --- a/vdi/host/src/server.rs +++ b/vdi/host/src/server.rs @@ -235,8 +235,26 @@ impl Server { let tx = self.inner.lock().unwrap().tx.clone(); let _client_thread = thread::spawn(move || loop {}); + let mut client = Client::new(self.clone(), true); self.run_console().await?; let rx = self.rx.lock().unwrap(); + loop { + let ev = if client.update_pending() { + match rx.try_recv() { + Ok(e) => Some(e), + Err(mpsc::TryRecvError::Empty) => None, + Err(e) => { + return Err(e.into()); + } + } + } else { + Some(rx.recv()?) + }; + if !client.handle_event(ev).await? { + break; + } + } + self.stop_console()?; Ok(()) } @@ -308,26 +326,15 @@ pub async fn run() -> ResultType<()> { .context("Failed to connect to DBus")?; let vm_name = VMProxy::new(&dbus).await?.name().await?; - let console = Console::new(&dbus.into(), 0) .await .context("Failed to get the console")?; let server = Server::new(format!("qemu-rustdesk ({})", vm_name), console).await?; for stream in listener.incoming() { - match stream { - Ok(stream) => { - tokio::spawn(async { - /* - if let Err(err) = server.handle_connection(stream).await { - log::error!("Connection closed: {err}"); - } - */ - }); - } - Err(err) => { - log::error!("Failed to accept connection: {}", err); - continue; - } + let stream = stream?; + let server = server.clone(); + if let Err(err) = server.handle_connection(stream).await { + log::error!("Connection closed: {err}"); } } From f441d2fe5ec4fdc64ff45344ca5aa8f8e3c7aaca Mon Sep 17 00:00:00 2001 From: asur4s Date: Mon, 20 Mar 2023 23:58:10 -0700 Subject: [PATCH 013/366] fix rename event in linux --- src/keyboard.rs | 12 ++++++------ src/ui_session_interface.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 42cff9ce0..0f969181a 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -460,8 +460,8 @@ pub fn event_type_to_event(event_type: EventType) -> Event { event_type, time: SystemTime::now(), unicode: None, - code: 0, - scan_code: 0, + platform_code: 0, + position_code: 0, } } @@ -763,15 +763,15 @@ pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> }; #[cfg(target_os = "linux")] let keycode = match peer { - OS_LOWER_WINDOWS => rdev::linux_code_to_win_scancode(event.code as _)?, + OS_LOWER_WINDOWS => rdev::linux_code_to_win_scancode(event.position_code as _)?, OS_LOWER_MACOS => { if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" { - rdev::linux_code_to_macos_iso_code(event.code as _)? + rdev::linux_code_to_macos_iso_code(event.position_code as _)? } else { - rdev::linux_code_to_macos_code(event.code as _)? + rdev::linux_code_to_macos_code(event.position_code as _)? } } - _ => event.code as _, + _ => event.position_code as _, }; #[cfg(any(target_os = "android", target_os = "ios"))] let keycode = 0; diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 2fd095347..8a1da6b8f 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -562,8 +562,8 @@ impl Session { let event = Event { time: SystemTime::now(), unicode: None, - code: keycode as _, - scan_code: scancode as _, + platform_code: keycode as _, + position_code: scancode as _, event_type: event_type, }; keyboard::client::process_event(&event, Some(lock_modes)); From f709118c69e0445e423d582a113a51a681783b88 Mon Sep 17 00:00:00 2001 From: asur4s Date: Tue, 21 Mar 2023 00:09:36 -0700 Subject: [PATCH 014/366] Check if event is altgr --- src/keyboard.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/keyboard.rs b/src/keyboard.rs index 0f969181a..3337ddf64 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -880,6 +880,10 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } + if event.platform_code == 0xFE03 { + return events; + } + #[cfg(target_os = "linux")] try_fill_unicode(event, &key_event, &mut events); From f35302b9f5aa8ad43ffe035e83254b400adb3153 Mon Sep 17 00:00:00 2001 From: asur4s Date: Tue, 21 Mar 2023 17:28:58 -0700 Subject: [PATCH 015/366] fix conflict --- flutter/lib/desktop/widgets/remote_toolbar.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index ec039b67c..804d74499 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1688,11 +1688,6 @@ class _KeyboardMenu extends StatelessWidget { if (pi.is_wayland && mode.key != _kKeyMapMode) { continue; } - if (mode.key == _kKeyTranslateMode) { - if (Platform.isLinux) { - continue; - } - } var text = translate(mode.menu); if (mode.key == _kKeyTranslateMode) { text = '$text beta'; From e90ac742f4f9c77cc589caf9af89227cac25c5e2 Mon Sep 17 00:00:00 2001 From: chiehw Date: Tue, 21 Mar 2023 15:23:48 +0800 Subject: [PATCH 016/366] fix rename event in win --- src/keyboard.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 3337ddf64..6cf61f2fd 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -227,8 +227,8 @@ pub fn start_grab_loop() { } let mut _keyboard_mode = KeyboardMode::Map; - let _scan_code = event.scan_code; - let _code = event.code; + let _scan_code = event.position_code; + let _code = event.platform_code; let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) { _keyboard_mode = client::process_event(&event, None); if is_press { @@ -546,7 +546,7 @@ pub fn legacy_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Vec> 8) == 0xE0 { + if (event.position_code >> 8) == 0xE0 { unsafe { IS_ALT_GR = true; } @@ -741,19 +741,19 @@ pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> OS_LOWER_WINDOWS => { // https://github.com/rustdesk/rustdesk/issues/1371 // Filter scancodes that are greater than 255 and the hight word is not 0xE0. - if event.scan_code > 255 && (event.scan_code >> 8) != 0xE0 { + if event.position_code > 255 && (event.position_code >> 8) != 0xE0 { return None; } - event.scan_code + event.position_code } OS_LOWER_MACOS => { if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" { - rdev::win_scancode_to_macos_iso_code(event.scan_code)? + rdev::win_scancode_to_macos_iso_code(event.position_code)? } else { - rdev::win_scancode_to_macos_code(event.scan_code)? + rdev::win_scancode_to_macos_code(event.position_code)? } } - _ => rdev::win_scancode_to_linux_code(event.scan_code)?, + _ => rdev::win_scancode_to_linux_code(event.position_code)?, }; #[cfg(target_os = "macos")] let keycode = match peer { @@ -795,7 +795,7 @@ fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec bool { #[cfg(target_os = "windows")] pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option { let mut key_event = map_keyboard_mode(peer, event, key_event)?; - key_event.set_chr((key_event.chr() & 0x0000FFFF) | ((event.code as u32) << 16)); + key_event.set_chr((key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16)); Some(key_event) } @@ -857,12 +857,12 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(target_os = "windows")] unsafe { - if event.scan_code == 0x021D { + if event.position_code == 0x021D { return events; } if IS_0X021D_DOWN { - if event.scan_code == 0xE038 { + if event.position_code == 0xE038 { return events; } } From 5b6cf273934cd2caf453148183ddc4c0bae4efea Mon Sep 17 00:00:00 2001 From: Asura Date: Wed, 22 Mar 2023 06:38:42 +0800 Subject: [PATCH 017/366] fix rename event in macos --- src/keyboard.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 6cf61f2fd..09d7f398b 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -757,9 +757,9 @@ pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> }; #[cfg(target_os = "macos")] let keycode = match peer { - OS_LOWER_WINDOWS => rdev::macos_code_to_win_scancode(event.code as _)?, - OS_LOWER_MACOS => event.code as _, - _ => rdev::macos_code_to_linux_code(event.code as _)?, + OS_LOWER_WINDOWS => rdev::macos_code_to_win_scancode(event.platform_code as _)?, + OS_LOWER_MACOS => event.platform_code as _, + _ => rdev::macos_code_to_linux_code(event.platform_code as _)?, }; #[cfg(target_os = "linux")] let keycode = match peer { @@ -851,7 +851,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(target_os = "macos")] // ignore right option key - if event.code as u32 == rdev::kVK_RightOption { + if event.platform_code as u32 == rdev::kVK_RightOption { return events; } From e99af2b0d4d54f80ac6d9bf217e71a8f73270145 Mon Sep 17 00:00:00 2001 From: chiehw Date: Wed, 22 Mar 2023 08:11:41 +0800 Subject: [PATCH 018/366] Refactor: is altgr --- src/keyboard.rs | 49 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 09d7f398b..b3d8e4211 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -833,6 +833,35 @@ pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Opt map_keyboard_mode(peer, event, key_event) } +#[inline] +fn is_altgr(event: &Event) -> bool { + #[cfg(target_os = "linux")] + if event.platform_code == 0xFE03 { + true + } else { + false + } + + #[cfg(target_os = "macos")] + // ignore right option key + if event.platform_code as u32 == rdev::kVK_RightOption { + true + } else { + false + } + + #[cfg(target_os = "windows")] + if unsafe { IS_0X021D_DOWN } { + if event.position_code == 0xE038 { + true + } else { + false + } + } else { + false + } +} + pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); if let Some(unicode_info) = &event.unicode { @@ -849,23 +878,13 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } - #[cfg(target_os = "macos")] - // ignore right option key - if event.platform_code as u32 == rdev::kVK_RightOption { + if is_altgr(event) { return events; } #[cfg(target_os = "windows")] - unsafe { - if event.position_code == 0x021D { - return events; - } - - if IS_0X021D_DOWN { - if event.position_code == 0xE038 { - return events; - } - } + if event.position_code == 0x021D { + return events; } #[cfg(target_os = "windows")] @@ -880,10 +899,6 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } - if event.platform_code == 0xFE03 { - return events; - } - #[cfg(target_os = "linux")] try_fill_unicode(event, &key_event, &mut events); From 8478c2ba48cfc32dea08209542349f9d2f6cc026 Mon Sep 17 00:00:00 2001 From: chiehw Date: Wed, 22 Mar 2023 08:17:11 +0800 Subject: [PATCH 019/366] Refactor: is press --- src/keyboard.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index b3d8e4211..2a131e13a 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -862,6 +862,10 @@ fn is_altgr(event: &Event) -> bool { } } +fn is_press(event: &Event) -> bool { + matches!(event.event_type, EventType::KeyPress(_)) +} + pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); if let Some(unicode_info) = &event.unicode { @@ -887,8 +891,8 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - return events; } - #[cfg(target_os = "windows")] - if matches!(event.event_type, EventType::KeyPress(_)) { + #[cfg(any(target_os = "linux", target_os = "windows"))] + if is_press(event) { try_fill_unicode(event, &key_event, &mut events); } @@ -899,9 +903,6 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } - #[cfg(target_os = "linux")] - try_fill_unicode(event, &key_event, &mut events); - #[cfg(target_os = "macos")] if !unsafe { IS_LEFT_OPTION_DOWN } { try_fill_unicode(event, &key_event, &mut events); From bbc6c98775de9a21f45260fdceba05b8d7c5bcd5 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 22 Mar 2023 14:13:24 +0800 Subject: [PATCH 020/366] vdi: split server.rs into server.rs + console.rs + connection.rs --- vdi/host/src/connection.rs | 1 + vdi/host/src/console.rs | 193 ++++++++++++++++++++++++++ vdi/host/src/lib.rs | 2 + vdi/host/src/server.rs | 275 ++++++------------------------------- 4 files changed, 235 insertions(+), 236 deletions(-) create mode 100644 vdi/host/src/connection.rs create mode 100644 vdi/host/src/console.rs diff --git a/vdi/host/src/connection.rs b/vdi/host/src/connection.rs new file mode 100644 index 000000000..f416ce4e0 --- /dev/null +++ b/vdi/host/src/connection.rs @@ -0,0 +1 @@ +use hbb_common::{message_proto::*, tokio, ResultType}; diff --git a/vdi/host/src/console.rs b/vdi/host/src/console.rs new file mode 100644 index 000000000..29dcbbfc9 --- /dev/null +++ b/vdi/host/src/console.rs @@ -0,0 +1,193 @@ +use hbb_common::{log, tokio, ResultType}; +use image::GenericImage; +use qemu_display::{Console, ConsoleListenerHandler, MouseButton}; +use std::{collections::HashSet, sync::Arc, time}; +pub use tokio::sync::{mpsc, Mutex}; + +#[derive(Debug)] +pub enum Event { + ConsoleUpdate((i32, i32, i32, i32)), + Disconnected, +} + +const PIXMAN_X8R8G8B8: u32 = 0x20020888; +pub type BgraImage = image::ImageBuffer, Vec>; +#[derive(Debug)] +pub struct ConsoleListener { + pub image: Arc>, + pub tx: mpsc::UnboundedSender, +} + +#[async_trait::async_trait] +impl ConsoleListenerHandler for ConsoleListener { + async fn scanout(&mut self, s: qemu_display::Scanout) { + *self.image.lock().await = image_from_vec(s.format, s.width, s.height, s.stride, s.data); + } + + async fn update(&mut self, u: qemu_display::Update) { + let update = image_from_vec(u.format, u.w as _, u.h as _, u.stride, u.data); + let mut image = self.image.lock().await; + if (u.x, u.y) == (0, 0) && update.dimensions() == image.dimensions() { + *image = update; + } else { + image.copy_from(&update, u.x as _, u.y as _).unwrap(); + } + self.tx + .send(Event::ConsoleUpdate((u.x, u.y, u.w, u.h))) + .ok(); + } + + async fn scanout_dmabuf(&mut self, _scanout: qemu_display::ScanoutDMABUF) { + unimplemented!() + } + + async fn update_dmabuf(&mut self, _update: qemu_display::UpdateDMABUF) { + unimplemented!() + } + + async fn mouse_set(&mut self, set: qemu_display::MouseSet) { + dbg!(set); + } + + async fn cursor_define(&mut self, cursor: qemu_display::Cursor) { + dbg!(cursor); + } + + fn disconnected(&mut self) { + dbg!(); + } +} + +#[derive(derivative::Derivative)] +#[derivative(Debug)] +pub struct Client { + #[derivative(Debug = "ignore")] + console: Arc>, + last_update: Option, + has_update: bool, + req_update: bool, + last_buttons: HashSet, + dimensions: (u16, u16), + image: Arc>, +} + +impl Client { + pub fn new(console: Arc>, image: Arc>) -> Self { + Self { + console, + image, + last_update: None, + has_update: false, + req_update: false, + last_buttons: HashSet::new(), + dimensions: (0, 0), + } + } + + pub fn update_pending(&self) -> bool { + self.has_update && self.req_update + } + + pub async fn key_event(&self, qnum: u32, down: bool) -> ResultType<()> { + let console = self.console.lock().await; + if down { + console.keyboard.press(qnum).await?; + } else { + console.keyboard.release(qnum).await?; + } + Ok(()) + } + + pub async fn desktop_resize(&mut self) -> ResultType<()> { + let image = self.image.lock().await; + let (width, height) = (image.width() as _, image.height() as _); + if (width, height) == self.dimensions { + return Ok(()); + } + self.dimensions = (width, height); + Ok(()) + } + + pub async fn send_framebuffer_update(&mut self) -> ResultType<()> { + self.desktop_resize().await?; + if self.has_update && self.req_update { + if let Some(last_update) = self.last_update { + if last_update.elapsed().as_millis() < 10 { + log::info!("TODO: <10ms, could delay update..") + } + } + // self.server.send_framebuffer_update(&self.vnc_server)?; + self.last_update = Some(time::Instant::now()); + self.has_update = false; + self.req_update = false; + } + Ok(()) + } + + pub async fn handle_event(&mut self, event: Option) -> ResultType { + match event { + Some(Event::ConsoleUpdate(_)) => { + self.has_update = true; + } + Some(Event::Disconnected) => { + return Ok(false); + } + None => { + self.send_framebuffer_update().await?; + } + } + + Ok(true) + } +} + +fn image_from_vec(format: u32, width: u32, height: u32, stride: u32, data: Vec) -> BgraImage { + if format != PIXMAN_X8R8G8B8 { + todo!("unhandled pixman format: {}", format) + } + if cfg!(target_endian = "big") { + todo!("pixman/image in big endian") + } + let layout = image::flat::SampleLayout { + channels: 4, + channel_stride: 1, + width, + width_stride: 4, + height, + height_stride: stride as _, + }; + let samples = image::flat::FlatSamples { + samples: data, + layout, + color_hint: None, + }; + samples + .try_into_buffer::>() + .or_else::<&str, _>(|(_err, samples)| { + let view = samples.as_view::>().unwrap(); + let mut img = BgraImage::new(width, height); + img.copy_from(&view, 0, 0).unwrap(); + Ok(img) + }) + .unwrap() +} + +fn button_mask_to_set(mask: u8) -> HashSet { + let mut set = HashSet::new(); + if mask & 0b0000_0001 != 0 { + set.insert(MouseButton::Left); + } + if mask & 0b0000_0010 != 0 { + set.insert(MouseButton::Middle); + } + if mask & 0b0000_0100 != 0 { + set.insert(MouseButton::Right); + } + if mask & 0b0000_1000 != 0 { + set.insert(MouseButton::WheelUp); + } + if mask & 0b0001_0000 != 0 { + set.insert(MouseButton::WheelDown); + } + set +} diff --git a/vdi/host/src/lib.rs b/vdi/host/src/lib.rs index 74f47ad34..e9f8d7ed3 100644 --- a/vdi/host/src/lib.rs +++ b/vdi/host/src/lib.rs @@ -1 +1,3 @@ pub mod server; +mod console; +mod connection; diff --git a/vdi/host/src/server.rs b/vdi/host/src/server.rs index 12f4526c9..5fd28d2d7 100644 --- a/vdi/host/src/server.rs +++ b/vdi/host/src/server.rs @@ -1,23 +1,15 @@ use clap::Parser; -use hbb_common::{ - anyhow::{anyhow, Context}, - log, - message_proto::*, - tokio, ResultType, -}; -use image::GenericImage; -use qemu_display::{Console, ConsoleListenerHandler, MouseButton, VMProxy}; +use hbb_common::{anyhow::Context, log, tokio, ResultType}; +use qemu_display::{Console, VMProxy}; use std::{ borrow::Borrow, - collections::HashSet, - error::Error, - io, - iter::FromIterator, net::{TcpListener, TcpStream}, - sync::{mpsc, Arc, Mutex}, - thread, time, + sync::Arc, + thread, }; +use crate::console::*; + #[derive(Parser, Debug)] pub struct SocketAddrArgs { /// IP address @@ -43,155 +35,12 @@ struct Cli { } #[derive(Debug)] -enum Event { - ConsoleUpdate((i32, i32, i32, i32)), - Disconnected, -} - -const PIXMAN_X8R8G8B8: u32 = 0x20020888; -type BgraImage = image::ImageBuffer, Vec>; - -#[derive(derivative::Derivative)] -#[derivative(Debug)] -struct Client { - #[derivative(Debug = "ignore")] - server: Server, - share: bool, - last_update: Option, - has_update: bool, - req_update: bool, - last_buttons: HashSet, - dimensions: (u16, u16), -} - -impl Client { - fn new(server: Server, share: bool) -> Self { - Self { - server, - share, - last_update: None, - has_update: false, - req_update: false, - last_buttons: HashSet::new(), - dimensions: (0, 0), - } - } - - fn update_pending(&self) -> bool { - self.has_update && self.req_update - } - - async fn key_event(&self, qnum: u32, down: bool) -> ResultType<()> { - let inner = self.server.inner.lock().unwrap(); - if down { - inner.console.keyboard.press(qnum).await?; - } else { - inner.console.keyboard.release(qnum).await?; - } - Ok(()) - } - - fn desktop_resize(&mut self) -> ResultType<()> { - let (width, height) = self.server.dimensions(); - if (width, height) == self.dimensions { - return Ok(()); - } - self.dimensions = (width, height); - Ok(()) - } - - fn send_framebuffer_update(&mut self) -> ResultType<()> { - self.desktop_resize()?; - if self.has_update && self.req_update { - if let Some(last_update) = self.last_update { - if last_update.elapsed().as_millis() < 10 { - println!("TODO: <10ms, could delay update..") - } - } - // self.server.send_framebuffer_update(&self.vnc_server)?; - self.last_update = Some(time::Instant::now()); - self.has_update = false; - self.req_update = false; - } - Ok(()) - } - - async fn handle_event(&mut self, event: Option) -> ResultType { - match event { - Some(Event::ConsoleUpdate(_)) => { - self.has_update = true; - } - Some(Event::Disconnected) => { - return Ok(false); - } - None => { - self.send_framebuffer_update()?; - } - } - - Ok(true) - } -} - -#[derive(Debug)] -struct ConsoleListener { - server: Server, -} - -#[async_trait::async_trait] -impl ConsoleListenerHandler for ConsoleListener { - async fn scanout(&mut self, s: qemu_display::Scanout) { - let mut inner = self.server.inner.lock().unwrap(); - inner.image = image_from_vec(s.format, s.width, s.height, s.stride, s.data); - } - - async fn update(&mut self, u: qemu_display::Update) { - let mut inner = self.server.inner.lock().unwrap(); - let update = image_from_vec(u.format, u.w as _, u.h as _, u.stride, u.data); - if (u.x, u.y) == (0, 0) && update.dimensions() == inner.image.dimensions() { - inner.image = update; - } else { - inner.image.copy_from(&update, u.x as _, u.y as _).unwrap(); - } - inner - .tx - .send(Event::ConsoleUpdate((u.x, u.y, u.w, u.h))) - .unwrap(); - } - - async fn scanout_dmabuf(&mut self, _scanout: qemu_display::ScanoutDMABUF) { - unimplemented!() - } - - async fn update_dmabuf(&mut self, _update: qemu_display::UpdateDMABUF) { - unimplemented!() - } - - async fn mouse_set(&mut self, set: qemu_display::MouseSet) { - dbg!(set); - } - - async fn cursor_define(&mut self, cursor: qemu_display::Cursor) { - dbg!(cursor); - } - - fn disconnected(&mut self) { - dbg!(); - } -} - -#[derive(Debug)] -struct ServerInner { - console: Console, - image: BgraImage, - tx: mpsc::Sender, -} - -#[derive(Clone, Debug)] struct Server { vm_name: String, - rx: Arc>>, - inner: Arc>, + rx: mpsc::UnboundedReceiver, + tx: mpsc::UnboundedSender, + image: Arc>, + console: Arc>, } impl Server { @@ -199,118 +48,73 @@ impl Server { let width = console.width().await?; let height = console.height().await?; let image = BgraImage::new(width as _, height as _); - let (tx, rx) = mpsc::channel(); + let (tx, rx) = mpsc::unbounded_channel(); Ok(Self { vm_name, - rx: Arc::new(Mutex::new(rx)), - inner: Arc::new(Mutex::new(ServerInner { console, image, tx })), + rx, + image: Arc::new(Mutex::new(image)), + tx, + console: Arc::new(Mutex::new(console)), }) } - fn stop_console(&self) -> ResultType<()> { - let mut inner = self.inner.lock().unwrap(); - inner.console.unregister_listener(); + async fn stop_console(&self) -> ResultType<()> { + self.console.lock().await.unregister_listener(); Ok(()) } async fn run_console(&self) -> ResultType<()> { - let inner = self.inner.lock().unwrap(); - inner - .console + self.console + .lock() + .await .register_listener(ConsoleListener { - server: self.clone(), + image: self.image.clone(), + tx: self.tx.clone(), }) .await?; Ok(()) } - fn dimensions(&self) -> (u16, u16) { - let inner = self.inner.lock().unwrap(); - (inner.image.width() as u16, inner.image.height() as u16) + async fn dimensions(&self) -> (u16, u16) { + let image = self.image.lock().await; + (image.width() as u16, image.height() as u16) } - async fn handle_connection(&self, stream: TcpStream) -> ResultType<()> { - let (width, height) = self.dimensions(); + async fn handle_connection(&mut self, stream: TcpStream) -> ResultType<()> { + let (width, height) = self.dimensions().await; - let tx = self.inner.lock().unwrap().tx.clone(); + let tx = self.tx.clone(); let _client_thread = thread::spawn(move || loop {}); - let mut client = Client::new(self.clone(), true); + let mut client = Client::new(self.console.clone(), self.image.clone()); self.run_console().await?; - let rx = self.rx.lock().unwrap(); loop { let ev = if client.update_pending() { - match rx.try_recv() { + match self.rx.try_recv() { Ok(e) => Some(e), - Err(mpsc::TryRecvError::Empty) => None, + Err(mpsc::error::TryRecvError::Empty) => None, Err(e) => { return Err(e.into()); } } } else { - Some(rx.recv()?) + Some( + self.rx + .recv() + .await + .context("Channel closed unexpectedly")?, + ) }; if !client.handle_event(ev).await? { break; } } - self.stop_console()?; + self.stop_console().await?; Ok(()) } } -fn button_mask_to_set(mask: u8) -> HashSet { - let mut set = HashSet::new(); - if mask & 0b0000_0001 != 0 { - set.insert(MouseButton::Left); - } - if mask & 0b0000_0010 != 0 { - set.insert(MouseButton::Middle); - } - if mask & 0b0000_0100 != 0 { - set.insert(MouseButton::Right); - } - if mask & 0b0000_1000 != 0 { - set.insert(MouseButton::WheelUp); - } - if mask & 0b0001_0000 != 0 { - set.insert(MouseButton::WheelDown); - } - set -} - -fn image_from_vec(format: u32, width: u32, height: u32, stride: u32, data: Vec) -> BgraImage { - if format != PIXMAN_X8R8G8B8 { - todo!("unhandled pixman format: {}", format) - } - if cfg!(target_endian = "big") { - todo!("pixman/image in big endian") - } - let layout = image::flat::SampleLayout { - channels: 4, - channel_stride: 1, - width, - width_stride: 4, - height, - height_stride: stride as _, - }; - let samples = image::flat::FlatSamples { - samples: data, - layout, - color_hint: None, - }; - samples - .try_into_buffer::>() - .or_else::<&str, _>(|(_err, samples)| { - let view = samples.as_view::>().unwrap(); - let mut img = BgraImage::new(width, height); - img.copy_from(&view, 0, 0).unwrap(); - Ok(img) - }) - .unwrap() -} - #[tokio::main] pub async fn run() -> ResultType<()> { let args = Cli::parse(); @@ -329,10 +133,9 @@ pub async fn run() -> ResultType<()> { let console = Console::new(&dbus.into(), 0) .await .context("Failed to get the console")?; - let server = Server::new(format!("qemu-rustdesk ({})", vm_name), console).await?; + let mut server = Server::new(format!("qemu-rustdesk ({})", vm_name), console).await?; for stream in listener.incoming() { let stream = stream?; - let server = server.clone(); if let Err(err) = server.handle_connection(stream).await { log::error!("Connection closed: {err}"); } From 3d5e8e027679ee8cacc311024079e964a835e227 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 22 Mar 2023 16:50:59 +0800 Subject: [PATCH 021/366] vdi: new message loop --- vdi/host/Cargo.lock | 1 - vdi/host/Cargo.toml | 1 - vdi/host/src/connection.rs | 10 ++++ vdi/host/src/console.rs | 92 +++--------------------------- vdi/host/src/server.rs | 111 +++++++++++++++++++++++-------------- 5 files changed, 88 insertions(+), 127 deletions(-) diff --git a/vdi/host/Cargo.lock b/vdi/host/Cargo.lock index 161664625..0b2e8ca2b 100644 --- a/vdi/host/Cargo.lock +++ b/vdi/host/Cargo.lock @@ -1528,7 +1528,6 @@ version = "0.1.0" dependencies = [ "async-trait", "clap", - "derivative", "hbb_common", "image", "qemu-display", diff --git a/vdi/host/Cargo.toml b/vdi/host/Cargo.toml index a1fd1e68e..42b6fc83a 100644 --- a/vdi/host/Cargo.toml +++ b/vdi/host/Cargo.toml @@ -9,6 +9,5 @@ qemu-display = { git = "https://github.com/rustdesk/qemu-display" } hbb_common = { path = "../../libs/hbb_common" } clap = { version = "4.1", features = ["derive"] } zbus = { version = "3.0" } -derivative = "2.2" image = "0.24" async-trait = "0.1" diff --git a/vdi/host/src/connection.rs b/vdi/host/src/connection.rs index f416ce4e0..9f856fa2e 100644 --- a/vdi/host/src/connection.rs +++ b/vdi/host/src/connection.rs @@ -1 +1,11 @@ use hbb_common::{message_proto::*, tokio, ResultType}; +pub use tokio::sync::{mpsc, Mutex}; +pub struct Connection { + pub tx: mpsc::UnboundedSender, +} + +impl Connection { + pub async fn on_message(&mut self, message: Message) -> ResultType { + Ok(true) + } +} diff --git a/vdi/host/src/console.rs b/vdi/host/src/console.rs index 29dcbbfc9..a342f1a9a 100644 --- a/vdi/host/src/console.rs +++ b/vdi/host/src/console.rs @@ -1,7 +1,7 @@ -use hbb_common::{log, tokio, ResultType}; +use hbb_common::{tokio, ResultType}; use image::GenericImage; use qemu_display::{Console, ConsoleListenerHandler, MouseButton}; -use std::{collections::HashSet, sync::Arc, time}; +use std::{collections::HashSet, sync::Arc}; pub use tokio::sync::{mpsc, Mutex}; #[derive(Debug)] @@ -54,91 +54,17 @@ impl ConsoleListenerHandler for ConsoleListener { } fn disconnected(&mut self) { - dbg!(); + self.tx.send(Event::Disconnected).ok(); } } -#[derive(derivative::Derivative)] -#[derivative(Debug)] -pub struct Client { - #[derivative(Debug = "ignore")] - console: Arc>, - last_update: Option, - has_update: bool, - req_update: bool, - last_buttons: HashSet, - dimensions: (u16, u16), - image: Arc>, -} - -impl Client { - pub fn new(console: Arc>, image: Arc>) -> Self { - Self { - console, - image, - last_update: None, - has_update: false, - req_update: false, - last_buttons: HashSet::new(), - dimensions: (0, 0), - } - } - - pub fn update_pending(&self) -> bool { - self.has_update && self.req_update - } - - pub async fn key_event(&self, qnum: u32, down: bool) -> ResultType<()> { - let console = self.console.lock().await; - if down { - console.keyboard.press(qnum).await?; - } else { - console.keyboard.release(qnum).await?; - } - Ok(()) - } - - pub async fn desktop_resize(&mut self) -> ResultType<()> { - let image = self.image.lock().await; - let (width, height) = (image.width() as _, image.height() as _); - if (width, height) == self.dimensions { - return Ok(()); - } - self.dimensions = (width, height); - Ok(()) - } - - pub async fn send_framebuffer_update(&mut self) -> ResultType<()> { - self.desktop_resize().await?; - if self.has_update && self.req_update { - if let Some(last_update) = self.last_update { - if last_update.elapsed().as_millis() < 10 { - log::info!("TODO: <10ms, could delay update..") - } - } - // self.server.send_framebuffer_update(&self.vnc_server)?; - self.last_update = Some(time::Instant::now()); - self.has_update = false; - self.req_update = false; - } - Ok(()) - } - - pub async fn handle_event(&mut self, event: Option) -> ResultType { - match event { - Some(Event::ConsoleUpdate(_)) => { - self.has_update = true; - } - Some(Event::Disconnected) => { - return Ok(false); - } - None => { - self.send_framebuffer_update().await?; - } - } - - Ok(true) +pub async fn key_event(console: &mut Console, qnum: u32, down: bool) -> ResultType<()> { + if down { + console.keyboard.press(qnum).await?; + } else { + console.keyboard.release(qnum).await?; } + Ok(()) } fn image_from_vec(format: u32, width: u32, height: u32, stride: u32, data: Vec) -> BgraImage { diff --git a/vdi/host/src/server.rs b/vdi/host/src/server.rs index 5fd28d2d7..b43bd364f 100644 --- a/vdi/host/src/server.rs +++ b/vdi/host/src/server.rs @@ -1,13 +1,18 @@ use clap::Parser; -use hbb_common::{anyhow::Context, log, tokio, ResultType}; -use qemu_display::{Console, VMProxy}; -use std::{ - borrow::Borrow, - net::{TcpListener, TcpStream}, - sync::Arc, - thread, +use hbb_common::{ + allow_err, + anyhow::{bail, Context}, + log, + message_proto::*, + protobuf::Message as _, + tokio, + tokio::net::TcpListener, + ResultType, Stream, }; +use qemu_display::{Console, VMProxy}; +use std::{borrow::Borrow, sync::Arc}; +use crate::connection::*; use crate::console::*; #[derive(Parser, Debug)] @@ -37,8 +42,10 @@ struct Cli { #[derive(Debug)] struct Server { vm_name: String, - rx: mpsc::UnboundedReceiver, - tx: mpsc::UnboundedSender, + rx_console: mpsc::UnboundedReceiver, + tx_console: mpsc::UnboundedSender, + rx_conn: mpsc::UnboundedReceiver, + tx_conn: mpsc::UnboundedSender, image: Arc>, console: Arc>, } @@ -48,12 +55,15 @@ impl Server { let width = console.width().await?; let height = console.height().await?; let image = BgraImage::new(width as _, height as _); - let (tx, rx) = mpsc::unbounded_channel(); + let (tx_console, rx_console) = mpsc::unbounded_channel(); + let (tx_conn, rx_conn) = mpsc::unbounded_channel(); Ok(Self { vm_name, - rx, + rx_console, + tx_console, + rx_conn, + tx_conn, image: Arc::new(Mutex::new(image)), - tx, console: Arc::new(Mutex::new(console)), }) } @@ -69,7 +79,7 @@ impl Server { .await .register_listener(ConsoleListener { image: self.image.clone(), - tx: self.tx.clone(), + tx: self.tx_console.clone(), }) .await?; Ok(()) @@ -80,33 +90,47 @@ impl Server { (image.width() as u16, image.height() as u16) } - async fn handle_connection(&mut self, stream: TcpStream) -> ResultType<()> { - let (width, height) = self.dimensions().await; - - let tx = self.tx.clone(); - let _client_thread = thread::spawn(move || loop {}); - - let mut client = Client::new(self.console.clone(), self.image.clone()); + async fn handle_connection(&mut self, stream: Stream) -> ResultType<()> { + let mut stream = stream; self.run_console().await?; + let mut conn = Connection { + tx: self.tx_conn.clone(), + }; + loop { - let ev = if client.update_pending() { - match self.rx.try_recv() { - Ok(e) => Some(e), - Err(mpsc::error::TryRecvError::Empty) => None, - Err(e) => { - return Err(e.into()); + tokio::select! { + Some(evt) = self.rx_console.recv() => { + match evt { + _ => {} + } + } + Some(msg) = self.rx_conn.recv() => { + allow_err!(stream.send(&msg).await); + } + res = stream.next() => { + if let Some(res) = res { + match res { + Err(err) => { + bail!(err); + } + Ok(bytes) => { + if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { + match conn.on_message(msg_in).await { + Ok(false) => { + break; + } + Err(err) => { + log::error!("{err}"); + } + _ => {} + } + } + } + } + } else { + bail!("Reset by the peer"); } } - } else { - Some( - self.rx - .recv() - .await - .context("Channel closed unexpectedly")?, - ) - }; - if !client.handle_event(ev).await? { - break; } } @@ -119,7 +143,9 @@ impl Server { pub async fn run() -> ResultType<()> { let args = Cli::parse(); - let listener = TcpListener::bind::(args.address.into()).unwrap(); + let listener = TcpListener::bind::(args.address.into()) + .await + .unwrap(); let dbus = if let Some(addr) = args.dbus_address { zbus::ConnectionBuilder::address(addr.borrow())? .build() @@ -134,12 +160,13 @@ pub async fn run() -> ResultType<()> { .await .context("Failed to get the console")?; let mut server = Server::new(format!("qemu-rustdesk ({})", vm_name), console).await?; - for stream in listener.incoming() { - let stream = stream?; + loop { + let (stream, addr) = listener.accept().await?; + stream.set_nodelay(true).ok(); + let laddr = stream.local_addr()?; + let stream = Stream::from(stream, laddr); if let Err(err) = server.handle_connection(stream).await { - log::error!("Connection closed: {err}"); + log::error!("Connection from {addr} closed: {err}"); } } - - Ok(()) } From 33a1ab0d321eb3ba23247c2604eafe7cd2fb450f Mon Sep 17 00:00:00 2001 From: asur4s Date: Wed, 22 Mar 2023 05:09:02 -0700 Subject: [PATCH 022/366] Refactor is altgr for clearer expression --- src/keyboard.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 2a131e13a..55c30a3f3 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -842,21 +842,9 @@ fn is_altgr(event: &Event) -> bool { false } - #[cfg(target_os = "macos")] - // ignore right option key - if event.platform_code as u32 == rdev::kVK_RightOption { - true - } else { - false - } - #[cfg(target_os = "windows")] - if unsafe { IS_0X021D_DOWN } { - if event.position_code == 0xE038 { - true - } else { - false - } + if unsafe { IS_0X021D_DOWN } && event.position_code == 0xE038 { + true } else { false } @@ -882,6 +870,13 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } + #[cfg(target_os = "macos")] + // ignore right option key + if event.code as u32 == rdev::kVK_RightOption { + return events; + } + + #[cfg(not(target_os = "macos"))] if is_altgr(event) { return events; } From 0e49cd0f01cf83c623318e1148d71978fed4c279 Mon Sep 17 00:00:00 2001 From: asur4s Date: Wed, 22 Mar 2023 05:09:20 -0700 Subject: [PATCH 023/366] Update rdev --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index b80b4d924..b62f82745 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4721,7 +4721,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#25a99ce71ab42843ad253dd51e6a35e83e87a8a4" +source = "git+https://github.com/fufesou/rdev#eaa35ff9af22891b4aae3a0a5e83472c16177cd8" dependencies = [ "cocoa", "core-foundation 0.9.3", From 4d076988cd8d675aa72bab643900d1df04d6a856 Mon Sep 17 00:00:00 2001 From: asur4s Date: Wed, 22 Mar 2023 05:32:15 -0700 Subject: [PATCH 024/366] fix ci --- src/keyboard.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 55c30a3f3..ce68657ea 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -834,6 +834,7 @@ pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Opt } #[inline] +#[cfg(any(target_os = "linux", target_os = "windows"))] fn is_altgr(event: &Event) -> bool { #[cfg(target_os = "linux")] if event.platform_code == 0xFE03 { @@ -872,11 +873,11 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(target_os = "macos")] // ignore right option key - if event.code as u32 == rdev::kVK_RightOption { + if event.platform_code as u32 == rdev::kVK_RightOption { return events; } - #[cfg(not(target_os = "macos"))] + #[cfg(any(target_os = "linux", target_os = "windows"))] if is_altgr(event) { return events; } From 98329f08c16109e66aec2c1dfa320922795a4469 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 23 Mar 2023 09:28:08 +0800 Subject: [PATCH 025/366] opt: add wayland cursor/egl, libpulse0 to appimage --- appimage/AppImageBuilder-aarch64.yml | 6 ++++++ appimage/AppImageBuilder-x86_64.yml | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/appimage/AppImageBuilder-aarch64.yml b/appimage/AppImageBuilder-aarch64.yml index f3cd8f568..87c854d5d 100644 --- a/appimage/AppImageBuilder-aarch64.yml +++ b/appimage/AppImageBuilder-aarch64.yml @@ -32,6 +32,9 @@ AppDir: - sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ bionic-backports main restricted universe multiverse key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32' + - sourceline: deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ bionic-security main restricted + universe multiverse + key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3b4fe6acc0b21f32' include: - libc6 - libgtk-3-0 @@ -47,6 +50,9 @@ AppDir: - libva-x11-2 - libvdpau1 - libgstreamer-plugins-base1.0-0 + - libwayland-cursor0 + - libwayland-egl1 + - libpulse0 exclude: - humanity-icon-theme - hicolor-icon-theme diff --git a/appimage/AppImageBuilder-x86_64.yml b/appimage/AppImageBuilder-x86_64.yml index 59dd5164f..8f72afd08 100644 --- a/appimage/AppImageBuilder-x86_64.yml +++ b/appimage/AppImageBuilder-x86_64.yml @@ -33,6 +33,8 @@ AppDir: - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-updates multiverse - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe multiverse + - sourceline: deb http://archive.ubuntu.com/ubuntu/ bionic-security main restricted + universe multiverse - sourceline: deb http://ppa.launchpad.net/pipewire-debian/pipewire-upstream/ubuntu bionic main include: @@ -50,6 +52,9 @@ AppDir: - libva-x11-2 - libvdpau1 - libgstreamer-plugins-base1.0-0 + - libwayland-cursor0 + - libwayland-egl1 + - libpulse0 exclude: - humanity-icon-theme - hicolor-icon-theme From 7f20dac5474443769904f4488f3eed51a1e1886d Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 23 Mar 2023 10:04:44 +0800 Subject: [PATCH 026/366] opt: reuse deps workflows --- .github/workflows/bridge.yml | 73 ++++++++++++ .github/workflows/flutter-ci.yml | 155 +------------------------ .github/workflows/flutter-nightly.yml | 155 +------------------------ .github/workflows/vcpkg-deps-linux.yml | 94 +++++++++++++++ .gitignore | 3 + flutter/pubspec.yaml | 2 +- 6 files changed, 175 insertions(+), 307 deletions(-) create mode 100644 .github/workflows/bridge.yml create mode 100644 .github/workflows/vcpkg-deps-linux.yml diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml new file mode 100644 index 000000000..5d6ebbb57 --- /dev/null +++ b/.github/workflows/bridge.yml @@ -0,0 +1,73 @@ +# This yaml shares the build bridge steps with ci and nightly. +name: Build flutter-rust-bridge + +on: + workflow_call: + +jobs: + generate_bridge: + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { + target: x86_64-unknown-linux-gnu, + os: ubuntu-18.04, + extra-build-args: "", + } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Install prerequisites + run: | + sudo apt update -y + sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang cmake libclang-dev ninja-build llvm-dev libclang-10-dev llvm-10-dev pkg-config + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: bridge-${{ matrix.job.os }} + workspace: "/tmp/flutter_rust_bridge/frb_codegen" + + - name: Cache Bridge + id: cache-bridge + uses: actions/cache@v3 + with: + path: /tmp/flutter_rust_bridge + key: vcpkg-${{ matrix.job.arch }} + + - name: Install flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + cache: true + + - name: Install flutter rust bridge deps + shell: bash + run: | + cargo install flutter_rust_bridge_codegen + pushd flutter && flutter pub get && popd + + - name: Run flutter rust bridge + run: | + ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart + + - name: Upload Artifact + uses: actions/upload-artifact@master + with: + name: bridge-artifact + path: | + ./src/bridge_generated.rs + ./src/bridge_generated.io.rs + ./flutter/lib/generated_bridge.dart + ./flutter/lib/generated_bridge.freezed.dart \ No newline at end of file diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index a4ddb15b5..ff3f29ee7 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -164,161 +164,10 @@ jobs: ./build.py --flutter ${{ matrix.job.extra-build-args }} build-vcpkg-deps-linux: - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: true - matrix: - job: - # - { arch: armv7, os: ubuntu-20.04 } - - { arch: x86_64, os: ubuntu-20.04 } - - { arch: aarch64, os: ubuntu-20.04 } - steps: - - name: Create vcpkg artifacts folder - run: mkdir -p /opt/artifacts - - - name: Cache Vcpkg - id: cache-vcpkg - uses: actions/cache@v3 - with: - path: /opt/artifacts - key: vcpkg-${{ matrix.job.arch }} - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Run vcpkg install on ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - githubToken: ${{ github.token }} - setup: | - ls -l "/opt/artifacts" - dockerRunArgs: | - --volume "/opt/artifacts:/artifacts" - shell: /bin/bash - install: | - apt update -y - case "${{ matrix.job.arch }}" in - x86_64) - # CMake 3.15+ - apt install -y gpg wget ca-certificates - echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ bionic main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null - wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null - apt update -y - apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev - ;; - aarch64|armv7) - apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev automake libtool - esac - cmake --version - gcc -v - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - case "${{ matrix.job.arch }}" in - x86_64) - export VCPKG_FORCE_SYSTEM_BINARIES=1 - pushd /artifacts - git clone https://github.com/microsoft/vcpkg.git || true - pushd vcpkg - git reset --hard ${{ env.VCPKG_COMMIT_ID }} - ./bootstrap-vcpkg.sh - ./vcpkg install libvpx libyuv opus - ;; - aarch64|armv7) - pushd /artifacts - # libyuv - git clone https://chromium.googlesource.com/libyuv/libyuv || true - pushd libyuv - git pull - mkdir -p build - pushd build - mkdir -p /artifacts/vcpkg/installed - cmake .. -DCMAKE_INSTALL_PREFIX=/artifacts/vcpkg/installed - make -j4 && make install - popd - popd - # libopus, ubuntu 18.04 prebuilt is not be compiled with -fPIC - wget -O opus.tar.gz http://archive.ubuntu.com/ubuntu/pool/main/o/opus/opus_1.1.2.orig.tar.gz - tar -zxvf opus.tar.gz; ls -l - pushd opus-1.1.2 - ./autogen.sh; ./configure --prefix=/artifacts/vcpkg/installed - make -j4; make install - ;; - esac - - name: Upload artifacts - uses: actions/upload-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: | - /opt/artifacts/vcpkg/installed + uses: ./.github/workflows/vcpkg-deps-linux.yml generate-bridge-linux: - name: generate bridge - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: true - matrix: - job: - - { - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-args: "", - } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install prerequisites - run: | - sudo apt update -y - sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang cmake libclang-dev ninja-build llvm-dev libclang-10-dev llvm-10-dev pkg-config - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: bridge-${{ matrix.job.os }} - workspace: "/tmp/flutter_rust_bridge/frb_codegen" - - - name: Cache Bridge - id: cache-bridge - uses: actions/cache@v3 - with: - path: /tmp/flutter_rust_bridge - key: vcpkg-${{ matrix.job.arch }} - - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - name: Install flutter rust bridge deps - shell: bash - run: | - cargo install flutter_rust_bridge_codegen - pushd flutter && flutter pub get && popd - - - name: Run flutter rust bridge - run: | - ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - - name: Upload Artifact - uses: actions/upload-artifact@master - with: - name: bridge-artifact - path: | - ./src/bridge_generated.rs - ./src/bridge_generated.io.rs - ./flutter/lib/generated_bridge.dart - ./flutter/lib/generated_bridge.freezed.dart + uses: ./.github/workflows/bridge.yml build-rustdesk-android: needs: [generate-bridge-linux] diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index e2093afc1..adef45cc1 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -264,161 +264,10 @@ jobs: rustdesk*-${{ matrix.job.target }}.dmg build-vcpkg-deps-linux: - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - # - { arch: armv7, os: ubuntu-20.04 } - - { arch: x86_64, os: ubuntu-20.04 } - - { arch: aarch64, os: ubuntu-20.04 } - steps: - - name: Create vcpkg artifacts folder - run: mkdir -p /opt/artifacts - - - name: Cache Vcpkg - id: cache-vcpkg - uses: actions/cache@v3 - with: - path: /opt/artifacts - key: vcpkg-${{ matrix.job.arch }} - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Run vcpkg install on ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - githubToken: ${{ github.token }} - setup: | - ls -l "/opt/artifacts" - dockerRunArgs: | - --volume "/opt/artifacts:/artifacts" - shell: /bin/bash - install: | - apt update -y - case "${{ matrix.job.arch }}" in - x86_64) - # CMake 3.15+ - apt install -y gpg wget ca-certificates - echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ bionic main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null - wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null - apt update -y - apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev - ;; - aarch64|armv7) - apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev automake libtool - esac - cmake --version - gcc -v - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - case "${{ matrix.job.arch }}" in - x86_64) - export VCPKG_FORCE_SYSTEM_BINARIES=1 - pushd /artifacts - git clone https://github.com/microsoft/vcpkg.git || true - pushd vcpkg - git reset --hard ${{ env.VCPKG_COMMIT_ID }} - ./bootstrap-vcpkg.sh - ./vcpkg install libvpx libyuv opus - ;; - aarch64|armv7) - pushd /artifacts - # libyuv - git clone https://chromium.googlesource.com/libyuv/libyuv || true - pushd libyuv - git pull - mkdir -p build - pushd build - mkdir -p /artifacts/vcpkg/installed - cmake .. -DCMAKE_INSTALL_PREFIX=/artifacts/vcpkg/installed - make -j4 && make install - popd - popd - # libopus, ubuntu 18.04 prebuilt is not be compiled with -fPIC - wget -O opus.tar.gz http://archive.ubuntu.com/ubuntu/pool/main/o/opus/opus_1.1.2.orig.tar.gz - tar -zxvf opus.tar.gz; ls -l - pushd opus-1.1.2 - ./autogen.sh; ./configure --prefix=/artifacts/vcpkg/installed - make -j4; make install - ;; - esac - - name: Upload artifacts - uses: actions/upload-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: | - /opt/artifacts/vcpkg/installed + uses: ./.github/workflows/vcpkg-deps-linux.yml generate-bridge-linux: - name: generate bridge - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { - target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, - extra-build-args: "", - } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install prerequisites - run: | - sudo apt update -y - sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang cmake libclang-dev ninja-build llvm-dev libclang-10-dev llvm-10-dev pkg-config - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: bridge-${{ matrix.job.os }} - workspace: "/tmp/flutter_rust_bridge/frb_codegen" - - - name: Cache Bridge - id: cache-bridge - uses: actions/cache@v3 - with: - path: /tmp/flutter_rust_bridge - key: vcpkg-${{ matrix.job.arch }} - - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - name: Install flutter rust bridge deps - shell: bash - run: | - cargo install flutter_rust_bridge_codegen - pushd flutter && flutter pub get && popd - - - name: Run flutter rust bridge - run: | - ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - - name: Upload Artifact - uses: actions/upload-artifact@master - with: - name: bridge-artifact - path: | - ./src/bridge_generated.rs - ./src/bridge_generated.io.rs - ./flutter/lib/generated_bridge.dart - ./flutter/lib/generated_bridge.freezed.dart + uses: ./.github/workflows/bridge.yml build-rustdesk-android: needs: [generate-bridge-linux] diff --git a/.github/workflows/vcpkg-deps-linux.yml b/.github/workflows/vcpkg-deps-linux.yml new file mode 100644 index 000000000..89381114a --- /dev/null +++ b/.github/workflows/vcpkg-deps-linux.yml @@ -0,0 +1,94 @@ +name: Build vcpkg dependencies for linux clients + +on: + workflow_call: + +jobs: + build-vcpkg-deps-linux: + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: true + matrix: + job: + # - { arch: armv7, os: ubuntu-20.04 } + - { arch: x86_64, os: ubuntu-20.04 } + - { arch: aarch64, os: ubuntu-20.04 } + steps: + - name: Create vcpkg artifacts folder + run: mkdir -p /opt/artifacts + + - name: Cache Vcpkg + id: cache-vcpkg + uses: actions/cache@v3 + with: + path: /opt/artifacts + key: vcpkg-${{ matrix.job.arch }} + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Run vcpkg install on ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04 + githubToken: ${{ github.token }} + setup: | + ls -l "/opt/artifacts" + dockerRunArgs: | + --volume "/opt/artifacts:/artifacts" + shell: /bin/bash + install: | + apt update -y + case "${{ matrix.job.arch }}" in + x86_64) + # CMake 3.15+ + apt install -y gpg wget ca-certificates + echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ bionic main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null + wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null + apt update -y + apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev + ;; + aarch64|armv7) + apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev automake libtool + esac + cmake --version + gcc -v + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + case "${{ matrix.job.arch }}" in + x86_64) + export VCPKG_FORCE_SYSTEM_BINARIES=1 + pushd /artifacts + git clone https://github.com/microsoft/vcpkg.git || true + pushd vcpkg + git reset --hard ${{ env.VCPKG_COMMIT_ID }} + ./bootstrap-vcpkg.sh + ./vcpkg install libvpx libyuv opus + ;; + aarch64|armv7) + pushd /artifacts + # libyuv + git clone https://chromium.googlesource.com/libyuv/libyuv || true + pushd libyuv + git pull + mkdir -p build + pushd build + mkdir -p /artifacts/vcpkg/installed + cmake .. -DCMAKE_INSTALL_PREFIX=/artifacts/vcpkg/installed + make -j4 && make install + popd + popd + # libopus, ubuntu 18.04 prebuilt is not be compiled with -fPIC + wget -O opus.tar.gz http://archive.ubuntu.com/ubuntu/pool/main/o/opus/opus_1.1.2.orig.tar.gz + tar -zxvf opus.tar.gz; ls -l + pushd opus-1.1.2 + ./autogen.sh; ./configure --prefix=/artifacts/vcpkg/installed + make -j4; make install + ;; + esac + - name: Upload artifacts + uses: actions/upload-artifact@master + with: + name: vcpkg-artifact-${{ matrix.job.arch }} + path: | + /opt/artifacts/vcpkg/installed \ No newline at end of file diff --git a/.gitignore b/.gitignore index a71c71a4e..f17f8155b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ rustdesk appimage/AppDir appimage/*.AppImage appimage/appimage-build +appimage/*.xz # flutter flutter/linux/build/** flutter/linux/cmake-build-debug/** @@ -38,6 +39,8 @@ flatpak/.flatpak-builder/shared-modules/** flatpak/.flatpak-builder/shared-modules/*.tar.xz flatpak/.flatpak-builder/debian-binary flatpak/build/** +flatpak/repo/** +flatpak/*.flatpak # bridge file lib/generated_bridge.dart # vscode devcontainer diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 5b98e85c0..abe5c0f38 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -92,7 +92,7 @@ dependencies: password_strength: ^0.2.0 flutter_launcher_icons: ^0.11.0 flutter_keyboard_visibility: ^5.4.0 - texture_rgba_renderer: ^0.0.13 + texture_rgba_renderer: ^0.0.14 percent_indicator: ^4.2.2 dropdown_button2: ^2.0.0 From b763ec30802222c64252c1a595a1cb754886c949 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 23 Mar 2023 14:31:50 +0800 Subject: [PATCH 027/366] revert #560 for #3748 --- libs/hbb_common/protos/message.proto | 2 - libs/scrap/src/common/vpxcodec.rs | 3 +- src/client.rs | 58 ++++---------------- src/client/helper.rs | 80 ++-------------------------- src/client/io_loop.rs | 2 - src/server/audio_service.rs | 3 -- src/server/connection.rs | 18 +++---- src/server/video_service.rs | 1 - 8 files changed, 20 insertions(+), 147 deletions(-) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index bb4492b74..6295c160b 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -25,7 +25,6 @@ message VideoFrame { EncodedVideoFrames h264s = 10; EncodedVideoFrames h265s = 11; } - int64 timestamp = 9; } message IdPk { @@ -513,7 +512,6 @@ message AudioFormat { message AudioFrame { bytes data = 1; - int64 timestamp = 2; } // Notify peer to show message box. diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index b91871c8f..3df9c0461 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -4,7 +4,7 @@ use hbb_common::anyhow::{anyhow, Context}; use hbb_common::message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}; -use hbb_common::{get_time, ResultType}; +use hbb_common::ResultType; use crate::STRIDE_ALIGN; use crate::{codec::EncoderApi, ImageFormat}; @@ -287,7 +287,6 @@ impl VpxEncoder { frames: vp9s.into(), ..Default::default() }); - vf.timestamp = get_time(); msg_out.set_video_frame(vf); msg_out } diff --git a/src/client.rs b/src/client.rs index 0cd90e1f3..607b96635 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,7 +1,7 @@ use std::{ collections::HashMap, net::SocketAddr, - ops::{Deref, Not}, + ops::Deref, str::FromStr, sync::{mpsc, Arc, Mutex, RwLock}, }; @@ -39,7 +39,6 @@ use hbb_common::{ tokio::time::Duration, AddrMangle, ResultType, Stream, }; -pub use helper::LatencyController; pub use helper::*; use scrap::{ codec::{Decoder, DecoderCfg}, @@ -707,19 +706,9 @@ pub struct AudioHandler { #[cfg(not(any(target_os = "android", target_os = "linux")))] audio_stream: Option>, channels: u16, - latency_controller: Arc>, - ignore_count: i32, } impl AudioHandler { - /// Create a new audio handler. - pub fn new(latency_controller: Arc>) -> Self { - AudioHandler { - latency_controller, - ..Default::default() - } - } - /// Start the audio playback. #[cfg(target_os = "linux")] fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> { @@ -802,24 +791,8 @@ impl AudioHandler { } /// Handle audio frame and play it. + #[inline] pub fn handle_frame(&mut self, frame: AudioFrame) { - if frame.timestamp != 0 { - if self - .latency_controller - .lock() - .unwrap() - .check_audio(frame.timestamp) - .not() - { - self.ignore_count += 1; - if self.ignore_count == 100 { - self.ignore_count = 0; - log::debug!("100 audio frames are ignored"); - } - return; - } - } - #[cfg(not(any(target_os = "android", target_os = "linux")))] if self.audio_stream.is_none() { return; @@ -914,7 +887,6 @@ impl AudioHandler { /// Video handler for the [`Client`]. pub struct VideoHandler { decoder: Decoder, - latency_controller: Arc>, pub rgb: Vec, recorder: Arc>>, record: bool, @@ -922,7 +894,7 @@ pub struct VideoHandler { impl VideoHandler { /// Create a new video handler. - pub fn new(latency_controller: Arc>) -> Self { + pub fn new() -> Self { VideoHandler { decoder: Decoder::new(DecoderCfg { vpx: VpxDecoderConfig { @@ -930,7 +902,6 @@ impl VideoHandler { num_threads: (num_cpus::get() / 2) as _, }, }), - latency_controller, rgb: Default::default(), recorder: Default::default(), record: false, @@ -938,14 +909,8 @@ impl VideoHandler { } /// Handle a new video frame. + #[inline] pub fn handle_frame(&mut self, vf: VideoFrame) -> ResultType { - if vf.timestamp != 0 { - // Update the latency controller with the latest timestamp. - self.latency_controller - .lock() - .unwrap() - .update_video(vf.timestamp); - } match &vf.union { Some(frame) => { let res = self.decoder.handle_video_frame( @@ -994,6 +959,7 @@ impl VideoHandler { } else { self.recorder = Default::default(); } + self.record = start; } } @@ -1696,11 +1662,8 @@ where let (video_sender, video_receiver) = mpsc::channel::(); let mut video_callback = video_callback; - let latency_controller = LatencyController::new(); - let latency_controller_cl = latency_controller.clone(); - std::thread::spawn(move || { - let mut video_handler = VideoHandler::new(latency_controller); + let mut video_handler = VideoHandler::new(); loop { if let Ok(data) = video_receiver.recv() { match data { @@ -1723,19 +1686,16 @@ where } log::info!("Video decoder loop exits"); }); - let audio_sender = start_audio_thread(Some(latency_controller_cl)); + let audio_sender = start_audio_thread(); return (video_sender, audio_sender); } /// Start an audio thread /// Return a audio [`MediaSender`] -pub fn start_audio_thread( - latency_controller: Option>>, -) -> MediaSender { - let latency_controller = latency_controller.unwrap_or(LatencyController::new()); +pub fn start_audio_thread() -> MediaSender { let (audio_sender, audio_receiver) = mpsc::channel::(); std::thread::spawn(move || { - let mut audio_handler = AudioHandler::new(latency_controller); + let mut audio_handler = AudioHandler::default(); loop { if let Ok(data) = audio_receiver.recv() { match data { diff --git a/src/client/helper.rs b/src/client/helper.rs index 20acd811a..a9696a8e8 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -1,82 +1,8 @@ -use std::{ - sync::{Arc, Mutex}, - time::Instant, -}; - use hbb_common::{ - log, - message_proto::{video_frame, VideoFrame, Message, VoiceCallRequest, VoiceCallResponse}, get_time, + get_time, + message_proto::{video_frame, Message, VideoFrame, VoiceCallRequest, VoiceCallResponse}, }; -const MAX_LATENCY: i64 = 500; -const MIN_LATENCY: i64 = 100; - -/// Latency controller for syncing audio with the video stream. -/// Only sync the audio to video, not the other way around. -#[derive(Debug)] -pub struct LatencyController { - last_video_remote_ts: i64, // generated on remote device - update_time: Instant, - allow_audio: bool, - audio_only: bool -} - -impl Default for LatencyController { - fn default() -> Self { - Self { - last_video_remote_ts: Default::default(), - update_time: Instant::now(), - allow_audio: Default::default(), - audio_only: false - } - } -} - -impl LatencyController { - /// Create a new latency controller. - pub fn new() -> Arc> { - Arc::new(Mutex::new(LatencyController::default())) - } - - /// Set whether this [LatencyController] should be working in audio only mode. - pub fn set_audio_only(&mut self, only: bool) { - self.audio_only = only; - } - - /// Update the latency controller with the latest video timestamp. - pub fn update_video(&mut self, timestamp: i64) { - self.last_video_remote_ts = timestamp; - self.update_time = Instant::now(); - } - - /// Check if the audio should be played based on the current latency. - pub fn check_audio(&mut self, timestamp: i64) -> bool { - // Compute audio latency. - let expected = self.update_time.elapsed().as_millis() as i64 + self.last_video_remote_ts; - let latency = if self.audio_only { - expected - } else { - expected - timestamp - }; - // Set MAX and MIN, avoid fixing too frequently. - if self.allow_audio { - if latency.abs() > MAX_LATENCY { - log::debug!("LATENCY > {}ms cut off, latency:{}", MAX_LATENCY, latency); - self.allow_audio = false; - } - } else { - if latency.abs() < MIN_LATENCY { - log::debug!("LATENCY < {}ms resume, latency:{}", MIN_LATENCY, latency); - self.allow_audio = true; - } - } - // No video frame here, which means the update time is not up to date. - // We manually update the time here. - self.update_time = Instant::now(); - self.allow_audio - } -} - #[derive(PartialEq, Debug, Clone)] pub enum CodecFormat { VP9, @@ -135,4 +61,4 @@ pub fn new_voice_call_response(request_timestamp: i64, accepted: bool) -> Messag let mut msg = Message::new(); msg.set_voice_call_response(resp); msg -} \ No newline at end of file +} diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 989c7c95b..4a020d197 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -318,13 +318,11 @@ impl Remote { let mut msg = Message::new(); msg.set_audio_frame(frame.clone()); tx_audio.send(Data::Message(msg)).ok(); - log::debug!("send audio frame {}", frame.timestamp); } Some(message::Union::Misc(misc)) => { let mut msg = Message::new(); msg.set_misc(misc.clone()); tx_audio.send(Data::Message(msg)).ok(); - log::debug!("send audio misc {:?}", misc.audio_format()); } _ => {} }, diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index c7a720ded..bef1ac1f3 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -13,7 +13,6 @@ // https://github.com/krruzic/pulsectl use super::*; -use hbb_common::get_time; use magnum_opus::{Application::*, Channels::*, Encoder}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -349,7 +348,6 @@ fn send_f32(data: &[f32], encoder: &mut Encoder, sp: &GenericService) { let mut msg_out = Message::new(); msg_out.set_audio_frame(AudioFrame { data: data.into(), - timestamp: get_time(), ..Default::default() }); sp.send(msg_out); @@ -369,7 +367,6 @@ fn send_f32(data: &[f32], encoder: &mut Encoder, sp: &GenericService) { let mut msg_out = Message::new(); msg_out.set_audio_frame(AudioFrame { data: data.into(), - timestamp: get_time(), ..Default::default() }); sp.send(msg_out); diff --git a/src/server/connection.rs b/src/server/connection.rs index 4b65013a3..1f89d905f 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -7,8 +7,7 @@ use crate::common::update_clipboard; use crate::portable_service::client as portable_client; use crate::{ client::{ - new_voice_call_request, new_voice_call_response, start_audio_thread, LatencyController, - MediaData, MediaSender, + new_voice_call_request, new_voice_call_response, start_audio_thread, MediaData, MediaSender, }, common::{get_default_sound_input, set_sound_input}, video_service, @@ -843,15 +842,16 @@ impl Connection { pi.hostname = DEVICE_NAME.lock().unwrap().clone(); pi.platform = "Android".into(); } - let mut platform_additions = serde_json::Map::new(); #[cfg(target_os = "linux")] { + let mut platform_additions = serde_json::Map::new(); if crate::platform::current_is_wayland() { platform_additions.insert("is_wayland".into(), json!(true)); } - } - if !platform_additions.is_empty() { - pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into()); + if !platform_additions.is_empty() { + pi.platform_additions = + serde_json::to_string(&platform_additions).unwrap_or("".into()); + } } #[cfg(feature = "hwcodec")] @@ -1612,11 +1612,7 @@ impl Connection { if !self.disable_audio { // Drop the audio sender previously. drop(std::mem::replace(&mut self.audio_sender, None)); - // Start a audio thread to play the audio sent by peer. - let latency_controller = LatencyController::new(); - // No video frame will be sent here, so we need to disable latency controller, or audio check may fail. - latency_controller.lock().unwrap().set_audio_only(true); - self.audio_sender = Some(start_audio_thread(Some(latency_controller))); + self.audio_sender = Some(start_audio_thread()); allow_err!(self .audio_sender .as_ref() diff --git a/src/server/video_service.rs b/src/server/video_service.rs index affb5eb17..9d6eec85c 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -787,7 +787,6 @@ fn create_msg(vp9s: Vec) -> Message { frames: vp9s.into(), ..Default::default() }); - vf.timestamp = hbb_common::get_time(); msg_out.set_video_frame(vf); msg_out } From 6ad3064ef900ffd0e211a8c5bc2a2c149564bba1 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 23 Mar 2023 17:02:55 +0800 Subject: [PATCH 028/366] fix ci --- libs/scrap/src/common/hwcodec.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 3c86af94e..fa1c00976 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -107,7 +107,6 @@ impl EncoderApi for HwEncoder { DataFormat::H264 => vf.set_h264s(frames), DataFormat::H265 => vf.set_h265s(frames), } - vf.timestamp = get_time(); msg_out.set_video_frame(vf); Ok(msg_out) } else { From d7f16a5c0aef080be0c1fdf81dacef32bc87d88a Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 23 Mar 2023 17:44:44 +0800 Subject: [PATCH 029/366] remove xrandr parser Signed-off-by: fufesou --- Cargo.lock | 72 ++------------------------- Cargo.toml | 1 - src/platform/linux.rs | 111 +++++++++++++++++++++++++++++++----------- 3 files changed, 86 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b62f82745..e632114af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1224,38 +1224,14 @@ dependencies = [ "zvariant", ] -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core 0.10.2", - "darling_macro 0.10.2", -] - [[package]] name = "darling" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.51", - "quote 1.0.23", - "strsim 0.9.3", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -1272,24 +1248,13 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core 0.10.2", - "quote 1.0.23", - "syn 1.0.109", -] - [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core 0.13.4", + "darling_core", "quote 1.0.23", "syn 1.0.109", ] @@ -1488,18 +1453,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive_setters" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1cf41b4580a37cca5ef2ada2cc43cf5d6be3983f4522e83010d67ab6925e84b" -dependencies = [ - "darling 0.10.2", - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", -] - [[package]] name = "detect-desktop-environment" version = "0.2.0" @@ -3705,7 +3658,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro-crate 1.3.1", "proc-macro2 1.0.51", "quote 1.0.23", @@ -5048,7 +5001,6 @@ dependencies = [ "winreg 0.10.1", "winres", "wol-rs", - "xrandr-parser", ] [[package]] @@ -5648,12 +5600,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - [[package]] name = "strsim" version = "0.10.0" @@ -7182,16 +7128,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" -[[package]] -name = "xrandr-parser" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5af43ba661cee58bd86b9f81a899e45a15ac7f42fa4401340f73c0c2950030c1" -dependencies = [ - "derive_setters", - "serde 1.0.154", -] - [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index 7ad979f8c..bf81664e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,6 @@ mouce = { git="https://github.com/fufesou/mouce.git" } evdev = { git="https://github.com/fufesou/evdev" } dbus = "0.9" dbus-crossroads = "0.5" -xrandr-parser = "0.3.0" [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" diff --git a/src/platform/linux.rs b/src/platform/linux.rs index bde2a5c58..6980385c8 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,12 +1,11 @@ use super::{CursorData, ResultType}; pub use hbb_common::platform::linux::*; use hbb_common::{ - allow_err, - anyhow::anyhow, - bail, + allow_err, bail, libc::{c_char, c_int, c_long, c_void}, log, message_proto::Resolution, + regex::{Captures, Regex}, }; use std::{ cell::RefCell, @@ -18,7 +17,6 @@ use std::{ }, time::{Duration, Instant}, }; -use xrandr_parser::Parser; type Xdo = *const c_void; @@ -671,44 +669,99 @@ pub fn get_double_click_time() -> u32 { } } +#[inline] +fn get_width_height_from_captures<'t>(caps: &Captures<'t>) -> Option<(i32, i32)> { + match (caps.name("width"), caps.name("height")) { + (Some(width), Some(height)) => { + match ( + width.as_str().parse::(), + height.as_str().parse::(), + ) { + (Ok(width), Ok(height)) => { + return Some((width, height)); + } + _ => {} + } + } + _ => {} + } + None +} + +#[inline] +fn get_xrandr_conn_pat(name: &str) -> String { + format!( + r"{}\s+connected.+?(?P\d+)x(?P\d+)\+(?P\d+)\+(?P\d+).*?\n", + name + ) +} + pub fn resolutions(name: &str) -> Vec { + let resolutions_pat = r"(?P(\s*\d+x\d+\s+\d+.*\n)+)"; + let connected_pat = get_xrandr_conn_pat(name); let mut v = vec![]; - let mut parser = Parser::new(); - if parser.parse().is_ok() { - if let Ok(connector) = parser.get_connector(name) { - if let Ok(resolutions) = &connector.available_resolutions() { - for r in resolutions { - if let Ok(width) = r.horizontal.parse::() { - if let Ok(height) = r.vertical.parse::() { - let resolution = Resolution { - width, - height, - ..Default::default() - }; - if !v.contains(&resolution) { - v.push(resolution); + if let Ok(re) = Regex::new(&format!("{}{}", connected_pat, resolutions_pat)) { + match run_cmds("xrandr --query | tr -s ' '".to_owned()) { + Ok(xrandr_output) => { + // There'are different kinds of xrandr output. + /* + 1. + Screen 0: minimum 320 x 175, current 1920 x 1080, maximum 1920 x 1080 + default connected 1920x1080+0+0 0mm x 0mm + 1920x1080 10.00* + 1280x720 25.00 + 1680x1050 60.00 + Virtual2 disconnected (normal left inverted right x axis y axis) + Virtual3 disconnected (normal left inverted right x axis y axis) + + XWAYLAND0 connected primary 1920x984+0+0 (normal left inverted right x axis y axis) 0mm x 0mm + Virtual1 connected primary 1920x984+0+0 (normal left inverted right x axis y axis) 0mm x 0mm + HDMI-0 connected (normal left inverted right x axis y axis) + + rdp0 connected primary 1920x1080+0+0 0mm x 0mm + */ + if let Some(caps) = re.captures(&xrandr_output) { + if let Some(resolutions) = caps.name("resolutions") { + let resolution_pat = + r"\s*(?P\d+)x(?P\d+)\s+(?P(\d+\.\d+[* ]*)+)\s*\n"; + let resolution_re = Regex::new(&format!(r"{}", resolution_pat)).unwrap(); + for resolution_caps in resolution_re.captures_iter(resolutions.as_str()) { + if let Some((width, height)) = + get_width_height_from_captures(&resolution_caps) + { + let resolution = Resolution { + width, + height, + ..Default::default() + }; + if !v.contains(&resolution) { + v.push(resolution); + } } } } } } + Err(e) => log::error!("Failed to run xrandr query, {}", e), } } + v } pub fn current_resolution(name: &str) -> ResultType { - let mut parser = Parser::new(); - parser.parse().map_err(|e| anyhow!(e))?; - let connector = parser.get_connector(name).map_err(|e| anyhow!(e))?; - let r = connector.current_resolution(); - let width = r.horizontal.parse::()?; - let height = r.vertical.parse::()?; - Ok(Resolution { - width, - height, - ..Default::default() - }) + let xrandr_output = run_cmds("xrandr --query | tr -s ' '".to_owned())?; + let re = Regex::new(&get_xrandr_conn_pat(name))?; + if let Some(caps) = re.captures(&xrandr_output) { + if let Some((width, height)) = get_width_height_from_captures(&caps) { + return Ok(Resolution { + width, + height, + ..Default::default() + }); + } + } + bail!("Failed to find current resolution for {}", name); } pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<()> { From 8b455d586b1e4e536342d815dcbcd81ccd5cd1a9 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 23 Mar 2023 21:17:02 +0800 Subject: [PATCH 030/366] fix, linux as controlled side, change resolution Signed-off-by: fufesou --- src/server/video_service.rs | 22 ++-------------------- src/server/wayland.rs | 13 ------------- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 9d6eec85c..3b9db0697 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -431,7 +431,7 @@ fn check_displays_new() -> Option> { } } -fn check_displays_changed() -> Option { +fn check_get_displays_changed_msg() -> Option { let displays = check_displays_new()?; let (current, displays) = get_displays_2(&displays); let mut pi = PeerInfo { @@ -585,11 +585,8 @@ fn run(sp: GenericService) -> ResultType<()> { bail!("SWITCH"); } - if let Some(msg_out) = check_displays_changed() { + if let Some(msg_out) = check_get_displays_changed_msg() { sp.send(msg_out); - } - - if c.ndisplay != get_display_num() { log::info!("Displays changed"); *SWITCH.lock().unwrap() = true; bail!("SWITCH"); @@ -845,21 +842,6 @@ pub fn handle_one_frame_encoded( Ok(send_conn_ids) } -fn get_display_num() -> usize { - #[cfg(target_os = "linux")] - { - if !scrap::is_x11() { - return if let Ok(n) = super::wayland::get_display_num() { - n - } else { - 0 - }; - } - } - - LAST_SYNC_DISPLAYS.read().unwrap().len() -} - pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { let mut displays = Vec::new(); let mut primary = 0; diff --git a/src/server/wayland.rs b/src/server/wayland.rs index 954f1ed1d..73a022871 100644 --- a/src/server/wayland.rs +++ b/src/server/wayland.rs @@ -230,19 +230,6 @@ pub(super) fn get_primary() -> ResultType { } } -pub(super) fn get_display_num() -> ResultType { - let addr = *CAP_DISPLAY_INFO.read().unwrap(); - if addr != 0 { - let cap_display_info: *const CapDisplayInfo = addr as _; - unsafe { - let cap_display_info = &*cap_display_info; - Ok(cap_display_info.num) - } - } else { - bail!("Failed to get capturer display info"); - } -} - #[allow(dead_code)] pub(super) fn release_resource() { if scrap::is_x11() { From f05ac099e7a1797dd2468de5d02202c49d0d7abd Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 23 Mar 2023 22:52:58 +0800 Subject: [PATCH 031/366] workaround for --- flutter/lib/models/model.dart | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index b8d4f2ebc..c5c3999ee 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -535,11 +535,18 @@ class FfiModel with ChangeNotifier { void setViewOnly(String id, bool value) { if (version_cmp(_pi.version, '1.2.0') < 0) return; - if (value) { - ShowRemoteCursorState.find(id).value = value; - } else { - ShowRemoteCursorState.find(id).value = - bind.sessionGetToggleOptionSync(id: id, arg: 'show-remote-cursor'); + // tmp fix for https://github.com/rustdesk/rustdesk/pull/3706#issuecomment-1481242389 + // because below rx not used in mobile version, so not initialized, below code will cause crash + // current our flutter code quality is fucking shit now. !!!!!!!!!!!!!!!! + try { + if (value) { + ShowRemoteCursorState.find(id).value = value; + } else { + ShowRemoteCursorState.find(id).value = + bind.sessionGetToggleOptionSync(id: id, arg: 'show-remote-cursor'); + } + } catch (e) { + // } if (_viewOnly != value) { _viewOnly = value; From 18022cf8253bca0ad7b7f75b714493cace8d462a Mon Sep 17 00:00:00 2001 From: NicKoehler Date: Thu, 23 Mar 2023 18:54:49 +0100 Subject: [PATCH 032/366] fix for #3746 --- flutter/lib/desktop/widgets/remote_toolbar.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 804d74499..c89bfa766 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -394,7 +394,7 @@ class _RemoteMenubarState extends State { } if (PrivacyModeState.find(widget.id).isFalse && - stateGlobal.displaysCount.value > 1) { + widget.ffi.ffiModel.pi.displays.length > 1) { toolbarItems.add( bind.mainGetUserDefaultOption(key: 'show_monitors_toolbar') == 'Y' ? _MultiMonitorMenu(id: widget.id, ffi: widget.ffi) @@ -2252,10 +2252,10 @@ class _MultiMonitorMenu extends StatelessWidget { final FFI ffi; const _MultiMonitorMenu({ - super.key, + Key? key, required this.id, required this.ffi, - }); + }) : super(key: key); @override Widget build(BuildContext context) { From 29f4106c727002553b4b80a641534bab6b96c58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=85=C3=BC?= <53787985+LaiYueTing@users.noreply.github.com> Date: Fri, 24 Mar 2023 02:55:25 +0800 Subject: [PATCH 033/366] Update tw.rs --- src/lang/tw.rs | 968 ++++++++++++++++++++++++------------------------- 1 file changed, 484 insertions(+), 484 deletions(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index d562cdfb4..d80aea781 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -1,484 +1,484 @@ -lazy_static::lazy_static! { -pub static ref T: std::collections::HashMap<&'static str, &'static str> = - [ - ("Status", "狀態"), - ("Your Desktop", "您的桌面"), - ("desk_tip", "您可以透過此 ID 及密碼存取您的桌面"), - ("Password", "密碼"), - ("Ready", "就緒"), - ("Established", "已建立"), - ("connecting_status", "正在連接至 RustDesk 網路..."), - ("Enable Service", "啟用服務"), - ("Start Service", "啟動服務"), - ("Service is running", "服務正在運行"), - ("Service is not running", "服務尚未執行"), - ("not_ready_status", "尚未就緒。請檢查您的網路連線"), - ("Control Remote Desktop", "控制遠端桌面"), - ("Transfer File", "傳輸檔案"), - ("Connect", "連接"), - ("Recent Sessions", "近期的工作階段"), - ("Address Book", "通訊錄"), - ("Confirmation", "確認"), - ("TCP Tunneling", "TCP 通道"), - ("Remove", "移除"), - ("Refresh random password", "重新產生隨機密碼"), - ("Set your own password", "自行設置密碼"), - ("Enable Keyboard/Mouse", "啟用鍵盤/滑鼠"), - ("Enable Clipboard", "啟用剪貼簿"), - ("Enable File Transfer", "啟用檔案傳輸"), - ("Enable TCP Tunneling", "啟用 TCP 通道"), - ("IP Whitelisting", "IP 白名單"), - ("ID/Relay Server", "ID/轉送伺服器"), - ("Import Server Config", "匯入伺服器設定"), - ("Export Server Config", "導出服務器配置"), - ("Import server configuration successfully", "匯入伺服器設定成功"), - ("Export server configuration successfully", "導出服務器配置信息成功"), - ("Invalid server configuration", "無效的伺服器設定"), - ("Clipboard is empty", "剪貼簿是空的"), - ("Stop service", "停止服務"), - ("Change ID", "更改 ID"), - ("Your new ID", "你的新 ID"), - ("length %min% to %max%", "長度在 %min% 與 %max% 之間"), - ("starts with a letter", "以字母開頭"), - ("allowed characters", "使用允許的字元"), - ("id_change_tip", "僅能使用以下字元:a-z、A-Z、0-9、_ (底線)。首字元必須為 a-z 或 A-Z。長度介於 6 到 16 之間。"), - ("Website", "網站"), - ("About", "關於"), - ("Slogan_tip", ""), - ("Privacy Statement", "隱私聲明"), - ("Mute", "靜音"), - ("Build Date", "建構日期"), - ("Version", "版本"), - ("Home", "主頁"), - ("Audio Input", "音訊輸入"), - ("Enhancements", "增強功能"), - ("Hardware Codec", "硬件編解碼"), - ("Adaptive Bitrate", "自適應碼率"), - ("ID Server", "ID 伺服器"), - ("Relay Server", "轉送伺服器"), - ("API Server", "API 伺服器"), - ("invalid_http", "開頭必須為 http:// 或 https://"), - ("Invalid IP", "IP 無效"), - ("Invalid format", "格式無效"), - ("server_not_support", "服務器暫不支持"), - ("Not available", "無法使用"), - ("Too frequent", "修改過於頻繁,請稍後再試。"), - ("Cancel", "取消"), - ("Skip", "跳過"), - ("Close", "關閉"), - ("Retry", "重試"), - ("OK", "確定"), - ("Password Required", "需要密碼"), - ("Please enter your password", "請輸入您的密碼"), - ("Remember password", "記住密碼"), - ("Wrong Password", "密碼錯誤"), - ("Do you want to enter again?", "您要重新輸入嗎?"), - ("Connection Error", "連線錯誤"), - ("Error", "錯誤"), - ("Reset by the peer", "對方重置了連線"), - ("Connecting...", "正在連接..."), - ("Connection in progress. Please wait.", "正在連接,請稍候。"), - ("Please try 1 minute later", "請於 1 分鐘後再試"), - ("Login Error", "登入錯誤"), - ("Successful", "成功"), - ("Connected, waiting for image...", "已連線,等待畫面傳輸..."), - ("Name", "名稱"), - ("Type", "類型"), - ("Modified", "修改時間"), - ("Size", "大小"), - ("Show Hidden Files", "顯示隱藏檔案"), - ("Receive", "接收"), - ("Send", "傳送"), - ("Refresh File", "刷新文件"), - ("Local", "本地"), - ("Remote", "遠端"), - ("Remote Computer", "遠端電腦"), - ("Local Computer", "本地電腦"), - ("Confirm Delete", "確認刪除"), - ("Delete", "刪除"), - ("Properties", "屬性"), - ("Multi Select", "多選"), - ("Select All", "全選"), - ("Unselect All", "取消全選"), - ("Empty Directory", "空文件夾"), - ("Not an empty directory", "不是一個空文件夾"), - ("Are you sure you want to delete this file?", "您確定要刪除此檔案嗎?"), - ("Are you sure you want to delete this empty directory?", "您確定要刪除此空目錄嗎?"), - ("Are you sure you want to delete the file of this directory?", "您確定要刪除此目錄中的檔案嗎?"), - ("Do this for all conflicts", "套用到其他衝突"), - ("This is irreversible!", "此操作不可逆!"), - ("Deleting", "正在刪除"), - ("files", "檔案"), - ("Waiting", "正在等候..."), - ("Finished", "已完成"), - ("Speed", "速度"), - ("Custom Image Quality", "自訂圖片品質"), - ("Privacy mode", "隱私模式"), - ("Block user input", "封鎖使用者輸入"), - ("Unblock user input", "取消封鎖使用者輸入"), - ("Adjust Window", "調整視窗"), - ("Original", "原始"), - ("Shrink", "縮減"), - ("Stretch", "延展"), - ("Scrollbar", "滾動條"), - ("ScrollAuto", "自動滾動"), - ("Good image quality", "畫面品質良好"), - ("Balanced", "平衡"), - ("Optimize reaction time", "回應速度最佳化"), - ("Custom", "自定義"), - ("Show remote cursor", "顯示遠端游標"), - ("Show quality monitor", "顯示質量監測"), - ("Disable clipboard", "停用剪貼簿"), - ("Lock after session end", "工作階段結束後鎖定電腦"), - ("Insert", "插入"), - ("Insert Lock", "鎖定遠端電腦"), - ("Refresh", "重新載入"), - ("ID does not exist", "ID 不存在"), - ("Failed to connect to rendezvous server", "無法連接至 rendezvous 伺服器"), - ("Please try later", "請稍候再試"), - ("Remote desktop is offline", "遠端電腦離線"), - ("Key mismatch", "金鑰不符"), - ("Timeout", "逾時"), - ("Failed to connect to relay server", "無法連接至轉送伺服器"), - ("Failed to connect via rendezvous server", "無法透過 rendezvous 伺服器連接"), - ("Failed to connect via relay server", "無法透過轉送伺服器連接"), - ("Failed to make direct connection to remote desktop", "無法直接連線至遠端電腦"), - ("Set Password", "設置密碼"), - ("OS Password", "作業系統密碼"), - ("install_tip", "UAC 會導致 RustDesk 在某些情況下無法正常以遠端電腦運作。若要避開 UAC,請點擊下方按鈕將 RustDesk 安裝到系統中。"), - ("Click to upgrade", "點擊以升級"), - ("Click to download", "點擊以下載"), - ("Click to update", "點擊以更新"), - ("Configure", "設定"), - ("config_acc", "您需要授予 RustDesk 「協助工具」 權限才能遠端存取電腦。"), - ("config_screen", "您需要授予 RustDesk 「畫面錄製」 權限才能遠端存取電腦。"), - ("Installing ...", "正在安裝..."), - ("Install", "安裝"), - ("Installation", "安裝"), - ("Installation Path", "安裝路徑"), - ("Create start menu shortcuts", "建立開始選單捷徑"), - ("Create desktop icon", "建立桌面圖示"), - ("agreement_tip", "開始安裝即表示接受許可協議"), - ("Accept and Install", "接受並安裝"), - ("End-user license agreement", "使用者授權合約"), - ("Generating ...", "正在產生 ..."), - ("Your installation is lower version.", "您的安裝版本過舊。"), - ("not_close_tcp_tip", "使用通道時請不要關閉此視窗"), - ("Listening ...", "正在等待通道連接..."), - ("Remote Host", "遠端主機"), - ("Remote Port", "遠端連接埠"), - ("Action", "操作"), - ("Add", "新增"), - ("Local Port", "本機連接埠"), - ("Local Address", "本機地址"), - ("Change Local Port", "修改本機連接埠"), - ("setup_server_tip", "若您需要更快的連接速度,可以選擇自行建立伺服器"), - ("Too short, at least 6 characters.", "過短,至少需 6 個字元。"), - ("The confirmation is not identical.", "兩次輸入不相符"), - ("Permissions", "權限"), - ("Accept", "接受"), - ("Dismiss", "關閉"), - ("Disconnect", "斷開連線"), - ("Allow using keyboard and mouse", "允許使用鍵盤和滑鼠"), - ("Allow using clipboard", "允許使用剪貼簿"), - ("Allow hearing sound", "允許分享音訊"), - ("Allow file copy and paste", "允許文件複製和粘貼"), - ("Connected", "已連接"), - ("Direct and encrypted connection", "加密直接連線"), - ("Relayed and encrypted connection", "加密轉送連線"), - ("Direct and unencrypted connection", "未加密直接連線"), - ("Relayed and unencrypted connection", "未加密轉送連線"), - ("Enter Remote ID", "輸入遠端 ID"), - ("Enter your password", "輸入您的密碼"), - ("Logging in...", "正在登入..."), - ("Enable RDP session sharing", "啟用 RDP 工作階段共享"), - ("Auto Login", "自動登入 (鎖定將在設定關閉後套用)"), - ("Enable Direct IP Access", "允許 IP 直接存取"), - ("Rename", "重新命名"), - ("Space", "空白"), - ("Create Desktop Shortcut", "建立桌面捷徑"), - ("Change Path", "更改路徑"), - ("Create Folder", "建立資料夾"), - ("Please enter the folder name", "請輸入資料夾名稱"), - ("Fix it", "修復"), - ("Warning", "警告"), - ("Login screen using Wayland is not supported", "不支援使用 Wayland 的登入畫面"), - ("Reboot required", "需要重新啟動"), - ("Unsupported display server", "不支援顯示伺服器"), - ("x11 expected", "預期 x11"), - ("Port", "端口"), - ("Settings", "設定"), - ("Username", "使用者名稱"), - ("Invalid port", "連接埠無效"), - ("Closed manually by the peer", "由對方手動關閉"), - ("Enable remote configuration modification", "啟用遠端更改設定"), - ("Run without install", "跳過安裝直接執行"), - ("Connect via relay", "中繼連線"), - ("Always connect via relay", "一律透過轉送連線"), - ("whitelist_tip", "只有白名單中的 IP 可以存取"), - ("Login", "登入"), - ("Verify", "驗證"), - ("Remember me", "記住我"), - ("Trust this device", "信任此設備"), - ("Verification code", "驗證碼"), - ("verification_tip", "檢測到新設備登錄,已向註冊郵箱發送了登入驗證碼,請輸入驗證碼繼續登錄"), - ("Logout", "登出"), - ("Tags", "標籤"), - ("Search ID", "搜尋 ID"), - ("whitelist_sep", "使用逗號、分號、空白,或是換行來分隔"), - ("Add ID", "新增 ID"), - ("Add Tag", "新增標籤"), - ("Unselect all tags", "取消選取所有標籤"), - ("Network error", "網路錯誤"), - ("Username missed", "缺少使用者名稱"), - ("Password missed", "缺少密碼"), - ("Wrong credentials", "提供的登入資訊有誤"), - ("Edit Tag", "編輯標籤"), - ("Unremember Password", "忘掉密碼"), - ("Favorites", "收藏"), - ("Add to Favorites", "加入到收藏"), - ("Remove from Favorites", "從收藏中刪除"), - ("Empty", "空空如也"), - ("Invalid folder name", "資料夾名稱無效"), - ("Socks5 Proxy", "Socks5 代理"), - ("Hostname", "主機名稱"), - ("Discovered", "已發現"), - ("install_daemon_tip", "為了開機啟動,請安裝系統服務。"), - ("Remote ID", "遠端 ID"), - ("Paste", "貼上"), - ("Paste here?", "貼上到這裡?"), - ("Are you sure to close the connection?", "您確定要關閉連線嗎?"), - ("Download new version", "下載新版本"), - ("Touch mode", "觸控模式"), - ("Mouse mode", "滑鼠模式"), - ("One-Finger Tap", "單指輕觸"), - ("Left Mouse", "滑鼠左鍵"), - ("One-Long Tap", "單指長按"), - ("Two-Finger Tap", "雙指輕觸"), - ("Right Mouse", "滑鼠右鍵"), - ("One-Finger Move", "單指移動"), - ("Double Tap & Move", "雙擊並移動"), - ("Mouse Drag", "滑鼠選中拖動"), - ("Three-Finger vertically", "三指垂直滑動"), - ("Mouse Wheel", "滑鼠滾輪"), - ("Two-Finger Move", "雙指移動"), - ("Canvas Move", "移動畫布"), - ("Pinch to Zoom", "雙指縮放"), - ("Canvas Zoom", "縮放畫布"), - ("Reset canvas", "重置畫布"), - ("No permission of file transfer", "無文件傳輸權限"), - ("Note", "備註"), - ("Connection", "連接"), - ("Share Screen", "共享畫面"), - ("Chat", "聊天消息"), - ("Total", "總計"), - ("items", "個項目"), - ("Selected", "已選擇"), - ("Screen Capture", "畫面錄製"), - ("Input Control", "輸入控制"), - ("Audio Capture", "音訊錄製"), - ("File Connection", "檔案連線"), - ("Screen Connection", "畫面連線"), - ("Do you accept?", "是否接受?"), - ("Open System Setting", "打開系統設定"), - ("How to get Android input permission?", "如何獲取 Android 的輸入權限?"), - ("android_input_permission_tip1", "取得輸入權限後可以讓遠端裝置通過滑鼠控制此 Android 裝置"), - ("android_input_permission_tip2", "請在接下來的系統設定頁面中,找到並進入 「已安裝的服務」 頁面,並將 「RustDesk Input」 服務開啟"), - ("android_new_connection_tip", "收到新的連接控制請求,對方想要控制您目前的設備"), - ("android_service_will_start_tip", "開啟畫面錄製權限將自動開啟服務,允許其他裝置向此裝置請求建立連接。"), - ("android_stop_service_tip", "關閉服務將自動關閉所有已建立的連接。"), - ("android_version_audio_tip", "目前的 Android 版本不支持音訊錄製,請升級至 Android 10 或以上版本。"), - ("android_start_service_tip", ""), - ("android_permission_may_not_change_tip", "對於已經建立的連接,權限可能不會立即發生改變,除非重新建立連接。"), - ("Account", "賬戶"), - ("Overwrite", "覆寫"), - ("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要跳過或是覆寫此檔案嗎?"), - ("Quit", "退出"), - ("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"), - ("Help", "幫助"), - ("Failed", "失敗"), - ("Succeeded", "成功"), - ("Someone turns on privacy mode, exit", "其他用戶開啟隱私模式,退出"), - ("Unsupported", "不支持"), - ("Peer denied", "被控端拒絕"), - ("Please install plugins", "請安裝插件"), - ("Peer exit", "被控端退出"), - ("Failed to turn off", "退出失敗"), - ("Turned off", "退出"), - ("In privacy mode", "開啟隱私模式"), - ("Out privacy mode", "退出隱私模式"), - ("Language", "語言"), - ("Keep RustDesk background service", "保持RustDesk後台服務"), - ("Ignore Battery Optimizations", "忽略電池優化"), - ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), - ("Start on Boot", ""), - ("Start the screen sharing service on boot, requires special permissions", ""), - ("Connection not allowed", "對方不允許連接"), - ("Legacy mode", "傳統模式"), - ("Map mode", "1:1傳輸"), - ("Translate mode", "翻譯模式"), - ("Use permanent password", "使用固定密碼"), - ("Use both passwords", "同時使用兩種密碼"), - ("Set permanent password", "設定固定密碼"), - ("Enable Remote Restart", "允許遠程重啓"), - ("Allow remote restart", "允許遠程重啓"), - ("Restart Remote Device", "重啓遠程電腦"), - ("Are you sure you want to restart", "确定要重启"), - ("Restarting Remote Device", "正在重啓遠程設備"), - ("remote_restarting_tip", "遠程設備正在重啓,請關閉當前提示框,並在一段時間後使用永久密碼重新連接"), - ("Copied", "已複製"), - ("Exit Fullscreen", "退出全屏"), - ("Fullscreen", "全屏"), - ("Mobile Actions", "移動端操作"), - ("Select Monitor", "選擇監視器"), - ("Control Actions", "控制操作"), - ("Display Settings", "顯示設置"), - ("Ratio", "比例"), - ("Image Quality", "畫質"), - ("Scroll Style", "滾動樣式"), - ("Show Menubar", "顯示菜單欄"), - ("Hide Menubar", "隱藏菜單欄"), - ("Direct Connection", "直接連接"), - ("Relay Connection", "中繼連接"), - ("Secure Connection", "安全連接"), - ("Insecure Connection", "非安全連接"), - ("Scale original", "原始尺寸"), - ("Scale adaptive", "適應窗口"), - ("General", "常規"), - ("Security", "安全"), - ("Theme", "主題"), - ("Dark Theme", "暗黑主題"), - ("Light Theme", "明亮主題"), - ("Dark", "黑暗"), - ("Light", "明亮"), - ("Follow System", "跟隨系統"), - ("Enable hardware codec", "使用硬件編解碼"), - ("Unlock Security Settings", "解鎖安全設置"), - ("Enable Audio", "允許傳輸音頻"), - ("Unlock Network Settings", "解鎖網絡設置"), - ("Server", "服務器"), - ("Direct IP Access", "IP直接訪問"), - ("Proxy", "代理"), - ("Apply", "應用"), - ("Disconnect all devices?", "斷開所有遠程連接?"), - ("Clear", "清空"), - ("Audio Input Device", "音頻輸入設備"), - ("Deny remote access", "拒絕遠程訪問"), - ("Use IP Whitelisting", "只允許白名單上的IP訪問"), - ("Network", "網絡"), - ("Enable RDP", "允許RDP訪問"), - ("Pin menubar", "固定菜單欄"), - ("Unpin menubar", "取消固定菜單欄"), - ("Recording", "錄屏"), - ("Directory", "目錄"), - ("Automatically record incoming sessions", "自動錄製來訪會話"), - ("Change", "變更"), - ("Start session recording", "開始錄屏"), - ("Stop session recording", "結束錄屏"), - ("Enable Recording Session", "允許錄製會話"), - ("Allow recording session", "允許錄製會話"), - ("Enable LAN Discovery", "允許局域網發現"), - ("Deny LAN Discovery", "拒絕局域網發現"), - ("Write a message", "輸入聊天消息"), - ("Prompt", "提示"), - ("Please wait for confirmation of UAC...", "請等待對方確認UAC"), - ("elevated_foreground_window_tip", "遠端桌面的當前窗口需要更高的權限才能操作, 暫時無法使用鼠標鍵盤, 可以請求對方最小化當前窗口, 或者在連接管理窗口點擊提升。為避免這個問題,建議在遠端設備上安裝本軟件。"), - ("Disconnected", "會話已結束"), - ("Other", "其他"), - ("Confirm before closing multiple tabs", "關閉多個分頁前跟我確認"), - ("Keyboard Settings", "鍵盤設置"), - ("Full Access", "完全訪問"), - ("Screen Share", "僅共享屏幕"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland 需要 Ubuntu 21.04 或更高版本。"), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland 需要更高版本的 linux 發行版。 請嘗試 X11 桌面或更改您的操作系統。"), - ("JumpLink", "查看"), - ("Please Select the screen to be shared(Operate on the peer side).", "請選擇要分享的畫面(在對端操作)。"), - ("Show RustDesk", "顯示 RustDesk"), - ("This PC", "此電腦"), - ("or", "或"), - ("Continue with", "使用"), - ("Elevate", "提權"), - ("Zoom cursor", "縮放游標"), - ("Accept sessions via password", "只允許密碼訪問"), - ("Accept sessions via click", "只允許點擊訪問"), - ("Accept sessions via both", "允許密碼或點擊訪問"), - ("Please wait for the remote side to accept your session request...", "請等待對方接受你的連接..."), - ("One-time Password", "一次性密碼"), - ("Use one-time password", "使用一次性密碼"), - ("One-time password length", "一次性密碼長度"), - ("Request access to your device", "請求訪問你的設備"), - ("Hide connection management window", "隱藏連接管理窗口"), - ("hide_cm_tip", "在只允許密碼連接並且只用固定密碼的情況下才允許隱藏"), - ("wayland_experiment_tip", "Wayland 支持處於實驗階段,如果你需要使用無人值守訪問,請使用 X11。"), - ("Right click to select tabs", "右鍵選擇選項卡"), - ("Skipped", "已略過"), - ("Add to Address Book", "添加到地址簿"), - ("Group", "小組"), - ("Search", "搜索"), - ("Closed manually by web console", "被web控制台手動關閉"), - ("Local keyboard type", "本地鍵盤類型"), - ("Select local keyboard type", "請選擇本地鍵盤類型"), - ("software_render_tip", "如果你使用英偉達顯卡, 並且遠程窗口在會話建立後會立刻關閉, 那麼安裝nouveau驅動並且選擇使用軟件渲染可能會有幫助。重啟軟件後生效。"), - ("Always use software rendering", "使用軟件渲染"), - ("config_input", "為了能夠通過鍵盤控制遠程桌面, 請給予 RustDesk \"輸入監控\" 權限。"), - ("config_microphone", "為了支持通過麥克風進行音訊傳輸,請給予 RustDesk \"錄音\"權限。"), - ("request_elevation_tip", "如果對面有人, 也可以請求提升權限。"), - ("Wait", "等待"), - ("Elevation Error", "提權失敗"), - ("Ask the remote user for authentication", "請求遠端用戶授權"), - ("Choose this if the remote account is administrator", "當對面電腦是管理員賬號時選擇該選項"), - ("Transmit the username and password of administrator", "發送管理員賬號的用戶名密碼"), - ("still_click_uac_tip", "依然需要被控端用戶在UAC窗口點擊確認。"), - ("Request Elevation", "請求提權"), - ("wait_accept_uac_tip", "請等待遠端用戶確認UAC對話框。"), - ("Elevate successfully", "提權成功"), - ("uppercase", "大寫字母"), - ("lowercase", "小寫字母"), - ("digit", "數字"), - ("special character", "特殊字符"), - ("length>=8", "長度不小於8"), - ("Weak", "弱"), - ("Medium", "中"), - ("Strong", "強"), - ("Switch Sides", "反轉訪問方向"), - ("Please confirm if you want to share your desktop?", "請確認是否要讓對方訪問你的桌面?"), - ("Display", "顯示"), - ("Default View Style", "默認顯示方式"), - ("Default Scroll Style", "默認滾動方式"), - ("Default Image Quality", "默認圖像質量"), - ("Default Codec", "默認編解碼"), - ("Bitrate", "波特率"), - ("FPS", "幀率"), - ("Auto", "自動"), - ("Other Default Options", "其它默認選項"), - ("Voice call", "語音通話"), - ("Text chat", "文字聊天"), - ("Stop voice call", "停止語音聊天"), - ("relay_hint_tip", "可能無法直連,可以嘗試中繼連接。 \n另外,如果想直接使用中繼連接,可以在ID後面添加/r,或者在卡片選項裡選擇強制走中繼連接。"), - ("Reconnect", "重連"), - ("Codec", "編解碼"), - ("Resolution", "分辨率"), - ("No transfers in progress", ""), - ("Set one-time password length", ""), - ("idd_driver_tip", ""), - ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), - ("empty_recent_tip", ""), - ("empty_favorite_tip", ""), - ("empty_lan_tip", ""), - ("empty_address_book_tip", ""), - ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), - ("Me", ""), - ("identical_file_tip", ""), - ("show_monitors_tip", ""), - ("View Mode", "瀏覽模式"), - ].iter().cloned().collect(); -} +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "狀態"), + ("Your Desktop", "您的桌面"), + ("desk_tip", "您可以透過此 ID 及密碼存取您的桌面"), + ("Password", "密碼"), + ("Ready", "就緒"), + ("Established", "已建立"), + ("connecting_status", "正在連線到 RustDesk 網路 ..."), + ("Enable Service", "啟用服務"), + ("Start Service", "啟動服務"), + ("Service is running", "服務正在執行"), + ("Service is not running", "服務尚未執行"), + ("not_ready_status", "尚未就緒,請檢查您的網路連線。"), + ("Control Remote Desktop", "控制遠端桌面"), + ("Transfer File", "傳輸檔案"), + ("Connect", "連線"), + ("Recent Sessions", "近期的工作階段"), + ("Address Book", "通訊錄"), + ("Confirmation", "確認"), + ("TCP Tunneling", "TCP 通道"), + ("Remove", "移除"), + ("Refresh random password", "重新產生隨機密碼"), + ("Set your own password", "自行設定密碼"), + ("Enable Keyboard/Mouse", "啟用鍵盤和滑鼠"), + ("Enable Clipboard", "啟用剪貼簿"), + ("Enable File Transfer", "啟用檔案傳輸"), + ("Enable TCP Tunneling", "啟用 TCP 通道"), + ("IP Whitelisting", "IP 白名單"), + ("ID/Relay Server", "ID / 轉送伺服器"), + ("Import Server Config", "匯入伺服器設定"), + ("Export Server Config", "匯出伺服器設定"), + ("Import server configuration successfully", "匯入伺服器設定成功"), + ("Export server configuration successfully", "匯出伺服器設定成功"), + ("Invalid server configuration", "無效的伺服器設定"), + ("Clipboard is empty", "剪貼簿是空的"), + ("Stop service", "停止服務"), + ("Change ID", "更改 ID"), + ("Your new ID", "您的新 ID"), + ("length %min% to %max%", "長度在 %min% 與 %max% 之間"), + ("starts with a letter", "以字母開頭"), + ("allowed characters", "使用允許的字元"), + ("id_change_tip", "僅能使用以下字元:a-z、A-Z、0-9、_ (底線)。首字元必須為 a-z 或 A-Z。長度介於 6 到 16 之間。"), + ("Website", "網站"), + ("About", "關於"), + ("Slogan_tip", ""), + ("Privacy Statement", "隱私權聲明"), + ("Mute", "靜音"), + ("Build Date", "構建日期"), + ("Version", "版本"), + ("Home", "首頁"), + ("Audio Input", "音訊輸入"), + ("Enhancements", "增強功能"), + ("Hardware Codec", "硬體編解碼器"), + ("Adaptive Bitrate", "自適應位元速率"), + ("ID Server", "ID 伺服器"), + ("Relay Server", "轉送伺服器"), + ("API Server", "API 伺服器"), + ("invalid_http", "開頭必須為 http:// 或 https://"), + ("Invalid IP", "IP 無效"), + ("Invalid format", "格式無效"), + ("server_not_support", "伺服器暫不支持"), + ("Not available", "無法使用"), + ("Too frequent", "修改過於頻繁,請稍後再試。"), + ("Cancel", "取消"), + ("Skip", "跳過"), + ("Close", "關閉"), + ("Retry", "重試"), + ("OK", "確定"), + ("Password Required", "需要密碼"), + ("Please enter your password", "請輸入您的密碼"), + ("Remember password", "記住密碼"), + ("Wrong Password", "密碼錯誤"), + ("Do you want to enter again?", "您要重新輸入嗎?"), + ("Connection Error", "連線錯誤"), + ("Error", "錯誤"), + ("Reset by the peer", "對方重設了連線"), + ("Connecting...", "正在連線 ..."), + ("Connection in progress. Please wait.", "正在連線,請稍候。"), + ("Please try 1 minute later", "請於 1 分鐘後再試"), + ("Login Error", "登入錯誤"), + ("Successful", "成功"), + ("Connected, waiting for image...", "已連線,等待畫面傳輸 ..."), + ("Name", "名稱"), + ("Type", "類型"), + ("Modified", "修改時間"), + ("Size", "大小"), + ("Show Hidden Files", "顯示隱藏檔案"), + ("Receive", "接收"), + ("Send", "傳送"), + ("Refresh File", "重新整理檔案"), + ("Local", "本地"), + ("Remote", "遠端"), + ("Remote Computer", "遠端電腦"), + ("Local Computer", "本地電腦"), + ("Confirm Delete", "確認刪除"), + ("Delete", "刪除"), + ("Properties", "屬性"), + ("Multi Select", "多選"), + ("Select All", "全選"), + ("Unselect All", "取消全選"), + ("Empty Directory", "空資料夾"), + ("Not an empty directory", "不是一個空資料夾"), + ("Are you sure you want to delete this file?", "您確定要刪除此檔案嗎?"), + ("Are you sure you want to delete this empty directory?", "您確定要刪除此空資料夾嗎?"), + ("Are you sure you want to delete the file of this directory?", "您確定要刪除此資料夾中的檔案嗎?"), + ("Do this for all conflicts", "套用到其他衝突"), + ("This is irreversible!", "此操作不可逆!"), + ("Deleting", "正在刪除 ..."), + ("files", "檔案"), + ("Waiting", "正在等候 ..."), + ("Finished", "已完成"), + ("Speed", "速度"), + ("Custom Image Quality", "自訂畫面品質"), + ("Privacy mode", "隱私模式"), + ("Block user input", "封鎖使用者輸入"), + ("Unblock user input", "取消封鎖使用者輸入"), + ("Adjust Window", "調整視窗"), + ("Original", "原始"), + ("Shrink", "縮減"), + ("Stretch", "延展"), + ("Scrollbar", "滾動條"), + ("ScrollAuto", "自動滾動"), + ("Good image quality", "最佳化畫面品質"), + ("Balanced", "平衡"), + ("Optimize reaction time", "最佳化反應時間"), + ("Custom", "自訂"), + ("Show remote cursor", "顯示遠端游標"), + ("Show quality monitor", "顯示質量監測"), + ("Disable clipboard", "停用剪貼簿"), + ("Lock after session end", "工作階段結束後鎖定電腦"), + ("Insert", "插入"), + ("Insert Lock", "鎖定遠端電腦"), + ("Refresh", "重新載入"), + ("ID does not exist", "ID 不存在"), + ("Failed to connect to rendezvous server", "無法連線到 rendezvous 伺服器"), + ("Please try later", "請稍候再試"), + ("Remote desktop is offline", "遠端桌面已離線"), + ("Key mismatch", "金鑰不符"), + ("Timeout", "逾時"), + ("Failed to connect to relay server", "無法連線到轉送伺服器"), + ("Failed to connect via rendezvous server", "無法透過 rendezvous 伺服器連線"), + ("Failed to connect via relay server", "無法透過轉送伺服器連線"), + ("Failed to make direct connection to remote desktop", "無法直接連線到遠端桌面"), + ("Set Password", "設定密碼"), + ("OS Password", "作業系統密碼"), + ("install_tip", "UAC 會導致 RustDesk 在某些情況下無法正常以遠端電腦執行。若要避開 UAC,請點擊下方按鈕將 RustDesk 安裝到系統中。"), + ("Click to upgrade", "點擊以升級"), + ("Click to download", "點擊以下載"), + ("Click to update", "點擊以更新"), + ("Configure", "設定"), + ("config_acc", "您需要授予 RustDesk「協助工具」權限才能存取遠端電腦。"), + ("config_screen", "您需要授予 RustDesk「畫面錄製」權限才能存取遠端電腦。"), + ("Installing ...", "正在安裝 ..."), + ("Install", "安裝"), + ("Installation", "安裝"), + ("Installation Path", "安裝路徑"), + ("Create start menu shortcuts", "新增開始功能表捷徑"), + ("Create desktop icon", "新增桌面捷徑"), + ("agreement_tip", "開始安裝即表示接受許可協議"), + ("Accept and Install", "接受並安裝"), + ("End-user license agreement", "使用者授權合約"), + ("Generating ...", "正在產生 ..."), + ("Your installation is lower version.", "您安裝的版本過舊。"), + ("not_close_tcp_tip", "使用通道時請不要關閉此視窗"), + ("Listening ...", "正在等待通道連線 ..."), + ("Remote Host", "遠端主機"), + ("Remote Port", "遠端連線端口"), + ("Action", "操作"), + ("Add", "新增"), + ("Local Port", "本機連線端口"), + ("Local Address", "本機地址"), + ("Change Local Port", "修改本機連線端口"), + ("setup_server_tip", "若您需要更快的連線速度,可以選擇自行建立伺服器"), + ("Too short, at least 6 characters.", "過短,至少需要 6 個字元。"), + ("The confirmation is not identical.", "兩次輸入不相符"), + ("Permissions", "權限"), + ("Accept", "接受"), + ("Dismiss", "關閉"), + ("Disconnect", "中斷連線"), + ("Allow using keyboard and mouse", "允許使用鍵盤和滑鼠"), + ("Allow using clipboard", "允許使用剪貼簿"), + ("Allow hearing sound", "允許分享音訊"), + ("Allow file copy and paste", "允許檔案複製和貼上"), + ("Connected", "已連線"), + ("Direct and encrypted connection", "加密直接連線"), + ("Relayed and encrypted connection", "加密轉送連線"), + ("Direct and unencrypted connection", "未加密直接連線"), + ("Relayed and unencrypted connection", "未加密轉送連線"), + ("Enter Remote ID", "輸入遠端 ID"), + ("Enter your password", "輸入您的密碼"), + ("Logging in...", "正在登入 ..."), + ("Enable RDP session sharing", "啟用 RDP 工作階段共享"), + ("Auto Login", "自動登入 (鎖定將在設定關閉後套用)"), + ("Enable Direct IP Access", "允許 IP 直接存取"), + ("Rename", "重新命名"), + ("Space", "空白"), + ("Create Desktop Shortcut", "新增桌面捷徑"), + ("Change Path", "更改路徑"), + ("Create Folder", "新增資料夾"), + ("Please enter the folder name", "請輸入資料夾名稱"), + ("Fix it", "修復"), + ("Warning", "警告"), + ("Login screen using Wayland is not supported", "不支援使用 Wayland 的登入畫面"), + ("Reboot required", "需要重新啟動"), + ("Unsupported display server", "不支援顯示伺服器"), + ("x11 expected", "預期 x11"), + ("Port", "端口"), + ("Settings", "設定"), + ("Username", "使用者名稱"), + ("Invalid port", "連線端口無效"), + ("Closed manually by the peer", "遠端使用者關閉了工作階段"), + ("Enable remote configuration modification", "允許遠端使用者更改設定"), + ("Run without install", "跳過安裝直接執行"), + ("Connect via relay", "中繼連線"), + ("Always connect via relay", "一律透過轉送連線"), + ("whitelist_tip", "只有白名單中的 IP 可以存取"), + ("Login", "登入"), + ("Verify", "驗證"), + ("Remember me", "記住我"), + ("Trust this device", "信任此裝置"), + ("Verification code", "驗證碼"), + ("verification_tip", "檢測到新裝置登入,已向註冊電子信箱發送了登入驗證碼,請輸入驗證碼以繼續登入"), + ("Logout", "登出"), + ("Tags", "標籤"), + ("Search ID", "搜尋 ID"), + ("whitelist_sep", "使用逗號、分號、空白,或是換行來分隔"), + ("Add ID", "新增 ID"), + ("Add Tag", "新增標籤"), + ("Unselect all tags", "取消選取所有標籤"), + ("Network error", "網路錯誤"), + ("Username missed", "缺少使用者名稱"), + ("Password missed", "缺少密碼"), + ("Wrong credentials", "提供的登入資訊有誤"), + ("Edit Tag", "編輯標籤"), + ("Unremember Password", "忘掉密碼"), + ("Favorites", "我的最愛"), + ("Add to Favorites", "新增到我的最愛"), + ("Remove from Favorites", "從我的最愛中刪除"), + ("Empty", "空空如也"), + ("Invalid folder name", "資料夾名稱無效"), + ("Socks5 Proxy", "Socks5 代理"), + ("Hostname", "主機名稱"), + ("Discovered", "已探索"), + ("install_daemon_tip", "為了能夠開機時自動啟動,請先安裝系統服務。"), + ("Remote ID", "遠端 ID"), + ("Paste", "貼上"), + ("Paste here?", "貼上到這裡?"), + ("Are you sure to close the connection?", "您確定要關閉連線嗎?"), + ("Download new version", "下載新版本"), + ("Touch mode", "觸控模式"), + ("Mouse mode", "滑鼠模式"), + ("One-Finger Tap", "單指輕觸"), + ("Left Mouse", "滑鼠左鍵"), + ("One-Long Tap", "單指長按"), + ("Two-Finger Tap", "雙指輕觸"), + ("Right Mouse", "滑鼠右鍵"), + ("One-Finger Move", "單指移動"), + ("Double Tap & Move", "雙擊並移動"), + ("Mouse Drag", "滑鼠選中拖動"), + ("Three-Finger vertically", "三指垂直滑動"), + ("Mouse Wheel", "滑鼠滾輪"), + ("Two-Finger Move", "雙指移動"), + ("Canvas Move", "移動畫布"), + ("Pinch to Zoom", "雙指縮放"), + ("Canvas Zoom", "縮放畫布"), + ("Reset canvas", "重設畫布"), + ("No permission of file transfer", "沒有檔案傳輸權限"), + ("Note", "備註"), + ("Connection", "連線"), + ("Share Screen", "共享螢幕畫面"), + ("Chat", "聊天訊息"), + ("Total", "總計"), + ("items", "個項目"), + ("Selected", "已選擇"), + ("Screen Capture", "畫面錄製"), + ("Input Control", "輸入控制"), + ("Audio Capture", "音訊錄製"), + ("File Connection", "檔案連線"), + ("Screen Connection", "畫面連線"), + ("Do you accept?", "是否接受?"), + ("Open System Setting", "開啟系統設定"), + ("How to get Android input permission?", "如何獲取 Android 的輸入權限?"), + ("android_input_permission_tip1", "取得輸入權限後可以讓遠端裝置透過滑鼠控制此 Android 裝置"), + ("android_input_permission_tip2", "請在接下來的系統設定頁面中,找到並進入「已安裝的服務」頁面,並將「RustDesk Input」服務開啟"), + ("android_new_connection_tip", "收到新的連線控制請求,對方想要控制您目前的裝置"), + ("android_service_will_start_tip", "開啟畫面錄製權限將自動開啟服務,允許其他裝置向此裝置請求建立連線。"), + ("android_stop_service_tip", "關閉服務將自動關閉所有已建立的連線。"), + ("android_version_audio_tip", "目前的 Android 版本不支援音訊錄製,請升級到 Android 10 或以上版本。"), + ("android_start_service_tip", "點擊「啟動服務」或啟用「螢幕錄製」權限,以啟動螢幕共享服務。"), + ("android_permission_may_not_change_tip", "對於已經建立的連線,權限可能不會立即發生改變,除非重新建立連線。"), + ("Account", "帳號"), + ("Overwrite", "取代"), + ("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要略過或是取代此檔案嗎?"), + ("Quit", "退出"), + ("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"), + ("Help", "幫助"), + ("Failed", "失敗"), + ("Succeeded", "成功"), + ("Someone turns on privacy mode, exit", "其他使用者開啟隱私模式,退出"), + ("Unsupported", "不支援"), + ("Peer denied", "被控端拒絕"), + ("Please install plugins", "請安裝插件"), + ("Peer exit", "被控端退出"), + ("Failed to turn off", "退出失敗"), + ("Turned off", "退出"), + ("In privacy mode", "開啟隱私模式"), + ("Out privacy mode", "退出隱私模式"), + ("Language", "語言"), + ("Keep RustDesk background service", "保持 RustDesk 後台服務"), + ("Ignore Battery Optimizations", "忽略電池最佳化"), + ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的 RustDesk 應用設定頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), + ("Start on Boot", "開機自動啟動"), + ("Start the screen sharing service on boot, requires special permissions", "開機自動啟動螢幕共享服務,此功能需要一些特殊權限。"), + ("Connection not allowed", "對方不允許連線"), + ("Legacy mode", "傳統模式"), + ("Map mode", "1:1 傳輸模式"), + ("Translate mode", "翻譯模式"), + ("Use permanent password", "使用固定密碼"), + ("Use both passwords", "同時使用兩種密碼"), + ("Set permanent password", "設定固定密碼"), + ("Enable Remote Restart", "啟用遠端重新啟動"), + ("Allow remote restart", "允許遠端重新啟動"), + ("Restart Remote Device", "重新啟動遠端電腦"), + ("Are you sure you want to restart", "確定要重新啟動"), + ("Restarting Remote Device", "正在重新啟動遠端裝置"), + ("remote_restarting_tip", "遠端裝置正在重新啟動,請關閉當前提示框,並在一段時間後使用永久密碼重新連線"), + ("Copied", "已複製"), + ("Exit Fullscreen", "退出全螢幕"), + ("Fullscreen", "全螢幕"), + ("Mobile Actions", "手機操作"), + ("Select Monitor", "選擇顯示器"), + ("Control Actions", "控制操作"), + ("Display Settings", "顯示設定"), + ("Ratio", "比例"), + ("Image Quality", "畫質"), + ("Scroll Style", "滾動樣式"), + ("Show Menubar", "顯示選單欄"), + ("Hide Menubar", "隱藏選單欄"), + ("Direct Connection", "直接連線"), + ("Relay Connection", "中繼連線"), + ("Secure Connection", "安全連線"), + ("Insecure Connection", "非安全連線"), + ("Scale original", "原始尺寸"), + ("Scale adaptive", "適應視窗"), + ("General", "通用"), + ("Security", "安全"), + ("Theme", "主題"), + ("Dark Theme", "黑暗主題"), + ("Light Theme", "明亮主題"), + ("Dark", "黑暗"), + ("Light", "明亮"), + ("Follow System", "跟隨系統"), + ("Enable hardware codec", "使用硬體編解碼器"), + ("Unlock Security Settings", "解鎖安全設定"), + ("Enable Audio", "允許傳輸音訊"), + ("Unlock Network Settings", "解鎖網路設定"), + ("Server", "伺服器"), + ("Direct IP Access", "IP 直接連線"), + ("Proxy", "代理"), + ("Apply", "應用"), + ("Disconnect all devices?", "中斷所有遠端連線?"), + ("Clear", "清空"), + ("Audio Input Device", "音訊輸入裝置"), + ("Deny remote access", "拒絕遠端存取"), + ("Use IP Whitelisting", "只允許白名單上的 IP 進行連線"), + ("Network", "網路"), + ("Enable RDP", "允許 RDP 訪問"), + ("Pin menubar", "固定選單欄"), + ("Unpin menubar", "取消固定選單欄"), + ("Recording", "錄製"), + ("Directory", "路徑"), + ("Automatically record incoming sessions", "自動錄製連入的工作階段"), + ("Change", "變更"), + ("Start session recording", "開始錄影"), + ("Stop session recording", "停止錄影"), + ("Enable Recording Session", "啟用錄製工作階段"), + ("Allow recording session", "允許錄製工作階段"), + ("Enable LAN Discovery", "允許區域網路探索"), + ("Deny LAN Discovery", "拒絕區域網路探索"), + ("Write a message", "輸入聊天訊息"), + ("Prompt", "提示"), + ("Please wait for confirmation of UAC...", "請等待對方確認 UAC ..."), + ("elevated_foreground_window_tip", "目前的遠端桌面視窗需要更高的權限才能繼續操作,暫時無法使用滑鼠、鍵盤,可以請求對方最小化目前視窗,或者在連線管理視窗點擊提升權限。為了避免這個問題,建議在遠端裝置上安裝本軟體。"), + ("Disconnected", "斷開連線"), + ("Other", "其他"), + ("Confirm before closing multiple tabs", "關閉多個分頁前詢問我"), + ("Keyboard Settings", "鍵盤設定"), + ("Full Access", "完全訪問"), + ("Screen Share", "僅分享螢幕畫面"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland 需要 Ubuntu 21.04 或更高版本。"), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland 需要更高版本的 Linux 發行版。請嘗試使用 X11 桌面或更改您的作業系統。"), + ("JumpLink", "查看"), + ("Please Select the screen to be shared(Operate on the peer side).", "請選擇要分享的螢幕畫面(在對端操作)。"), + ("Show RustDesk", "顯示 RustDesk"), + ("This PC", "此電腦"), + ("or", "或"), + ("Continue with", "繼續"), + ("Elevate", "提升權限"), + ("Zoom cursor", "縮放游標"), + ("Accept sessions via password", "只允許透過輸入密碼進行連線"), + ("Accept sessions via click", "只允許透過點擊接受進行連線"), + ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), + ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), + ("One-time Password", "一次性密碼"), + ("Use one-time password", "使用一次性密碼"), + ("One-time password length", "一次性密碼長度"), + ("Request access to your device", "請求訪問您的裝置"), + ("Hide connection management window", "隱藏連線管理視窗"), + ("hide_cm_tip", "在只允許密碼連線並且只用固定密碼的情況下才允許隱藏"), + ("wayland_experiment_tip", "Wayland 支援處於實驗階段,如果您需要使用無人值守訪問,請使用 X11。"), + ("Right click to select tabs", "右鍵選擇分頁"), + ("Skipped", "已略過"), + ("Add to Address Book", "新增到通訊錄"), + ("Group", "群組"), + ("Search", "搜尋"), + ("Closed manually by web console", "被 Web 控制台手動關閉"), + ("Local keyboard type", "本地鍵盤類型"), + ("Select local keyboard type", "請選擇本地鍵盤類型"), + ("software_render_tip", "如果您使用 NVIDIA 顯示卡,並且遠端視窗在建立連線後會立刻關閉,那麼請安裝 nouveau 顯示卡驅動程式並且選擇使用軟體渲染可能會有幫助。重新啟動軟體後生效。"), + ("Always use software rendering", "使用軟體渲染"), + ("config_input", "為了能夠透過鍵盤控制遠端桌面,請給予 RustDesk \"輸入監控\" 權限。"), + ("config_microphone", "為了支援透過麥克風進行音訊傳輸,請給予 RustDesk \"錄音\"權限。"), + ("request_elevation_tip", "如果遠端使用者可以操作電腦,也可以請求提升權限。"), + ("Wait", "等待"), + ("Elevation Error", "權限提升失敗"), + ("Ask the remote user for authentication", "請求遠端使用者進行身分驗證"), + ("Choose this if the remote account is administrator", "當遠端使用者帳戶是管理員時,請選擇此選項"), + ("Transmit the username and password of administrator", "發送管理員的使用者名稱和密碼"), + ("still_click_uac_tip", "依然需要遠端使用者在 UAC 視窗點擊確認。"), + ("Request Elevation", "請求權限提升"), + ("wait_accept_uac_tip", "請等待遠端使用者確認 UAC 對話框。"), + ("Elevate successfully", "權限提升成功"), + ("uppercase", "大寫字母"), + ("lowercase", "小寫字母"), + ("digit", "數字"), + ("special character", "特殊字元"), + ("length>=8", "長度不能小於 8"), + ("Weak", "弱"), + ("Medium", "中"), + ("Strong", "強"), + ("Switch Sides", "反轉存取方向"), + ("Please confirm if you want to share your desktop?", "請確認是否要讓對方存取您的桌面?"), + ("Display", "顯示"), + ("Default View Style", "預設顯示方式"), + ("Default Scroll Style", "預設滾動方式"), + ("Default Image Quality", "預設圖像質量"), + ("Default Codec", "預設編解碼器"), + ("Bitrate", "位元速率"), + ("FPS", "幀率"), + ("Auto", "自動"), + ("Other Default Options", "其他預設選項"), + ("Voice call", "語音通話"), + ("Text chat", "文字聊天"), + ("Stop voice call", "停止語音通話"), + ("relay_hint_tip", "可能無法直接連線,可以嘗試中繼連線。\n另外,如果想要直接使用中繼連線,可以在 ID 後面新增/r,或者在卡片選項裡選擇強制走中繼連線。"), + ("Reconnect", "重新連線"), + ("Codec", "編解碼器"), + ("Resolution", "解析度"), + ("No transfers in progress", "沒有正在進行的傳輸"), + ("Set one-time password length", "設定一次性密碼長度"), + ("idd_driver_tip", "安裝虛擬顯示器驅動程式,以便在沒有連接顯示器的情況下啟動虛擬顯示器進行控制。"), + ("confirm_idd_driver_tip", "安裝虛擬顯示器驅動程式的選項已勾選。請注意,測試證書將被安裝以信任虛擬顯示器驅動。測試證書僅會用於信任 RustDesk 的驅動程式。"), + ("RDP Settings", "RDP 設定"), + ("Sort by", "排序方式"), + ("New Connection", "新連線"), + ("Restore", "還原"), + ("Minimize", "最小化"), + ("Maximize", "最大化"), + ("Your Device", "您的裝置"), + ("empty_recent_tip", "空空如也"), + ("empty_favorite_tip", "空空如也"), + ("empty_lan_tip", "空空如也"), + ("empty_address_book_tip", "空空如也"), + ("eg: admin", "例如:admin"), + ("Empty Username", "空使用者帳號"), + ("Empty Password", "空密碼"), + ("Me", "我"), + ("identical_file_tip", "此檔案與對方的檔案一致"), + ("show_monitors_tip", "在工具列中顯示顯示器"), + ("View Mode", "瀏覽模式"), + ].iter().cloned().collect(); +} \ No newline at end of file From 8ebfd3f62890aa1fa1192af9c554d97860227cf9 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 24 Mar 2023 15:21:14 +0800 Subject: [PATCH 034/366] refactor dialog Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 546 ++++++++++++++++++ flutter/lib/common/widgets/login.dart | 59 +- .../lib/desktop/pages/connection_page.dart | 2 + flutter/lib/desktop/pages/remote_page.dart | 2 +- .../lib/desktop/widgets/remote_toolbar.dart | 2 +- .../lib/mobile/pages/file_manager_page.dart | 2 +- flutter/lib/mobile/pages/remote_page.dart | 1 + flutter/lib/mobile/widgets/dialog.dart | 425 +------------- flutter/lib/models/model.dart | 7 +- src/lang/ca.rs | 3 + src/lang/cn.rs | 3 + src/lang/cs.rs | 3 + src/lang/da.rs | 3 + src/lang/de.rs | 3 + src/lang/el.rs | 3 + src/lang/eo.rs | 3 + src/lang/es.rs | 3 + src/lang/fa.rs | 3 + src/lang/fr.rs | 3 + src/lang/hu.rs | 3 + src/lang/id.rs | 3 + src/lang/it.rs | 3 + src/lang/ja.rs | 3 + src/lang/ko.rs | 3 + src/lang/kz.rs | 3 + src/lang/nl.rs | 3 + src/lang/pl.rs | 3 + src/lang/pt_PT.rs | 3 + src/lang/ptbr.rs | 3 + src/lang/ro.rs | 3 + src/lang/ru.rs | 3 + src/lang/sk.rs | 3 + src/lang/sl.rs | 3 + src/lang/sq.rs | 3 + src/lang/sr.rs | 3 + src/lang/sv.rs | 3 + src/lang/template.rs | 3 + src/lang/th.rs | 3 + src/lang/tr.rs | 3 + src/lang/tw.rs | 5 +- src/lang/ua.rs | 3 + src/lang/vn.rs | 3 + 42 files changed, 671 insertions(+), 476 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index ff1a372d0..97662d822 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -1,10 +1,18 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import '../../common.dart'; +import '../../models/model.dart'; import '../../models/platform_model.dart'; +void clientClose(String id, OverlayDialogManager dialogManager) { + msgBox(id, 'info', 'Close', 'Are you sure to close the connection?', '', + dialogManager); +} + abstract class ValidationRule { String get name; bool validate(String value); @@ -290,3 +298,541 @@ Future changeDirectAccessPort( }); return controller.text; } + +class DialogTextField extends StatelessWidget { + final String title; + final String? hintText; + final bool obscureText; + final String? errorText; + final String? helperText; + final Widget? prefixIcon; + final Widget? suffixIcon; + final TextEditingController controller; + final FocusNode? focusNode; + + static const kUsernameTitle = 'Username'; + static const kUsernameIcon = Icon(Icons.account_circle_outlined); + static const kPasswordTitle = 'Password'; + static const kPasswordIcon = Icon(Icons.lock_outline); + + DialogTextField( + {Key? key, + this.focusNode, + this.obscureText = false, + this.errorText, + this.helperText, + this.prefixIcon, + this.suffixIcon, + this.hintText, + required this.title, + required this.controller}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: TextField( + decoration: InputDecoration( + labelText: title, + hintText: hintText, + prefixIcon: prefixIcon, + suffixIcon: suffixIcon, + helperText: helperText, + helperMaxLines: 8, + errorText: errorText, + ), + controller: controller, + focusNode: focusNode, + autofocus: true, + obscureText: obscureText, + ), + ), + ], + ).paddingSymmetric(vertical: 4.0); + } +} + +class PasswordWidget extends StatefulWidget { + PasswordWidget({ + Key? key, + required this.controller, + this.autoFocus = true, + this.hintText, + this.errorText, + }) : super(key: key); + + final TextEditingController controller; + final bool autoFocus; + final String? hintText; + final String? errorText; + + @override + State createState() => _PasswordWidgetState(); +} + +class _PasswordWidgetState extends State { + bool _passwordVisible = false; + final _focusNode = FocusNode(); + Timer? _timer; + + @override + void initState() { + super.initState(); + if (widget.autoFocus) { + _timer = + Timer(Duration(milliseconds: 50), () => _focusNode.requestFocus()); + } + } + + @override + void dispose() { + _timer?.cancel(); + _focusNode.unfocus(); + _focusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return DialogTextField( + title: translate(DialogTextField.kPasswordTitle), + hintText: translate(widget.hintText ?? 'Enter your password'), + controller: widget.controller, + prefixIcon: DialogTextField.kPasswordIcon, + suffixIcon: IconButton( + icon: Icon( + // Based on passwordVisible state choose the icon + _passwordVisible ? Icons.visibility : Icons.visibility_off, + color: MyTheme.lightTheme.primaryColor), + onPressed: () { + // Update the state i.e. toggle the state of passwordVisible variable + setState(() { + _passwordVisible = !_passwordVisible; + }); + }, + ), + obscureText: !_passwordVisible, + errorText: widget.errorText, + focusNode: _focusNode, + ); + } +} + +void wrongPasswordDialog( + String id, OverlayDialogManager dialogManager, type, title, text) { + dialogManager.dismissAll(); + dialogManager.show((setState, close) { + cancel() { + close(); + closeConnection(); + } + + submit() { + enterPasswordDialog(id, dialogManager); + } + + return CustomAlertDialog( + title: null, + content: msgboxContent(type, title, text), + onSubmit: submit, + onCancel: cancel, + actions: [ + dialogButton( + 'Cancel', + onPressed: cancel, + isOutline: true, + ), + dialogButton( + 'Retry', + onPressed: submit, + ), + ]); + }); +} + +void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async { + await _connectDialog( + id, + dialogManager, + peerPasswordController: TextEditingController(), + ); +} + +void enterUserLoginDialog(String id, OverlayDialogManager dialogManager) async { + await _connectDialog( + id, + dialogManager, + usernameController: TextEditingController(), + passwordController: TextEditingController(), + ); +} + +void enterUserLoginAndPasswordDialog( + String id, OverlayDialogManager dialogManager) async { + await _connectDialog( + id, + dialogManager, + usernameController: TextEditingController(), + passwordController: TextEditingController(), + peerPasswordController: TextEditingController(), + ); +} + +_connectDialog( + String id, + OverlayDialogManager dialogManager, { + TextEditingController? usernameController, + TextEditingController? passwordController, + TextEditingController? peerPasswordController, +}) async { + var remember = await bind.sessionGetRemember(id: id) ?? false; + dialogManager.dismissAll(); + dialogManager.show((setState, close) { + cancel() { + close(); + closeConnection(); + } + + submit() { + final username = usernameController?.text.trim() ?? ''; + final password = passwordController?.text.trim() ?? ''; + final peerPassword = peerPasswordController?.text.trim() ?? ''; + if (peerPasswordController != null && peerPassword.isEmpty) return; + gFFI.login( + // username, + // password, + id, + peerPassword, + remember, + ); + close(); + dialogManager.showLoading(translate('Logging in...'), + onCancel: closeConnection); + } + + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.password_rounded, color: MyTheme.accent), + Text(translate(usernameController == null + ? 'Password Required' + : 'Login Required')) + .paddingOnly(left: 10), + ], + ), + content: Column(mainAxisSize: MainAxisSize.min, children: [ + usernameController == null + ? Offstage() + : DialogTextField( + title: translate(DialogTextField.kUsernameTitle), + controller: usernameController, + prefixIcon: DialogTextField.kUsernameIcon, + errorText: null, + ), + passwordController == null + ? Offstage() + : PasswordWidget( + controller: passwordController, + autoFocus: false, + ), + peerPasswordController == null + ? Offstage() + : PasswordWidget( + controller: peerPasswordController, + autoFocus: usernameController == null, + hintText: 'Enter RustDesk password', + ), + peerPasswordController == null + ? Offstage() + : CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text( + translate('Remember RustDesk password'), + ), + value: remember, + onChanged: (v) { + if (v != null) { + setState(() => remember = v); + } + }, + ), + ]), + 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, + ); + }); +} + +void showWaitUacDialog( + String id, OverlayDialogManager dialogManager, String type) { + dialogManager.dismissAll(); + dialogManager.show( + tag: '$id-wait-uac', + (setState, close) => CustomAlertDialog( + title: null, + content: msgboxContent(type, 'Wait', 'wait_accept_uac_tip'), + )); +} + +// Another username && password dialog? +void showRequestElevationDialog(String id, OverlayDialogManager dialogManager) { + RxString groupValue = ''.obs; + RxString errUser = ''.obs; + RxString errPwd = ''.obs; + TextEditingController userController = TextEditingController(); + TextEditingController pwdController = TextEditingController(); + + void onRadioChanged(String? value) { + if (value != null) { + groupValue.value = value; + } + } + + const minTextStyle = TextStyle(fontSize: 14); + + var content = Obx(() => Column(children: [ + Row( + children: [ + Radio( + value: '', + groupValue: groupValue.value, + onChanged: onRadioChanged), + Expanded( + child: + Text(translate('Ask the remote user for authentication'))), + ], + ), + Align( + alignment: Alignment.centerLeft, + child: Text( + translate( + 'Choose this if the remote account is administrator'), + style: TextStyle(fontSize: 13)) + .marginOnly(left: 40), + ).marginOnly(bottom: 15), + Row( + children: [ + Radio( + value: 'logon', + groupValue: groupValue.value, + onChanged: onRadioChanged), + Expanded( + child: Text(translate( + 'Transmit the username and password of administrator')), + ) + ], + ), + Row( + children: [ + Expanded( + flex: 1, + child: Text( + '${translate('Username')}:', + style: minTextStyle, + ).marginOnly(right: 10)), + Expanded( + flex: 3, + child: TextField( + controller: userController, + style: minTextStyle, + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.symmetric(vertical: 15), + hintText: translate('eg: admin'), + errorText: errUser.isEmpty ? null : errUser.value), + onChanged: (s) { + if (s.isNotEmpty) { + errUser.value = ''; + } + }, + ), + ) + ], + ).marginOnly(left: 40), + Row( + children: [ + Expanded( + flex: 1, + child: Text( + '${translate('Password')}:', + style: minTextStyle, + ).marginOnly(right: 10)), + Expanded( + flex: 3, + child: TextField( + controller: pwdController, + obscureText: true, + style: minTextStyle, + decoration: InputDecoration( + isDense: true, + contentPadding: EdgeInsets.symmetric(vertical: 15), + errorText: errPwd.isEmpty ? null : errPwd.value), + onChanged: (s) { + if (s.isNotEmpty) { + errPwd.value = ''; + } + }, + ), + ), + ], + ).marginOnly(left: 40), + Align( + alignment: Alignment.centerLeft, + child: Text(translate('still_click_uac_tip'), + style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)) + .marginOnly(top: 20)), + ])); + + dialogManager.dismissAll(); + dialogManager.show(tag: '$id-request-elevation', (setState, close) { + void submit() { + if (groupValue.value == 'logon') { + if (userController.text.isEmpty) { + errUser.value = translate('Empty Username'); + return; + } + if (pwdController.text.isEmpty) { + errPwd.value = translate('Empty Password'); + return; + } + bind.sessionElevateWithLogon( + id: id, + username: userController.text, + password: pwdController.text); + } else { + bind.sessionElevateDirect(id: id); + } + } + + return CustomAlertDialog( + title: Text(translate('Request Elevation')), + content: content, + actions: [ + dialogButton('Cancel', onPressed: close, isOutline: true), + dialogButton('OK', onPressed: submit), + ], + onSubmit: submit, + onCancel: close, + ); + }); +} + +void showOnBlockDialog( + String id, + String type, + String title, + String text, + OverlayDialogManager dialogManager, +) { + if (dialogManager.existing('$id-wait-uac') || + dialogManager.existing('$id-request-elevation')) { + return; + } + dialogManager.show(tag: '$id-$type', (setState, close) { + void submit() { + close(); + showRequestElevationDialog(id, dialogManager); + } + + return CustomAlertDialog( + title: null, + content: msgboxContent(type, title, + "${translate(text)}${type.contains('uac') ? '\n' : '\n\n'}${translate('request_elevation_tip')}"), + actions: [ + dialogButton('Wait', onPressed: close, isOutline: true), + dialogButton('Request Elevation', onPressed: submit), + ], + onSubmit: submit, + onCancel: close, + ); + }); +} + +void showElevationError(String id, String type, String title, String text, + OverlayDialogManager dialogManager) { + dialogManager.show(tag: '$id-$type', (setState, close) { + void submit() { + close(); + showRequestElevationDialog(id, dialogManager); + } + + return CustomAlertDialog( + title: null, + content: msgboxContent(type, title, text), + actions: [ + dialogButton('Cancel', onPressed: () { + close(); + }, isOutline: true), + dialogButton('Retry', onPressed: submit), + ], + onSubmit: submit, + onCancel: close, + ); + }); +} + +void showWaitAcceptDialog(String id, String type, String title, String text, + OverlayDialogManager dialogManager) { + dialogManager.dismissAll(); + dialogManager.show((setState, close) { + onCancel() { + closeConnection(); + } + + return CustomAlertDialog( + title: null, + content: msgboxContent(type, title, text), + actions: [ + dialogButton('Cancel', onPressed: onCancel, isOutline: true), + ], + onCancel: onCancel, + ); + }); +} + +void showRestartRemoteDevice( + PeerInfo pi, String id, OverlayDialogManager dialogManager) async { + final res = + await dialogManager.show((setState, close) => CustomAlertDialog( + title: Row(children: [ + Icon(Icons.warning_rounded, color: Colors.redAccent, size: 28), + Text(translate("Restart Remote Device")).paddingOnly(left: 10), + ]), + content: Text( + "${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"), + actions: [ + dialogButton( + "Cancel", + icon: Icon(Icons.close_rounded), + onPressed: close, + isOutline: true, + ), + dialogButton( + "OK", + icon: Icon(Icons.done_rounded), + onPressed: () => close(true), + ), + ], + onCancel: close, + onSubmit: () => close(true), + )); + if (res == true) bind.sessionRestartRemoteDevice(id: id); +} diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart index 7b37c590d..3959c301c 100644 --- a/flutter/lib/common/widgets/login.dart +++ b/flutter/lib/common/widgets/login.dart @@ -9,6 +9,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../common.dart'; +import './dialog.dart'; class _IconOP extends StatelessWidget { final String icon; @@ -324,17 +325,16 @@ class LoginWidgetUserPass extends StatelessWidget { children: [ const SizedBox(height: 8.0), DialogTextField( - title: translate("Username"), + title: translate(DialogTextField.kUsernameTitle), controller: username, focusNode: userFocusNode, - prefixIcon: Icon(Icons.account_circle_outlined), + prefixIcon: DialogTextField.kUsernameIcon, errorText: usernameMsg), - DialogTextField( - title: translate("Password"), - obscureText: true, - controller: pass, - prefixIcon: Icon(Icons.lock_outline), - errorText: passMsg), + PasswordWidget( + controller: pass, + autoFocus: false, + errorText: passMsg, + ), Obx(() => CheckboxListTile( contentPadding: const EdgeInsets.all(0), dense: true, @@ -377,49 +377,6 @@ class LoginWidgetUserPass extends StatelessWidget { } } -class DialogTextField extends StatelessWidget { - final String title; - final bool obscureText; - final String? errorText; - final String? helperText; - final Widget? prefixIcon; - final TextEditingController controller; - final FocusNode? focusNode; - - DialogTextField( - {Key? key, - this.focusNode, - this.obscureText = false, - this.errorText, - this.helperText, - this.prefixIcon, - required this.title, - required this.controller}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Expanded( - child: TextField( - decoration: InputDecoration( - labelText: title, - prefixIcon: prefixIcon, - helperText: helperText, - helperMaxLines: 8, - errorText: errorText), - controller: controller, - focusNode: focusNode, - autofocus: true, - obscureText: obscureText, - ), - ), - ], - ).paddingSymmetric(vertical: 4.0); - } -} - /// common login dialog for desktop /// call this directly Future loginDialog() async { diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index b6debbd8d..a593810fd 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -19,6 +19,8 @@ import '../../common/widgets/peer_tab_page.dart'; import '../../models/platform_model.dart'; import '../widgets/button.dart'; +import 'package:flutter_hbb/common/widgets/dialog.dart'; + /// Connection page for connecting to a remote peer. class ConnectionPage extends StatefulWidget { const ConnectionPage({Key? key}) : super(key: key); diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index aea073ace..1bc09f137 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -17,7 +17,7 @@ import '../../consts.dart'; import '../../common/widgets/overlay.dart'; import '../../common/widgets/remote_input.dart'; import '../../common.dart'; -import '../../mobile/widgets/dialog.dart'; +import '../../common/widgets/dialog.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; import '../../common/shared_state.dart'; diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index c89bfa766..45a1a55bc 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -16,7 +16,7 @@ import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:window_size/window_size.dart' as window_size; import '../../common.dart'; -import '../../mobile/widgets/dialog.dart'; +import '../../common/widgets/dialog.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; import '../../common/shared_state.dart'; diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart index c6ba42d31..30e0661f6 100644 --- a/flutter/lib/mobile/pages/file_manager_page.dart +++ b/flutter/lib/mobile/pages/file_manager_page.dart @@ -8,7 +8,7 @@ import 'package:toggle_switch/toggle_switch.dart'; import 'package:wakelock/wakelock.dart'; import '../../common.dart'; -import '../widgets/dialog.dart'; +import '../../common/widgets/dialog.dart'; class FileManagerPage extends StatefulWidget { FileManagerPage({Key? key, required this.id}) : super(key: key); diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 4d94dc669..8f4efaece 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -14,6 +14,7 @@ import 'package:wakelock/wakelock.dart'; import '../../common.dart'; import '../../common/widgets/overlay.dart'; +import '../../common/widgets/dialog.dart'; import '../../common/widgets/remote_input.dart'; import '../../models/input_model.dart'; import '../../models/model.dart'; diff --git a/flutter/lib/mobile/widgets/dialog.dart b/flutter/lib/mobile/widgets/dialog.dart index b14401795..b281895ff 100644 --- a/flutter/lib/mobile/widgets/dialog.dart +++ b/flutter/lib/mobile/widgets/dialog.dart @@ -4,51 +4,16 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../common.dart'; -import '../../models/model.dart'; import '../../models/platform_model.dart'; -void clientClose(String id, OverlayDialogManager dialogManager) { - msgBox(id, 'info', 'Close', 'Are you sure to close the connection?', '', - dialogManager); -} - -void showSuccess() { +void _showSuccess() { showToast(translate("Successful")); } -void showError() { +void _showError() { showToast(translate("Error")); } -void showRestartRemoteDevice( - PeerInfo pi, String id, OverlayDialogManager dialogManager) async { - final res = - await dialogManager.show((setState, close) => CustomAlertDialog( - title: Row(children: [ - Icon(Icons.warning_rounded, color: Colors.redAccent, size: 28), - Text(translate("Restart Remote Device")).paddingOnly(left: 10), - ]), - content: Text( - "${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"), - actions: [ - dialogButton( - "Cancel", - icon: Icon(Icons.close_rounded), - onPressed: close, - isOutline: true, - ), - dialogButton( - "OK", - icon: Icon(Icons.done_rounded), - onPressed: () => close(true), - ), - ], - onCancel: close, - onSubmit: () => close(true), - )); - if (res == true) bind.sessionRestartRemoteDevice(id: id); -} - void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async { final pw = await bind.mainGetPermanentPassword(); final p0 = TextEditingController(text: pw); @@ -61,10 +26,10 @@ void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async { dialogManager.showLoading(translate("Waiting")); if (await gFFI.serverModel.setPermanentPassword(p0.text)) { dialogManager.dismissAll(); - showSuccess(); + _showSuccess(); } else { dialogManager.dismissAll(); - showError(); + _showError(); } } @@ -157,7 +122,7 @@ void setTemporaryPasswordLengthDialog( bind.mainUpdateTemporaryPassword(); Future.delayed(Duration(milliseconds: 200), () { close(); - showSuccess(); + _showSuccess(); }); } @@ -173,101 +138,6 @@ void setTemporaryPasswordLengthDialog( }, backDismiss: true, clickMaskDismiss: true); } -void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async { - final controller = TextEditingController(); - var remember = await bind.sessionGetRemember(id: id) ?? false; - dialogManager.dismissAll(); - dialogManager.show((setState, close) { - cancel() { - close(); - closeConnection(); - } - - submit() { - var text = controller.text.trim(); - if (text == '') return; - gFFI.login(id, text, remember); - close(); - dialogManager.showLoading(translate('Logging in...'), - onCancel: closeConnection); - } - - return CustomAlertDialog( - title: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.password_rounded, color: MyTheme.accent), - Text(translate('Password Required')).paddingOnly(left: 10), - ], - ), - content: Column(mainAxisSize: MainAxisSize.min, children: [ - PasswordWidget(controller: controller), - CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate('Remember password'), - ), - value: remember, - onChanged: (v) { - if (v != null) { - setState(() => remember = v); - } - }, - ), - ]), - 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, - ); - }); -} - -void wrongPasswordDialog( - String id, OverlayDialogManager dialogManager, type, title, text) { - dialogManager.dismissAll(); - dialogManager.show((setState, close) { - cancel() { - close(); - closeConnection(); - } - - submit() { - enterPasswordDialog(id, dialogManager); - } - - return CustomAlertDialog( - title: null, - content: msgboxContent(type, title, text), - onSubmit: submit, - onCancel: cancel, - actions: [ - dialogButton( - 'Cancel', - onPressed: cancel, - isOutline: true, - ), - dialogButton( - 'Retry', - onPressed: submit, - ), - ]); - }); -} - void showServerSettingsWithValue( ServerConfig serverConfig, OverlayDialogManager dialogManager) async { Map oldOptions = jsonDecode(await bind.mainGetOptions()); @@ -393,232 +263,6 @@ void showServerSettingsWithValue( }); } -void showWaitUacDialog( - String id, OverlayDialogManager dialogManager, String type) { - dialogManager.dismissAll(); - dialogManager.show( - tag: '$id-wait-uac', - (setState, close) => CustomAlertDialog( - title: null, - content: msgboxContent(type, 'Wait', 'wait_accept_uac_tip'), - )); -} - -void showRequestElevationDialog(String id, OverlayDialogManager dialogManager) { - RxString groupValue = ''.obs; - RxString errUser = ''.obs; - RxString errPwd = ''.obs; - TextEditingController userController = TextEditingController(); - TextEditingController pwdController = TextEditingController(); - - void onRadioChanged(String? value) { - if (value != null) { - groupValue.value = value; - } - } - - const minTextStyle = TextStyle(fontSize: 14); - - var content = Obx(() => Column(children: [ - Row( - children: [ - Radio( - value: '', - groupValue: groupValue.value, - onChanged: onRadioChanged), - Expanded( - child: - Text(translate('Ask the remote user for authentication'))), - ], - ), - Align( - alignment: Alignment.centerLeft, - child: Text( - translate( - 'Choose this if the remote account is administrator'), - style: TextStyle(fontSize: 13)) - .marginOnly(left: 40), - ).marginOnly(bottom: 15), - Row( - children: [ - Radio( - value: 'logon', - groupValue: groupValue.value, - onChanged: onRadioChanged), - Expanded( - child: Text(translate( - 'Transmit the username and password of administrator')), - ) - ], - ), - Row( - children: [ - Expanded( - flex: 1, - child: Text( - '${translate('Username')}:', - style: minTextStyle, - ).marginOnly(right: 10)), - Expanded( - flex: 3, - child: TextField( - controller: userController, - style: minTextStyle, - decoration: InputDecoration( - isDense: true, - contentPadding: EdgeInsets.symmetric(vertical: 15), - hintText: translate('eg: admin'), - errorText: errUser.isEmpty ? null : errUser.value), - onChanged: (s) { - if (s.isNotEmpty) { - errUser.value = ''; - } - }, - ), - ) - ], - ).marginOnly(left: 40), - Row( - children: [ - Expanded( - flex: 1, - child: Text( - '${translate('Password')}:', - style: minTextStyle, - ).marginOnly(right: 10)), - Expanded( - flex: 3, - child: TextField( - controller: pwdController, - obscureText: true, - style: minTextStyle, - decoration: InputDecoration( - isDense: true, - contentPadding: EdgeInsets.symmetric(vertical: 15), - errorText: errPwd.isEmpty ? null : errPwd.value), - onChanged: (s) { - if (s.isNotEmpty) { - errPwd.value = ''; - } - }, - ), - ), - ], - ).marginOnly(left: 40), - Align( - alignment: Alignment.centerLeft, - child: Text(translate('still_click_uac_tip'), - style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)) - .marginOnly(top: 20)), - ])); - - dialogManager.dismissAll(); - dialogManager.show(tag: '$id-request-elevation', (setState, close) { - void submit() { - if (groupValue.value == 'logon') { - if (userController.text.isEmpty) { - errUser.value = translate('Empty Username'); - return; - } - if (pwdController.text.isEmpty) { - errPwd.value = translate('Empty Password'); - return; - } - bind.sessionElevateWithLogon( - id: id, - username: userController.text, - password: pwdController.text); - } else { - bind.sessionElevateDirect(id: id); - } - } - - return CustomAlertDialog( - title: Text(translate('Request Elevation')), - content: content, - actions: [ - dialogButton('Cancel', onPressed: close, isOutline: true), - dialogButton('OK', onPressed: submit), - ], - onSubmit: submit, - onCancel: close, - ); - }); -} - -void showOnBlockDialog( - String id, - String type, - String title, - String text, - OverlayDialogManager dialogManager, -) { - if (dialogManager.existing('$id-wait-uac') || - dialogManager.existing('$id-request-elevation')) { - return; - } - dialogManager.show(tag: '$id-$type', (setState, close) { - void submit() { - close(); - showRequestElevationDialog(id, dialogManager); - } - - return CustomAlertDialog( - title: null, - content: msgboxContent(type, title, - "${translate(text)}${type.contains('uac') ? '\n' : '\n\n'}${translate('request_elevation_tip')}"), - actions: [ - dialogButton('Wait', onPressed: close, isOutline: true), - dialogButton('Request Elevation', onPressed: submit), - ], - onSubmit: submit, - onCancel: close, - ); - }); -} - -void showElevationError(String id, String type, String title, String text, - OverlayDialogManager dialogManager) { - dialogManager.show(tag: '$id-$type', (setState, close) { - void submit() { - close(); - showRequestElevationDialog(id, dialogManager); - } - - return CustomAlertDialog( - title: null, - content: msgboxContent(type, title, text), - actions: [ - dialogButton('Cancel', onPressed: () { - close(); - }, isOutline: true), - dialogButton('Retry', onPressed: submit), - ], - onSubmit: submit, - onCancel: close, - ); - }); -} - -void showWaitAcceptDialog(String id, String type, String title, String text, - OverlayDialogManager dialogManager) { - dialogManager.dismissAll(); - dialogManager.show((setState, close) { - onCancel() { - closeConnection(); - } - - return CustomAlertDialog( - title: null, - content: msgboxContent(type, title, text), - actions: [ - dialogButton('Cancel', onPressed: onCancel, isOutline: true), - ], - onCancel: onCancel, - ); - }); -} - Future validateAsync(String value) async { value = value.trim(); if (value.isEmpty) { @@ -627,62 +271,3 @@ Future validateAsync(String value) async { final res = await bind.mainTestIfValidServer(server: value); return res.isEmpty ? null : res; } - -class PasswordWidget extends StatefulWidget { - PasswordWidget({Key? key, required this.controller, this.autoFocus = true}) - : super(key: key); - - final TextEditingController controller; - final bool autoFocus; - - @override - State createState() => _PasswordWidgetState(); -} - -class _PasswordWidgetState extends State { - bool _passwordVisible = false; - final _focusNode = FocusNode(); - - @override - void initState() { - super.initState(); - if (widget.autoFocus) { - Timer(Duration(milliseconds: 50), () => _focusNode.requestFocus()); - } - } - - @override - void dispose() { - _focusNode.unfocus(); - _focusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return TextField( - focusNode: _focusNode, - controller: widget.controller, - obscureText: !_passwordVisible, - //This will obscure text dynamically - keyboardType: TextInputType.visiblePassword, - decoration: InputDecoration( - labelText: translate('Password'), - hintText: translate('Enter your password'), - // Here is key idea - suffixIcon: IconButton( - icon: Icon( - // Based on passwordVisible state choose the icon - _passwordVisible ? Icons.visibility : Icons.visibility_off, - color: MyTheme.lightTheme.primaryColor), - onPressed: () { - // Update the state i.e. toggle the state of passwordVisible variable - setState(() { - _passwordVisible = !_passwordVisible; - }); - }, - ), - ), - ); - } -} diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index c5c3999ee..701fae48c 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -25,7 +25,7 @@ import 'package:get/get.dart'; import '../common.dart'; import '../utils/image.dart' as img; -import '../mobile/widgets/dialog.dart'; +import '../common/widgets/dialog.dart'; import 'input_model.dart'; import 'platform_model.dart'; @@ -293,6 +293,11 @@ class FfiModel with ChangeNotifier { wrongPasswordDialog(id, dialogManager, type, title, text); } else if (type == 'input-password') { enterPasswordDialog(id, dialogManager); + } else if (type == 'xsession-login' || type == 'xsession-re-login') { + // to-do + } else if (type == 'xsession-login-password' || + type == 'xsession-login-password') { + // to-do } else if (type == 'restarting') { showMsgBox(id, type, title, text, link, false, dialogManager, hasCancel: false); diff --git a/src/lang/ca.rs b/src/lang/ca.rs index c77d73944..5a9e7b914 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 14dc440ae..ee3433282 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此文件与对方的一致"), ("show_monitors_tip", ""), ("View Mode", "浏览模式"), + ("Enter RustDesk password", "请输入RustDesk密码"), + ("Remember RustDesk password", "记住RustDesk密码"), + ("Login Required", "需要登陆"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 5e8bdba5f..cfb23eca6 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 542543155..0c5615902 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 876c6dd46..46c6d5ded 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Diese Datei ist identisch mit der Datei der Gegenstelle."), ("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"), ("View Mode", "Ansichtsmodus"), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 6d8a77d20..46a482a1b 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Το αρχείο είναι πανομοιότυπο με αυτό του άλλου υπολογιστή."), ("show_monitors_tip", "Εμφάνιση οθονών στη γραμμή εργαλείων"), ("View Mode", "Λειτουργία προβολής"), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 6d697528d..f3d9923a5 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index ed53d22bd..5885a3647 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Este archivo es idéntico al del par."), ("show_monitors_tip", "Mostrar monitores en la barra de herramientas"), ("View Mode", "Modo Vista"), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index ffdf22a50..b41713f5c 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "این فایل با فایل همتا یکسان است."), ("show_monitors_tip", "نمایش مانیتورها در نوار ابزار"), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 6aa7f12f6..7e42b53b6 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 474b3a70d..03ea22b5b 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 3bdef56bb..d6717ae93 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 5b3e4d92f..56229059b 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Questo file è identico a quello del peer."), ("show_monitors_tip", "Mostra schermi nella barra degli strumenti"), ("View Mode", "Modalità di visualizzazione"), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 925ccebde..dbf69b76b 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 89c65a332..95138b2b1 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 0ec4d099b..cb7ccace4 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 46ae3799a..b20895b71 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index aabf14fc7..28c870f2c 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 25561d43a..15eff4cbd 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 6a6df5078..b036637fa 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 886f27bd4..cc7ecdf5d 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 0dd1c6fe9..95ddba4b9 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Файл идентичен файлу на удалённом узле."), ("show_monitors_tip", "Показывать мониторы на панели инструментов"), ("View Mode", "Режим просмотра"), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index ceed59b43..00233f8a7 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 14e50fc1d..d6c276ae6 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 142074539..1da1c45dd 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 7ea384def..adea7f7cf 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 31db07b30..ca93ba385 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 343d6c1ea..538f9454a 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index a05f8ff88..c740eb6f4 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index e00877118..4d2370c94 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index d80aea781..f85d72034 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此檔案與對方的檔案一致"), ("show_monitors_tip", "在工具列中顯示顯示器"), ("View Mode", "瀏覽模式"), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); -} \ No newline at end of file +} diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 7abfd4188..70abb42db 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 918106363..d73882558 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -480,5 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), + ("Enter RustDesk password", ""), + ("Remember RustDesk password", ""), + ("Login Required", ""), ].iter().cloned().collect(); } From 9bca87a51f9f9e7f8faf8eb9cb6706e735349c60 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 24 Mar 2023 15:49:29 +0800 Subject: [PATCH 035/366] trivial changes Signed-off-by: fufesou --- src/lang/cn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/cn.rs b/src/lang/cn.rs index ee3433282..982fe6d69 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此文件与对方的一致"), ("show_monitors_tip", ""), ("View Mode", "浏览模式"), - ("Enter RustDesk password", "请输入RustDesk密码"), - ("Remember RustDesk password", "记住RustDesk密码"), + ("Enter RustDesk password", "请输入 RustDesk 密码"), + ("Remember RustDesk password", "记住 RustDesk 密码"), ("Login Required", "需要登陆"), ].iter().cloned().collect(); } From 260346fa3e81e64ac944e400c10f069d08d0c2d1 Mon Sep 17 00:00:00 2001 From: solokot Date: Fri, 24 Mar 2023 11:16:05 +0300 Subject: [PATCH 036/366] ru.rs small improove --- src/lang/ru.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 0dd1c6fe9..589820b50 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -460,7 +460,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Resolution", "Разрешение"), ("No transfers in progress", "Передача не осуществляется"), ("Set one-time password length", "Установить длину одноразового пароля"), - ("idd_driver_tip", "Установите драйвер виртуального дисплея, который используется при отсутствии физических дисплеев."), + ("idd_driver_tip", "Установить драйвер виртуального дисплея (используется при отсутствии физических дисплеев)"), ("confirm_idd_driver_tip", "Включена функция установки драйвера виртуального дисплея. Обратите внимание, что для доверия к драйверу будет установлен тестовый сертификат. Этот сертификат будет использоваться только для подтверждения доверия драйверам Rustdesk."), ("RDP Settings", "Настройки RDP"), ("Sort by", "Сортировка"), From 98f24bb85a1af7dcb6599259097cbb07bfa3448c Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 24 Mar 2023 16:22:05 +0800 Subject: [PATCH 037/366] better connect dialog tips Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 16 +++++++++++----- src/lang/ca.rs | 6 +++--- src/lang/cn.rs | 7 ++++--- src/lang/cs.rs | 6 +++--- src/lang/da.rs | 6 +++--- src/lang/de.rs | 6 +++--- src/lang/el.rs | 6 +++--- src/lang/en.rs | 6 +++++- src/lang/eo.rs | 6 +++--- src/lang/es.rs | 6 +++--- src/lang/fa.rs | 6 +++--- src/lang/fr.rs | 6 +++--- src/lang/hu.rs | 6 +++--- src/lang/id.rs | 6 +++--- src/lang/it.rs | 6 +++--- src/lang/ja.rs | 6 +++--- src/lang/ko.rs | 6 +++--- src/lang/kz.rs | 6 +++--- src/lang/nl.rs | 6 +++--- src/lang/pl.rs | 6 +++--- src/lang/pt_PT.rs | 6 +++--- src/lang/ptbr.rs | 6 +++--- src/lang/ro.rs | 6 +++--- src/lang/ru.rs | 6 +++--- src/lang/sk.rs | 6 +++--- src/lang/sl.rs | 6 +++--- src/lang/sq.rs | 6 +++--- src/lang/sr.rs | 6 +++--- src/lang/sv.rs | 6 +++--- src/lang/template.rs | 7 ++++--- src/lang/th.rs | 6 +++--- src/lang/tr.rs | 6 +++--- src/lang/tw.rs | 6 +++--- src/lang/ua.rs | 6 +++--- src/lang/vn.rs | 6 +++--- 35 files changed, 117 insertions(+), 105 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 97662d822..9d7165c0a 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -517,9 +517,12 @@ _connectDialog( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.password_rounded, color: MyTheme.accent), - Text(translate(usernameController == null - ? 'Password Required' - : 'Login Required')) + (usernameController == null + ? Text(translate('Password Required')) + : Tooltip( + message: translate('login_linux_tooltip_tip'), + child: Text(translate('login_linux_tip')), + )) .paddingOnly(left: 10), ], ), @@ -538,12 +541,15 @@ _connectDialog( controller: passwordController, autoFocus: false, ), + usernameController == null || usernameController == null + ? Offstage() + : const Divider(), peerPasswordController == null ? Offstage() : PasswordWidget( controller: peerPasswordController, autoFocus: usernameController == null, - hintText: 'Enter RustDesk password', + hintText: 'enter_rustdesk_passwd_tip', ), peerPasswordController == null ? Offstage() @@ -552,7 +558,7 @@ _connectDialog( dense: true, controlAffinity: ListTileControlAffinity.leading, title: Text( - translate('Remember RustDesk password'), + translate('remember_rustdesk_passwd_tip'), ), value: remember, onChanged: (v) { diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 5a9e7b914..162a48883 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 982fe6d69..7ded75e0d 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -480,8 +480,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此文件与对方的一致"), ("show_monitors_tip", ""), ("View Mode", "浏览模式"), - ("Enter RustDesk password", "请输入 RustDesk 密码"), - ("Remember RustDesk password", "记住 RustDesk 密码"), - ("Login Required", "需要登陆"), + ("enter_rustdesk_passwd_tip", "请输入 RustDesk 密码"), + ("remember_rustdesk_passwd_tip", "记住 RustDesk 密码"), + ("login_linux_tip", "登陆被控端的 Linux 账户"), + ("login_linux_tooltip_tip", "登陆被控端的 Linux 账户,才能启用 X 桌面"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index cfb23eca6..2f9e52f6c 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 0c5615902..a9ca8e8a6 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 46c6d5ded..ec4850b3a 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Diese Datei ist identisch mit der Datei der Gegenstelle."), ("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"), ("View Mode", "Ansichtsmodus"), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 46a482a1b..51ac6f041 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Το αρχείο είναι πανομοιότυπο με αυτό του άλλου υπολογιστή."), ("show_monitors_tip", "Εμφάνιση οθονών στη γραμμή εργαλείων"), ("View Mode", "Λειτουργία προβολής"), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index eb09dacd2..8a981564b 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -53,6 +53,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("empty_lan_tip", "Oh no, it looks like we haven't discovered any peers yet."), ("empty_address_book_tip", "Oh dear, it appears that there are currently no peers listed in your address book."), ("identical_file_tip", "This file is identical with the peer's one."), - ("show_monitors_tip", "Show monitors in toolbar") + ("show_monitors_tip", "Show monitors in toolbar."), + ("enter_rustdesk_passwd_tip", "Enter RustDesk password."), + ("remember_rustdesk_passwd_tip", "Remember RustDesk password."), + ("login_linux_tip", "Login to remote Linux account"), + ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index f3d9923a5..e430f8f67 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 5885a3647..c4e78943a 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Este archivo es idéntico al del par."), ("show_monitors_tip", "Mostrar monitores en la barra de herramientas"), ("View Mode", "Modo Vista"), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index b41713f5c..e4d026586 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "این فایل با فایل همتا یکسان است."), ("show_monitors_tip", "نمایش مانیتورها در نوار ابزار"), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 7e42b53b6..bf3853d96 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 03ea22b5b..2e6a20b56 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index d6717ae93..cca4ba6c1 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 56229059b..f5a7d174c 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Questo file è identico a quello del peer."), ("show_monitors_tip", "Mostra schermi nella barra degli strumenti"), ("View Mode", "Modalità di visualizzazione"), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index dbf69b76b..29bf3c346 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 95138b2b1..929934016 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index cb7ccace4..a2c7c5983 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index b20895b71..d46be2b3f 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 28c870f2c..30459ffa0 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 15eff4cbd..45226dec6 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index b036637fa..8cf8ca6ef 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index cc7ecdf5d..e81fa0cfd 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 95ddba4b9..9163ae8da 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Файл идентичен файлу на удалённом узле."), ("show_monitors_tip", "Показывать мониторы на панели инструментов"), ("View Mode", "Режим просмотра"), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 00233f8a7..7d28cfb72 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index d6c276ae6..544e544f7 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 1da1c45dd..afd5c2c2d 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index adea7f7cf..05204f728 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index ca93ba385..7941c2f58 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 538f9454a..56508d80a 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -480,8 +480,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index c740eb6f4..4f178bf1d 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 4d2370c94..f5209129b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index f85d72034..2158db175 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此檔案與對方的檔案一致"), ("show_monitors_tip", "在工具列中顯示顯示器"), ("View Mode", "瀏覽模式"), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 70abb42db..e21cd88df 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index d73882558..f716bd536 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("Enter RustDesk password", ""), - ("Remember RustDesk password", ""), - ("Login Required", ""), + ("enter_rustdesk_passwd_tip", ""), + ("remember_rustdesk_passwd_tip", ""), + ("login_linux_tip", ""), ].iter().cloned().collect(); } From 8aaefe861204ee3a863b37c779674cc3997179dd Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 24 Mar 2023 16:28:37 +0800 Subject: [PATCH 038/366] misspell Signed-off-by: fufesou --- src/lang/cn.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 7ded75e0d..250f9a5b6 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -482,7 +482,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("View Mode", "浏览模式"), ("enter_rustdesk_passwd_tip", "请输入 RustDesk 密码"), ("remember_rustdesk_passwd_tip", "记住 RustDesk 密码"), - ("login_linux_tip", "登陆被控端的 Linux 账户"), - ("login_linux_tooltip_tip", "登陆被控端的 Linux 账户,才能启用 X 桌面"), + ("login_linux_tip", "登录被控端的 Linux 账户"), + ("login_linux_tooltip_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), ].iter().cloned().collect(); } From 3b42b331d11e853c53fd488d9d600e747c9efb44 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 24 Mar 2023 17:39:31 +0800 Subject: [PATCH 039/366] fix home page tabbar label language cannot be changed Signed-off-by: 21pages --- flutter/lib/desktop/pages/desktop_tab_page.dart | 4 ++-- flutter/lib/desktop/widgets/tabbar_widget.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 4a1a40242..a642e2590 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -23,7 +23,7 @@ class DesktopTabPage extends StatefulWidget { DesktopTabController tabController = Get.find(); tabController.add(TabInfo( key: kTabLabelSettingPage, - label: translate(kTabLabelSettingPage), + label: kTabLabelSettingPage, selectedIcon: Icons.build_sharp, unselectedIcon: Icons.build_outlined, page: DesktopSettingPage( @@ -46,7 +46,7 @@ class _DesktopTabPageState extends State { RemoteCountState.init(); tabController.add(TabInfo( key: kTabLabelHomePage, - label: translate(kTabLabelHomePage), + label: kTabLabelHomePage, selectedIcon: Icons.home_sharp, unselectedIcon: Icons.home_outlined, closable: false, diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 4211911ff..2186e879e 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -869,7 +869,7 @@ class _TabState extends State<_Tab> with RestorationMixin { return ConstrainedBox( constraints: BoxConstraints(maxWidth: widget.maxLabelWidth ?? 200), child: Text( - widget.label.value, + translate(widget.label.value), textAlign: TextAlign.center, style: TextStyle( color: isSelected From a6cfed3f11a4a179a77272c1a897b5f5ad4aa02d Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 24 Mar 2023 19:09:50 +0800 Subject: [PATCH 040/366] fix connect dialog, check controller null Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 9d7165c0a..af75a557b 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -541,7 +541,7 @@ _connectDialog( controller: passwordController, autoFocus: false, ), - usernameController == null || usernameController == null + usernameController == null || peerPasswordController == null ? Offstage() : const Divider(), peerPasswordController == null From 06cb59cb3bd825961dcc18fb3037295d85317623 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Fri, 24 Mar 2023 14:34:23 +0100 Subject: [PATCH 041/366] Update de.rs --- src/lang/de.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index ec4850b3a..b70e9f872 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -218,7 +218,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("whitelist_tip", "Nur IPs auf der Whitelist können zugreifen."), ("Login", "Anmelden"), ("Verify", "Überprüfen"), - ("Remember me", "Login speichern"), + ("Remember me", "Login merken"), ("Trust this device", "Diesem Gerät vertrauen"), ("Verification code", "Verifizierungscode"), ("verification_tip", "Es wurde ein neues Gerät erkannt und ein Verifizierungscode an die registrierte E-Mail-Adresse gesendet. Geben Sie den Verifizierungscode ein, um sich erneut anzumelden."), @@ -480,8 +480,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Diese Datei ist identisch mit der Datei der Gegenstelle."), ("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"), ("View Mode", "Ansichtsmodus"), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("enter_rustdesk_passwd_tip", "RustDesk-Passwort eingeben."), + ("remember_rustdesk_passwd_tip", "RustDesk-Passwort merken."), + ("login_linux_tip", "Anmeldung am entfernten Linux-Konto"), + ("login_linux_tooltip_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), ].iter().cloned().collect(); } From bdafa86e0a452c6166ee764005eb84239c92e853 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Sat, 25 Mar 2023 11:15:09 +0800 Subject: [PATCH 042/366] fix: shortcut not working due to range error --- flutter/lib/common.dart | 6 +++--- flutter/lib/main.dart | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index f0fb00c2b..1b6afd584 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1522,14 +1522,14 @@ bool checkArguments() { return false; } String? id = - kBootArgs.length < connectIndex + 1 ? null : kBootArgs[connectIndex + 1]; + kBootArgs.length <= connectIndex + 1 ? null : kBootArgs[connectIndex + 1]; String? password = - kBootArgs.length < connectIndex + 2 ? null : kBootArgs[connectIndex + 2]; + kBootArgs.length <= connectIndex + 2 ? null : kBootArgs[connectIndex + 2]; if (password != null && password.startsWith("--")) { password = null; } final switchUuidIndex = kBootArgs.indexOf("--switch_uuid"); - String? switchUuid = kBootArgs.length < switchUuidIndex + 1 + String? switchUuid = kBootArgs.length <= switchUuidIndex + 1 ? null : kBootArgs[switchUuidIndex + 1]; if (id != null) { diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index f0a9a938f..164de04d6 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -134,10 +134,9 @@ void runMainApp(bool startService) async { await restoreWindowPosition(WindowType.Main); // Check the startup argument, if we successfully handle the argument, we keep the main window hidden. final handledByUniLinks = await initUniLinks(); - final handledByCli = checkArguments(); debugPrint( - "handled by uni links: $handledByUniLinks, handled by cli: $handledByCli"); - if (handledByUniLinks || handledByCli) { + "handled by uni links: $handledByUniLinks"); + if (handledByUniLinks || checkArguments()) { windowManager.hide(); } else { windowManager.show(); From 415b67f85335ec0ef8d5fcc8d3ae6a021b527773 Mon Sep 17 00:00:00 2001 From: 2mao Date: Sat, 25 Mar 2023 13:05:05 +0800 Subject: [PATCH 043/366] Update flutter-nightly.yml --- .github/workflows/flutter-nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index adef45cc1..9acba580f 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -33,6 +33,7 @@ jobs: # - { target: i686-pc-windows-msvc , os: windows-2019 } # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - { target: x86_64-pc-windows-msvc, os: windows-2019 } + - { target: aarch64-pc-windows-msvc, os: windows-2019 } steps: - name: Checkout source code uses: actions/checkout@v3 From 94ccf60244b298326ea1e0c5eceabda27b890265 Mon Sep 17 00:00:00 2001 From: 2mao Date: Sat, 25 Mar 2023 13:25:24 +0800 Subject: [PATCH 044/366] Update flutter-nightly.yml --- .github/workflows/flutter-nightly.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 9acba580f..8990c9609 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -146,6 +146,11 @@ jobs: os: macos-latest, extra-build-args: "", } + - { + target: aarch64-apple-darwin, + os: macos-latest, + extra-build-args: "", + } steps: - name: Checkout source code uses: actions/checkout@v3 From 63441e6411c4aa43c74c02fa9fa416605377eeaf Mon Sep 17 00:00:00 2001 From: FastAct <93490087+FastAct@users.noreply.github.com> Date: Sat, 25 Mar 2023 09:26:30 +0100 Subject: [PATCH 045/366] Update nl.rs --- src/lang/nl.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lang/nl.rs b/src/lang/nl.rs index d46be2b3f..f31135c6c 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -476,12 +476,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("eg: admin", "bijv: admin"), ("Empty Username", "Gebruikersnaam Leeg"), ("Empty Password", "Wachtwoord Leeg"), - ("Me", ""), - ("identical_file_tip", ""), - ("show_monitors_tip", ""), - ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("Me", "Ik"), + ("identical_file_tip", "Dit bestand is identiek aan het bestand van het externe station."), + ("show_monitors_tip", "Monitoren weergeven in de werkbalk"), + ("View Mode", "Weergave Mode"), + ("enter_rustdesk_passwd_tip", "Geef het RustDesk-wachtwoord op."), + ("remember_rustdesk_passwd_tip", "RustDesk Wachtwoord onthouden."), + ("login_linux_tip", "Je moet inloggen op een Linux Account op afstand om een X desktop sessie te openen."), ].iter().cloned().collect(); } From defe9e607852574a2c22fa95159058a8c15530bc Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 25 Mar 2023 16:27:30 +0800 Subject: [PATCH 046/366] linux, flutter texture render Signed-off-by: fufesou --- .github/workflows/flutter-nightly.yml | 2 +- flutter/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 8990c9609..a72b90249 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -586,7 +586,7 @@ jobs: x86_64) # no need mock on x86_64 export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release ;; esac diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index abe5c0f38..fd5ce4336 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -92,7 +92,7 @@ dependencies: password_strength: ^0.2.0 flutter_launcher_icons: ^0.11.0 flutter_keyboard_visibility: ^5.4.0 - texture_rgba_renderer: ^0.0.14 + texture_rgba_renderer: ^0.0.15 percent_indicator: ^4.2.2 dropdown_button2: ^2.0.0 From db6513818daa6686f078b633b6398d655c14ee2f Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 25 Mar 2023 16:34:41 +0800 Subject: [PATCH 047/366] some comments Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index af75a557b..cc815c5a3 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -496,6 +496,11 @@ _connectDialog( } submit() { + // to-do: + // username and password are about remote OS account. + // If the remote side is headless. + // The client side should login to remote OS account, to enable X desktop session. + // `username` and `password` will be used in the near future. final username = usernameController?.text.trim() ?? ''; final password = passwordController?.text.trim() ?? ''; final peerPassword = peerPasswordController?.text.trim() ?? ''; From 6c87c93df138f9409a5fb30b010cd5c74de36696 Mon Sep 17 00:00:00 2001 From: solokot Date: Sat, 25 Mar 2023 15:07:39 +0300 Subject: [PATCH 048/366] Update ru.rs --- src/lang/ru.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index b9eaf71a5..12c3dfb34 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -480,8 +480,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Файл идентичен файлу на удалённом узле."), ("show_monitors_tip", "Показывать мониторы на панели инструментов"), ("View Mode", "Режим просмотра"), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("enter_rustdesk_passwd_tip", "Введите пароль RustDesk"), + ("remember_rustdesk_passwd_tip", "Запомнить пароль RustDesk"), + ("login_linux_tip", "Вход в удалённый аккаунт Linux"), + ("login_linux_tooltip_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), ].iter().cloned().collect(); } From ec47c3f8947a71b6ef69b3b605656fe923aa07a3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 25 Mar 2023 17:47:39 +0800 Subject: [PATCH 049/366] refact, keyboard lock modifiers Signed-off-by: fufesou --- src/keyboard.rs | 72 +++++++++++++++++++++++++++---- src/server/input_service.rs | 85 +++++++++++++++++++++++-------------- 2 files changed, 116 insertions(+), 41 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index ce68657ea..4c2a0a428 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -46,6 +46,35 @@ lazy_static::lazy_static! { m.insert(Key::MetaRight, false); Mutex::new(m) }; + static ref NUMPAD_POSITION_CODES: Arc> = { + let numpad_keys = [ + rdev::Key::KpMinus, + rdev::Key::KpPlus, + rdev::Key::KpMultiply, + rdev::Key::KpDivide, + rdev::Key::KpDecimal, + rdev::Key::KpReturn, + rdev::Key::KpEqual, + rdev::Key::KpComma, + rdev::Key::Kp0, + rdev::Key::Kp1, + rdev::Key::Kp2, + rdev::Key::Kp3, + rdev::Key::Kp4, + rdev::Key::Kp5, + rdev::Key::Kp6, + rdev::Key::Kp7, + rdev::Key::Kp8, + rdev::Key::Kp9, + ]; + #[cfg(target_os = "windows")] + let codes = numpad_keys.iter().filter_map(|k| rdev::win_scancode_from_key(*k)).collect(); + #[cfg(target_os = "linux")] + let codes = numpad_keys.iter().filter_map(|k| rdev::linux_code_from_keycode(*k)).collect(); + #[cfg(target_os = "macos")] + let codes = numpad_keys.iter().filter_map(|k| rdev::macos_code_from_keycode(*k)).collect(); + Arc::new(codes) + }; } pub mod client { @@ -333,19 +362,43 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { match client::get_keyboard_mode().as_str() { "map" => KeyboardMode::Map, "translate" => KeyboardMode::Translate, - _ => KeyboardMode::Legacy, + "legacy" => KeyboardMode::Legacy, + _ => { + // Set "map" as default mode if version > 1.2.0. + let mut is_peer_version_gt_1_2_0 = false; + + #[cfg(not(any(feature = "flutter", feature = "cli")))] + if let Some(session) = CUR_SESSION.lock().unwrap().as_ref() { + is_peer_version_gt_1_2_0 = + session.get_peer_version() > hbb_common::get_version_number("1.2.0"); + } + #[cfg(feature = "flutter")] + if let Some(session) = SESSIONS + .read() + .unwrap() + .get(&*CUR_SESSION_ID.read().unwrap()) + { + is_peer_version_gt_1_2_0 = + session.get_peer_version() > hbb_common::get_version_number("1.2.0"); + } + if is_peer_version_gt_1_2_0 { + KeyboardMode::Map + } else { + KeyboardMode::Legacy + } + } } } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn add_numlock_capslock_with_lock_modes(key_event: &mut KeyEvent, lock_modes: i32) { +fn parse_add_lock_modes_modifiers(key_event: &mut KeyEvent, lock_modes: i32, is_numpad_key: bool) { const CAPS_LOCK: i32 = 1; const NUM_LOCK: i32 = 2; // const SCROLL_LOCK: i32 = 3; - if lock_modes & (1 << CAPS_LOCK) != 0 { + if !is_numpad_key && (lock_modes & (1 << CAPS_LOCK) != 0) { key_event.modifiers.push(ControlKey::CapsLock.into()); } - if lock_modes & (1 << NUM_LOCK) != 0 { + if is_numpad_key && (lock_modes & (1 << NUM_LOCK) != 0) { key_event.modifiers.push(ControlKey::NumLock.into()); } // if lock_modes & (1 << SCROLL_LOCK) != 0 { @@ -354,11 +407,11 @@ fn add_numlock_capslock_with_lock_modes(key_event: &mut KeyEvent, lock_modes: i3 } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn add_numlock_capslock_status(key_event: &mut KeyEvent) { - if get_key_state(enigo::Key::CapsLock) { +fn add_lock_modes_modifiers(key_event: &mut KeyEvent, is_numpad_key: bool) { + if !is_numpad_key && get_key_state(enigo::Key::CapsLock) { key_event.modifiers.push(ControlKey::CapsLock.into()); } - if get_key_state(enigo::Key::NumLock) { + if is_numpad_key && get_key_state(enigo::Key::NumLock) { key_event.modifiers.push(ControlKey::NumLock.into()); } } @@ -443,12 +496,13 @@ pub fn event_to_key_events( }; if keyboard_mode != KeyboardMode::Translate { + let is_numpad_key = NUMPAD_POSITION_CODES.contains(&event.position_code); for key_event in &mut key_events { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(lock_modes) = lock_modes { - add_numlock_capslock_with_lock_modes(key_event, lock_modes); + parse_add_lock_modes_modifiers(key_event, lock_modes, is_numpad_key); } else { - add_numlock_capslock_status(key_event); + add_lock_modes_modifiers(key_event, is_numpad_key); } } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index bb9ba167c..178b26de2 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -113,6 +113,52 @@ impl Subscriber for MouseCursorSub { } } +struct LockModesHandler { + caps_lock_changed: bool, + num_lock_changed: bool, +} + +impl LockModesHandler { + fn new(key_event: &KeyEvent) -> Self { + let mut en = ENIGO.lock().unwrap(); + + let event_caps_enabled = key_event.modifiers.contains(&ControlKey::CapsLock.into()); + let local_caps_enabled = en.get_key_state(enigo::Key::CapsLock); + let caps_lock_changed = event_caps_enabled != local_caps_enabled; + if caps_lock_changed { + click_capslock(&mut en); + } + + let event_num_enabled = key_event.modifiers.contains(&ControlKey::NumLock.into()); + let local_num_enabled = en.get_key_state(enigo::Key::NumLock); + #[cfg(not(target_os = "windows"))] + let disable_numlock = false; + #[cfg(target_os = "windows")] + let disable_numlock = is_numlock_disabled(key_event); + let num_lock_changed = event_num_enabled != local_num_enabled && !disable_numlock; + if num_lock_changed { + click_numlock(&mut en); + } + + Self { + caps_lock_changed, + num_lock_changed, + } + } +} + +impl Drop for LockModesHandler { + fn drop(&mut self) { + let mut en = ENIGO.lock().unwrap(); + if self.caps_lock_changed { + click_capslock(&mut en); + } + if self.num_lock_changed { + click_numlock(&mut en); + } + } +} + pub const NAME_CURSOR: &'static str = "mouse_cursor"; pub const NAME_POS: &'static str = "mouse_pos"; pub type MouseCursorService = ServiceTmpl; @@ -850,10 +896,6 @@ fn char_value_to_key(value: u32) -> Key { Key::Layout(std::char::from_u32(value).unwrap_or('\0')) } -fn is_not_same_status(client_locking: bool, remote_locking: bool) -> bool { - client_locking != remote_locking -} - #[cfg(target_os = "windows")] fn has_numpad_key(key_event: &KeyEvent) -> bool { key_event @@ -900,37 +942,13 @@ fn click_capslock(en: &mut Enigo) { let _ = en.key_down(enigo::Key::CapsLock); } +#[inline] fn click_numlock(_en: &mut Enigo) { // without numlock in macos #[cfg(not(target_os = "macos"))] _en.key_click(enigo::Key::NumLock); } -fn sync_numlock_capslock_status(key_event: &KeyEvent) { - let mut en = ENIGO.lock().unwrap(); - - let client_caps_locking = is_modifier_in_key_event(ControlKey::CapsLock, key_event); - let client_num_locking = is_modifier_in_key_event(ControlKey::NumLock, key_event); - let remote_caps_locking = en.get_key_state(enigo::Key::CapsLock); - let remote_num_locking = en.get_key_state(enigo::Key::NumLock); - - let need_click_capslock = is_not_same_status(client_caps_locking, remote_caps_locking); - let need_click_numlock = is_not_same_status(client_num_locking, remote_num_locking); - - #[cfg(not(target_os = "windows"))] - let disable_numlock = false; - #[cfg(target_os = "windows")] - let disable_numlock = is_numlock_disabled(key_event); - - if need_click_capslock { - click_capslock(&mut en); - } - - if need_click_numlock && !disable_numlock { - click_numlock(&mut en); - } -} - fn map_keyboard_mode(evt: &KeyEvent) { #[cfg(windows)] crate::platform::windows::try_change_desktop(); @@ -1174,9 +1192,12 @@ pub fn handle_key_(evt: &KeyEvent) { return; } - if evt.down { - sync_numlock_capslock_status(evt) - } + let lock_mode_handler = if evt.down { + Some(LockModesHandler::new(&evt)) + } else { + None + }; + match evt.mode.unwrap() { KeyboardMode::Map => { map_keyboard_mode(evt); From fa272273a4f953bf181b6bf3d1c42e1e98670013 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 25 Mar 2023 18:03:46 +0800 Subject: [PATCH 050/366] build Signed-off-by: fufesou --- src/keyboard.rs | 4 ++-- src/server/input_service.rs | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 4c2a0a428..9c90a8ae3 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -70,9 +70,9 @@ lazy_static::lazy_static! { #[cfg(target_os = "windows")] let codes = numpad_keys.iter().filter_map(|k| rdev::win_scancode_from_key(*k)).collect(); #[cfg(target_os = "linux")] - let codes = numpad_keys.iter().filter_map(|k| rdev::linux_code_from_keycode(*k)).collect(); + let codes = numpad_keys.iter().filter_map(|k| rdev::linux_code_from_code(*k)).collect(); #[cfg(target_os = "macos")] - let codes = numpad_keys.iter().filter_map(|k| rdev::macos_code_from_keycode(*k)).collect(); + let codes = numpad_keys.iter().filter_map(|k| rdev::macos_code_from_code(*k)).collect(); Arc::new(codes) }; } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 178b26de2..524e76cea 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -878,14 +878,6 @@ fn simulate_(event_type: &EventType) { } } -fn is_modifier_in_key_event(control_key: ControlKey, key_event: &KeyEvent) -> bool { - key_event - .modifiers - .iter() - .position(|&m| m == control_key.into()) - .is_some() -} - #[inline] fn control_key_value_to_key(value: i32) -> Option { KEY_MAP.get(&value).and_then(|k| Some(*k)) @@ -1192,7 +1184,7 @@ pub fn handle_key_(evt: &KeyEvent) { return; } - let lock_mode_handler = if evt.down { + let _lock_mode_handler = if evt.down { Some(LockModesHandler::new(&evt)) } else { None From c0188c8e073c97dabef8c3ae6bb3cd3592c30bcc Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 25 Mar 2023 18:23:57 +0800 Subject: [PATCH 051/366] debug done Signed-off-by: fufesou --- src/keyboard.rs | 6 ++++-- src/server/input_service.rs | 16 ++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 9c90a8ae3..aaddc9e4f 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -70,9 +70,9 @@ lazy_static::lazy_static! { #[cfg(target_os = "windows")] let codes = numpad_keys.iter().filter_map(|k| rdev::win_scancode_from_key(*k)).collect(); #[cfg(target_os = "linux")] - let codes = numpad_keys.iter().filter_map(|k| rdev::linux_code_from_code(*k)).collect(); + let codes = numpad_keys.iter().filter_map(|k| rdev::linux_keycode_from_key(*k)).collect(); #[cfg(target_os = "macos")] - let codes = numpad_keys.iter().filter_map(|k| rdev::macos_code_from_code(*k)).collect(); + let codes = numpad_keys.iter().filter_map(|k| rdev::macos_keycode_from_key(*k)).collect(); Arc::new(codes) }; } @@ -905,6 +905,8 @@ fn is_altgr(event: &Event) -> bool { } } +#[inline] +#[cfg(any(target_os = "linux", target_os = "windows"))] fn is_press(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(_)) } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 524e76cea..e9ffa3b3e 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1184,20 +1184,24 @@ pub fn handle_key_(evt: &KeyEvent) { return; } - let _lock_mode_handler = if evt.down { - Some(LockModesHandler::new(&evt)) - } else { - None - }; - match evt.mode.unwrap() { KeyboardMode::Map => { + let _lock_mode_handler = if evt.down { + Some(LockModesHandler::new(&evt)) + } else { + None + }; map_keyboard_mode(evt); } KeyboardMode::Translate => { translate_keyboard_mode(evt); } _ => { + let _lock_mode_handler = if evt.down { + Some(LockModesHandler::new(&evt)) + } else { + None + }; legacy_keyboard_mode(evt); } } From d0881cee10b11b715b27abaab28ef2eb110632f8 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 25 Mar 2023 18:37:05 +0800 Subject: [PATCH 052/366] debug Signed-off-by: fufesou --- src/server/input_service.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index e9ffa3b3e..dad98230d 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1184,24 +1184,27 @@ pub fn handle_key_(evt: &KeyEvent) { return; } - match evt.mode.unwrap() { - KeyboardMode::Map => { - let _lock_mode_handler = if evt.down { + let _lock_mode_handler = match &evt.union { + Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)) => { + Some(LockModesHandler::new(&evt)) + }, + _ => { + if evt.down { Some(LockModesHandler::new(&evt)) } else { None - }; + } + } + }; + + match evt.mode.unwrap() { + KeyboardMode::Map => { map_keyboard_mode(evt); } KeyboardMode::Translate => { translate_keyboard_mode(evt); } _ => { - let _lock_mode_handler = if evt.down { - Some(LockModesHandler::new(&evt)) - } else { - None - }; legacy_keyboard_mode(evt); } } From 291dbe842448f307dff7d96db319d293378ccabe Mon Sep 17 00:00:00 2001 From: rustdesk Date: Sat, 25 Mar 2023 22:52:31 +0800 Subject: [PATCH 053/366] disable aarch64-pc-windows-msvc for now, because engine and topwindows.dll and idddriver.dll not ready --- .github/workflows/flutter-nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index a72b90249..b9fcb6bb4 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -33,7 +33,7 @@ jobs: # - { target: i686-pc-windows-msvc , os: windows-2019 } # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - { target: x86_64-pc-windows-msvc, os: windows-2019 } - - { target: aarch64-pc-windows-msvc, os: windows-2019 } + # - { target: aarch64-pc-windows-msvc, os: windows-2019 } steps: - name: Checkout source code uses: actions/checkout@v3 From f1e0d647321279395fca0189380ce7342bbd275d Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 25 Mar 2023 23:44:20 +0800 Subject: [PATCH 054/366] refact, sync lock modifiers, macOS, not debug yet Signed-off-by: fufesou --- Cargo.lock | 2 +- src/keyboard.rs | 48 +++++++++++----------- src/server/input_service.rs | 80 +++++++++++++++++++++++-------------- src/ui_session_interface.rs | 10 ++--- 4 files changed, 81 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e632114af..a33e523c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4674,7 +4674,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#eaa35ff9af22891b4aae3a0a5e83472c16177cd8" +source = "git+https://github.com/fufesou/rdev#cd0d6ad907f8fda5cf77487bc962ef4246221d0c" dependencies = [ "cocoa", "core-foundation 0.9.3", diff --git a/src/keyboard.rs b/src/keyboard.rs index aaddc9e4f..8fec0ec3f 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -10,7 +10,7 @@ use crate::ui::CUR_SESSION; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::log; use hbb_common::message_proto::*; -use rdev::{Event, EventType, Key}; +use rdev::{Event, EventType, Key, KeyCode}; #[cfg(any(target_os = "windows", target_os = "macos"))] use std::sync::atomic::{AtomicBool, Ordering}; use std::{ @@ -46,26 +46,26 @@ lazy_static::lazy_static! { m.insert(Key::MetaRight, false); Mutex::new(m) }; - static ref NUMPAD_POSITION_CODES: Arc> = { + static ref NUMPAD_POSITION_CODES: Arc> = { let numpad_keys = [ - rdev::Key::KpMinus, - rdev::Key::KpPlus, - rdev::Key::KpMultiply, - rdev::Key::KpDivide, - rdev::Key::KpDecimal, - rdev::Key::KpReturn, - rdev::Key::KpEqual, - rdev::Key::KpComma, - rdev::Key::Kp0, - rdev::Key::Kp1, - rdev::Key::Kp2, - rdev::Key::Kp3, - rdev::Key::Kp4, - rdev::Key::Kp5, - rdev::Key::Kp6, - rdev::Key::Kp7, - rdev::Key::Kp8, - rdev::Key::Kp9, + Key::KpMinus, + Key::KpPlus, + Key::KpMultiply, + Key::KpDivide, + Key::KpDecimal, + Key::KpReturn, + Key::KpEqual, + Key::KpComma, + Key::Kp0, + Key::Kp1, + Key::Kp2, + Key::Kp3, + Key::Kp4, + Key::Kp5, + Key::Kp6, + Key::Kp7, + Key::Kp8, + Key::Kp9, ]; #[cfg(target_os = "windows")] let codes = numpad_keys.iter().filter_map(|k| rdev::win_scancode_from_key(*k)).collect(); @@ -257,7 +257,7 @@ pub fn start_grab_loop() { let mut _keyboard_mode = KeyboardMode::Map; let _scan_code = event.position_code; - let _code = event.platform_code; + let _code = event.platform_code as KeyCode; let res = if KEYBOARD_HOOKED.load(Ordering::SeqCst) { _keyboard_mode = client::process_event(&event, None); if is_press { @@ -293,7 +293,7 @@ pub fn start_grab_loop() { #[cfg(target_os = "macos")] unsafe { - if _code as u32 == rdev::kVK_Option { + if _code == rdev::kVK_Option { IS_LEFT_OPTION_DOWN = is_press; } } @@ -496,7 +496,7 @@ pub fn event_to_key_events( }; if keyboard_mode != KeyboardMode::Translate { - let is_numpad_key = NUMPAD_POSITION_CODES.contains(&event.position_code); + let is_numpad_key = NUMPAD_POSITION_CODES.contains(&(event.position_code as _)); for key_event in &mut key_events { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(lock_modes) = lock_modes { @@ -929,7 +929,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(target_os = "macos")] // ignore right option key - if event.platform_code as u32 == rdev::kVK_RightOption { + if event.platform_code == rdev::kVK_RightOption as u32 { return events; } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index dad98230d..85271f168 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -5,7 +5,7 @@ use crate::common::IS_X11; use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; use hbb_common::{config::COMPRESS_LEVEL, get_time, protobuf::EnumOrUnknown}; -use rdev::{self, EventType, Key as RdevKey, RawKey}; +use rdev::{self, EventType, Key as RdevKey, KeyCode, RawKey}; #[cfg(target_os = "macos")] use rdev::{CGEventSourceStateID, CGEventTapLocation, VirtualInput}; use std::time::Duration; @@ -113,23 +113,32 @@ impl Subscriber for MouseCursorSub { } } +#[cfg(not(target_os = "macos"))] struct LockModesHandler { caps_lock_changed: bool, num_lock_changed: bool, } +#[cfg(target_os = "macos")] +struct LockModesHandler; + impl LockModesHandler { + #[inline] + fn is_modifier_enabled(key_event: &KeyEvent, modifier: ControlKey) { + key_event.modifiers.contains(&modifier.into()); + } + + #[cfg(not(target_os = "macos"))] fn new(key_event: &KeyEvent) -> Self { let mut en = ENIGO.lock().unwrap(); - - let event_caps_enabled = key_event.modifiers.contains(&ControlKey::CapsLock.into()); + let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock); let local_caps_enabled = en.get_key_state(enigo::Key::CapsLock); let caps_lock_changed = event_caps_enabled != local_caps_enabled; if caps_lock_changed { - click_capslock(&mut en); + en.key_click(enigo::Key::CapsLock); } - let event_num_enabled = key_event.modifiers.contains(&ControlKey::NumLock.into()); + let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock); let local_num_enabled = en.get_key_state(enigo::Key::NumLock); #[cfg(not(target_os = "windows"))] let disable_numlock = false; @@ -137,7 +146,7 @@ impl LockModesHandler { let disable_numlock = is_numlock_disabled(key_event); let num_lock_changed = event_num_enabled != local_num_enabled && !disable_numlock; if num_lock_changed { - click_numlock(&mut en); + en.key_click(enigo::Key::NumLock); } Self { @@ -145,16 +154,36 @@ impl LockModesHandler { num_lock_changed, } } + + #[cfg(target_os = "macos")] + fn new(key_event: &KeyEvent) -> Self { + let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock); + let local_caps_enabled = VirtualInput::get_key_state( + CGEventSourceStateID::CombinedSessionState, + rdev::kVK_CapsLock, + ); + let caps_key = RdevKey::RawKey(rdev::RawKey::MacVirtualKeycode(rdev::kVK_CapsLock)); + if event_caps_enabled && !local_caps_enabled { + simulate_(&EventType::KeyPress(caps_key)); + key_sleep(); + } else if !event_caps_enabled && local_caps_enabled { + simulate_(&EventType::KeyRelease(caps_key)); + key_sleep(); + } + + Self {} + } } +#[cfg(not(target_os = "macos"))] impl Drop for LockModesHandler { fn drop(&mut self) { let mut en = ENIGO.lock().unwrap(); if self.caps_lock_changed { - click_capslock(&mut en); + en.key_click(enigo::Key::CapsLock); } if self.num_lock_changed { - click_numlock(&mut en); + en.key_click(enigo::Key::NumLock); } } } @@ -391,6 +420,12 @@ fn is_pressed(key: &Key, en: &mut Enigo) -> bool { get_modifier_state(key.clone(), en) } +#[inline] +#[cfg(target_os = "macos")] +fn key_sleep() { + std::thread::sleep(Duration::from_millis(20)); +} + #[inline] fn get_modifier_state(key: Key, en: &mut Enigo) -> bool { // https://github.com/rustdesk/rustdesk/issues/332 @@ -459,6 +494,7 @@ pub fn fix_key_down_timeout_at_exit() { log::info!("fix_key_down_timeout_at_exit"); } +#[inline] #[cfg(target_os = "linux")] pub fn clear_remapped_keycode() { ENIGO.lock().unwrap().tfc_clear_remapped(); @@ -792,7 +828,7 @@ pub fn handle_key(evt: &KeyEvent) { // having GUI, run main GUI thread, otherwise crash let evt = evt.clone(); QUEUE.exec_async(move || handle_key_(&evt)); - std::thread::sleep(Duration::from_millis(20)); + key_sleep(); return; } #[cfg(windows)] @@ -800,7 +836,7 @@ pub fn handle_key(evt: &KeyEvent) { #[cfg(not(windows))] handle_key_(evt); #[cfg(target_os = "macos")] - std::thread::sleep(Duration::from_millis(20)); + key_sleep(); } #[cfg(target_os = "macos")] @@ -822,7 +858,7 @@ pub fn reset_input_ondisconn() { } } -fn sim_rdev_rawkey_position(code: u32, keydown: bool) { +fn sim_rdev_rawkey_position(code: KeyCode, keydown: bool) { #[cfg(target_os = "windows")] let rawkey = RawKey::ScanCode(code); #[cfg(target_os = "linux")] @@ -927,20 +963,6 @@ fn is_numlock_disabled(key_event: &KeyEvent) -> bool { } } -fn click_capslock(en: &mut Enigo) { - #[cfg(not(targe_os = "macos"))] - en.key_click(enigo::Key::CapsLock); - #[cfg(target_os = "macos")] - let _ = en.key_down(enigo::Key::CapsLock); -} - -#[inline] -fn click_numlock(_en: &mut Enigo) { - // without numlock in macos - #[cfg(not(target_os = "macos"))] - _en.key_click(enigo::Key::NumLock); -} - fn map_keyboard_mode(evt: &KeyEvent) { #[cfg(windows)] crate::platform::windows::try_change_desktop(); @@ -959,7 +981,7 @@ fn map_keyboard_mode(evt: &KeyEvent) { return; } - sim_rdev_rawkey_position(evt.chr(), evt.down); + sim_rdev_rawkey_position(evt.chr() as _, evt.down); } #[cfg(target_os = "macos")] @@ -1137,7 +1159,7 @@ fn legacy_keyboard_mode(evt: &KeyEvent) { fn translate_process_code(code: u32, down: bool) { crate::platform::windows::try_change_desktop(); match code >> 16 { - 0 => sim_rdev_rawkey_position(code, down), + 0 => sim_rdev_rawkey_position(code as _, down), vk_code => sim_rdev_rawkey_virtual(vk_code, down), }; } @@ -1168,7 +1190,7 @@ fn translate_keyboard_mode(evt: &KeyEvent) { #[cfg(target_os = "windows")] translate_process_code(evt.chr(), evt.down); #[cfg(not(target_os = "windows"))] - sim_rdev_rawkey_position(evt.chr(), evt.down); + sim_rdev_rawkey_position(evt.chr() as _, evt.down); } Some(key_event::Union::Unicode(..)) => { // Do not handle unicode for now. @@ -1187,7 +1209,7 @@ pub fn handle_key_(evt: &KeyEvent) { let _lock_mode_handler = match &evt.union { Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)) => { Some(LockModesHandler::new(&evt)) - }, + } _ => { if evt.down { Some(LockModesHandler::new(&evt)) diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 8a1da6b8f..824f8befa 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -10,7 +10,7 @@ use std::time::{Duration, SystemTime}; use async_trait::async_trait; use bytes::Bytes; -use rdev::{Event, EventType::*}; +use rdev::{Event, EventType::*, KeyCode}; use uuid::Uuid; use hbb_common::config::{Config, LocalConfig, PeerConfig}; @@ -421,7 +421,7 @@ impl Session { rdev::win_scancode_from_key(key).unwrap_or_default() } "macos" => { - let key = rdev::macos_key_from_code(code); + let key = rdev::macos_key_from_code(code as _); let key = match key { rdev::Key::ControlLeft => rdev::Key::MetaLeft, rdev::Key::MetaLeft => rdev::Key::ControlLeft, @@ -429,7 +429,7 @@ impl Session { rdev::Key::MetaRight => rdev::Key::ControlLeft, _ => key, }; - rdev::macos_keycode_from_key(key).unwrap_or_default() + rdev::macos_keycode_from_key(key).unwrap_or_default() as _ } _ => { let key = rdev::linux_key_from_code(code); @@ -545,8 +545,8 @@ impl Session { if scancode < 0 || keycode < 0 { return; } - let keycode: u32 = keycode as u32; - let scancode: u32 = scancode as u32; + let keycode: KeyCode = keycode as _; + let scancode: u32 = scancode as _; #[cfg(not(target_os = "windows"))] let key = rdev::key_from_code(keycode) as rdev::Key; From 8b4370773dad53b96cba64460cd4516eb24b31c8 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 26 Mar 2023 00:02:04 +0800 Subject: [PATCH 055/366] debug, macOS Signed-off-by: fufesou --- src/server/input_service.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 85271f168..902cd3998 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -124,8 +124,8 @@ struct LockModesHandler; impl LockModesHandler { #[inline] - fn is_modifier_enabled(key_event: &KeyEvent, modifier: ControlKey) { - key_event.modifiers.contains(&modifier.into()); + fn is_modifier_enabled(key_event: &KeyEvent, modifier: ControlKey) -> bool { + key_event.modifiers.contains(&modifier.into()) } #[cfg(not(target_os = "macos"))] From 2f7245495d02dfb2c59cbfd50b5ccf14b8ebdaa3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 26 Mar 2023 00:14:05 +0800 Subject: [PATCH 056/366] fix build Signed-off-by: fufesou --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a33e523c2..65ae17357 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4674,7 +4674,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#cd0d6ad907f8fda5cf77487bc962ef4246221d0c" +source = "git+https://github.com/fufesou/rdev#0dad4a59f911fe14934fefc8eed18d58cb057a81" dependencies = [ "cocoa", "core-foundation 0.9.3", From 706e993f835a014cc8d0d664c928c8fbe5749194 Mon Sep 17 00:00:00 2001 From: Theofanis Sarmidis <126983335+tsarmis@users.noreply.github.com> Date: Sat, 25 Mar 2023 21:05:10 +0200 Subject: [PATCH 057/366] Update el.rs --- src/lang/el.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lang/el.rs b/src/lang/el.rs index 51ac6f041..85c269d29 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -480,8 +480,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Το αρχείο είναι πανομοιότυπο με αυτό του άλλου υπολογιστή."), ("show_monitors_tip", "Εμφάνιση οθονών στη γραμμή εργαλείων"), ("View Mode", "Λειτουργία προβολής"), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("enter_rustdesk_passwd_tip", "Εισαγωγή του κωδικού RustDesk."), + ("remember_rustdesk_passwd_tip", "Να θυμάσαι τον κωδικό του RustDesk."), + ("login_linux_tip", "Είσοδος σε απομακρυσμένο λογαριασμό Linux"), + ("login_linux_tooltip_tip", "Απαιτείται είσοδος σε απομακρυσμένο λογαριασμό Linux για την ενεργοποίηση του περιβάλλον εργασίας Χ."), ].iter().cloned().collect(); } From 7f33165f06c31786807bf4fbd9b5bab086856554 Mon Sep 17 00:00:00 2001 From: "Miguel F. G" <116861809+flusheDData@users.noreply.github.com> Date: Sun, 26 Mar 2023 05:49:18 +0200 Subject: [PATCH 058/366] Update es.rs New terms added --- src/lang/es.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/es.rs b/src/lang/es.rs index c4e78943a..a85a5e491 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Este archivo es idéntico al del par."), ("show_monitors_tip", "Mostrar monitores en la barra de herramientas"), ("View Mode", "Modo Vista"), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("enter_rustdesk_passwd_tip", "Introduzca la contraseña de RustDesk"), + ("remember_rustdesk_passwd_tip", "Recordar la contraseña de RustDesk"), + ("login_linux_tip", "Iniciar sesión para la cuenta remota de Linux"), ].iter().cloned().collect(); } From fc98b0355e674526387d8f6625f6f147c6001438 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 26 Mar 2023 12:21:17 +0800 Subject: [PATCH 059/366] derive is_numpad_key from https://github.com/rustdesk/rustdesk/pull/3785/files Signed-off-by: fufesou --- src/keyboard.rs | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 8fec0ec3f..f2a6c6833 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -46,35 +46,6 @@ lazy_static::lazy_static! { m.insert(Key::MetaRight, false); Mutex::new(m) }; - static ref NUMPAD_POSITION_CODES: Arc> = { - let numpad_keys = [ - Key::KpMinus, - Key::KpPlus, - Key::KpMultiply, - Key::KpDivide, - Key::KpDecimal, - Key::KpReturn, - Key::KpEqual, - Key::KpComma, - Key::Kp0, - Key::Kp1, - Key::Kp2, - Key::Kp3, - Key::Kp4, - Key::Kp5, - Key::Kp6, - Key::Kp7, - Key::Kp8, - Key::Kp9, - ]; - #[cfg(target_os = "windows")] - let codes = numpad_keys.iter().filter_map(|k| rdev::win_scancode_from_key(*k)).collect(); - #[cfg(target_os = "linux")] - let codes = numpad_keys.iter().filter_map(|k| rdev::linux_keycode_from_key(*k)).collect(); - #[cfg(target_os = "macos")] - let codes = numpad_keys.iter().filter_map(|k| rdev::macos_keycode_from_key(*k)).collect(); - Arc::new(codes) - }; } pub mod client { @@ -390,6 +361,16 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { } } +#[inline] +fn is_numpad_key(event: &Event) -> bool { + matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { + Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3| Key::Kp4| Key::Kp5| Key::Kp6| + Key::Kp7| Key::Kp8| Key::Kp9 | Key::KpMinus | Key::KpMultiply | + Key::KpDivide | Key::KpPlus | Key::KpDecimal => true, + _ => false + }) +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn parse_add_lock_modes_modifiers(key_event: &mut KeyEvent, lock_modes: i32, is_numpad_key: bool) { const CAPS_LOCK: i32 = 1; @@ -496,7 +477,7 @@ pub fn event_to_key_events( }; if keyboard_mode != KeyboardMode::Translate { - let is_numpad_key = NUMPAD_POSITION_CODES.contains(&(event.position_code as _)); + let is_numpad_key = is_numpad_key(&event.event_type); for key_event in &mut key_events { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(lock_modes) = lock_modes { From 1cbce779953d99abee197494624c66afde708949 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 26 Mar 2023 12:22:13 +0800 Subject: [PATCH 060/366] debug macOS Signed-off-by: fufesou --- src/server/input_service.rs | 88 ++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 15 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 902cd3998..797488845 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -158,17 +158,23 @@ impl LockModesHandler { #[cfg(target_os = "macos")] fn new(key_event: &KeyEvent) -> Self { let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock); - let local_caps_enabled = VirtualInput::get_key_state( - CGEventSourceStateID::CombinedSessionState, - rdev::kVK_CapsLock, - ); - let caps_key = RdevKey::RawKey(rdev::RawKey::MacVirtualKeycode(rdev::kVK_CapsLock)); + // Do not use the following code to detect `local_caps_enabled`. + // Because the state of get_key_state will not affect simuation of `VIRTUAL_INPUT_STATE` in this file. + // + // let local_caps_enabled = VirtualInput::get_key_state( + // CGEventSourceStateID::CombinedSessionState, + // rdev::kVK_CapsLock, + // ); + let local_caps_enabled = unsafe { + let _lock = VIRTUAL_INPUT_MTX.lock(); + VIRTUAL_INPUT_STATE + .as_ref() + .map_or(false, |input| input.capslock_down) + }; if event_caps_enabled && !local_caps_enabled { - simulate_(&EventType::KeyPress(caps_key)); - key_sleep(); + press_capslock(); } else if !event_caps_enabled && local_caps_enabled { - simulate_(&EventType::KeyRelease(caps_key)); - key_sleep(); + release_capslock(); } Self {} @@ -343,10 +349,33 @@ lazy_static::lazy_static! { static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned()); } +#[cfg(target_os = "macos")] +struct VirtualInputState { + virtual_input: VirtualInput, + capslock_down: bool, +} + +#[cfg(target_os = "macos")] +impl VirtualInputState { + fn new() -> Option { + VirtualInput::new(CGEventSourceStateID::Private, CGEventTapLocation::Session) + .map(|virtual_input| Self { + virtual_input, + capslock_down: false, + }) + .ok() + } + + #[inline] + fn simulate(&self, event_type: &EventType) -> ResultType<()> { + Ok(self.virtual_input.simulate(&event_type)?) + } +} + #[cfg(target_os = "macos")] static mut VIRTUAL_INPUT_MTX: Mutex<()> = Mutex::new(()); #[cfg(target_os = "macos")] -static mut VIRTUAL_INPUT: Option = None; +static mut VIRTUAL_INPUT_STATE: Option = None; // First call set_uinput() will create keyboard and mouse clients. // The clients are ipc connections that must live shorter than tokio runtime. @@ -844,8 +873,7 @@ pub fn handle_key(evt: &KeyEvent) { fn reset_input() { unsafe { let _lock = VIRTUAL_INPUT_MTX.lock(); - VIRTUAL_INPUT = - VirtualInput::new(CGEventSourceStateID::Private, CGEventTapLocation::Session).ok(); + VIRTUAL_INPUT_STATE = VirtualInputState::new(); } } @@ -892,13 +920,43 @@ fn sim_rdev_rawkey_virtual(code: u32, keydown: bool) { simulate_(&event_type); } -#[cfg(target_os = "macos")] #[inline] +#[cfg(target_os = "macos")] fn simulate_(event_type: &EventType) { unsafe { let _lock = VIRTUAL_INPUT_MTX.lock(); - if let Some(virtual_input) = &VIRTUAL_INPUT { - let _ = virtual_input.simulate(&event_type); + if let Some(input) = &VIRTUAL_INPUT_STATE { + let _ = input.simulate(&event_type); + } + } +} + +#[inline] +#[cfg(target_os = "macos")] +fn press_capslock() { + let caps_key = RdevKey::RawKey(rdev::RawKey::MacVirtualKeycode(rdev::kVK_CapsLock)); + unsafe { + let _lock = VIRTUAL_INPUT_MTX.lock(); + if let Some(input) = &mut VIRTUAL_INPUT_STATE { + if input.simulate(&EventType::KeyPress(caps_key)).is_ok() { + input.capslock_down = true; + key_sleep(); + } + } + } +} + +#[cfg(target_os = "macos")] +#[inline] +fn release_capslock() { + let caps_key = RdevKey::RawKey(rdev::RawKey::MacVirtualKeycode(rdev::kVK_CapsLock)); + unsafe { + let _lock = VIRTUAL_INPUT_MTX.lock(); + if let Some(input) = &mut VIRTUAL_INPUT_STATE { + if input.simulate(&EventType::KeyRelease(caps_key)).is_ok() { + input.capslock_down = false; + key_sleep(); + } } } } From 2baf83ce2784fe44d25e3bf91983b0bc174cf9f1 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 26 Mar 2023 12:27:06 +0800 Subject: [PATCH 061/366] fix build Signed-off-by: fufesou --- src/keyboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index f2a6c6833..0cd435cb4 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -477,7 +477,7 @@ pub fn event_to_key_events( }; if keyboard_mode != KeyboardMode::Translate { - let is_numpad_key = is_numpad_key(&event.event_type); + let is_numpad_key = is_numpad_key(&event); for key_event in &mut key_events { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(lock_modes) = lock_modes { From 83deb71ce28bfca89d9cbb75686baeb8fb40e55c Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 26 Mar 2023 12:47:55 +0800 Subject: [PATCH 062/366] update rdev Signed-off-by: fufesou --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 65ae17357..6fdb513e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4674,7 +4674,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#0dad4a59f911fe14934fefc8eed18d58cb057a81" +source = "git+https://github.com/fufesou/rdev#89d2cb5c4bac81da4aafaedcf78af6af7c80c9d0" dependencies = [ "cocoa", "core-foundation 0.9.3", From 68eaa9f00013239962d787ce0ab8fa2304f58e01 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 26 Mar 2023 12:57:32 +0800 Subject: [PATCH 063/366] macOS, fix build Signed-off-by: fufesou --- src/keyboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 0cd435cb4..dd0fae615 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -811,7 +811,7 @@ pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> #[cfg(any(target_os = "android", target_os = "ios"))] let keycode = 0; - key_event.set_chr(keycode); + key_event.set_chr(keycode as _); Some(key_event) } From 35dd1fdfe64639dcc693ab3d4b3e24aa3908cedd Mon Sep 17 00:00:00 2001 From: NicKoehler <53040044+NicKoehler@users.noreply.github.com> Date: Sun, 26 Mar 2023 10:42:56 +0200 Subject: [PATCH 064/366] Update it.rs --- src/lang/it.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index f5a7d174c..3c5973053 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -480,8 +480,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Questo file è identico a quello del peer."), ("show_monitors_tip", "Mostra schermi nella barra degli strumenti"), ("View Mode", "Modalità di visualizzazione"), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("enter_rustdesk_passwd_tip", "Inserisci la password di RustDesk."), + ("remember_rustdesk_passwd_tip", "Ricorda la passowrd di RustDesk."), + ("login_linux_tip", "Effettua l'accesso sul tuo account Linux"), ].iter().cloned().collect(); } From 6418fadcd95b2304e69cc7afd3005713bec37330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=85=C3=BC?= <53787985+LaiYueTing@users.noreply.github.com> Date: Sun, 26 Mar 2023 18:12:49 +0800 Subject: [PATCH 065/366] Update tw.rs --- src/lang/tw.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 2158db175..870105b09 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -480,8 +480,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此檔案與對方的檔案一致"), ("show_monitors_tip", "在工具列中顯示顯示器"), ("View Mode", "瀏覽模式"), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("enter_rustdesk_passwd_tip", "輸入 RustDesk 密碼"), + ("remember_rustdesk_passwd_tip", "記住 RustDesk 密碼"), + ("login_linux_tip", "登入到遠端 Linux 使用者帳戶"), + ("login_linux_tooltip_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), ].iter().cloned().collect(); } From dbe151609638571a76073e831d79aa6ae0c884c0 Mon Sep 17 00:00:00 2001 From: chiehw Date: Fri, 24 Mar 2023 21:51:56 +0800 Subject: [PATCH 066/366] Simulate char in win --- src/server/input_service.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 797488845..b362c8d30 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1233,16 +1233,23 @@ fn translate_keyboard_mode(evt: &KeyEvent) { // remote: Shift + 1 => 1 let mut en = ENIGO.lock().unwrap(); - #[cfg(target_os = "linux")] + #[cfg(target_os = "macos")] + en.key_sequence(seq); + #[cfg(any(target_os = "linux", target_os = "windows"))] { - simulate_(&EventType::KeyRelease(RdevKey::ShiftLeft)); - simulate_(&EventType::KeyRelease(RdevKey::ShiftRight)); + if get_modifier_state(Key::Shift, &mut en) { + simulate_(&EventType::KeyRelease(RdevKey::ShiftLeft)); + } + if get_modifier_state(Key::RightShift, &mut en) { + simulate_(&EventType::KeyRelease(RdevKey::ShiftRight)); + } for chr in seq.chars() { + #[cfg(target_os = "windows")] + rdev::simulate_char(chr).ok(); + #[cfg(target_os = "linux")] en.key_click(Key::Layout(chr)); } } - #[cfg(not(target_os = "linux"))] - en.key_sequence(seq); } Some(key_event::Union::Chr(..)) => { #[cfg(target_os = "windows")] From 82a7554be13343bb00d825dd1ef92429d1fff4d9 Mon Sep 17 00:00:00 2001 From: chiehw Date: Sun, 26 Mar 2023 07:52:05 +0800 Subject: [PATCH 067/366] Send key instead of char in numpad --- src/keyboard.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/keyboard.rs b/src/keyboard.rs index dd0fae615..b82290605 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -892,6 +892,15 @@ fn is_press(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(_)) } +fn is_numpad_key(event: &Event) -> bool { + matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { + Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3| Key::Kp4| Key::Kp5| Key::Kp6| + Key::Kp7| Key::Kp8| Key::Kp9 | Key::KpMinus | Key::KpMultiply | + Key::KpDivide | Key::KpPlus | Key::KpDecimal => true, + _ => false + }) +} + pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); if let Some(unicode_info) = &event.unicode { @@ -907,6 +916,12 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - return events; } } + if is_numpad_key(&event) { + if let Some(evt) = translate_key_code(peer, event, key_event) { + events.push(evt); + } + return events; + } #[cfg(target_os = "macos")] // ignore right option key From 77125b7a98a722f7015148cee4771e3d716940c1 Mon Sep 17 00:00:00 2001 From: asur4s Date: Sun, 26 Mar 2023 06:08:58 -0700 Subject: [PATCH 068/366] fix conflict --- src/keyboard.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index b82290605..40de9b5df 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -10,7 +10,9 @@ use crate::ui::CUR_SESSION; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::log; use hbb_common::message_proto::*; -use rdev::{Event, EventType, Key, KeyCode}; +#[cfg(any(target_os = "windows", target_os = "macos"))] +use rdev::KeyCode; +use rdev::{Event, EventType, Key}; #[cfg(any(target_os = "windows", target_os = "macos"))] use std::sync::atomic::{AtomicBool, Ordering}; use std::{ @@ -892,15 +894,6 @@ fn is_press(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(_)) } -fn is_numpad_key(event: &Event) -> bool { - matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { - Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3| Key::Kp4| Key::Kp5| Key::Kp6| - Key::Kp7| Key::Kp8| Key::Kp9 | Key::KpMinus | Key::KpMultiply | - Key::KpDivide | Key::KpPlus | Key::KpDecimal => true, - _ => false - }) -} - pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); if let Some(unicode_info) = &event.unicode { From e242df76b883970b86ec4a732a4b0f3f13f049e4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 27 Mar 2023 11:59:07 +0800 Subject: [PATCH 069/366] simplify keyboard lock modifiers Signed-off-by: fufesou --- src/keyboard.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 40de9b5df..aaa12afa0 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -374,14 +374,14 @@ fn is_numpad_key(event: &Event) -> bool { } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn parse_add_lock_modes_modifiers(key_event: &mut KeyEvent, lock_modes: i32, is_numpad_key: bool) { +fn parse_add_lock_modes_modifiers(key_event: &mut KeyEvent, lock_modes: i32) { const CAPS_LOCK: i32 = 1; const NUM_LOCK: i32 = 2; // const SCROLL_LOCK: i32 = 3; - if !is_numpad_key && (lock_modes & (1 << CAPS_LOCK) != 0) { + if lock_modes & (1 << CAPS_LOCK) != 0 { key_event.modifiers.push(ControlKey::CapsLock.into()); } - if is_numpad_key && (lock_modes & (1 << NUM_LOCK) != 0) { + if lock_modes & (1 << NUM_LOCK) != 0 { key_event.modifiers.push(ControlKey::NumLock.into()); } // if lock_modes & (1 << SCROLL_LOCK) != 0 { @@ -390,11 +390,11 @@ fn parse_add_lock_modes_modifiers(key_event: &mut KeyEvent, lock_modes: i32, is_ } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn add_lock_modes_modifiers(key_event: &mut KeyEvent, is_numpad_key: bool) { - if !is_numpad_key && get_key_state(enigo::Key::CapsLock) { +fn add_lock_modes_modifiers(key_event: &mut KeyEvent) { + if get_key_state(enigo::Key::CapsLock) { key_event.modifiers.push(ControlKey::CapsLock.into()); } - if is_numpad_key && get_key_state(enigo::Key::NumLock) { + if get_key_state(enigo::Key::NumLock) { key_event.modifiers.push(ControlKey::NumLock.into()); } } @@ -479,13 +479,12 @@ pub fn event_to_key_events( }; if keyboard_mode != KeyboardMode::Translate { - let is_numpad_key = is_numpad_key(&event); for key_event in &mut key_events { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(lock_modes) = lock_modes { - parse_add_lock_modes_modifiers(key_event, lock_modes, is_numpad_key); + parse_add_lock_modes_modifiers(key_event, lock_modes); } else { - add_lock_modes_modifiers(key_event, is_numpad_key); + add_lock_modes_modifiers(key_event); } } } From 17fe62aec18d0051f5207135f39bb670432df269 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 27 Mar 2023 12:06:38 +0800 Subject: [PATCH 070/366] Do not send lock modifiers if no related keys are pressed Signed-off-by: fufesou --- src/keyboard.rs | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index aaa12afa0..003925e96 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -366,22 +366,37 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { #[inline] fn is_numpad_key(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { - Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3| Key::Kp4| Key::Kp5| Key::Kp6| - Key::Kp7| Key::Kp8| Key::Kp9 | Key::KpMinus | Key::KpMultiply | - Key::KpDivide | Key::KpPlus | Key::KpDecimal => true, + Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3 | Key::Kp4| Key::Kp5| Key::Kp6| Key::Kp7 | Key::Kp8 | + Key::Kp9 | Key::KpMinus | Key::KpMultiply | Key::KpDivide | Key::KpPlus | Key::KpDecimal => true, + _ => false + }) +} + +#[inline] +fn is_letter_key(event: &Event) -> bool { + matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { + Key::KeyA | Key::KeyB | Key::KeyC | Key::KeyD | Key::KeyE | Key::KeyF | Key::KeyG | Key::KeyH | + Key::KeyI | Key::KeyJ | Key::KeyK | Key::KeyL | Key::KeyM | Key::KeyN | Key::KeyO | Key::KeyP | + Key::KeyQ | Key::KeyR | Key::KeyS | Key::KeyT | Key::KeyU | Key::KeyV | Key::KeyW | Key::KeyX | + Key::KeyY | Key::KeyZ => true, _ => false }) } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn parse_add_lock_modes_modifiers(key_event: &mut KeyEvent, lock_modes: i32) { +fn parse_add_lock_modes_modifiers( + key_event: &mut KeyEvent, + lock_modes: i32, + is_numpad_key: bool, + is_letter_key: bool, +) { const CAPS_LOCK: i32 = 1; const NUM_LOCK: i32 = 2; // const SCROLL_LOCK: i32 = 3; - if lock_modes & (1 << CAPS_LOCK) != 0 { + if is_letter_key && (lock_modes & (1 << CAPS_LOCK) != 0) { key_event.modifiers.push(ControlKey::CapsLock.into()); } - if lock_modes & (1 << NUM_LOCK) != 0 { + if is_numpad_key && lock_modes & (1 << NUM_LOCK) != 0 { key_event.modifiers.push(ControlKey::NumLock.into()); } // if lock_modes & (1 << SCROLL_LOCK) != 0 { @@ -390,11 +405,11 @@ fn parse_add_lock_modes_modifiers(key_event: &mut KeyEvent, lock_modes: i32) { } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn add_lock_modes_modifiers(key_event: &mut KeyEvent) { - if get_key_state(enigo::Key::CapsLock) { +fn add_lock_modes_modifiers(key_event: &mut KeyEvent, is_numpad_key: bool, is_letter_key: bool) { + if is_letter_key && get_key_state(enigo::Key::CapsLock) { key_event.modifiers.push(ControlKey::CapsLock.into()); } - if get_key_state(enigo::Key::NumLock) { + if is_numpad_key && get_key_state(enigo::Key::NumLock) { key_event.modifiers.push(ControlKey::NumLock.into()); } } @@ -479,12 +494,14 @@ pub fn event_to_key_events( }; if keyboard_mode != KeyboardMode::Translate { + let is_numpad_key = is_numpad_key(&event); + let is_letter_key = is_letter_key(&event); for key_event in &mut key_events { #[cfg(not(any(target_os = "android", target_os = "ios")))] if let Some(lock_modes) = lock_modes { - parse_add_lock_modes_modifiers(key_event, lock_modes); + parse_add_lock_modes_modifiers(key_event, lock_modes, is_numpad_key, is_letter_key); } else { - add_lock_modes_modifiers(key_event); + add_lock_modes_modifiers(key_event, is_numpad_key, is_letter_key); } } } From 3fdffa1371f37d1efd0f53dd55e3feeb8d78e063 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 27 Mar 2023 12:08:01 +0800 Subject: [PATCH 071/366] format Signed-off-by: fufesou --- src/keyboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 003925e96..5a41d6c5c 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -366,7 +366,7 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { #[inline] fn is_numpad_key(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { - Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3 | Key::Kp4| Key::Kp5| Key::Kp6| Key::Kp7 | Key::Kp8 | + Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3 | Key::Kp4 | Key::Kp5 | Key::Kp6 | Key::Kp7 | Key::Kp8 | Key::Kp9 | Key::KpMinus | Key::KpMultiply | Key::KpDivide | Key::KpPlus | Key::KpDecimal => true, _ => false }) From 0f2ab54ca32d801662aff9b6e3cb7eedf3bd2abe Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 27 Mar 2023 12:12:49 +0800 Subject: [PATCH 072/366] linux, texture render update Signed-off-by: fufesou --- flutter/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index fd5ce4336..596f565ff 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -92,7 +92,7 @@ dependencies: password_strength: ^0.2.0 flutter_launcher_icons: ^0.11.0 flutter_keyboard_visibility: ^5.4.0 - texture_rgba_renderer: ^0.0.15 + texture_rgba_renderer: ^0.0.16 percent_indicator: ^4.2.2 dropdown_button2: ^2.0.0 From 1fb5cc7dd8766b781227617b1c157ce1bbf5251d Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 27 Mar 2023 16:11:17 +0800 Subject: [PATCH 073/366] fix setting conn type Signed-off-by: fufesou --- flutter/lib/models/model.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 701fae48c..95813097d 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -576,7 +576,13 @@ class ImageModel with ChangeNotifier { addCallbackOnFirstImage(Function(String) cb) => callbacksOnFirstImage.add(cb); onRgba(Uint8List rgba) { - if (_waitForImage[id]!) { + final waitforImage = _waitForImage[id]; + if (waitforImage == null) { + debugPrint('Exception, peer $id not found for waiting image'); + return; + } + + if (waitforImage == true) { _waitForImage[id] = false; parent.target?.dialogManager.dismissAll(); if (isDesktop) { @@ -1564,6 +1570,7 @@ class FFI { id = 'pf_$id'; } else { chatModel.resetClientMode(); + connType = ConnType.defaultConn; canvasModel.id = id; imageModel.id = id; cursorModel.id = id; From 8cb361c51e6c9d67c5a131c721c46f7a09a319ef Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 27 Mar 2023 19:13:29 +0800 Subject: [PATCH 074/366] remove android build warns Signed-off-by: fufesou --- libs/hbb_common/src/config.rs | 1 + src/client/io_loop.rs | 21 +++++++++++++++------ src/core_main.rs | 1 + src/flutter_ffi.rs | 12 +++++------- src/hbbs_http/sync.rs | 1 + src/keyboard.rs | 28 ++++++++++++++++++++-------- src/lan.rs | 11 ++++++++--- src/platform/mod.rs | 2 +- src/server.rs | 2 +- src/server/audio_service.rs | 1 + src/server/connection.rs | 19 ++++++++++++++----- src/server/video_service.rs | 3 +-- src/ui_cm_interface.rs | 16 +++++++++++++--- src/ui_interface.rs | 23 ++++++++++++++++------- src/ui_session_interface.rs | 3 +++ 15 files changed, 101 insertions(+), 43 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 0624af672..76a87153e 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -297,6 +297,7 @@ pub struct TransferSerde { pub read_jobs: Vec, } +#[cfg(not(any(target_os = "android", target_os = "ios")))] fn patch(path: PathBuf) -> PathBuf { if let Some(_tmp) = path.to_str() { #[cfg(windows)] diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 4a020d197..135e92a75 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1,7 +1,11 @@ use std::collections::HashMap; use std::num::NonZeroI64; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::{Arc, Mutex}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use std::sync::Mutex; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; #[cfg(windows)] use clipboard::{cliprdr::CliprdrClientContext, ContextSend}; @@ -13,6 +17,8 @@ use hbb_common::fs::{ use hbb_common::message_proto::permission_info::Permission; use hbb_common::protobuf::Message as _; use hbb_common::rendezvous_proto::ConnType; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::sleep; use hbb_common::tokio::sync::mpsc::error::TryRecvError; #[cfg(windows)] use hbb_common::tokio::sync::Mutex as TokioMutex; @@ -21,18 +27,17 @@ use hbb_common::tokio::{ sync::mpsc, time::{self, Duration, Instant, Interval}, }; -use hbb_common::{allow_err, get_time, message_proto::*, sleep}; -use hbb_common::{fs, log, Stream}; +use hbb_common::{allow_err, fs, get_time, log, message_proto::*, Stream}; use crate::client::{ new_voice_call_request, Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::common::update_clipboard; +use crate::common::{self, update_clipboard}; use crate::common::{get_default_sound_input, set_sound_input}; use crate::ui_session_interface::{InvokeUiSession, Session}; -use crate::{audio_service, common, ConnInner, CLIENT_SERVER}; +use crate::{audio_service, ConnInner, CLIENT_SERVER}; use crate::{client::Data, client::Interface}; pub struct Remote { @@ -44,6 +49,7 @@ pub struct Remote { // Stop sending local audio to remote client. stop_voice_call_sender: Option>, voice_call_request_timestamp: Option, + #[cfg(not(any(target_os = "android", target_os = "ios")))] old_clipboard: Arc>, read_jobs: Vec, write_jobs: Vec, @@ -74,6 +80,7 @@ impl Remote { audio_sender, receiver, sender, + #[cfg(not(any(target_os = "android", target_os = "ios")))] old_clipboard: Default::default(), read_jobs: Vec::new(), write_jobs: Vec::new(), @@ -840,7 +847,9 @@ impl Remote { self.handler.handle_peer_info(pi); self.check_clipboard_file_context(); if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) { + #[cfg(not(any(target_os = "android", target_os = "ios")))] let sender = self.sender.clone(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let permission_config = self.handler.get_permission_config(); #[cfg(feature = "flutter")] diff --git a/src/core_main.rs b/src/core_main.rs index 2c209bd07..e5eb64136 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -8,6 +8,7 @@ use hbb_common::platform::register_breakdown_handler; /// [Note] /// If it returns [`None`], then the process will terminate, and flutter gui will not be started. /// If it returns [`Some`], then the process will continue, and flutter gui will be started. +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn core_main() -> Option> { let mut args = Vec::new(); let mut flutter_args = Vec::new(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index a3e8c38f2..aee486b94 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -168,14 +168,12 @@ pub fn session_reconnect(id: String, force_relay: bool) { } pub fn session_toggle_option(id: String, value: String) { - let mut is_found = false; if let Some(session) = SESSIONS.write().unwrap().get_mut(&id) { - is_found = true; log::warn!("toggle option {}", &value); session.toggle_option(value.clone()); } #[cfg(not(any(target_os = "android", target_os = "ios")))] - if is_found && value == "disable-clipboard" { + if SESSIONS.read().unwrap().get(&id).is_some() && value == "disable-clipboard" { crate::flutter::update_text_clipboard_required(); } } @@ -338,10 +336,10 @@ pub fn session_handle_flutter_key_event( } } -pub fn session_enter_or_leave(id: String, enter: bool) { +pub fn session_enter_or_leave(_id: String, _enter: bool) { #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Some(session) = SESSIONS.read().unwrap().get(&id) { - if enter { + if let Some(session) = SESSIONS.read().unwrap().get(&_id) { + if _enter { session.enter(); } else { session.leave(); @@ -1405,7 +1403,7 @@ pub mod server_side { #[no_mangle] pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_startService( - env: JNIEnv, + _env: JNIEnv, _class: JClass, ) { log::debug!("startService from jvm"); diff --git a/src/hbbs_http/sync.rs b/src/hbbs_http/sync.rs index a060d6a20..ae91c60aa 100644 --- a/src/hbbs_http/sync.rs +++ b/src/hbbs_http/sync.rs @@ -16,6 +16,7 @@ lazy_static::lazy_static! { static ref SENDER : Mutex>> = Mutex::new(start_hbbs_sync()); } +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn start() { let _sender = SENDER.lock().unwrap(); } diff --git a/src/keyboard.rs b/src/keyboard.rs index 5a41d6c5c..294e058c5 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -179,6 +179,7 @@ pub mod client { } #[inline] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn lock_screen() { send_key_event(&event_lock_screen()); } @@ -198,6 +199,7 @@ pub mod client { } #[inline] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn ctrl_alt_del() { send_key_event(&event_ctrl_alt_del()); } @@ -364,6 +366,7 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { } #[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] fn is_numpad_key(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3 | Key::Kp4 | Key::Kp5 | Key::Kp6 | Key::Kp7 | Key::Kp8 | @@ -373,6 +376,7 @@ fn is_numpad_key(event: &Event) -> bool { } #[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] fn is_letter_key(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { Key::KeyA | Key::KeyB | Key::KeyC | Key::KeyD | Key::KeyE | Key::KeyF | Key::KeyG | Key::KeyH | @@ -456,7 +460,7 @@ fn update_modifiers_state(event: &Event) { pub fn event_to_key_events( event: &Event, keyboard_mode: KeyboardMode, - lock_modes: Option, + _lock_modes: Option, ) -> Vec { let mut key_event = KeyEvent::new(); update_modifiers_state(event); @@ -475,7 +479,12 @@ pub fn event_to_key_events( peer.retain(|c| !c.is_whitespace()); key_event.mode = keyboard_mode.into(); - let mut key_events = match keyboard_mode { + + #[cfg(not(any(target_os = "android", target_os = "ios")))] + let mut key_events; + #[cfg(any(target_os = "android", target_os = "ios"))] + let key_events; + key_events = match keyboard_mode { KeyboardMode::Map => match map_keyboard_mode(peer.as_str(), event, key_event) { Some(event) => [event].to_vec(), None => Vec::new(), @@ -493,12 +502,12 @@ pub fn event_to_key_events( } }; + #[cfg(not(any(target_os = "android", target_os = "ios")))] if keyboard_mode != KeyboardMode::Translate { let is_numpad_key = is_numpad_key(&event); let is_letter_key = is_letter_key(&event); for key_event in &mut key_events { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Some(lock_modes) = lock_modes { + if let Some(lock_modes) = _lock_modes { parse_add_lock_modes_modifiers(key_event, lock_modes, is_numpad_key, is_letter_key); } else { add_lock_modes_modifiers(key_event, is_numpad_key, is_letter_key); @@ -778,7 +787,7 @@ pub fn legacy_keyboard_mode(event: &Event, mut key_event: KeyEvent) -> Vec Option { +pub fn map_keyboard_mode(_peer: &str, event: &Event, mut key_event: KeyEvent) -> Option { match event.event_type { EventType::KeyPress(..) => { key_event.down = true; @@ -790,7 +799,7 @@ pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> }; #[cfg(target_os = "windows")] - let keycode = match peer { + let keycode = match _peer { OS_LOWER_WINDOWS => { // https://github.com/rustdesk/rustdesk/issues/1371 // Filter scancodes that are greater than 255 and the hight word is not 0xE0. @@ -809,13 +818,13 @@ pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> _ => rdev::win_scancode_to_linux_code(event.position_code)?, }; #[cfg(target_os = "macos")] - let keycode = match peer { + let keycode = match _peer { OS_LOWER_WINDOWS => rdev::macos_code_to_win_scancode(event.platform_code as _)?, OS_LOWER_MACOS => event.platform_code as _, _ => rdev::macos_code_to_linux_code(event.platform_code as _)?, }; #[cfg(target_os = "linux")] - let keycode = match peer { + let keycode = match _peer { OS_LOWER_WINDOWS => rdev::linux_code_to_win_scancode(event.position_code as _)?, OS_LOWER_MACOS => { if hbb_common::config::LocalConfig::get_kb_layout_type() == "ISO" { @@ -833,6 +842,7 @@ pub fn map_keyboard_mode(peer: &str, event: &Event, mut key_event: KeyEvent) -> Some(key_event) } +#[cfg(not(any(target_os = "android", target_os = "ios")))] fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec) { match &event.unicode { Some(unicode_info) => { @@ -925,6 +935,8 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - return events; } } + + #[cfg(not(any(target_os = "android", target_os = "ios")))] if is_numpad_key(&event) { if let Some(evt) = translate_key_code(peer, event, key_event) { events.push(evt); diff --git a/src/lan.rs b/src/lan.rs index 5b52ee1a9..15914965b 100644 --- a/src/lan.rs +++ b/src/lan.rs @@ -1,7 +1,9 @@ +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::config::Config; use hbb_common::{ allow_err, anyhow::bail, - config::{self, Config, RENDEZVOUS_PORT}, + config::{self, RENDEZVOUS_PORT}, log, protobuf::Message as _, rendezvous_proto::*, @@ -11,6 +13,7 @@ use hbb_common::{ }, ResultType, }; + use std::{ collections::{HashMap, HashSet}, net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}, @@ -19,6 +22,7 @@ use std::{ type Message = RendezvousMessage; +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub(super) fn start_listening() -> ResultType<()> { let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); let socket = std::net::UdpSocket::bind(addr)?; @@ -98,9 +102,9 @@ fn get_broadcast_port() -> u16 { (RENDEZVOUS_PORT + 3) as _ } -fn get_mac(ip: &IpAddr) -> String { +fn get_mac(_ip: &IpAddr) -> String { #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Ok(mac) = get_mac_by_ip(ip) { + if let Ok(mac) = get_mac_by_ip(_ip) { mac.to_string() } else { "".to_owned() @@ -119,6 +123,7 @@ fn get_all_ipv4s() -> ResultType> { Ok(ipv4s) } +#[cfg(not(any(target_os = "android", target_os = "ios")))] fn get_mac_by_ip(ip: &IpAddr) -> ResultType { for interface in default_net::get_interfaces() { match ip { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 2ef39e92a..c30083155 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -19,7 +19,7 @@ pub mod linux; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::{message_proto::CursorData, ResultType}; -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", target_os = "android", target_os = "ios")))] const SERVICE_INTERVAL: u64 = 300; pub fn is_xfce() -> bool { diff --git a/src/server.rs b/src/server.rs index 1f174ef03..681e7bed1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -365,7 +365,7 @@ pub fn check_zombie() { /// Otherwise, client will check if there's already a server and start one if not. #[cfg(any(target_os = "android", target_os = "ios"))] #[tokio::main] -pub async fn start_server(is_server: bool) { +pub async fn start_server(_is_server: bool) { crate::RendezvousMediator::start_all().await; } diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index bef1ac1f3..3d2c226c5 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -64,6 +64,7 @@ mod pa_impl { ))) .await ); + #[cfg(target_os = "linux")] let zero_audio_frame: Vec = vec![0.; AUDIO_DATA_SIZE_U8 / 4]; while sp.ok() && !RESTARTING.load(Ordering::SeqCst) { sp.snapshot(|sps| { diff --git a/src/server/connection.rs b/src/server/connection.rs index 1f89d905f..ed7476284 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -64,7 +64,9 @@ pub struct ConnInner { } enum MessageInput { + #[cfg(not(any(target_os = "android", target_os = "ios")))] Mouse((MouseEvent, i32)), + #[cfg(not(any(target_os = "android", target_os = "ios")))] Key((KeyEvent, bool)), BlockOn, BlockOff, @@ -125,6 +127,7 @@ pub struct Connection { #[cfg(windows)] portable: PortableState, from_switch: bool, + #[cfg(not(any(target_os = "android", target_os = "ios")))] origin_resolution: HashMap, voice_call_request_timestamp: Option, audio_input_device_before_voice_call: Option, @@ -187,9 +190,10 @@ impl Connection { let (tx_to_cm, rx_to_cm) = mpsc::unbounded_channel::(); let (tx, mut rx) = mpsc::unbounded_channel::<(Instant, Arc)>(); let (tx_video, mut rx_video) = mpsc::unbounded_channel::<(Instant, Arc)>(); - let (tx_input, rx_input) = std_mpsc::channel(); + let (tx_input, _rx_input) = std_mpsc::channel(); let mut hbbs_rx = crate::hbbs_http::sync::signal_receiver(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let tx_cloned = tx.clone(); let mut conn = Self { inner: ConnInner { @@ -233,6 +237,7 @@ impl Connection { #[cfg(windows)] portable: Default::default(), from_switch: false, + #[cfg(not(any(target_os = "android", target_os = "ios")))] origin_resolution: Default::default(), audio_sender: None, voice_call_request_timestamp: None, @@ -282,7 +287,7 @@ impl Connection { ); #[cfg(not(any(target_os = "android", target_os = "ios")))] - std::thread::spawn(move || Self::handle_input(rx_input, tx_cloned)); + std::thread::spawn(move || Self::handle_input(_rx_input, tx_cloned)); let mut second_timer = time::interval(Duration::from_secs(1)); loop { @@ -1042,11 +1047,13 @@ impl Connection { } #[inline] + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn input_mouse(&self, msg: MouseEvent, conn_id: i32) { self.tx_input.send(MessageInput::Mouse((msg, conn_id))).ok(); } #[inline] + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn input_key(&self, msg: KeyEvent, press: bool) { self.tx_input.send(MessageInput::Key((msg, press))).ok(); } @@ -1342,8 +1349,10 @@ impl Connection { self.input_mouse(me, self.inner.id()); } } + #[cfg(any(target_os = "android", target_os = "ios"))] + Some(message::Union::KeyEvent(..)) => {} + #[cfg(not(any(target_os = "android", target_os = "ios")))] Some(message::Union::KeyEvent(me)) => { - #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.peer_keyboard_enabled() { if is_enter(&me) { CLICK_TIME.store(get_time(), Ordering::SeqCst); @@ -1371,11 +1380,11 @@ impl Connection { } } } - Some(message::Union::Clipboard(cb)) => + Some(message::Union::Clipboard(_cb)) => { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.clipboard { - update_clipboard(cb, None); + update_clipboard(_cb, None); } } Some(message::Union::Cliprdr(_clip)) => { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 3b9db0697..205d0584c 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -831,14 +831,13 @@ pub fn handle_one_frame_encoded( } Ok(()) })?; - let mut send_conn_ids: HashSet = Default::default(); let vp9_frame = EncodedVideoFrame { data: frame.to_vec().into(), key: true, pts: ms, ..Default::default() }; - send_conn_ids = sp.send_video_frame(create_msg(vec![vp9_frame])); + let send_conn_ids = sp.send_video_frame(create_msg(vec![vp9_frame])); Ok(send_conn_ids) } diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index bd6eab3bf..8612b5751 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -15,7 +15,11 @@ use std::{ use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, set_conn_enabled, ContextSend}; use serde_derive::Serialize; -use crate::ipc::{self, Connection, Data}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::ipc::Connection; +use crate::ipc::{self, Data}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::tokio::sync::mpsc::unbounded_channel; #[cfg(windows)] use hbb_common::tokio::sync::Mutex as TokioMutex; use hbb_common::{ @@ -28,7 +32,7 @@ use hbb_common::{ protobuf::Message as _, tokio::{ self, - sync::mpsc::{self, unbounded_channel, UnboundedSender}, + sync::mpsc::{self, UnboundedSender}, task::spawn_blocking, }, }; @@ -55,6 +59,7 @@ pub struct Client { tx: UnboundedSender, } +#[cfg(not(any(target_os = "android", target_os = "ios")))] struct IpcTaskRunner { stream: Connection, cm: ConnectionManager, @@ -143,7 +148,7 @@ impl ConnectionManager { from_switch, tx, in_voice_call: false, - incoming_voice_call: false + incoming_voice_call: false, }; CLIENTS .write() @@ -183,10 +188,12 @@ impl ConnectionManager { self.ui_handler.remove_connection(id, close); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn show_elevation(&self, show: bool) { self.ui_handler.show_elevation(show); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn voice_call_started(&self, id: i32) { if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { client.incoming_voice_call = false; @@ -195,6 +202,7 @@ impl ConnectionManager { } } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn voice_call_incoming(&self, id: i32) { if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { client.incoming_voice_call = true; @@ -203,6 +211,7 @@ impl ConnectionManager { } } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn voice_call_closed(&self, id: i32, _reason: &str) { if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { client.incoming_voice_call = false; @@ -282,6 +291,7 @@ pub fn switch_back(id: i32) { }; } +#[cfg(not(any(target_os = "android", target_os = "ios")))] impl IpcTaskRunner { #[cfg(windows)] async fn enable_cliprdr_file_context(&mut self, conn_id: i32, enabled: bool) { diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 17b4fafca..11357be4a 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -9,8 +9,12 @@ use hbb_common::password_security; use hbb_common::{ allow_err, config::{self, Config, LocalConfig, PeerConfig}, - directories_next, log, sleep, - tokio::{self, sync::mpsc, time}, + directories_next, log, tokio, +}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::{ + sleep, + tokio::{sync::mpsc, time}, }; use hbb_common::{ @@ -339,8 +343,8 @@ pub fn get_socks() -> Vec { } #[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn set_socks(proxy: String, username: String, password: String) { - #[cfg(not(any(target_os = "android", target_os = "ios")))] ipc::set_socks(config::Socks5Server { proxy, username, @@ -349,6 +353,9 @@ pub fn set_socks(proxy: String, username: String, password: String) { .ok(); } +#[cfg(any(target_os = "android", target_os = "ios"))] +pub fn set_socks(_: String, _: String, _: String) {} + #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_installed() -> bool { @@ -791,14 +798,15 @@ pub fn check_zombie(children: Children) { } } +// Make sure `SENDER` is inited here. +#[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn start_option_status_sync() { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - { - let _sender = SENDER.lock().unwrap(); - } + let _sender = SENDER.lock().unwrap(); } // not call directly +#[cfg(not(any(target_os = "android", target_os = "ios")))] fn check_connect_status(reconnect: bool) -> mpsc::UnboundedSender { let (tx, rx) = mpsc::unbounded_channel::(); std::thread::spawn(move || check_connect_status_(reconnect, rx)); @@ -834,6 +842,7 @@ pub fn get_user_default_option(key: String) -> String { // notice: avoiding create ipc connection repeatedly, // because windows named pipe has serious memory leak issue. +#[cfg(not(any(target_os = "android", target_os = "ios")))] #[tokio::main(flavor = "current_thread")] async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver) { let mut key_confirmed = false; diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 824f8befa..7ee49a3b7 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1053,6 +1053,9 @@ impl Session { #[tokio::main(flavor = "current_thread")] pub async fn io_loop(handler: Session) { + #[cfg(any(target_os = "android", target_os = "ios"))] + let (sender, receiver) = mpsc::unbounded_channel::(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let (sender, mut receiver) = mpsc::unbounded_channel::(); *handler.sender.write().unwrap() = Some(sender.clone()); let token = LocalConfig::get_option("access_token"); From 947c7dbb971d9cb621f9566bb96fd04f02a45db6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 27 Mar 2023 22:56:48 +0800 Subject: [PATCH 075/366] try wait hw check config subprocess Signed-off-by: fufesou --- libs/scrap/src/common/hwcodec.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index fa1c00976..f55fd452a 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -3,7 +3,7 @@ use crate::{ hw, ImageFormat, HW_STRIDE_ALIGN, }; use hbb_common::{ - anyhow::{anyhow, Context}, + anyhow::{allow_err, anyhow, Context}, bytes::Bytes, config::HwCodecConfig, get_time, lazy_static, log, @@ -352,7 +352,19 @@ pub fn check_config_process(force_reset: bool) { let second = 3; std::thread::sleep(std::time::Duration::from_secs(second)); // kill: Different platforms have different results - child.kill().ok(); + allow_err!(child.kill()); + std::thread::sleep(std::time::Duration::from_millis(30)); + match ps.try_wait() { + Ok(Some(status)) => log::info!("Check hwcodec config, exit with: {status}"), + Ok(None) => { + log::info!( + "Check hwcodec config, status not ready yet, let's really wait" + ); + let res = child.wait(); + log::info!("Check hwcodec config, wait result: {res:?}"); + } + Err(e) => println!("Check hwcodec config, error attempting to wait: {e}"), + } HwCodecConfig::refresh(); } } From 90a43a51103eff49572590e4276bb7832e641ca5 Mon Sep 17 00:00:00 2001 From: djordans Date: Mon, 27 Mar 2023 17:02:46 +0200 Subject: [PATCH 076/366] add content-type to api/ab/get --- flutter/lib/models/ab_model.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index 175c8424b..133343297 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -27,7 +27,9 @@ class AbModel { abError.value = ""; final api = "${await bind.mainGetApiServer()}/api/ab/get"; try { - final resp = await http.post(Uri.parse(api), headers: getHttpHeaders()); + var authHeaders = getHttpHeaders(); + authHeaders['Content-Type'] = "application/json"; + final resp = await http.post(Uri.parse(api), headers: authHeaders); if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") { Map json = jsonDecode(resp.body); if (json.containsKey('error')) { From ee1fbb0a76c1d3c4eca5bdfd0da26e18ea57bc5a Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 27 Mar 2023 23:29:28 +0800 Subject: [PATCH 077/366] fix build Signed-off-by: fufesou --- libs/scrap/src/common/hwcodec.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index f55fd452a..db8c6b2ef 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -3,7 +3,8 @@ use crate::{ hw, ImageFormat, HW_STRIDE_ALIGN, }; use hbb_common::{ - anyhow::{allow_err, anyhow, Context}, + allow_err, + anyhow::{anyhow, Context}, bytes::Bytes, config::HwCodecConfig, get_time, lazy_static, log, @@ -354,7 +355,7 @@ pub fn check_config_process(force_reset: bool) { // kill: Different platforms have different results allow_err!(child.kill()); std::thread::sleep(std::time::Duration::from_millis(30)); - match ps.try_wait() { + match child.try_wait() { Ok(Some(status)) => log::info!("Check hwcodec config, exit with: {status}"), Ok(None) => { log::info!( From a1d1c8270fbd0907aecbe21749c7bbdd20581f75 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 08:20:19 +0800 Subject: [PATCH 078/366] log Signed-off-by: fufesou --- libs/scrap/src/common/hwcodec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index db8c6b2ef..ee7d8aea7 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -364,7 +364,7 @@ pub fn check_config_process(force_reset: bool) { let res = child.wait(); log::info!("Check hwcodec config, wait result: {res:?}"); } - Err(e) => println!("Check hwcodec config, error attempting to wait: {e}"), + Err(e) => log::info!("Check hwcodec config, error attempting to wait: {e}"), } HwCodecConfig::refresh(); } From 8af20489bd92ff42a610821c784583078b5f2344 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 08:21:47 +0800 Subject: [PATCH 079/366] log::error Signed-off-by: fufesou --- libs/scrap/src/common/hwcodec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index ee7d8aea7..6e6638b10 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -364,7 +364,7 @@ pub fn check_config_process(force_reset: bool) { let res = child.wait(); log::info!("Check hwcodec config, wait result: {res:?}"); } - Err(e) => log::info!("Check hwcodec config, error attempting to wait: {e}"), + Err(e) => log::error!("Check hwcodec config, error attempting to wait: {e}"), } HwCodecConfig::refresh(); } From eebddb2206bd1e66819be42b22f5e94bd24dd813 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 08:22:46 +0800 Subject: [PATCH 080/366] format Signed-off-by: fufesou --- libs/scrap/src/common/hwcodec.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 6e6638b10..42bf76cb5 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -364,7 +364,9 @@ pub fn check_config_process(force_reset: bool) { let res = child.wait(); log::info!("Check hwcodec config, wait result: {res:?}"); } - Err(e) => log::error!("Check hwcodec config, error attempting to wait: {e}"), + Err(e) => { + log::error!("Check hwcodec config, error attempting to wait: {e}") + } } HwCodecConfig::refresh(); } From 77c4cce3683b197d5d60a192f894dcbd3863dd29 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 10:36:59 +0800 Subject: [PATCH 081/366] show keyboard button, on remote toolbar Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 51 ++++++++----------- flutter/lib/models/model.dart | 2 +- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 45a1a55bc..25209912d 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -317,6 +317,9 @@ class _RemoteMenubarState extends State { RxBool get show => widget.state.show; bool get pin => widget.state.pin; + PeerInfo get pi => widget.ffi.ffiModel.pi; + FfiModel get ffiModel => widget.ffi.ffiModel; + @override initState() { super.initState(); @@ -393,8 +396,7 @@ class _RemoteMenubarState extends State { toolbarItems.add(_MobileActionMenu(ffi: widget.ffi)); } - if (PrivacyModeState.find(widget.id).isFalse && - widget.ffi.ffiModel.pi.displays.length > 1) { + if (PrivacyModeState.find(widget.id).isFalse && pi.displays.length > 1) { toolbarItems.add( bind.mainGetUserDefaultOption(key: 'show_monitors_toolbar') == 'Y' ? _MultiMonitorMenu(id: widget.id, ffi: widget.ffi) @@ -819,11 +821,10 @@ class _ControlMenu extends StatelessWidget { } ctrlAltDel() { - final perms = ffi.ffiModel.permissions; final viewOnly = ffi.ffiModel.viewOnly; final pi = ffi.ffiModel.pi; final visible = !viewOnly && - perms['keyboard'] != false && + ffi.ffiModel.keyboard && (pi.platform == kPeerPlatformLinux || pi.sasEnabled); if (!visible) return Offstage(); return _MenuItemButton( @@ -847,9 +848,8 @@ class _ControlMenu extends StatelessWidget { } insertLock() { - final perms = ffi.ffiModel.permissions; final viewOnly = ffi.ffiModel.viewOnly; - final visible = !viewOnly && perms['keyboard'] != false; + final visible = !viewOnly && ffi.ffiModel.keyboard; if (!visible) return Offstage(); return _MenuItemButton( child: Text(translate('Insert Lock')), @@ -858,10 +858,9 @@ class _ControlMenu extends StatelessWidget { } blockUserInput() { - final perms = ffi.ffiModel.permissions; final pi = ffi.ffiModel.pi; final visible = - perms['keyboard'] != false && pi.platform == kPeerPlatformWindows; + ffi.ffiModel.keyboard && pi.platform == kPeerPlatformWindows; if (!visible) return Offstage(); return _MenuItemButton( child: Obx(() => Text(translate( @@ -876,9 +875,8 @@ class _ControlMenu extends StatelessWidget { } switchSides() { - final perms = ffi.ffiModel.permissions; final pi = ffi.ffiModel.pi; - final visible = perms['keyboard'] != false && + final visible = ffi.ffiModel.keyboard && pi.platform != kPeerPlatformAndroid && pi.platform != kPeerPlatformMacOS && version_cmp(pi.version, '1.2.0') >= 0; @@ -948,6 +946,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { Map get perms => widget.ffi.ffiModel.permissions; PeerInfo get pi => widget.ffi.ffiModel.pi; + FfiModel get ffiModel => widget.ffi.ffiModel; @override Widget build(BuildContext context) { @@ -1419,11 +1418,10 @@ class _DisplayMenuState extends State<_DisplayMenu> { } resolutions() { - final resolutions = widget.ffi.ffiModel.pi.resolutions; - final visible = widget.ffi.ffiModel.permissions["keyboard"] != false && - resolutions.length > 1; + final resolutions = pi.resolutions; + final visible = ffiModel.keyboard && resolutions.length > 1; if (!visible) return Offstage(); - final display = widget.ffi.ffiModel.display; + final display = ffiModel.display; final groupValue = "${display.width}x${display.height}"; onChanged(String? value) async { if (value == null) return; @@ -1435,7 +1433,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { await bind.sessionChangeResolution( id: widget.id, width: w, height: h); Future.delayed(Duration(seconds: 3), () async { - final display = widget.ffi.ffiModel.display; + final display = ffiModel.display; if (w == display.width && h == display.height) { if (_isWindowCanBeAdjusted()) { _doAdjustWindow(); @@ -1460,10 +1458,9 @@ class _DisplayMenuState extends State<_DisplayMenu> { } showRemoteCursor() { - if (widget.ffi.ffiModel.pi.platform == kPeerPlatformAndroid) { + if (pi.platform == kPeerPlatformAndroid) { return Offstage(); } - final ffiModel = widget.ffi.ffiModel; final visible = !widget.ffi.canvasModel.cursorEmbedded && !ffiModel.pi.is_wayland; if (!visible) return Offstage(); @@ -1485,7 +1482,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { } zoomCursor() { - if (widget.ffi.ffiModel.pi.platform == kPeerPlatformAndroid) { + if (pi.platform == kPeerPlatformAndroid) { return Offstage(); } final visible = widget.state.viewStyle.value != kRemoteViewStyleOriginal; @@ -1551,8 +1548,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { } disableClipboard() { - final ffiModel = widget.ffi.ffiModel; - final visible = perms['keyboard'] != false && perms['clipboard'] != false; + final visible = ffiModel.keyboard && perms['clipboard'] != false; if (!visible) return Offstage(); final enabled = !ffiModel.viewOnly; final option = 'disable-clipboard'; @@ -1571,8 +1567,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { } lockAfterSessionEnd() { - final visible = perms['keyboard'] != false; - if (!visible) return Offstage(); + if (!ffiModel.keyboard) return Offstage(); final option = 'lock-after-session-end'; final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option); return _CheckboxMenuButton( @@ -1586,7 +1581,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { } privacyMode() { - bool visible = perms['keyboard'] != false && pi.features.privacyMode; + bool visible = ffiModel.keyboard && pi.features.privacyMode; if (!visible) return Offstage(); final option = 'privacy-mode'; final rxValue = PrivacyModeState.find(widget.id); @@ -1594,7 +1589,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { value: rxValue.value, onChanged: (value) { if (value == null) return; - if (widget.ffi.ffiModel.pi.currentDisplay != 0) { + if (ffiModel.pi.currentDisplay != 0) { msgBox( widget.id, 'custom-nook-nocancel-hasclose', @@ -1611,7 +1606,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { } swapKey() { - final visible = perms['keyboard'] != false && + final visible = ffiModel.keyboard && ((Platform.isMacOS && pi.platform != kPeerPlatformMacOS) || (!Platform.isMacOS && pi.platform == kPeerPlatformMacOS)); if (!visible) return Offstage(); @@ -1642,7 +1637,7 @@ class _KeyboardMenu extends StatelessWidget { @override Widget build(BuildContext context) { var ffiModel = Provider.of(context); - if (ffiModel.permissions['keyboard'] == false) return Offstage(); + if (!ffiModel.keyboard) return Offstage(); if (stateGlobal.grabKeyboard) { if (bind.sessionIsKeyboardModeSupported(id: id, mode: _kKeyMapMode)) { bind.sessionSetKeyboardMode(id: id, value: _kKeyMapMode); @@ -1650,7 +1645,6 @@ class _KeyboardMenu extends StatelessWidget { id: id, mode: _kKeyLegacyMode)) { bind.sessionSetKeyboardMode(id: id, value: _kKeyLegacyMode); } - return Offstage(); } return _IconSubmenuButton( tooltip: 'Keyboard Settings', @@ -1728,8 +1722,7 @@ class _KeyboardMenu extends StatelessWidget { view_mode() { final ffiModel = ffi.ffiModel; - final enabled = version_cmp(pi.version, '1.2.0') >= 0 && - ffiModel.permissions["keyboard"] != false; + final enabled = version_cmp(pi.version, '1.2.0') >= 0 && ffiModel.keyboard; return _CheckboxMenuButton( value: ffiModel.viewOnly, onChanged: enabled diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 95813097d..82c8efbca 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -93,7 +93,7 @@ class FfiModel with ChangeNotifier { notifyListeners(); } - bool keyboard() => _permissions['keyboard'] != false; + bool get keyboard => _permissions['keyboard'] != false; clear() { _pi = PeerInfo(); From c845ddf5106ed3d09932e70601d128b8749e7eaf Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 10:52:43 +0800 Subject: [PATCH 082/366] fix build Signed-off-by: fufesou --- flutter/lib/desktop/pages/remote_page.dart | 2 +- flutter/lib/models/input_model.dart | 15 +++++++++------ flutter/lib/models/model.dart | 2 +- libs/scrap/src/common/hwcodec.rs | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 1bc09f137..3bc450345 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -310,7 +310,7 @@ class _RemotePageState extends State } void leaveView(PointerExitEvent evt) { - if (_ffi.ffiModel.keyboard()) { + if (_ffi.ffiModel.keyboard) { _ffi.inputModel.tryMoveEdgeOnExit(evt.position); } diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 310a3cadb..95f0f2634 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -59,6 +59,8 @@ class InputModel { get id => parent.target?.id ?? ""; + bool get keyboardPerm => parent.target!.ffiModel.keyboard; + InputModel(this.parent); KeyEventResult handleRawKeyEvent(FocusNode data, RawKeyEvent e) { @@ -203,7 +205,7 @@ class InputModel { /// [down] indicates the key's state(down or up). /// [press] indicates a click event(down and up). void inputKey(String name, {bool? down, bool? press}) { - if (!parent.target!.ffiModel.keyboard()) return; + if (!keyboardPerm) return; bind.sessionInputKey( id: id, name: name, @@ -286,7 +288,7 @@ class InputModel { /// Send mouse press event. void sendMouse(String type, MouseButtons button) { - if (!parent.target!.ffiModel.keyboard()) return; + if (!keyboardPerm) return; bind.sessionSendMouse( id: id, msg: json.encode(modify({'type': type, 'buttons': button.value}))); @@ -303,7 +305,7 @@ class InputModel { /// Send mouse movement event with distance in [x] and [y]. void moveMouse(double x, double y) { - if (!parent.target!.ffiModel.keyboard()) return; + if (!keyboardPerm) return; var x2 = x.toInt(); var y2 = y.toInt(); bind.sessionSendMouse( @@ -379,7 +381,7 @@ class InputModel { } void _scheduleFling2(double x, double y, int delay) { - if ((x ==0 && y == 0) || _stopFling) { + if ((x == 0 && y == 0) || _stopFling) { return; } @@ -394,7 +396,7 @@ class InputModel { final dx0 = x * _trackpadSpeed * 2; final dy0 = y * _trackpadSpeed * 2; - // Try set delta (x,y) and delay. + // Try set delta (x,y) and delay. var dx = dx0.toInt(); var dy = dy0.toInt(); var delay = _flingBaseDelay; @@ -432,7 +434,8 @@ class InputModel { void onPointerPanZoomEnd(PointerPanZoomEndEvent e) { _stopFling = false; _trackpadScrollUnsent = Offset.zero; - _scheduleFling2(_trackpadLastDelta.dx, _trackpadLastDelta.dy, _flingBaseDelay); + _scheduleFling2( + _trackpadLastDelta.dx, _trackpadLastDelta.dy, _flingBaseDelay); _trackpadLastDelta = Offset.zero; } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 82c8efbca..06bfeec45 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -906,7 +906,7 @@ class CanvasModel with ChangeNotifier { } // If keyboard is not permitted, do not move cursor when mouse is moving. - if (parent.target != null && parent.target!.ffiModel.keyboard()) { + if (parent.target != null && parent.target!.ffiModel.keyboard) { // Draw cursor if is not desktop. if (!isDesktop) { parent.target!.cursorModel.moveLocal(x, y); diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 42bf76cb5..e5ede45b1 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -7,7 +7,7 @@ use hbb_common::{ anyhow::{anyhow, Context}, bytes::Bytes, config::HwCodecConfig, - get_time, lazy_static, log, + lazy_static, log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}, ResultType, }; From efaf63c57cee55ab967324e85657821b4f7cd734 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 12:10:58 +0800 Subject: [PATCH 083/366] specify keyboard mode only Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 25209912d..0ef8674ef 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1638,12 +1638,15 @@ class _KeyboardMenu extends StatelessWidget { Widget build(BuildContext context) { var ffiModel = Provider.of(context); if (!ffiModel.keyboard) return Offstage(); + String? modeOnly; if (stateGlobal.grabKeyboard) { if (bind.sessionIsKeyboardModeSupported(id: id, mode: _kKeyMapMode)) { bind.sessionSetKeyboardMode(id: id, value: _kKeyMapMode); + modeOnly = _kKeyMapMode; } else if (bind.sessionIsKeyboardModeSupported( id: id, mode: _kKeyLegacyMode)) { bind.sessionSetKeyboardMode(id: id, value: _kKeyLegacyMode); + modeOnly = _kKeyLegacyMode; } } return _IconSubmenuButton( @@ -1653,14 +1656,14 @@ class _KeyboardMenu extends StatelessWidget { color: _MenubarTheme.blueColor, hoverColor: _MenubarTheme.hoverBlueColor, menuChildren: [ - mode(), + mode(modeOnly), localKeyboardType(), Divider(), view_mode(), ]); } - mode() { + mode(String? modeOnly) { return futureBuilder(future: () async { return await bind.sessionGetKeyboardMode(id: id) ?? _kKeyLegacyMode; }(), hasData: (data) { @@ -1678,22 +1681,28 @@ class _KeyboardMenu extends StatelessWidget { } for (KeyboardModeMenu mode in modes) { - if (bind.sessionIsKeyboardModeSupported(id: id, mode: mode.key)) { - if (pi.is_wayland && mode.key != _kKeyMapMode) { - continue; - } - var text = translate(mode.menu); - if (mode.key == _kKeyTranslateMode) { - text = '$text beta'; - } - list.add(_RadioMenuButton( - child: Text(text), - value: mode.key, - groupValue: groupValue, - onChanged: enabled ? onChanged : null, - ffi: ffi, - )); + if (modeOnly != null && mode.key != modeOnly) { + continue; + } else if (!bind.sessionIsKeyboardModeSupported( + id: id, mode: mode.key)) { + continue; } + + if (pi.is_wayland && mode.key != _kKeyMapMode) { + continue; + } + + var text = translate(mode.menu); + if (mode.key == _kKeyTranslateMode) { + text = '$text beta'; + } + list.add(_RadioMenuButton( + child: Text(text), + value: mode.key, + groupValue: groupValue, + onChanged: enabled ? onChanged : null, + ffi: ffi, + )); } return Column(children: list); }); From 3ba157c74eb02eb37c1feca2e6bf6973a803cf55 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Tue, 28 Mar 2023 21:30:45 +0200 Subject: [PATCH 084/366] Update pl.rs --- src/lang/pl.rs | 66 +++++++++++++++++++++++++------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 30459ffa0..852aa520d 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -37,10 +37,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Clipboard is empty", "Schowek jest pusty"), ("Stop service", "Zatrzymaj usługę"), ("Change ID", "Zmień ID"), - ("Your new ID", ""), - ("length %min% to %max%", ""), - ("starts with a letter", ""), - ("allowed characters", ""), + ("Your new ID", "Twój nowy ID"), + ("length %min% to %max%", "o długości od %min% do %max%"), + ("starts with a letter", "rozpoczyna się literą"), + ("allowed characters", "dozwolone znaki"), ("id_change_tip", "Nowy ID może być złożony z małych i dużych liter a-zA-z, cyfry 0-9 oraz _ (podkreślenie). Pierwszym znakiem powinna być litera a-zA-Z, a całe ID powinno składać się z 6 do 16 znaków."), ("Website", "Strona internetowa"), ("About", "O aplikacji"), @@ -213,7 +213,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Closed manually by the peer", "Połączenie zakończone ręcznie przez peer"), ("Enable remote configuration modification", "Włącz zdalną modyfikację konfiguracji"), ("Run without install", "Uruchom bez instalacji"), - ("Connect via relay", ""), + ("Connect via relay", "Połącz bezpośrednio"), ("Always connect via relay", "Zawsze łącz pośrednio"), ("whitelist_tip", "Zezwalaj na łączenie z tym komputerem tylko z adresów IP znajdujących się na białej liście"), ("Login", "Zaloguj"), @@ -288,8 +288,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "Włączenie opcji „Przechwytywanie ekranu” spowoduje automatyczne uruchomienie usługi, umożliwiając innym urządzeniom żądanie połączenia z Twoim urządzeniem."), ("android_stop_service_tip", "Zamknięcie usługi spowoduje automatyczne zamknięcie wszystkich nawiązanych połączeń."), ("android_version_audio_tip", "Bieżąca wersja systemu Android nie obsługuje przechwytywania dźwięku, zaktualizuj system do wersji Android 10 lub nowszej."), - ("android_start_service_tip", ""), - ("android_permission_may_not_change_tip", ""), + ("android_start_service_tip", "Kliknij [Uruchom serwis] lub włącz uprawnienia [Zrzuty ekranu], aby uruchomić usługę udostępniania ekranu."), + ("android_permission_may_not_change_tip", "Uprawnienia do nawiązanych połączeń nie mogą być zmieniane automatycznie, dopiero po ponownym połączeniu."), ("Account", "Konto"), ("Overwrite", "Nadpisz"), ("This file exists, skip or overwrite this file?", "Ten plik istnieje, pominąć czy nadpisać ten plik?"), @@ -348,7 +348,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Security", "Zabezpieczenia"), ("Theme", "Motyw"), ("Dark Theme", "Ciemny motyw"), - ("Light Theme", ""), + ("Light Theme", "Jasny motyw"), ("Dark", "Ciemny"), ("Light", "Jasny"), ("Follow System", "Zgodny z systemem"), @@ -421,7 +421,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("software_render_tip", "Jeżeli posiadasz kartę graficzną Nvidia i okno zamyka się natychmiast po nawiązaniu połączenia, instalacja sterownika nouveau i wybór renderowania programowego mogą pomóc. Restart aplikacji jest wymagany."), ("Always use software rendering", "Zawsze używaj renderowania programowego"), ("config_input", "By kontrolować zdalne urządzenie przy pomocy klawiatury, musisz udzielić aplikacji RustDesk uprawnień do \"Urządzeń Wejściowych\"."), - ("config_microphone", ""), + ("config_microphone", "Aby umozliwić zdalne rozmowy należy przyznać RuskDesk uprawnienia do \"Nagrań audio\"."), ("request_elevation_tip", "Możesz poprosić o podniesienie uprawnień jeżeli ktoś posiada dostęp do zdalnego urządzenia."), ("Wait", "Czekaj"), ("Elevation Error", "Błąd przy podnoszeniu uprawnień"), @@ -459,29 +459,29 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Codec", "Kodek"), ("Resolution", "Rozdzielczość"), ("No transfers in progress", "Brak transferów w toku"), - ("Set one-time password length", ""), - ("idd_driver_tip", ""), - ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), - ("empty_recent_tip", ""), - ("empty_favorite_tip", ""), - ("empty_lan_tip", ""), - ("empty_address_book_tip", ""), - ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), - ("Me", ""), - ("identical_file_tip", ""), - ("show_monitors_tip", ""), - ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("Set one-time password length", "Ustaw długość jednorazowego hasła"), + ("idd_driver_tip", "Zainstaluj sterownik wirtualnego wyświetlacza, który jest używany gdy nie masz fizycznych monitorów."), + ("confirm_idd_driver_tip", "Opcja instalacji sterownika wirtualnego wyświetlacza jest zaznaczona. Pamiętaj, że zostanie zainstalowany testowy certyfikat, aby zaufać wirtualnemu sterownikowi. Ten certyfikat będzie używany tylko do weryfikacji sterowników RustDesk."), + ("RDP Settings", "Ustawienia RDP"), + ("Sort by", "Sortuj wg"), + ("New Connection", "Nowe połączenie"), + ("Restore", "Przywróć"), + ("Minimize", "Minimalizuj"), + ("Maximize", "Maksymalizuj"), + ("Your Device", "Twoje urządzenie"), + ("empty_recent_tip", "Ups, żadnych nowych sesji!\nCzas zaplanować nową."), + ("empty_favorite_tip", "Brak ulubionych?\nZnajdźmy kogoś, z kim możesz się połączyć i dodaj Go do ulubionych!"), + ("empty_lan_tip", "Ojej, wygląda na to że odkryliśmy żadnych urządzeń z RustDesk w Twojej sieci."), + ("empty_address_book_tip", "Ojej, wygląda na to że nie ma żadnych wpisów w Twojej książce adresowej."), + ("eg: admin", "np. admin"), + ("Empty Username", "Pusty użytkownik"), + ("Empty Password", "Puste hasło"), + ("Me", "Ja"), + ("identical_file_tip", "Ten plik jest identyczny jak plik na drugim komputerze."), + ("show_monitors_tip", "Pokaż monitory w zasobniku."), + ("View Mode", "Tryb widoku"), + ("enter_rustdesk_passwd_tip", "Podaj hasło RustDesk."), + ("remember_rustdesk_passwd_tip", "Zapamiętaj hasło RustDesk."), + ("login_linux_tip", "Zaloguj do zdalnego konta Linux"), ].iter().cloned().collect(); } From c1a302c3f3532b67f067d2e1113e8195fd7b2eb8 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Tue, 28 Mar 2023 21:48:46 +0200 Subject: [PATCH 085/366] Update README-PL.md --- docs/README-PL.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/README-PL.md b/docs/README-PL.md index df8254f3d..27da5f02e 100644 --- a/docs/README-PL.md +++ b/docs/README-PL.md @@ -1,5 +1,5 @@

- RustDesk - Your remote desktop
+ RustDesk - Twój zdalny pulpit
SerweryKompilacjaDocker • @@ -13,7 +13,7 @@ Porozmawiaj z nami na: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](http [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) -Kolejny program do zdalnego pulpitu, napisany w Rust. Działa od samego początku, nie wymaga konfiguracji. Masz pełną kontrolę nad swoimi danymi, bez obaw o bezpieczeństwo. Możesz skorzystać z naszego darmowego serwera publicznego , [skonfigurować własny](https://rustdesk.com/server), lub [napisać własny serwer rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). +Kolejny program do zdalnego pulpitu, napisany w Rust. Działa od samego początku, nie wymaga konfiguracji. Masz pełną kontrolę nad swoimi danymi, bez obaw o bezpieczeństwo. Możesz skorzystać z naszego darmowego serwera publicznego , [skonfigurować własny](https://rustdesk.com/server), lub [napisać własny serwer](https://github.com/rustdesk/rustdesk-server-demo). RustDesk zaprasza do współpracy każdego. Zobacz [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) pomoc w uruchomieniu programu. @@ -24,16 +24,16 @@ RustDesk zaprasza do współpracy każdego. Zobacz [`docs/CONTRIBUTING.md`](CONT Poniżej znajdują się serwery, z których można korzystać za darmo, może się to zmienić z upływem czasu. Jeśli nie znajdujesz się w pobliżu jednego z nich, Twoja prędkość połączenia może być niska. | Lokalizacja | Dostawca | Specyfikacja | | --------- | ------------- | ------------------ | -| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM | -| Germany | Hetzner | 2 vCPU / 4GB RAM | -| Germany | Codext | 4 vCPU / 8GB RAM | -| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM | +| Korea Płd. (Seul) | AWS lightsail | 1 vCPU / 0.5GB RAM | +| Niemcy | Hetzner | 2 vCPU / 4GB RAM | +| Niemcy | Codext | 4 vCPU / 8GB RAM | +| Finlandia (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM | | USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM | -| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | +| Ukraina (Kijów) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | ## Zależności -Wersje desktopowe używają [sciter](https://sciter.com/) dla GUI, proszę pobrać bibliotekę dynamiczną sciter samodzielnie. +Wersje desktopowe używają [sciter](https://sciter.com/) dla GUI, proszę pobrać samodzielnie bibliotekę sciter. [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) | @@ -43,7 +43,7 @@ Wersje desktopowe używają [sciter](https://sciter.com/) dla GUI, proszę pobra - Przygotuj środowisko programistyczne Rust i środowisko programowania C++ -- Zainstaluj [vcpkg](https://github.com/microsoft/vcpkg), i ustaw `VCPKG_ROOT` env zmienną prawidłowo +- Zainstaluj [vcpkg](https://github.com/microsoft/vcpkg), i ustaw prawidłowo zmienną `VCPKG_ROOT` - Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static - Linux/MacOS: vcpkg install libvpx libyuv opus @@ -82,7 +82,7 @@ export VCPKG_ROOT=$HOME/vcpkg vcpkg/vcpkg install libvpx libyuv opus ``` -### Fix libvpx (For Fedora) +### Popraw libvpx (Dla Fedora) ```sh cd vcpkg/buildtrees/libvpx/src @@ -110,7 +110,7 @@ cargo run ### Zmień Wayland na X11 (Xorg) -RustDesk nie obsługuje Waylanda. Sprawdź [this](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) by skonfigurować Xorg jako domyślną sesję GNOME. +RustDesk nie obsługuje Waylanda. Sprawdź [tutaj](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/), jak skonfigurować Xorg jako domyślną sesję GNOME. ## Jak kompilować za pomocą Dockera @@ -134,7 +134,7 @@ Zauważ, że pierwsza kompilacja może potrwać dłużej zanim zależności zost target/debug/rustdesk ``` -Lub, jeśli uruchamiasz plik wykonywalny wersji: +Lub jeśli uruchamiasz plik wykonywalny wersji: ```sh target/release/rustdesk @@ -144,16 +144,16 @@ Upewnij się, że uruchamiasz te polecenia z katalogu głównego repozytorium Ru ## Struktura plików -- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: kodek wideo, config, wrapper tcp/udp, protobuf, funkcje fs do transferu plików i kilka innych funkcji użytkowych +- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: kodek wideo, konfiguracja, obsługa tcp/udp, protobuf, funkcje systemu plików do transferu plików i kilka innych funkcji użytkowych - **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: przechwytywanie ekranu - **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: specyficzne dla danej platformy sterowanie klawiaturą/myszą - **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI - **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: audio/schowek/wejście(input)/wideo oraz połączenia sieciowe -- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: uruchamia połączenie peer -- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Komunikacja z [rustdesk-server](https://github.com/rustdesk/rustdesk-server), wait for remote direct (TCP hole punching) or relayed connection -- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: specyficzny dla danej platformy kod +- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: uruchamia połączenie bezpośrednie +- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Komunikacja z [rustdesk-server](https://github.com/rustdesk/rustdesk-server), czekanie na bezpośrednie (odpytywanie TCP) lub przekazywane połączenie +- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: kod specyficzny dla danej platformy -## Migawki(Snapshoty) +## Zrzuty ekranu ![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png) From 46ed7c4d2ae7303d46b5a677f2e566a193b7d626 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Tue, 28 Mar 2023 21:55:28 +0200 Subject: [PATCH 086/366] Create SECURITY-PL.md --- docs/SECURITY-PL.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 docs/SECURITY-PL.md diff --git a/docs/SECURITY-PL.md b/docs/SECURITY-PL.md new file mode 100644 index 000000000..0d4975ba0 --- /dev/null +++ b/docs/SECURITY-PL.md @@ -0,0 +1,9 @@ +# Polityka bezpieczeństwa + +## Zgłaszanie podatności + +Bardzo cenimy sobie bezpieczeństwo projektu. Zachęcamy wszystkich użytkowników do zgłaszania nam wszelkich wykrytych luk. +Jeżeli znajdziesz lukę w projekcie RustDesk, proszę zgłosić ją jak najszybciej wysyłając e-mail na adres info@rustdesk.com. + +W tym momencie, nie mamy uruchomionego programu nagradzania za wykryte błędy. Jesteśmy małym zespołem próbującym rozwiązywać duże problemy. +Prosimy o odpowidzialne zgłaszanie wszelkich podatności w zabezpieczeniach, abyśmy mogli kontynuować tworzenie bezpiecznej aplikacji dla całej społeczności. From b3f9d8bdc87dc8f994f778e619294cf4811040ee Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Tue, 28 Mar 2023 22:10:18 +0200 Subject: [PATCH 087/366] Create DEVCONTAINER-PL.md --- docs/DEVCONTAINER-PL.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docs/DEVCONTAINER-PL.md diff --git a/docs/DEVCONTAINER-PL.md b/docs/DEVCONTAINER-PL.md new file mode 100644 index 000000000..0aae2b975 --- /dev/null +++ b/docs/DEVCONTAINER-PL.md @@ -0,0 +1,14 @@ + +Po uruchomieniu devcontainer w kontenerze docker, tworzony jest plik binarny linux w trybue debugowania. + +Obecnie devcontainer oferuje kompilowanie wersji dla linux i android w obu trybach - debugowania i wersji finalnej. + +Poniżej tabela poleceń do uruchomienia z głównego folderu do tworzenia wybranych kompilacji. + +Polecenie|Typ kompilacji|Tryb +-|-|-| +`.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|debug + From bd7901af001ea57e17e89d3fecb4df14977593d0 Mon Sep 17 00:00:00 2001 From: asur4s Date: Tue, 28 Mar 2023 21:49:22 -0700 Subject: [PATCH 088/366] Release modifiers when exit --- src/platform/mod.rs | 3 ++- src/server/connection.rs | 1 + src/server/input_service.rs | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index c30083155..04a3feeee 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -35,7 +35,8 @@ pub fn is_xfce() -> bool { pub fn breakdown_callback() { #[cfg(target_os = "linux")] - crate::input_service::clear_remapped_keycode() + crate::input_service::clear_remapped_keycode(); + crate::input_service::release_modifiers(); } // Android diff --git a/src/server/connection.rs b/src/server/connection.rs index ed7476284..6b0cbefc3 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -618,6 +618,7 @@ impl Connection { } #[cfg(target_os = "linux")] clear_remapped_keycode(); + release_modifiers(); log::info!("Input thread exited"); } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index b362c8d30..49fd1e188 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -551,6 +551,24 @@ fn record_key_to_key(record_key: u64) -> Option { } } +pub fn release_modifiers() { + let mut en = ENIGO.lock().unwrap(); + for modifier in [ + Key::Shift, + Key::Control, + Key::Alt, + Key::Meta, + Key::RightShift, + Key::RightControl, + Key::RightAlt, + Key::RWin, + ] { + if get_modifier_state(modifier, &mut en) { + en.key_up(modifier); + } + } +} + #[inline] fn release_record_key(record_key: KeysDown) { let func = move || match record_key { From c30479f02fa57012a790542e199f0a9918c9caea Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 27 Mar 2023 10:50:24 +0800 Subject: [PATCH 089/366] feat: add sciter nightly build --- .github/workflows/flutter-ci.yml | 23 +++++++++- .github/workflows/flutter-nightly.yml | 60 ++++++++++++++++++++++++++- .github/workflows/sciter.yml | 53 +++++++++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/sciter.yml diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index ff3f29ee7..5525586de 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -26,7 +26,7 @@ env: NDK_VERSION: "r23" jobs: - build-for-windows: + build-for-windows-flutter: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) runs-on: ${{ matrix.job.os }} strategy: @@ -92,6 +92,27 @@ jobs: - name: Build rustdesk run: python3 .\build.py --portable --hwcodec --flutter + + # The fallback for the flutter version, we use Sciter for 32bit Windows. + build-for-windows-sciter: + name: ${{ matrix.job.target }} (${{ matrix.job.os }}) + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + # - { target: i686-pc-windows-msvc , os: windows-2019 } + # - { target: x86_64-pc-windows-gnu , os: windows-2019 } + - { target: i686-pc-windows-msvc, os: windows-2019 } + # - { target: aarch64-pc-windows-msvc, os: windows-2019 } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Build rustdesk sciter + uses: ./.github/workflows/sciter.yml + with: + target: ${{ matrix.job.target }} build-for-macOS: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}] diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index b9fcb6bb4..183c39d68 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -23,7 +23,7 @@ env: RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}' jobs: - build-for-windows: + build-for-windows-flutter: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) runs-on: ${{ matrix.job.os }} strategy: @@ -134,6 +134,64 @@ jobs: files: | ./SignOutput/rustdesk-*.exe + # The fallback for the flutter version, we use Sciter for 32bit Windows. + build-for-windows-sciter: + name: ${{ matrix.job.target }} (${{ matrix.job.os }}) + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + # - { target: i686-pc-windows-msvc , os: windows-2019 } + # - { target: x86_64-pc-windows-gnu , os: windows-2019 } + - { target: i686-pc-windows-msvc, os: windows-2019 } + # - { target: aarch64-pc-windows-msvc, os: windows-2019 } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Build rustdesk sciter + uses: ./.github/workflows/sciter.yml + with: + target: ${{ matrix.job.target }} + + - name: Sign rustdesk files + uses: GermanBluefox/code-sign-action@v7 + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.CERTNAME }}' + folder: './Release/' + recursive: true + + - name: Build self-extracted executable + shell: bash + run: | + pushd ./libs/portable + python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe + popd + mkdir -p ./SignOutput + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe + + - name: Sign rustdesk self-extracted file + uses: GermanBluefox/code-sign-action@v7 + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' + folder: './SignOutput' + recursive: false + + - name: Publish Release + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./SignOutput/rustdesk-*.exe + build-for-macOS: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}] runs-on: ${{ matrix.job.os }} diff --git a/.github/workflows/sciter.yml b/.github/workflows/sciter.yml new file mode 100644 index 000000000..cc0c801d2 --- /dev/null +++ b/.github/workflows/sciter.yml @@ -0,0 +1,53 @@ +name: Build Sciter + +on: + workflow_call: + inputs: + target: + required: true + type: string + outputs: + output_folder: + description: "The relative directory of the executable folder which contains all dependencies for RustDesk." + value: ${{ jobs.build-sciter.outputs.output_folder }} +jobs: + build-sciter: + runs-on: windows-2019 + outputs: + output_folder: ${{ steps.build.outputs.output_folder }} + steps: + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ inputs.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }}-sciter + + - name: Restore from cache and install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + $VCPKG_ROOT/vcpkg install libvpx:x86-windows-static libyuv:x86-windows-static opus:x86-windows-static + shell: bash + + - name: Build rustdesk + id: build + shell: bash + run: | + python3 res/inline-sciter.py + # Replace the link for the ico. + cp flutter/windows/runner/resources/app_icon.ico res/icon.ico + cargo build --features inline --target=${{ matrix.job.target }} --release + mkdir -p ./Release + mv ./target/release/rustdesk.exe ./Release/rustdesk.exe + wget -O ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll + echo "output_folder=./Release" >> $GITHUB_OUTPUT From 260694fdaae359d3738399b9c5985453a02cdd13 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 27 Mar 2023 11:21:23 +0800 Subject: [PATCH 090/366] add: nearby dll check --- .github/workflows/flutter-ci.yml | 72 ++++++++++++++++++++++++++- .github/workflows/flutter-nightly.yml | 35 ++++++++++++- .github/workflows/sciter.yml | 53 -------------------- src/ui.rs | 12 +++++ 4 files changed, 115 insertions(+), 57 deletions(-) delete mode 100644 .github/workflows/sciter.yml diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index 5525586de..428d538bf 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -109,10 +109,78 @@ jobs: - name: Checkout source code uses: actions/checkout@v3 - - name: Build rustdesk sciter - uses: ./.github/workflows/sciter.yml + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 with: + toolchain: stable target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }}-sciter + + - name: Restore from cache and install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + $VCPKG_ROOT/vcpkg install libvpx:x86-windows-static libyuv:x86-windows-static opus:x86-windows-static + shell: bash + + - name: Build rustdesk + id: build + shell: bash + run: | + python3 res/inline-sciter.py + # Replace the link for the ico. + rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico + cargo build --features inline --target=${{ matrix.job.target }} --release + mkdir -p ./Release + mv ./target/release/rustdesk.exe ./Release/rustdesk.exe + wget -O ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll + echo "output_folder=./Release" >> $GITHUB_OUTPUT + + - name: Sign rustdesk files + uses: GermanBluefox/code-sign-action@v7 + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.CERTNAME }}' + folder: './Release/' + recursive: true + + - name: Build self-extracted executable + shell: bash + run: | + pushd ./libs/portable + python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe + popd + mkdir -p ./SignOutput + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe + + - name: Sign rustdesk self-extracted file + uses: GermanBluefox/code-sign-action@v7 + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' + folder: './SignOutput' + recursive: false + + - name: Publish Release + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./SignOutput/rustdesk-*.exe build-for-macOS: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}] diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 183c39d68..e9fd70568 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -150,10 +150,41 @@ jobs: - name: Checkout source code uses: actions/checkout@v3 - - name: Build rustdesk sciter - uses: ./.github/workflows/sciter.yml + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 with: + toolchain: stable target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }}-sciter + + - name: Restore from cache and install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + $VCPKG_ROOT/vcpkg install libvpx:x86-windows-static libyuv:x86-windows-static opus:x86-windows-static + shell: bash + + - name: Build rustdesk + id: build + shell: bash + run: | + python3 res/inline-sciter.py + # Replace the link for the ico. + rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico + cargo build --features inline --target=${{ matrix.job.target }} --release + mkdir -p ./Release + mv ./target/release/rustdesk.exe ./Release/rustdesk.exe + wget -O ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll + echo "output_folder=./Release" >> $GITHUB_OUTPUT - name: Sign rustdesk files uses: GermanBluefox/code-sign-action@v7 diff --git a/.github/workflows/sciter.yml b/.github/workflows/sciter.yml deleted file mode 100644 index cc0c801d2..000000000 --- a/.github/workflows/sciter.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Build Sciter - -on: - workflow_call: - inputs: - target: - required: true - type: string - outputs: - output_folder: - description: "The relative directory of the executable folder which contains all dependencies for RustDesk." - value: ${{ jobs.build-sciter.outputs.output_folder }} -jobs: - build-sciter: - runs-on: windows-2019 - outputs: - output_folder: ${{ steps.build.outputs.output_folder }} - steps: - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ inputs.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }}-sciter - - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - $VCPKG_ROOT/vcpkg install libvpx:x86-windows-static libyuv:x86-windows-static opus:x86-windows-static - shell: bash - - - name: Build rustdesk - id: build - shell: bash - run: | - python3 res/inline-sciter.py - # Replace the link for the ico. - cp flutter/windows/runner/resources/app_icon.ico res/icon.ico - cargo build --features inline --target=${{ matrix.job.target }} --release - mkdir -p ./Release - mv ./target/release/rustdesk.exe ./Release/rustdesk.exe - wget -O ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll - echo "output_folder=./Release" >> $GITHUB_OUTPUT diff --git a/src/ui.rs b/src/ui.rs index f7419cd34..8f9d14c54 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -54,6 +54,18 @@ pub fn start(args: &mut [String]) { let dir = "/usr"; sciter::set_library(&(prefix + dir + "/lib/rustdesk/libsciter-gtk.so")).ok(); } + #[cfg(windows)] + // Check if there is a sciter.dll nearby. + if let Ok(exe) = std::env::current_exe() { + if let Some(parent) = exe.parent() { + let sciter_dll_path = parent.join("sciter.dll"); + if sciter_dll_path.exists() { + // Try to set the sciter dll. + let p = sciter_dll_path.to_string_lossy().to_string(); + println!("Found dll:{}, \n {:?}", p, sciter::set_library(&p)); + } + } + } // https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-types.h // https://github.com/rustdesk/rustdesk/issues/132#issuecomment-886069737 #[cfg(windows)] From b8d8bf0a09061043f2b1d98a4ad5bafcf3c2fc43 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 27 Mar 2023 15:26:20 +0800 Subject: [PATCH 091/366] opt: use cross to build armv7 --- .github/workflows/flutter-nightly.yml | 100 +++++++++++++++++++++++-- .github/workflows/vcpkg-deps-linux.yml | 36 ++++----- Cargo.lock | 4 +- Cross.toml | 21 ++++++ build.py | 51 ++++++++++++- res/install-rust-deps-arm.sh | 11 +++ 6 files changed, 190 insertions(+), 33 deletions(-) create mode 100644 Cross.toml create mode 100644 res/install-rust-deps-arm.sh diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index e9fd70568..f798459e1 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -182,7 +182,7 @@ jobs: rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico cargo build --features inline --target=${{ matrix.job.target }} --release mkdir -p ./Release - mv ./target/release/rustdesk.exe ./Release/rustdesk.exe + mv ./target/${{ matrix.job.target }}/release/rustdesk.exe ./Release/rustdesk.exe wget -O ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll echo "output_folder=./Release" >> $GITHUB_OUTPUT @@ -716,7 +716,7 @@ jobs: # use-cross: true, # extra-build-features: "", # } - # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } + # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } steps: - name: Maximize build space @@ -846,11 +846,11 @@ jobs: cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release ;; armv7) - cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/ - cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/ - mkdir -p /vcpkg/installed/arm-linux - ln -s /usr/lib/arm-linux-gnueabihf /vcpkg/installed/arm-linux/lib - ln -s /usr/include /vcpkg/installed/arm-linux/include + # cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/ + # cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/ + # mkdir -p /vcpkg/installed/arm-linux + # ln -s /usr/lib/arm-linux-gnueabihf /vcpkg/installed/arm-linux/lib + # ln -s /usr/include /vcpkg/installed/arm-linux/include export VCPKG_ROOT=/vcpkg # disable hwcodec for compilation cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release @@ -863,6 +863,90 @@ jobs: name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so path: target/release/liblibrustdesk.so + build-rustdesk-sciter-arm: + needs: [build-vcpkg-deps-linux] + name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + # use a high level qemu-user-static + job: + - { + arch: armv7, + target: arm-unknown-linux-gnueabihf, + deb-arch: armhf, + os: ubuntu-latest, + use-cross: true, + extra-build-features: "", + } + # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } + # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } + steps: + + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + cache-directories: "/opt/rust-registry" + + - name: Restore vcpkg files + uses: actions/download-artifact@master + with: + name: vcpkg-artifact-${{ matrix.job.arch }} + path: /opt/artifacts/vcpkg/installed + + - name: Install cross tool + run: | + wget https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-gnu.tar.gz + tar -zxvf cross-x86_64-unknown-linux-gnu.tar.gz + mv cross* /usr/local/bin/ + + - name: Build RustDesk + env: + ARCH: ${{ matrix.job.deb-arch }} + VCPKG_ROOT: /opt/artifacts/vcpkg + run: | + python3 ./res/inline-sciter.py + cross build --target ${{ matrix.job.target }} --features inline --release + wget -O ./target/${{ matrix.job.target }}/release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/blob/master/bin.lnx/arm32/libsciter-gtk.so + ./build.py --package ./target/${{ matrix.job.target }}/release + + - name: Rename rustdesk + shell: bash + run: | + for name in rustdesk*??.deb; do + # use cp to duplicate deb files to fit other packages. + cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + done + + - name: Publish debian package + if: ${{ matrix.job.extra-build-features == '' }} + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Upload Artifact + uses: actions/upload-artifact@master + if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + build-rustdesk-linux-arm: needs: [build-rustdesk-lib-linux-arm] name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] @@ -893,7 +977,7 @@ jobs: # extra-build-features: "flatpak", # } # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" } - # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } + # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } steps: - name: Checkout source code diff --git a/.github/workflows/vcpkg-deps-linux.yml b/.github/workflows/vcpkg-deps-linux.yml index 89381114a..8eee01011 100644 --- a/.github/workflows/vcpkg-deps-linux.yml +++ b/.github/workflows/vcpkg-deps-linux.yml @@ -10,7 +10,7 @@ jobs: fail-fast: true matrix: job: - # - { arch: armv7, os: ubuntu-20.04 } + - { arch: armv7, os: ubuntu-20.04 } - { arch: x86_64, os: ubuntu-20.04 } - { arch: aarch64, os: ubuntu-20.04 } steps: @@ -46,12 +46,12 @@ jobs: wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null apt update -y apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev + cmake --version + gcc -v ;; aarch64|armv7) - apt install -y curl zip unzip tar git cmake g++ gcc build-essential pkg-config wget nasm yasm ninja-build libjpeg8-dev automake libtool + apt install -y curl zip unzip git esac - cmake --version - gcc -v run: | # disable git safe.directory git config --global --add safe.directory "*" @@ -65,25 +65,19 @@ jobs: ./bootstrap-vcpkg.sh ./vcpkg install libvpx libyuv opus ;; - aarch64|armv7) + aarch64) pushd /artifacts - # libyuv - git clone https://chromium.googlesource.com/libyuv/libyuv || true - pushd libyuv - git pull - mkdir -p build - pushd build + rm -rf rustdesk_thirdparty_lib + git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 mkdir -p /artifacts/vcpkg/installed - cmake .. -DCMAKE_INSTALL_PREFIX=/artifacts/vcpkg/installed - make -j4 && make install - popd - popd - # libopus, ubuntu 18.04 prebuilt is not be compiled with -fPIC - wget -O opus.tar.gz http://archive.ubuntu.com/ubuntu/pool/main/o/opus/opus_1.1.2.orig.tar.gz - tar -zxvf opus.tar.gz; ls -l - pushd opus-1.1.2 - ./autogen.sh; ./configure --prefix=/artifacts/vcpkg/installed - make -j4; make install + mv ./rustdesk_thirdparty_lib/vcpkg/installed/arm64-linux /artifacts/vcpkg/installed/arm64-linux + ;; + armv7) + pushd /artifacts + rm -rf rustdesk_thirdparty_lib + git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 + mkdir -p /artifacts/vcpkg/installed + mv ./rustdesk_thirdparty_lib/vcpkg/installed/arm-linux /artifacts/vcpkg/installed/arm-linux ;; esac - name: Upload artifacts diff --git a/Cargo.lock b/Cargo.lock index 6fdb513e4..908f32a8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4674,7 +4674,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#89d2cb5c4bac81da4aafaedcf78af6af7c80c9d0" +source = "git+https://github.com/fufesou/rdev#aeea78dec3e731dd616da09668bbbd835a773d6c" dependencies = [ "cocoa", "core-foundation 0.9.3", @@ -5885,7 +5885,7 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "tfc" version = "0.6.1" -source = "git+https://github.com/fufesou/The-Fat-Controller#1dba9a39c089ac9a7853b9dd5399c1d4aa3157d3" +source = "git+https://github.com/fufesou/The-Fat-Controller#9dd86151525fd010dc93f6bc9b6aedd1a75cc342" dependencies = [ "anyhow", "core-graphics 0.22.3", diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 000000000..952e753a2 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,21 @@ +[build] +build-std = false # do not build the std library. has precedence over xargo +xargo = true # enable the use of xargo by default +# zig = false # do not use zig cc for the builds +default-target = "x86_64-unknown-linux-gnu" # use this target if none is explicitly provided + +[build.env] +volumes = ["VCPKG_ROOT"] # "VOL2_ARG=/path/to/volume" +passthrough = ["VCPKG_ROOT"] + +[target.aarch64-unknown-linux-gnu] +# pre-build = ["env ARCH=arm64 ./res/install-rust-deps.sh"] +# build-std = false # always build the std library. has precedence over xargo +# xargo = false # disable the use of xargo +# image = "test-image" # use a different image for the target +# runner = "qemu-user" # wrapper to run the binary (must be `qemu-system`, `qemu-user`, or `native`). + +[target.arm-unknown-linux-gnueabihf] +pre-build = "./res/install-rust-deps-arm.sh" +# volumes = ["VCPKG_ROOT"] # "VOL2_ARG=/path/to/volume" +# passthrough = ["VCPKG_ROOT"] \ No newline at end of file diff --git a/build.py b/build.py index 4a39f596d..7acf97a7d 100755 --- a/build.py +++ b/build.py @@ -18,6 +18,12 @@ exe_path = 'target/release/' + hbb_name flutter_win_target_dir = 'flutter/build/windows/runner/Release/' skip_cargo = False +def get_arch() -> str: + custom_arch = os.environ.get("ARCH") + if custom_arch is None: + return "amd64" + return custom_arch + def system2(cmd): err = os.system(cmd) if err != 0: @@ -106,6 +112,10 @@ def make_parser(): action='store_true', help='Skip cargo build process, only flutter version + Linux supported currently' ) + parser.add_argument( + "--package", + type=str + ) return parser @@ -251,13 +261,13 @@ def generate_control_file(version): content = """Package: rustdesk Version: %s -Architecture: amd64 +Architecture: %s Maintainer: open-trade Homepage: https://rustdesk.com Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0 Description: A remote control software. -""" % version +""" % (version, get_arch()) file = open(control_file_path, "w") file.write(content) file.close() @@ -307,6 +317,39 @@ def build_flutter_deb(version, features): os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version) os.chdir("..") +def build_deb_from_folder(version, binary_folder): + os.chdir('flutter') + system2('mkdir -p tmpdeb/usr/bin/') + system2('mkdir -p tmpdeb/usr/lib/rustdesk') + system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/') + system2('mkdir -p tmpdeb/usr/share/applications/') + system2('mkdir -p tmpdeb/usr/share/polkit-1/actions') + system2('rm tmpdeb/usr/bin/rustdesk || true') + system2( + f'cp -r ../{binary_folder}/* tmpdeb/usr/lib/rustdesk/') + system2( + 'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/') + system2( + 'cp ../res/128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png') + system2( + 'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop') + system2( + 'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop') + system2( + 'cp ../res/com.rustdesk.RustDesk.policy tmpdeb/usr/share/polkit-1/actions/') + system2( + "echo \"#!/bin/sh\" >> tmpdeb/usr/share/rustdesk/files/polkit && chmod a+x tmpdeb/usr/share/rustdesk/files/polkit") + + system2('mkdir -p tmpdeb/DEBIAN') + generate_control_file(version) + system2('cp -a ../res/DEBIAN/* tmpdeb/DEBIAN/') + md5_file('usr/share/rustdesk/files/systemd/rustdesk.service') + system2('dpkg-deb -b tmpdeb rustdesk.deb;') + + system2('/bin/rm -rf tmpdeb/') + system2('/bin/rm -rf ../res/DEBIAN/control') + os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version) + os.chdir("..") def build_flutter_dmg(version, features): if not skip_cargo: @@ -381,6 +424,10 @@ def main(): if args.skip_cargo: skip_cargo = True portable = args.portable + package = args.package + if package: + build_deb_from_folder(version, package) + return if windows: # build virtual display dynamic library os.chdir('libs/virtual_display/dylib') diff --git a/res/install-rust-deps-arm.sh b/res/install-rust-deps-arm.sh new file mode 100644 index 000000000..14cf772fd --- /dev/null +++ b/res/install-rust-deps-arm.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# apt-get install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev pkg-config g++ gcc libvpx-dev + +# For developers in China, we use sources from ustc. +# sed -i 's/archive.archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list +# sed -i 's/security.archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list + +dpkg --add-architecture armhf + +apt update -y +apt install -y libdbus-1-dev:armhf pkg-config nasm yasm libglib2.0-dev:armhf libxcb-randr0-dev:armhf libxdo-dev:armhf libxfixes-dev:armhf libxcb-shape0-dev:armhf libxcb-xfixes0-dev:armhf libasound2-dev:armhf libpulse-dev:armhf libgstreamer1.0-dev:armhf libgstreamer-plugins-base1.0-dev:armhf libappindicator3-dev:armhf libvpx-dev:armhf libvdpau-dev:armhf libva-dev:armhf libgtk-3-dev:armhf clang gcc libclang-dev From 67821f1d42b9f6b007f6b8ca6bf2af6576b8c40c Mon Sep 17 00:00:00 2001 From: Kingtous Date: Tue, 28 Mar 2023 00:54:09 +0800 Subject: [PATCH 092/366] fix: change to run on arch opt: use windows latest for sciter --- .github/workflows/flutter-nightly.yml | 136 +++++++++++++++++--------- 1 file changed, 91 insertions(+), 45 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index f798459e1..642869da1 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -180,7 +180,7 @@ jobs: python3 res/inline-sciter.py # Replace the link for the ico. rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico - cargo build --features inline --target=${{ matrix.job.target }} --release + cargo build --features inline --target=${{ matrix.job.target }} --release --bins mkdir -p ./Release mv ./target/${{ matrix.job.target }}/release/rustdesk.exe ./Release/rustdesk.exe wget -O ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll @@ -711,12 +711,12 @@ jobs: # - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } # - { # arch: armv7, - # target: arm-unknown-linux-gnueabihf, + # target: armv7-unknown-linux-gnueabihf, # os: ubuntu-20.04, # use-cross: true, # extra-build-features: "", # } - # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "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 } steps: - name: Maximize build space @@ -832,30 +832,8 @@ jobs: cat ~/.cargo/config # start build pushd /workspace - # mock - case "${{ matrix.job.arch }}" in - aarch64) - cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/aarch64-linux-gnu/ - cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/ - ls -l /opt/artifacts/vcpkg/installed/lib/ - mkdir -p /vcpkg/installed/arm64-linux - ln -s /usr/lib/aarch64-linux-gnu /vcpkg/installed/arm64-linux/lib - ln -s /usr/include /vcpkg/installed/arm64-linux/include - export VCPKG_ROOT=/vcpkg - # disable hwcodec for compilation - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release - ;; - armv7) - # cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/ - # cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/ - # mkdir -p /vcpkg/installed/arm-linux - # ln -s /usr/lib/arm-linux-gnueabihf /vcpkg/installed/arm-linux/lib - # ln -s /usr/include /vcpkg/installed/arm-linux/include - export VCPKG_ROOT=/vcpkg - # disable hwcodec for compilation - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release - ;; - esac + export VCPKG_ROOT=/opt/artifacts/vcpkg + cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release - name: Upload Artifacts uses: actions/upload-artifact@master @@ -874,19 +852,36 @@ jobs: job: - { arch: armv7, - target: arm-unknown-linux-gnueabihf, + target: armv7-unknown-linux-gnueabihf, deb-arch: armhf, os: ubuntu-latest, use-cross: true, extra-build-features: "", } - # - { arch: armv7, target: arm-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 } steps: + - name: Maximize build space + run: | + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo apt update -y + sudo apt install qemu-user-static + - name: Checkout source code uses: actions/checkout@v3 + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 12 + + - name: Free Space + run: | + df + - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: @@ -901,27 +896,78 @@ jobs: key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} cache-directories: "/opt/rust-registry" + - name: Install local registry + run: | + mkdir -p /opt/rust-registry + cargo install cargo-local-registry + + - name: Build local registry + uses: nick-fields/retry@v2 + id: build-local-registry + continue-on-error: true + with: + max_attempts: 3 + timeout_minutes: 15 + retry_on: error + command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry + - name: Restore vcpkg files uses: actions/download-artifact@master with: name: vcpkg-artifact-${{ matrix.job.arch }} path: /opt/artifacts/vcpkg/installed - - name: Install cross tool - run: | - wget https://github.com/cross-rs/cross/releases/download/v0.2.5/cross-x86_64-unknown-linux-gnu.tar.gz - tar -zxvf cross-x86_64-unknown-linux-gnu.tar.gz - mv cross* /usr/local/bin/ + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk sciter binary for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/rust-registry:/opt/rust-registry" + shell: /bin/bash + install: | + apt update -y + apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm libclang-dev + apt-get -qq install -y libdbus-1-dev pkg-config nasm yasm libglib2.0-dev libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev + apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # rust + pushd /opt + wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz + tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz + cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh + rm -rf rust-1.64.0-${{ matrix.job.target }} + # edit config + mkdir -p ~/.cargo/ + echo """ + [source.crates-io] + registry = 'https://github.com/rust-lang/crates.io-index' + replace-with = 'local-registry' - - name: Build RustDesk - env: - ARCH: ${{ matrix.job.deb-arch }} - VCPKG_ROOT: /opt/artifacts/vcpkg - run: | - python3 ./res/inline-sciter.py - cross build --target ${{ matrix.job.target }} --features inline --release - wget -O ./target/${{ matrix.job.target }}/release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/blob/master/bin.lnx/arm32/libsciter-gtk.so - ./build.py --package ./target/${{ matrix.job.target }}/release + [source.local-registry] + local-registry = '/opt/rust-registry/' + """ > ~/.cargo/config + cat ~/.cargo/config + + # build + pushd /workspace + python3 ./res/inline-sciter.py + export VCPKG_ROOT=/opt/artifacts/vcpkg + export ARCH=armhf + cargo build --features inline --release --bins + # package + mkdir -p ./Release + mv ./target/release/rustdesk ./Release/rustdesk + wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/arm32/libsciter-gtk.so + ./build.py --package ./Release - name: Rename rustdesk shell: bash @@ -976,8 +1022,8 @@ jobs: # use-cross: true, # extra-build-features: "flatpak", # } - # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" } - # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } steps: - name: Checkout source code From fffeda30a30a6f34cb8ddce6286c580f2a22b4d8 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Tue, 28 Mar 2023 17:08:51 +0800 Subject: [PATCH 093/366] feat: reuse nightly --- .github/workflows/flutter-ci.yml | 898 +------------------------- .github/workflows/flutter-nightly.yml | 41 +- 2 files changed, 35 insertions(+), 904 deletions(-) diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index 428d538bf..14027f9f8 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -16,897 +16,9 @@ on: - "docs/**" - "README.md" -env: - LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.7.5" - # vcpkg version: 2022.05.10 - # for multiarch gcc compatibility - VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44" - VERSION: "1.2.0" - NDK_VERSION: "r23" - jobs: - build-for-windows-flutter: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: true - matrix: - job: - # - { target: i686-pc-windows-msvc , os: windows-2019 } - # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: x86_64-pc-windows-msvc, os: windows-2019 } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install LLVM and Clang - uses: KyleMayes/install-llvm-action@v1 - with: - version: ${{ env.LLVM_VERSION }} - - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - name: Replace engine with rustdesk custom flutter engine - run: | - flutter doctor -v - flutter precache --windows - Invoke-WebRequest -Uri https://github.com/Kingtous/engine/releases/download/v3.7.0-rustdesk/windows-x64-release-flutter.zip -OutFile windows-x64-flutter-release.zip - Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine - mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/ - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - components: rustfmt - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }} - - - name: Install flutter rust bridge deps - run: | - cargo install flutter_rust_bridge_codegen - Push-Location flutter ; flutter pub get ; Pop-Location - ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - $VCPKG_ROOT/vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static - shell: bash - - - name: Build rustdesk - run: python3 .\build.py --portable --hwcodec --flutter - - # The fallback for the flutter version, we use Sciter for 32bit Windows. - build-for-windows-sciter: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - # - { target: i686-pc-windows-msvc , os: windows-2019 } - # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: i686-pc-windows-msvc, os: windows-2019 } - # - { target: aarch64-pc-windows-msvc, os: windows-2019 } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }}-sciter - - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - $VCPKG_ROOT/vcpkg install libvpx:x86-windows-static libyuv:x86-windows-static opus:x86-windows-static - shell: bash - - - name: Build rustdesk - id: build - shell: bash - run: | - python3 res/inline-sciter.py - # Replace the link for the ico. - rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico - cargo build --features inline --target=${{ matrix.job.target }} --release - mkdir -p ./Release - mv ./target/release/rustdesk.exe ./Release/rustdesk.exe - wget -O ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll - echo "output_folder=./Release" >> $GITHUB_OUTPUT - - - name: Sign rustdesk files - uses: GermanBluefox/code-sign-action@v7 - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.CERTNAME }}' - folder: './Release/' - recursive: true - - - name: Build self-extracted executable - shell: bash - run: | - pushd ./libs/portable - python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe - popd - mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe - - - name: Sign rustdesk self-extracted file - uses: GermanBluefox/code-sign-action@v7 - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' - folder: './SignOutput' - recursive: false - - - name: Publish Release - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./SignOutput/rustdesk-*.exe - - build-for-macOS: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: true - matrix: - job: - - { - target: x86_64-apple-darwin, - os: macos-latest, - extra-build-args: "", - } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install build runtime - run: | - brew install llvm create-dmg nasm yasm cmake gcc wget ninja pkg-config - - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }} - - - name: Install flutter rust bridge deps - shell: bash - run: | - cargo install flutter_rust_bridge_codegen - pushd flutter && flutter pub get && popd - ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - $VCPKG_ROOT/vcpkg install libvpx libyuv opus - - - name: Show version information (Rust, cargo, Clang) - shell: bash - run: | - clang --version || true - rustup -V - rustup toolchain list - rustup default - cargo -V - rustc -V - - - name: Build rustdesk - run: | - # --hwcodec not supported on macos yet - ./build.py --flutter ${{ matrix.job.extra-build-args }} - - build-vcpkg-deps-linux: - uses: ./.github/workflows/vcpkg-deps-linux.yml - - generate-bridge-linux: - uses: ./.github/workflows/bridge.yml - - build-rustdesk-android: - needs: [generate-bridge-linux] - name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: true - matrix: - job: - - { - arch: x86_64, - target: aarch64-linux-android, - os: ubuntu-20.04, - extra-build-features: "", - openssl-arch: android-arm64 - } - - { - arch: x86_64, - target: armv7-linux-androideabi, - os: ubuntu-18.04, - extra-build-features: "", - openssl-arch: android-arm - } - steps: - - name: Install dependencies - run: | - sudo apt update - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless - - name: Checkout source code - uses: actions/checkout@v3 - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - - uses: nttld/setup-ndk@v1 - id: setup-ndk - with: - ndk-version: ${{ env.NDK_VERSION }} - add-to-path: true - - - name: Clone deps - shell: bash - run: | - pushd /opt - git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - - - name: Build rustdesk lib - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} - VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg - run: | - rustup target add ${{ matrix.job.target }} - cargo install cargo-ndk - case ${{ matrix.job.target }} in - aarch64-linux-android) - ./flutter/ndk_arm64.sh - mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - ;; - armv7-linux-androideabi) - ./flutter/ndk_arm.sh - mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - ;; - esac - - - name: Build rustdesk - shell: bash - env: - JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64 - run: | - export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH - # temporary use debug sign config - sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle - case ${{ matrix.job.target }} in - aarch64-linux-android) - mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a - cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/arm64-v8a/*.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - # build flutter - pushd flutter - flutter build apk --release --target-platform android-arm64 --split-per-abi - mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk - ;; - armv7-linux-androideabi) - mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a - cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/armeabi-v7a/*.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - # build flutter - pushd flutter - flutter build apk --release --target-platform android-arm --split-per-abi - mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk - ;; - esac - popd - mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk . - - build-rustdesk-lib-linux-amd64: - needs: [generate-bridge-linux, build-vcpkg-deps-linux] - name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: true - matrix: - # use a high level qemu-user-static - job: - # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "flatpak", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "appimage", - } - # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - steps: - - name: Maximize build space - run: | - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/share/dotnet - sudo apt update -y - sudo apt install qemu-user-static - - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 12 - - - name: Free Space - run: | - df - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - cache-directories: "/opt/rust-registry" - - - name: Install local registry - run: | - mkdir -p /opt/rust-registry - cargo install cargo-local-registry - - - name: Build local registry - uses: nick-fields/retry@v2 - id: build-local-registry - continue-on-error: true - with: - max_attempts: 3 - timeout_minutes: 15 - retry_on: error - command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - # only build cdylib - sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Restore vcpkg files - uses: actions/download-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: /opt/artifacts/vcpkg/installed - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk library for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - # not ready yet - # distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - ls -l /opt/artifacts/vcpkg/installed - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/rust-registry:/opt/rust-registry" - shell: /bin/bash - install: | - apt update -y - echo -e "installing deps" - apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null - # we have libopus compiled by us. - apt remove -y libopus-dev || true - # output devs - ls -l ./ - tree -L 3 /opt/artifacts/vcpkg/installed - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # rust - pushd /opt - wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz - tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz - cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh - rm -rf rust-1.64.0-${{ matrix.job.target }} - # edit config - mkdir -p ~/.cargo/ - echo """ - [source.crates-io] - registry = 'https://github.com/rust-lang/crates.io-index' - replace-with = 'local-registry' - - [source.local-registry] - local-registry = '/opt/rust-registry/' - """ > ~/.cargo/config - cat ~/.cargo/config - # start build - pushd /workspace - # mock - case "${{ matrix.job.arch }}" in - x86_64) - # no need mock on x86_64 - export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release - ;; - esac - - - name: Upload Artifacts - uses: actions/upload-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: target/release/liblibrustdesk.so - - build-rustdesk-lib-linux-arm: - needs: [generate-bridge-linux, build-vcpkg-deps-linux] - name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: true - matrix: - # use a high level qemu-user-static - job: - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-20.04, - use-cross: true, - extra-build-features: "", - } - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "appimage", - } - # - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } - # - { - # arch: armv7, - # target: arm-unknown-linux-gnueabihf, - # os: ubuntu-20.04, - # use-cross: true, - # extra-build-features: "", - # } - # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } - # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - steps: - - name: Maximize build space - run: | - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/share/dotnet - sudo apt update -y - sudo apt install qemu-user-static - - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 12 - - - name: Free Space - run: | - df - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - cache-directories: "/opt/rust-registry" - - - name: Install local registry - run: | - mkdir -p /opt/rust-registry - cargo install cargo-local-registry - - - name: Build local registry - uses: nick-fields/retry@v2 - id: build-local-registry - continue-on-error: true - with: - max_attempts: 3 - timeout_minutes: 15 - retry_on: error - command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - # only build cdylib - sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Restore vcpkg files - uses: actions/download-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: /opt/artifacts/vcpkg/installed - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk library for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - ls -l /opt/artifacts/vcpkg/installed - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/rust-registry:/opt/rust-registry" - shell: /bin/bash - install: | - apt update -y - echo -e "installing deps" - apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null - # we have libopus compiled by us. - apt remove -y libopus-dev || true - # output devs - ls -l ./ - tree -L 3 /opt/artifacts/vcpkg/installed - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # rust - pushd /opt - wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz - tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz - cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh - rm -rf rust-1.64.0-${{ matrix.job.target }} - # edit config - mkdir -p ~/.cargo/ - echo """ - [source.crates-io] - registry = 'https://github.com/rust-lang/crates.io-index' - replace-with = 'local-registry' - - [source.local-registry] - local-registry = '/opt/rust-registry/' - """ > ~/.cargo/config - cat ~/.cargo/config - # start build - pushd /workspace - # mock - case "${{ matrix.job.arch }}" in - aarch64) - cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/aarch64-linux-gnu/ - cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/ - ls -l /opt/artifacts/vcpkg/installed/lib/ - mkdir -p /vcpkg/installed/arm64-linux - ln -s /usr/lib/aarch64-linux-gnu /vcpkg/installed/arm64-linux/lib - ln -s /usr/include /vcpkg/installed/arm64-linux/include - export VCPKG_ROOT=/vcpkg - # disable hwcodec for compilation - cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release - ;; - armv7) - cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/ - cp -r /opt/artifacts/vcpkg/installed/include/* /usr/include/ - mkdir -p /vcpkg/installed/arm-linux - ln -s /usr/lib/arm-linux-gnueabihf /vcpkg/installed/arm-linux/lib - ln -s /usr/include /vcpkg/installed/arm-linux/include - export VCPKG_ROOT=/vcpkg - # disable hwcodec for compilation - cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release - ;; - esac - - - name: Upload Artifacts - uses: actions/upload-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: target/release/liblibrustdesk.so - - build-rustdesk-linux-arm: - needs: [build-rustdesk-lib-linux-arm] - name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ubuntu-20.04 # 20.04 has more performance on arm build - strategy: - fail-fast: true - matrix: - job: - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "", - } - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "appimage", - } - # - { - # arch: aarch64, - # target: aarch64-unknown-linux-gnu, - # os: ubuntu-18.04, # just for naming package, not running host - # use-cross: true, - # extra-build-features: "flatpak", - # } - # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" } - # - { arch: armv7, target: arm-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } - # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Prepare env - run: | - sudo apt update -y - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools - mkdir -p ./target/release/ - - - name: Restore the rustdesk lib file - uses: actions/download-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: ./target/release/ - - - name: Download Flutter - shell: bash - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /opt - # clone repo and reset to flutter 3.7.0 - git clone https://github.com/sony/flutter-elinux.git || true - pushd flutter-elinux - # reset to flutter 3.7.0 - git fetch - git reset --hard 51a1d685901f79fbac51665a967c3a1a789ecee5 - popd - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk binary for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/flutter-elinux:/opt/flutter-elinux" - shell: /bin/bash - install: | - apt update -y - apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /workspace - # we use flutter-elinux to build our rustdesk - export PATH=/opt/flutter-elinux/bin:$PATH - sed -i "s/flutter build linux --release/flutter-elinux build linux/g" ./build.py - # Setup flutter-elinux. Run doctor to check if issues here. - flutter-elinux doctor -v - # Patch arm64 engine for flutter 3.6.0+ - flutter-elinux precache --linux - pushd /tmp - curl -O https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.7.0-stable.tar.xz - tar -xvf flutter_linux_3.7.0-stable.tar.xz flutter/bin/cache/artifacts/engine/linux-x64/shader_lib - cp -R flutter/bin/cache/artifacts/engine/linux-x64/shader_lib /opt/flutter-elinux/flutter/bin/cache/artifacts/engine/linux-arm64 - popd - case ${{ matrix.job.arch }} in - aarch64) - sed -i "s/Architecture: amd64/Architecture: arm64/g" ./build.py - sed -i "s/x64\/release/arm64\/release/g" ./build.py - ;; - armv7) - sed -i "s/Architecture: amd64/Architecture: arm/g" ./build.py - sed -i "s/x64\/release/arm\/release/g" ./build.py - ;; - esac - python3 ./build.py --flutter --hwcodec --skip-cargo - - build-rustdesk-linux-amd64: - needs: [build-rustdesk-lib-linux-amd64] - name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ubuntu-20.04 - strategy: - fail-fast: true - matrix: - job: - # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "flatpak", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "appimage", - } - # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Prepare env - run: | - sudo apt update -y - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools - mkdir -p ./target/release/ - - - name: Restore the rustdesk lib file - uses: actions/download-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: ./target/release/ - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk binary for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - shell: /bin/bash - install: | - apt update -y - apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # Setup Flutter - pushd /opt - wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz - tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz - ls -l . - export PATH=/opt/flutter/bin:$PATH - flutter doctor -v - pushd /workspace - python3 ./build.py --flutter --hwcodec --skip-cargo + run-ci: + uses: ./.github/workflows/flutter-nightly.yml + with: + upload-artifact: false + \ No newline at end of file diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 642869da1..08dd6bb4d 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -5,6 +5,16 @@ on: # schedule build every night - cron: "0 0 * * *" workflow_dispatch: + inputs: + upload-artifact: + description: "Upload the artifact produced by this workflow to the Release page." + type: boolean + default: true + workflow_call: + inputs: + upload-artifact: + type: boolean + default: true env: LLVM_VERSION: "15.0.6" @@ -21,6 +31,7 @@ env: # To make a custom build with your own servers set the below secret values RS_PUB_KEY: '${{ secrets.RS_PUB_KEY }}' RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}' + UPLOAD_ARTIFACT: '${{ inputs.upload-artifact }}' jobs: build-for-windows-flutter: @@ -92,6 +103,7 @@ jobs: - name: Sign rustdesk files uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' with: certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' @@ -102,6 +114,7 @@ jobs: - name: Build self-extracted executable shell: bash + if: env.UPLOAD_ARTIFACT == 'true' run: | pushd ./libs/portable python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe @@ -118,6 +131,7 @@ jobs: - name: Sign rustdesk self-extracted file uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' with: certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' @@ -128,6 +142,7 @@ jobs: - name: Publish Release uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' with: prerelease: true tag_name: ${{ env.TAG_NAME }} @@ -188,6 +203,7 @@ jobs: - name: Sign rustdesk files uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' with: certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' @@ -217,6 +233,7 @@ jobs: - name: Publish Release uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' with: prerelease: true tag_name: ${{ env.TAG_NAME }} @@ -351,6 +368,7 @@ jobs: done - name: Publish DMG package + if: env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -500,14 +518,14 @@ jobs: BUILD_TOOLS_VERSION: "30.0.2" - name: Upload Artifacts - if: env.ANDROID_SIGNING_KEY != null + if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' uses: actions/upload-artifact@master with: name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} - name: Publish signed apk package - if: env.ANDROID_SIGNING_KEY != null + if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -516,7 +534,7 @@ jobs: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} - name: Publish unsigned apk package - if: env.ANDROID_SIGNING_KEY == null + if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -978,7 +996,7 @@ jobs: done - name: Publish debian package - if: ${{ matrix.job.extra-build-features == '' }} + if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1151,7 +1169,7 @@ jobs: done - name: Publish debian package - if: ${{ matrix.job.extra-build-features == '' }} + if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1174,7 +1192,7 @@ jobs: sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml - name: Publish appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} + if: ${{ matrix.job.extra-build-features == 'appimage' }} && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1248,7 +1266,7 @@ jobs: # res/rustdesk*.zst - name: Publish fedora28/centos8 package - if: ${{ matrix.job.extra-build-features == '' }} + if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1371,7 +1389,7 @@ jobs: done - name: Publish debian package - if: ${{ matrix.job.extra-build-features == '' }} + if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1427,7 +1445,7 @@ jobs: cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f - name: Publish archlinux package - if: ${{ matrix.job.extra-build-features == '' }} + if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1450,7 +1468,7 @@ jobs: sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml - name: Publish appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} + if: ${{ matrix.job.extra-build-features == 'appimage' }} && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1459,7 +1477,7 @@ jobs: ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage - name: Publish fedora28/centos8 package - if: ${{ matrix.job.extra-build-features == '' }} + if: ${{ matrix.job.extra-build-features == '' }} && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1588,6 +1606,7 @@ jobs: - name: Publish flatpak package uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' with: prerelease: true tag_name: ${{ env.TAG_NAME }} From aace91ac5a96c8bdef2df1d5e0f14e28dc475950 Mon Sep 17 00:00:00 2001 From: asur4s Date: Wed, 29 Mar 2023 00:41:27 -0700 Subject: [PATCH 094/366] Fix ci --- src/platform/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 04a3feeee..f8fe7a6dd 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -36,6 +36,7 @@ pub fn is_xfce() -> bool { pub fn breakdown_callback() { #[cfg(target_os = "linux")] crate::input_service::clear_remapped_keycode(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] crate::input_service::release_modifiers(); } From 7b219b847ca474b34ac60793956544752a0d6bff Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Wed, 29 Mar 2023 10:05:18 +0200 Subject: [PATCH 095/366] Update pl.rs --- src/lang/pl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 852aa520d..5866886e7 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -240,7 +240,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Remove from Favorites", "Usuń z ulubionych"), ("Empty", "Pusto"), ("Invalid folder name", "Nieprawidłowa nazwa folderu"), - ("Socks5 Proxy", "Socks5 Proxy"), + ("Socks5 Proxy", "Proxy Socks5"), ("Hostname", "Nazwa hosta"), ("Discovered", "Wykryte"), ("install_daemon_tip", "Podpowiedź instalacji daemona"), @@ -446,7 +446,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Default View Style", "Domyślny styl wyświetlania"), ("Default Scroll Style", "Domyślny styl przewijania"), ("Default Image Quality", "Domyślna jakość obrazu"), - ("Default Codec", "Dokyślny kodek"), + ("Default Codec", "Domyślny kodek"), ("Bitrate", "Bitrate"), ("FPS", "FPS"), ("Auto", "Auto"), From b52aa08e8687f236de8324b02ab5d1d1854f2c5c Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Wed, 29 Mar 2023 10:23:26 +0200 Subject: [PATCH 096/366] Update README-PL.md --- docs/README-PL.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/docs/README-PL.md b/docs/README-PL.md index 27da5f02e..3effd8b41 100644 --- a/docs/README-PL.md +++ b/docs/README-PL.md @@ -15,9 +15,19 @@ Porozmawiaj z nami na: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](http Kolejny program do zdalnego pulpitu, napisany w Rust. Działa od samego początku, nie wymaga konfiguracji. Masz pełną kontrolę nad swoimi danymi, bez obaw o bezpieczeństwo. Możesz skorzystać z naszego darmowego serwera publicznego , [skonfigurować własny](https://rustdesk.com/server), lub [napisać własny serwer](https://github.com/rustdesk/rustdesk-server-demo). +![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) + RustDesk zaprasza do współpracy każdego. Zobacz [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) pomoc w uruchomieniu programu. -[**POBIERZ KOMPILACJE**](https://github.com/rustdesk/rustdesk/releases) +[**PYTANIA I ODPOWIEDZI (FAQ)**](https://github.com/rustdesk/rustdesk/wiki/FAQ) + +[**POBIERANIE BINARIÓW**](https://github.com/rustdesk/rustdesk/releases) + +[**WERSJE TESTOWE (NIGHTLY)**](https://github.com/rustdesk/rustdesk/releases/tag/nightly) + +[Get it on F-Droid](https://f-droid.org/en/packages/com.carriez.flutter_hbb) ## Darmowe Serwery Publiczne @@ -31,6 +41,14 @@ Poniżej znajdują się serwery, z których można korzystać za darmo, może si | USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM | | Ukraina (Kijów) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM | +## Konterner Programisty (Dev Container) + +[![Otwórz w Kontenerze programisty](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) + +Jeżeli masz zainstalowany VS Code i Docker, możesz kliknąć w powyższy link, aby rozpocząć. Kliknięcie spowoduje automatyczną instalację rozszrzenia Kontenera Programisty w VS Code (jeżeli wymagany), sklonuje kod źródłowy do kontenera, i przygotuje kontener do użycia. + +Więcej informacji w pliku [DEVCONTAINER-PL.md](docs/DEVCONTAINER-PL.md) for more info. + ## Zależności Wersje desktopowe używają [sciter](https://sciter.com/) dla GUI, proszę pobrać samodzielnie bibliotekę sciter. @@ -58,6 +76,12 @@ Wersje desktopowe używają [sciter](https://sciter.com/) dla GUI, proszę pobra 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 ``` +### 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) ```sh @@ -112,6 +136,30 @@ cargo run RustDesk nie obsługuje Waylanda. Sprawdź [tutaj](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/), jak skonfigurować Xorg jako domyślną sesję GNOME. +## Wspracie Wayland + +Wygląda na to, że Wayland nie wspiera żadnego API do wysyłania naciśnięć klawiszy do innych okien. Dlatego rustdesk używa API z niższego poziomu, urządzenia o nazwie `/dev/uinput` (poziom jądra Linux). + +Gdy po stronie kontrolowanej pracuje Wayland, musisz uruchomić program w następujący sposób: +```bash +# Start uinput service +$ sudo rustdesk --service +$ rustdesk +``` +**Uwaga**: Nagrywanie ekranu Wayland wykorzystuje różne interfejsy. RustDesk obecnie obsługuje tylko 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 +``` + ## Jak kompilować za pomocą Dockera Rozpocznij od sklonowania repozytorium i stworzenia kontenera docker: @@ -152,6 +200,8 @@ Upewnij się, że uruchamiasz te polecenia z katalogu głównego repozytorium Ru - **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: uruchamia połączenie bezpośrednie - **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Komunikacja z [rustdesk-server](https://github.com/rustdesk/rustdesk-server), czekanie na bezpośrednie (odpytywanie TCP) lub przekazywane połączenie - **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: kod specyficzny dla danej platformy +- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: kod Flutter dla urządzeń mobilnych +- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: JavaScript dla Flutter - klient web ## Zrzuty ekranu From f86e718977f6fc963c25abdb29c9dd1bfa3eb202 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Wed, 29 Mar 2023 10:25:54 +0200 Subject: [PATCH 097/366] Create CONTIBUTING-PL.md --- docs/CONTIBUTING-PL.md | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 docs/CONTIBUTING-PL.md diff --git a/docs/CONTIBUTING-PL.md b/docs/CONTIBUTING-PL.md new file mode 100644 index 000000000..b0a7783b7 --- /dev/null +++ b/docs/CONTIBUTING-PL.md @@ -0,0 +1,45 @@ +# Współtworzenie RustDesk + +RustDesk z zadowoleniem przyjmuje wkład od każdego. Oto wytyczne, jeśli chcesz nam pomóc: + +## Współtwórcy + +Contributions to RustDesk or its dependencies should be made in the form of GitHub +pull requests. Each pull request will be reviewed by a core contributor +(someone with permission to land patches) and either landed in the main tree or +given feedback for changes that would be required. All contributions should +follow this format, even those from core contributors. + +Should you wish to work on an issue, please claim it first by commenting on +the GitHub issue that you want to work on it. This is to prevent duplicated +efforts from contributors on the same issue. + +## Pull Request Checklist + +- Branch from the master branch and, if needed, rebase to the current master + branch before submitting your pull request. If it doesn't merge cleanly with + master you may be asked to rebase your changes. + +- Commits should be as small as possible, while ensuring that each commit is + correct independently (i.e., each commit should compile and pass tests). + +- Commits should be accompanied by a Developer Certificate of Origin + (http://developercertificate.org) sign-off, which indicates that you (and + your employer if applicable) agree to be bound by the terms of the + [project license](../LICENCE). In git, this is the `-s` option to `git commit` + +- If your patch is not getting reviewed or you need a specific person to review + it, you can @-reply a reviewer asking for a review in the pull request or a + comment, or you can ask for a review via [email](mailto:info@rustdesk.com). + +- Add tests relevant to the fixed bug or new feature. + +For specific git instructions, see [GitHub workflow 101](https://github.com/servo/servo/wiki/GitHub-workflow). + +## Conduct + +https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT-PL.md + +## Communication + +RustDesk contributors frequent the [Discord](https://discord.gg/nDceKgxnkV). From 642a19dc915ab7042ae33830e40c619fc2277279 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Wed, 29 Mar 2023 10:27:37 +0200 Subject: [PATCH 098/366] Create CODE_OF_CONDUCT-PL.md --- docs/CODE_OF_CONDUCT-PL.md | 133 +++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 docs/CODE_OF_CONDUCT-PL.md diff --git a/docs/CODE_OF_CONDUCT-PL.md b/docs/CODE_OF_CONDUCT-PL.md new file mode 100644 index 000000000..8aedf837d --- /dev/null +++ b/docs/CODE_OF_CONDUCT-PL.md @@ -0,0 +1,133 @@ + +# Kod postępowania Contributor Covenant Code of Conduct + +## Nasza przysięga + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Nasze standardy + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[info@rustdesk.com](mailto:info@rustdesk.com). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations From ce5d993a3a2a45aaf8b109d226d9ca6f59300c43 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Wed, 29 Mar 2023 10:29:04 +0200 Subject: [PATCH 099/366] Create CONTRIBUTING-PL.md --- docs/CONTRIBUTING-PL.md | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 docs/CONTRIBUTING-PL.md diff --git a/docs/CONTRIBUTING-PL.md b/docs/CONTRIBUTING-PL.md new file mode 100644 index 000000000..b0a7783b7 --- /dev/null +++ b/docs/CONTRIBUTING-PL.md @@ -0,0 +1,45 @@ +# Współtworzenie RustDesk + +RustDesk z zadowoleniem przyjmuje wkład od każdego. Oto wytyczne, jeśli chcesz nam pomóc: + +## Współtwórcy + +Contributions to RustDesk or its dependencies should be made in the form of GitHub +pull requests. Each pull request will be reviewed by a core contributor +(someone with permission to land patches) and either landed in the main tree or +given feedback for changes that would be required. All contributions should +follow this format, even those from core contributors. + +Should you wish to work on an issue, please claim it first by commenting on +the GitHub issue that you want to work on it. This is to prevent duplicated +efforts from contributors on the same issue. + +## Pull Request Checklist + +- Branch from the master branch and, if needed, rebase to the current master + branch before submitting your pull request. If it doesn't merge cleanly with + master you may be asked to rebase your changes. + +- Commits should be as small as possible, while ensuring that each commit is + correct independently (i.e., each commit should compile and pass tests). + +- Commits should be accompanied by a Developer Certificate of Origin + (http://developercertificate.org) sign-off, which indicates that you (and + your employer if applicable) agree to be bound by the terms of the + [project license](../LICENCE). In git, this is the `-s` option to `git commit` + +- If your patch is not getting reviewed or you need a specific person to review + it, you can @-reply a reviewer asking for a review in the pull request or a + comment, or you can ask for a review via [email](mailto:info@rustdesk.com). + +- Add tests relevant to the fixed bug or new feature. + +For specific git instructions, see [GitHub workflow 101](https://github.com/servo/servo/wiki/GitHub-workflow). + +## Conduct + +https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT-PL.md + +## Communication + +RustDesk contributors frequent the [Discord](https://discord.gg/nDceKgxnkV). From dbc39cdb2a5d73b1c5a6bdb1ab7aa63367e92f51 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Wed, 29 Mar 2023 10:30:28 +0200 Subject: [PATCH 100/366] Delete CONTIBUTING-PL.md --- docs/CONTIBUTING-PL.md | 45 ------------------------------------------ 1 file changed, 45 deletions(-) delete mode 100644 docs/CONTIBUTING-PL.md diff --git a/docs/CONTIBUTING-PL.md b/docs/CONTIBUTING-PL.md deleted file mode 100644 index b0a7783b7..000000000 --- a/docs/CONTIBUTING-PL.md +++ /dev/null @@ -1,45 +0,0 @@ -# Współtworzenie RustDesk - -RustDesk z zadowoleniem przyjmuje wkład od każdego. Oto wytyczne, jeśli chcesz nam pomóc: - -## Współtwórcy - -Contributions to RustDesk or its dependencies should be made in the form of GitHub -pull requests. Each pull request will be reviewed by a core contributor -(someone with permission to land patches) and either landed in the main tree or -given feedback for changes that would be required. All contributions should -follow this format, even those from core contributors. - -Should you wish to work on an issue, please claim it first by commenting on -the GitHub issue that you want to work on it. This is to prevent duplicated -efforts from contributors on the same issue. - -## Pull Request Checklist - -- Branch from the master branch and, if needed, rebase to the current master - branch before submitting your pull request. If it doesn't merge cleanly with - master you may be asked to rebase your changes. - -- Commits should be as small as possible, while ensuring that each commit is - correct independently (i.e., each commit should compile and pass tests). - -- Commits should be accompanied by a Developer Certificate of Origin - (http://developercertificate.org) sign-off, which indicates that you (and - your employer if applicable) agree to be bound by the terms of the - [project license](../LICENCE). In git, this is the `-s` option to `git commit` - -- If your patch is not getting reviewed or you need a specific person to review - it, you can @-reply a reviewer asking for a review in the pull request or a - comment, or you can ask for a review via [email](mailto:info@rustdesk.com). - -- Add tests relevant to the fixed bug or new feature. - -For specific git instructions, see [GitHub workflow 101](https://github.com/servo/servo/wiki/GitHub-workflow). - -## Conduct - -https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT-PL.md - -## Communication - -RustDesk contributors frequent the [Discord](https://discord.gg/nDceKgxnkV). From d9552971482f1f812e9f697333a6e0b1192c51a0 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Wed, 29 Mar 2023 10:31:37 +0200 Subject: [PATCH 101/366] Update README-PL.md --- docs/README-PL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README-PL.md b/docs/README-PL.md index 3effd8b41..fb5addb69 100644 --- a/docs/README-PL.md +++ b/docs/README-PL.md @@ -17,7 +17,7 @@ Kolejny program do zdalnego pulpitu, napisany w Rust. Działa od samego początk ![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) -RustDesk zaprasza do współpracy każdego. Zobacz [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) pomoc w uruchomieniu programu. +RustDesk zaprasza do współpracy każdego. Zobacz [`docs/CONTRIBUTING-PL.md`](CONTRIBUTING-PL.md) pomoc w uruchomieniu programu. [**PYTANIA I ODPOWIEDZI (FAQ)**](https://github.com/rustdesk/rustdesk/wiki/FAQ) From d2b71b96c1548c0cb6feb4da83648c6c9feeedac Mon Sep 17 00:00:00 2001 From: Kingtous Date: Wed, 29 Mar 2023 09:38:24 +0800 Subject: [PATCH 102/366] feat: add llvm 32bit for sciter --- .github/workflows/flutter-nightly.yml | 30 +++++++++++++++++++-------- Cross.toml | 21 ------------------- res/install-rust-deps-arm.sh | 11 ---------- src/ui.rs | 2 +- 4 files changed, 22 insertions(+), 42 deletions(-) delete mode 100644 Cross.toml delete mode 100644 res/install-rust-deps-arm.sh diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 08dd6bb4d..4b2ccfc69 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -153,6 +153,8 @@ jobs: build-for-windows-sciter: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) runs-on: ${{ matrix.job.os }} + # Temporarily disable this action due to additional test is needed. + if: false strategy: fail-fast: false matrix: @@ -165,13 +167,22 @@ jobs: - name: Checkout source code uses: actions/checkout@v3 + - name: Install LLVM and Clang + uses: Kingtous/install-llvm-action-32bit@master + with: + version: ${{ env.LLVM_VERSION }} + - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: - toolchain: stable + toolchain: stable-${{ matrix.job.target }} target: ${{ matrix.job.target }} override: true profile: minimal # minimal component installation (ie, no documentation) + + - name: Set Rust toolchain to the target + run: | + rustup default stable-${{ matrix.job.target }} - uses: Swatinem/rust-cache@v2 with: @@ -195,10 +206,10 @@ jobs: python3 res/inline-sciter.py # Replace the link for the ico. rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico - cargo build --features inline --target=${{ matrix.job.target }} --release --bins + cargo build --features inline --release --bins mkdir -p ./Release - mv ./target/${{ matrix.job.target }}/release/rustdesk.exe ./Release/rustdesk.exe - wget -O ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll + mv ./target/release/rustdesk.exe ./Release/rustdesk.exe + curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll echo "output_folder=./Release" >> $GITHUB_OUTPUT - name: Sign rustdesk files @@ -216,10 +227,11 @@ jobs: shell: bash run: | pushd ./libs/portable + pip3 install -r requirements.txt python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe popd mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-sciter.exe - name: Sign rustdesk self-extracted file uses: GermanBluefox/code-sign-action@v7 @@ -992,7 +1004,7 @@ jobs: run: | for name in rustdesk*??.deb; do # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb" done - name: Publish debian package @@ -1002,14 +1014,14 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - name: Upload Artifact uses: actions/upload-artifact@master if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb build-rustdesk-linux-arm: needs: [build-rustdesk-lib-linux-arm] diff --git a/Cross.toml b/Cross.toml deleted file mode 100644 index 952e753a2..000000000 --- a/Cross.toml +++ /dev/null @@ -1,21 +0,0 @@ -[build] -build-std = false # do not build the std library. has precedence over xargo -xargo = true # enable the use of xargo by default -# zig = false # do not use zig cc for the builds -default-target = "x86_64-unknown-linux-gnu" # use this target if none is explicitly provided - -[build.env] -volumes = ["VCPKG_ROOT"] # "VOL2_ARG=/path/to/volume" -passthrough = ["VCPKG_ROOT"] - -[target.aarch64-unknown-linux-gnu] -# pre-build = ["env ARCH=arm64 ./res/install-rust-deps.sh"] -# build-std = false # always build the std library. has precedence over xargo -# xargo = false # disable the use of xargo -# image = "test-image" # use a different image for the target -# runner = "qemu-user" # wrapper to run the binary (must be `qemu-system`, `qemu-user`, or `native`). - -[target.arm-unknown-linux-gnueabihf] -pre-build = "./res/install-rust-deps-arm.sh" -# volumes = ["VCPKG_ROOT"] # "VOL2_ARG=/path/to/volume" -# passthrough = ["VCPKG_ROOT"] \ No newline at end of file diff --git a/res/install-rust-deps-arm.sh b/res/install-rust-deps-arm.sh deleted file mode 100644 index 14cf772fd..000000000 --- a/res/install-rust-deps-arm.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# apt-get install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev pkg-config g++ gcc libvpx-dev - -# For developers in China, we use sources from ustc. -# sed -i 's/archive.archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list -# sed -i 's/security.archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list - -dpkg --add-architecture armhf - -apt update -y -apt install -y libdbus-1-dev:armhf pkg-config nasm yasm libglib2.0-dev:armhf libxcb-randr0-dev:armhf libxdo-dev:armhf libxfixes-dev:armhf libxcb-shape0-dev:armhf libxcb-xfixes0-dev:armhf libasound2-dev:armhf libpulse-dev:armhf libgstreamer1.0-dev:armhf libgstreamer-plugins-base1.0-dev:armhf libappindicator3-dev:armhf libvpx-dev:armhf libvdpau-dev:armhf libva-dev:armhf libgtk-3-dev:armhf clang gcc libclang-dev diff --git a/src/ui.rs b/src/ui.rs index 8f9d14c54..ca50fbf9d 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -62,7 +62,7 @@ pub fn start(args: &mut [String]) { if sciter_dll_path.exists() { // Try to set the sciter dll. let p = sciter_dll_path.to_string_lossy().to_string(); - println!("Found dll:{}, \n {:?}", p, sciter::set_library(&p)); + log::debug!("Found dll:{}, \n {:?}", p, sciter::set_library(&p)); } } } From bdc11eb03524013ba1cfc0db13bfe743d6f3db31 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 29 Mar 2023 07:32:35 +0800 Subject: [PATCH 103/366] don't handle audio frame util ready Signed-off-by: 21pages --- src/client.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 607b96635..1ad996165 100644 --- a/src/client.rs +++ b/src/client.rs @@ -706,6 +706,8 @@ pub struct AudioHandler { #[cfg(not(any(target_os = "android", target_os = "linux")))] audio_stream: Option>, channels: u16, + #[cfg(not(any(target_os = "android", target_os = "linux")))] + ready: Arc>, } impl AudioHandler { @@ -794,7 +796,7 @@ impl AudioHandler { #[inline] pub fn handle_frame(&mut self, frame: AudioFrame) { #[cfg(not(any(target_os = "android", target_os = "linux")))] - if self.audio_stream.is_none() { + if self.audio_stream.is_none() || !self.ready.lock().unwrap().clone() { return; } #[cfg(target_os = "linux")] @@ -860,9 +862,13 @@ impl AudioHandler { log::trace!("an error occurred on stream: {}", err); }; let audio_buffer = self.audio_buffer.clone(); + let ready = self.ready.clone(); let stream = device.build_output_stream( config, move |data: &mut [T], _: &_| { + if !*ready.lock().unwrap() { + *ready.lock().unwrap() = true; + } let mut lock = audio_buffer.lock().unwrap(); let mut n = data.len(); if lock.len() < n { From 80b5033fb7c35961c9290f66dd832a3418321803 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 29 Mar 2023 16:59:50 +0800 Subject: [PATCH 104/366] use ringbuf to limit audio latency Signed-off-by: 21pages --- Cargo.lock | 62 ++++++++++++++++++++++++++++++--------------------- Cargo.toml | 1 + src/client.rs | 37 ++++++++++++++++++++---------- 3 files changed, 62 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fdb513e4..29c4ea8b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4814,6 +4814,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "ringbuf" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ca10b9c9e53ac855a2d6953bce34cef6edbac32c4b13047a4d59d67299420a" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "rpassword" version = "2.1.0" @@ -4972,6 +4981,7 @@ dependencies = [ "rdev", "repng", "reqwest", + "ringbuf", "rpassword 7.2.0", "rubato", "runas", @@ -6773,12 +6783,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -6792,24 +6802,24 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.1", + "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_msvc" @@ -6837,9 +6847,9 @@ checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" @@ -6867,9 +6877,9 @@ checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" @@ -6897,9 +6907,9 @@ checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" @@ -6927,15 +6937,15 @@ checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" @@ -6963,9 +6973,9 @@ checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "winit" diff --git a/Cargo.toml b/Cargo.toml index bf81664e5..990ccb352 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ cidr-utils = "0.5.9" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] cpal = "0.14" +ringbuf = "0.3" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] machine-uid = "0.2" diff --git a/src/client.rs b/src/client.rs index 1ad996165..1efd8b18e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -14,6 +14,8 @@ use cpal::{ Device, Host, StreamConfig, }; use magnum_opus::{Channels::*, Decoder as AudioDecoder}; +#[cfg(not(any(target_os = "android", target_os = "linux")))] +use ringbuf::{ring_buffer::RbBase, Rb}; use sha2::{Digest, Sha256}; use uuid::Uuid; @@ -701,7 +703,7 @@ pub struct AudioHandler { #[cfg(target_os = "linux")] simple: Option, #[cfg(not(any(target_os = "android", target_os = "linux")))] - audio_buffer: Arc>>, + audio_buffer: AudioBuffer, sample_rate: (u32, u32), #[cfg(not(any(target_os = "android", target_os = "linux")))] audio_stream: Option>, @@ -710,6 +712,18 @@ pub struct AudioHandler { ready: Arc>, } +#[cfg(not(any(target_os = "android", target_os = "linux")))] +struct AudioBuffer(pub Arc>>); + +#[cfg(not(any(target_os = "android", target_os = "linux")))] +impl Default for AudioBuffer { + fn default() -> Self { + Self(Arc::new(std::sync::Mutex::new( + ringbuf::HeapRb::::new(48000 * 2), // 48000hz, 2 channel, 1 second + ))) + } +} + impl AudioHandler { /// Start the audio playback. #[cfg(target_os = "linux")] @@ -816,11 +830,7 @@ impl AudioHandler { { let sample_rate0 = self.sample_rate.0; let sample_rate = self.sample_rate.1; - let audio_buffer = self.audio_buffer.clone(); - // avoiding memory overflow if audio_buffer consumer side has problem - if audio_buffer.lock().unwrap().len() as u32 > sample_rate * 120 { - *audio_buffer.lock().unwrap() = Default::default(); - } + let audio_buffer = self.audio_buffer.0.clone(); if sample_rate != sample_rate0 { let buffer = crate::resample_channels( &buffer[0..n], @@ -828,12 +838,12 @@ impl AudioHandler { sample_rate, channels, ); - audio_buffer.lock().unwrap().extend(buffer); + audio_buffer.lock().unwrap().push_slice_overwrite(&buffer); } else { audio_buffer .lock() .unwrap() - .extend(buffer[0..n].iter().cloned()); + .push_slice_overwrite(&buffer[0..n]); } } #[cfg(target_os = "android")] @@ -861,7 +871,7 @@ impl AudioHandler { // too many errors, will improve later log::trace!("an error occurred on stream: {}", err); }; - let audio_buffer = self.audio_buffer.clone(); + let audio_buffer = self.audio_buffer.0.clone(); let ready = self.ready.clone(); let stream = device.build_output_stream( config, @@ -871,10 +881,13 @@ impl AudioHandler { } let mut lock = audio_buffer.lock().unwrap(); let mut n = data.len(); - if lock.len() < n { - n = lock.len(); + if lock.occupied_len() < n { + n = lock.occupied_len(); } - let mut input = lock.drain(0..n); + let mut elems = vec![0.0f32; n]; + lock.pop_slice(&mut elems); + drop(lock); + let mut input = elems.into_iter(); for sample in data.iter_mut() { *sample = match input.next() { Some(x) => T::from(&x), From e974867535e7e0992c319fcbfebbab0122685124 Mon Sep 17 00:00:00 2001 From: grummbeer Date: Wed, 29 Mar 2023 16:14:55 +0200 Subject: [PATCH 105/366] Dialogs. Adjust spacing and alignment --- flutter/lib/common.dart | 92 ++++++++++++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 19 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 1b6afd584..2f2ab368c 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -186,6 +186,46 @@ class MyTheme { static const Color button = Color(0xFF2C8CFF); static const Color hoverBorder = Color(0xFF999999); + // TextButton + // Value is used to calculate "dialog.actionsPadding" + static const double mobileTextButtonPaddingLR = 20; + + // TextButton on mobile needs a fixed padding, otherwise small buttons + // like "OK" has a larger left/right padding. + static const EdgeInsets mobileTextButtonPadding = + EdgeInsets.symmetric(horizontal: mobileTextButtonPaddingLR); + + // Dialogs + static const double dialogPadding = 24; + + // padding bottom depend on content (some dialogs has no content) + static EdgeInsets dialogTitlePadding({bool content = true}) { + final double p = dialogPadding; + + return EdgeInsets.fromLTRB(p, p, p, content ? 0 : p); + } + + // padding bottom depend on actions (mobile has dialogs without actions) + static EdgeInsets dialogContentPadding({bool actions = true}) { + final double p = dialogPadding; + + return isDesktop + ? EdgeInsets.fromLTRB(p, p, p, actions ? (p - 4) : p) + : EdgeInsets.fromLTRB(p, p, p, actions ? (p / 2) : p); + } + + static EdgeInsets dialogActionsPadding() { + final double p = dialogPadding; + + return isDesktop + ? EdgeInsets.fromLTRB(p, 0, p, (p - 4)) + : EdgeInsets.fromLTRB(p, 0, (p - mobileTextButtonPaddingLR), (p / 2)); + } + + static EdgeInsets dialogButtonPadding = isDesktop + ? EdgeInsets.only(left: dialogPadding) + : EdgeInsets.only(left: dialogPadding / 3); + static ThemeData lightTheme = ThemeData( brightness: Brightness.light, hoverColor: Color.fromARGB(255, 224, 224, 224), @@ -236,7 +276,14 @@ class MyTheme { ), ), ) - : null, + : TextButtonThemeData( + style: TextButton.styleFrom( + padding: mobileTextButtonPadding, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: MyTheme.accent, @@ -334,7 +381,14 @@ class MyTheme { ), ), ) - : null, + : TextButtonThemeData( + style: TextButton.styleFrom( + padding: mobileTextButtonPadding, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: MyTheme.accent, @@ -771,6 +825,10 @@ void showToast(String text, {Duration timeout = const Duration(seconds: 2)}) { }); } +// TODO +// - Remove argument "contentPadding", no need for it, all should look the same. +// - Remove "required" for argument "content". See simple confirm dialog "delete peer", only title and actions are used. No need to "content: SizedBox.shrink()". +// - Make dead code alive, transform arguments "onSubmit" and "onCancel" into correspondenting buttons "ConfirmOkButton", "CancelButton". class CustomAlertDialog extends StatelessWidget { const CustomAlertDialog( {Key? key, @@ -798,8 +856,8 @@ class CustomAlertDialog extends StatelessWidget { Future.delayed(Duration.zero, () { if (!scopeNode.hasFocus) scopeNode.requestFocus(); }); - const double padding = 30; bool tabTapped = false; + return FocusScope( node: scopeNode, autofocus: true, @@ -824,22 +882,18 @@ class CustomAlertDialog extends StatelessWidget { return KeyEventResult.ignored; }, child: AlertDialog( - scrollable: true, - title: title, - titlePadding: EdgeInsets.fromLTRB(padding, 24, padding, 0), - contentPadding: EdgeInsets.fromLTRB( - contentPadding ?? padding, - 25, - contentPadding ?? padding, - actions is List ? 10 : padding, - ), - content: ConstrainedBox( - constraints: contentBoxConstraints, - child: content, - ), - actions: actions, - actionsPadding: EdgeInsets.fromLTRB(padding, 0, padding, padding), - ), + scrollable: true, + title: title, + content: ConstrainedBox( + constraints: contentBoxConstraints, + child: content, + ), + actions: actions, + titlePadding: MyTheme.dialogTitlePadding(content: content != null), + contentPadding: + MyTheme.dialogContentPadding(actions: actions is List), + actionsPadding: MyTheme.dialogActionsPadding(), + buttonPadding: MyTheme.dialogButtonPadding), ); } } From 1dbaf7b6e988bea5068831e19b906c56d1d3754b Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Wed, 29 Mar 2023 17:43:16 +0200 Subject: [PATCH 106/366] Update CONTRIBUTING-PL.md --- docs/CONTRIBUTING-PL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTING-PL.md b/docs/CONTRIBUTING-PL.md index b0a7783b7..8341692ba 100644 --- a/docs/CONTRIBUTING-PL.md +++ b/docs/CONTRIBUTING-PL.md @@ -36,10 +36,10 @@ efforts from contributors on the same issue. For specific git instructions, see [GitHub workflow 101](https://github.com/servo/servo/wiki/GitHub-workflow). -## Conduct +## Kodeks postępowania -https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT-PL.md +[Kodeks postępowania](CODE_OF_CONDUCT-PL.md) -## Communication +## Komunikacja RustDesk contributors frequent the [Discord](https://discord.gg/nDceKgxnkV). From 245dfda8008a4b390ce514823ecd789d133a4235 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 30 Mar 2023 00:40:26 +0800 Subject: [PATCH 107/366] Update flutter-nightly.yml --- .github/workflows/flutter-nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 4b2ccfc69..50c5c1e19 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -266,6 +266,7 @@ jobs: } - { target: aarch64-apple-darwin, + arch: aarch64, os: macos-latest, extra-build-args: "", } From d3c2a12bde1ab24090bc0142872fac8ec51df98e Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 30 Mar 2023 00:41:26 +0800 Subject: [PATCH 108/366] Update flutter-nightly.yml --- .github/workflows/flutter-nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 50c5c1e19..ef2a50e10 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -44,7 +44,7 @@ jobs: # - { target: i686-pc-windows-msvc , os: windows-2019 } # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - { target: x86_64-pc-windows-msvc, os: windows-2019 } - # - { target: aarch64-pc-windows-msvc, os: windows-2019 } + # - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 } steps: - name: Checkout source code uses: actions/checkout@v3 From 4e02e8d0050bbab0a032a57aeeffbc6c1aef1896 Mon Sep 17 00:00:00 2001 From: grummbeer Date: Wed, 29 Mar 2023 23:22:31 +0200 Subject: [PATCH 109/366] File Manager. Fix wrong state of checkbox "show hidden files" --- flutter/lib/desktop/pages/file_manager_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 602dd5171..5588c577f 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -802,7 +802,7 @@ class _FileManagerViewState extends State { switchType: SwitchType.scheckbox, text: translate("Show Hidden Files"), getter: () async { - return controller.options.value.isWindows; + return controller.options.value.showHidden; }, setter: (bool v) async { controller.toggleShowHidden(); From a0b5239ecfc8c9882672dc6276e5a51206f8a3a0 Mon Sep 17 00:00:00 2001 From: grummbeer Date: Thu, 30 Mar 2023 00:03:15 +0200 Subject: [PATCH 110/366] Theme. Share general styles --- flutter/lib/common.dart | 85 +++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 50 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 2f2ab368c..2d6c5401d 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -186,26 +186,51 @@ class MyTheme { static const Color button = Color(0xFF2C8CFF); static const Color hoverBorder = Color(0xFF999999); + // ListTile + static const ListTileThemeData listTileTheme = ListTileThemeData( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(5), + ), + ), + ); + + // Checkbox + static const CheckboxThemeData checkboxTheme = CheckboxThemeData( + splashRadius: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(5), + ), + ), + ); + // TextButton // Value is used to calculate "dialog.actionsPadding" static const double mobileTextButtonPaddingLR = 20; // TextButton on mobile needs a fixed padding, otherwise small buttons // like "OK" has a larger left/right padding. - static const EdgeInsets mobileTextButtonPadding = - EdgeInsets.symmetric(horizontal: mobileTextButtonPaddingLR); + static TextButtonThemeData mobileTextButtonTheme = TextButtonThemeData( + style: TextButton.styleFrom( + padding: EdgeInsets.symmetric(horizontal: mobileTextButtonPaddingLR), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + ); // Dialogs static const double dialogPadding = 24; - // padding bottom depend on content (some dialogs has no content) + // padding bottom depends on content (some dialogs has no content) static EdgeInsets dialogTitlePadding({bool content = true}) { final double p = dialogPadding; return EdgeInsets.fromLTRB(p, p, p, content ? 0 : p); } - // padding bottom depend on actions (mobile has dialogs without actions) + // padding bottom depends on actions (mobile has dialogs without actions) static EdgeInsets dialogContentPadding({bool actions = true}) { final double p = dialogPadding; @@ -276,14 +301,7 @@ class MyTheme { ), ), ) - : TextButtonThemeData( - style: TextButton.styleFrom( - padding: mobileTextButtonPadding, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - ), - ), + : mobileTextButtonTheme, elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: MyTheme.accent, @@ -301,21 +319,8 @@ class MyTheme { ), ), ), - checkboxTheme: const CheckboxThemeData( - splashRadius: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), - ), - listTileTheme: ListTileThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), - ), + checkboxTheme: checkboxTheme, + listTileTheme: listTileTheme, menuBarTheme: MenuBarThemeData( style: MenuStyle(backgroundColor: MaterialStatePropertyAll(Colors.white))), @@ -381,14 +386,7 @@ class MyTheme { ), ), ) - : TextButtonThemeData( - style: TextButton.styleFrom( - padding: mobileTextButtonPadding, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - ), - ), + : mobileTextButtonTheme, elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: MyTheme.accent, @@ -411,21 +409,8 @@ class MyTheme { ), ), ), - checkboxTheme: const CheckboxThemeData( - splashRadius: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), - ), - listTileTheme: ListTileThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(5), - ), - ), - ), + checkboxTheme: checkboxTheme, + listTileTheme: listTileTheme, menuBarTheme: MenuBarThemeData( style: MenuStyle( backgroundColor: MaterialStatePropertyAll(Color(0xFF121212)))), From 7351c583363306e3d494ce0770a11895240cbc92 Mon Sep 17 00:00:00 2001 From: grummbeer Date: Thu, 30 Mar 2023 01:51:29 +0200 Subject: [PATCH 111/366] Dialogs Mobile. Remove custom contentPadding --- flutter/lib/mobile/pages/remote_page.dart | 7 ++-- flutter/lib/mobile/pages/settings_page.dart | 39 ++++++++++----------- flutter/lib/mobile/widgets/dialog.dart | 2 -- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 8f4efaece..083cdcd1c 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -696,10 +696,8 @@ class _RemotePageState extends State { // return CustomAlertDialog( // title: Text(translate('Physical Keyboard Input Mode')), // content: Column(mainAxisSize: MainAxisSize.min, children: [ - // getRadio('Legacy mode', 'legacy', current, setMode, - // contentPadding: EdgeInsets.zero), - // getRadio('Map mode', 'map', current, setMode, - // contentPadding: EdgeInsets.zero), + // getRadio('Legacy mode', 'legacy', current, setMode), + // getRadio('Map mode', 'map', current, setMode), // ])); // }, clickMaskDismiss: true); // } @@ -1069,7 +1067,6 @@ void showOptions( content: Column( mainAxisSize: MainAxisSize.min, children: displays + radios + toggles + more), - contentPadding: 0, ); }, clickMaskDismiss: true, backDismiss: true); } diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index c19601956..96038f983 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -502,19 +502,18 @@ void showLanguageSettings(OverlayDialogManager dialogManager) async { } return CustomAlertDialog( - title: SizedBox.shrink(), - content: Column( - children: [ - getRadio('Default', '', lang, setLang), - Divider(color: MyTheme.border), - ] + - langs.map((e) { - final key = e[0] as String; - final name = e[1] as String; - return getRadio(name, key, lang, setLang); - }).toList(), - ), - actions: []); + content: Column( + children: [ + getRadio('Default', '', lang, setLang), + Divider(color: MyTheme.border), + ] + + langs.map((e) { + final key = e[0] as String; + final name = e[1] as String; + return getRadio(name, key, lang, setLang); + }).toList(), + ), + ); }, backDismiss: true, clickMaskDismiss: true); } catch (e) { // @@ -536,14 +535,12 @@ void showThemeSettings(OverlayDialogManager dialogManager) async { } return CustomAlertDialog( - title: SizedBox.shrink(), - contentPadding: 10, - content: Column(children: [ - getRadio('Light', ThemeMode.light, themeMode, setTheme), - getRadio('Dark', ThemeMode.dark, themeMode, setTheme), - getRadio('Follow System', ThemeMode.system, themeMode, setTheme) - ]), - actions: []); + content: Column(children: [ + getRadio('Light', ThemeMode.light, themeMode, setTheme), + getRadio('Dark', ThemeMode.dark, themeMode, setTheme), + getRadio('Follow System', ThemeMode.system, themeMode, setTheme) + ]), + ); }, backDismiss: true, clickMaskDismiss: true); } diff --git a/flutter/lib/mobile/widgets/dialog.dart b/flutter/lib/mobile/widgets/dialog.dart index b281895ff..cd7e0dab0 100644 --- a/flutter/lib/mobile/widgets/dialog.dart +++ b/flutter/lib/mobile/widgets/dialog.dart @@ -132,8 +132,6 @@ void setTemporaryPasswordLengthDialog( mainAxisSize: MainAxisSize.min, children: lengths.map((e) => getRadio(e, e, length, setLength)).toList()), - actions: [], - contentPadding: 14, ); }, backDismiss: true, clickMaskDismiss: true); } From 331d2d0fc588a75584f684cb16830ff568354361 Mon Sep 17 00:00:00 2001 From: grummbeer Date: Thu, 30 Mar 2023 02:33:04 +0200 Subject: [PATCH 112/366] Dialogs Mobile. Adjust radio und checkbox tiles --- flutter/lib/common.dart | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 2d6c5401d..49cbd6091 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1154,25 +1154,32 @@ class AndroidPermissionManager { } } +// TODO move this to mobile/widgets. +// Used only for mobile, pages remote, settings, dialog +// TODO remove argument contentPadding, it’s not used, getToggle() has not RadioListTile getRadio( String name, T toValue, T curValue, void Function(T?) onChange, {EdgeInsetsGeometry? contentPadding}) { return RadioListTile( - contentPadding: contentPadding, + contentPadding: contentPadding ?? EdgeInsets.zero, + visualDensity: VisualDensity.compact, controlAffinity: ListTileControlAffinity.trailing, title: Text(translate(name)), value: toValue, groupValue: curValue, onChanged: onChange, - dense: true, ); } +// TODO move this to mobile/widgets. +// Used only for mobile, pages remote, settings, dialog CheckboxListTile getToggle( String id, void Function(void Function()) setState, option, name, {FFI? ffi}) { final opt = bind.sessionGetToggleOptionSync(id: id, arg: option); return CheckboxListTile( + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, value: opt, onChanged: (v) { setState(() { @@ -1182,7 +1189,6 @@ CheckboxListTile getToggle( (ffi ?? gFFI).qualityMonitorModel.checkShowQualityMonitor(id); } }, - dense: true, title: Text(translate(name))); } From 18ebd2e72714bd7375a2dc4d22b20016bc75dfa0 Mon Sep 17 00:00:00 2001 From: grummbeer Date: Thu, 30 Mar 2023 04:11:48 +0200 Subject: [PATCH 113/366] Dialogs Mobile. Options for password length horizontal --- flutter/lib/mobile/widgets/dialog.dart | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/flutter/lib/mobile/widgets/dialog.dart b/flutter/lib/mobile/widgets/dialog.dart index cd7e0dab0..3f22cee7c 100644 --- a/flutter/lib/mobile/widgets/dialog.dart +++ b/flutter/lib/mobile/widgets/dialog.dart @@ -128,10 +128,19 @@ void setTemporaryPasswordLengthDialog( return CustomAlertDialog( title: Text(translate("Set one-time password length")), - content: Column( - mainAxisSize: MainAxisSize.min, - children: - lengths.map((e) => getRadio(e, e, length, setLength)).toList()), + content: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: lengths + .map( + (value) => Row( + children: [ + Text(value), + Radio( + value: value, groupValue: length, onChanged: setLength), + ], + ), + ) + .toList()), ); }, backDismiss: true, clickMaskDismiss: true); } From 632f9e264c23468b2d38a5c440bd20207be61773 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 30 Mar 2023 10:54:54 +0800 Subject: [PATCH 114/366] Update flutter-nightly.yml #3795 --- .github/workflows/flutter-nightly.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index ef2a50e10..06f6f8150 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -264,12 +264,6 @@ jobs: os: macos-latest, extra-build-args: "", } - - { - target: aarch64-apple-darwin, - arch: aarch64, - os: macos-latest, - extra-build-args: "", - } steps: - name: Checkout source code uses: actions/checkout@v3 From 5685a8b5461b5965ca3ecf337d7d41549c3291a4 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 30 Mar 2023 16:44:09 +0800 Subject: [PATCH 115/366] fix elevated process crash when remote insert lock screen Signed-off-by: 21pages --- src/server/input_service.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 49fd1e188..25b41d0ef 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1193,7 +1193,9 @@ fn is_function_key(ck: &EnumOrUnknown) -> bool { }); res = true; } else if ck.value() == ControlKey::LockScreen.value() { - lock_screen_2(); + std::thread::spawn(|| { + lock_screen_2(); + }); res = true; } return res; From 96c9d255f880af7463bd21a1600e1bb6666af6ef Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 30 Mar 2023 18:24:54 +0800 Subject: [PATCH 116/366] update cargo.toml and fix broken icon.ico, copied from flutter/windows/runner/resources/app_icon.ic --- Cargo.lock | 1022 +++++++++++++++++++++++++++++--------------------- Cargo.toml | 19 +- res/icon.ico | Bin 48 -> 1961 bytes 3 files changed, 602 insertions(+), 439 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86bf2e7ab..e20d0b3d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,10 +134,50 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.69" +name = "anstream" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-wincon", + "concolor-override", + "concolor-query", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" + +[[package]] +name = "anstyle-parse" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-wincon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +dependencies = [ + "anstyle", + "windows-sys 0.45.0", +] + +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "arboard" @@ -208,22 +248,22 @@ dependencies = [ [[package]] name = "async-io" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock", "autocfg 1.1.0", + "cfg-if 1.0.0", "concurrent-queue", "futures-lite", - "libc", "log", "parking", "polling", + "rustix", "slab", "socket2 0.4.9", "waker-fn", - "windows-sys 0.42.0", ] [[package]] @@ -255,30 +295,30 @@ dependencies = [ [[package]] name = "async-recursion" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" +checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 2.0.11", ] [[package]] name = "async-task" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "async-trait" -version = "0.1.66" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 2.0.11", ] [[package]] @@ -302,7 +342,7 @@ dependencies = [ "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] @@ -388,34 +428,14 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "regex", "rustc-hash", "shlex", "which", ] -[[package]] -name = "bindgen" -version = "0.61.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a022e58a142a46fea340d68012b9201c094e93ec3d033a944a24f8fd4a4f09a" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "proc-macro2 1.0.51", - "quote 1.0.23", - "regex", - "rustc-hash", - "shlex", - "syn 1.0.109", -] - [[package]] name = "bindgen" version = "0.64.0" @@ -429,8 +449,8 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "regex", "rustc-hash", "shlex", @@ -542,7 +562,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", ] [[package]] @@ -567,7 +587,7 @@ checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" dependencies = [ "glib-sys 0.16.3", "libc", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] @@ -582,11 +602,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", ] [[package]] @@ -595,7 +615,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", ] [[package]] @@ -606,9 +626,9 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.16", - "serde 1.0.154", - "serde_json 1.0.94", + "semver 1.0.17", + "serde 1.0.159", + "serde_json 1.0.95", ] [[package]] @@ -621,10 +641,10 @@ dependencies = [ "heck 0.4.1", "indexmap", "log", - "proc-macro2 1.0.51", - "quote 1.0.23", - "serde 1.0.154", - "serde_json 1.0.94", + "proc-macro2 1.0.54", + "quote 1.0.26", + "serde 1.0.159", + "serde_json 1.0.95", "syn 1.0.109", "tempfile", "toml 0.5.11", @@ -656,9 +676,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.11.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +checksum = "a35b255461940a32985c627ce82900867c61db1659764d3675ea81963f72a4c6" dependencies = [ "smallvec", ] @@ -677,9 +697,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", "js-sys", @@ -705,9 +725,9 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -737,10 +757,8 @@ checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "atty", "bitflags", - "clap_derive", "clap_lex 0.2.4", "indexmap", - "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.16.0", @@ -748,28 +766,38 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.8" +version = "4.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +dependencies = [ + "anstream", + "anstyle", "bitflags", - "clap_lex 0.3.2", - "is-terminal", + "clap_lex 0.4.1", "strsim 0.10.0", - "termcolor", ] [[package]] name = "clap_derive" -version = "3.2.18" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" dependencies = [ "heck 0.4.1", - "proc-macro-error", - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 2.0.11", ] [[package]] @@ -783,12 +811,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" -dependencies = [ - "os_str_bytes", -] +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "clipboard" @@ -797,7 +822,7 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.154", + "serde 1.0.159", "serde_derive", "thiserror", ] @@ -824,9 +849,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.49" +version = "0.1.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" dependencies = [ "cc", ] @@ -849,9 +874,9 @@ dependencies = [ [[package]] name = "cocoa-foundation" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" dependencies = [ "bitflags", "block", @@ -899,6 +924,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "concolor-override" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" + +[[package]] +name = "concolor-query" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" +dependencies = [ + "windows-sys 0.45.0", +] + [[package]] name = "concurrent-queue" version = "2.1.0" @@ -914,7 +954,7 @@ version = "0.4.0" source = "git+https://github.com/open-trade/confy#630cc28a396cb7d01eefdd9f3824486fe4d8554b" dependencies = [ "directories-next", - "serde 1.0.154", + "serde 1.0.159", "thiserror", "toml 0.5.11", ] @@ -1029,11 +1069,11 @@ dependencies = [ [[package]] name = "coreaudio-sys" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9444b94b8024feecc29e01a9706c69c1e26bfee480221c90764200cfd778fb" +checksum = "f034b2258e6c4ade2f73bf87b21047567fb913ee9550837c2316d139b0262b24" dependencies = [ - "bindgen 0.61.0", + "bindgen 0.64.0", ] [[package]] @@ -1062,9 +1102,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ "libc", ] @@ -1165,9 +1205,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "cxx" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -1177,34 +1217,34 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", "once_cell", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "scratch", - "syn 1.0.109", + "syn 2.0.11", ] [[package]] name = "cxxbridge-flags" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.92" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 2.0.11", ] [[package]] @@ -1242,8 +1282,8 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "strsim 0.10.0", "syn 1.0.109", ] @@ -1255,7 +1295,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", - "quote 1.0.23", + "quote 1.0.26", "syn 1.0.109", ] @@ -1421,14 +1461,19 @@ checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" [[package]] name = "default-net" -version = "0.12.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e349ed1e06fb344a7dd8b5a676375cf671b31e8900075dd2be816efc063a63" +checksum = "a4898b43aed56499fad6b294d15b3e76a51df68079bf492e5daae38ca084e003" dependencies = [ + "dlopen2", "libc", "memalloc", + "netlink-packet-core", + "netlink-packet-route", + "netlink-sys", + "once_cell", "system-configuration", - "windows 0.30.0", + "windows 0.32.0", ] [[package]] @@ -1437,8 +1482,8 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "082a24a9967533dc5d743c602157637116fc1b52806d694a5a45e6f32567fcdd" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -1448,8 +1493,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -1563,6 +1608,29 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "dlopen2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b121caccfc363e4d9a4589528f3bef7c71b83c6ed01c8dc68cbeeb7fd29ec698" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi 0.3.9", +] + +[[package]] +name = "dlopen2_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a09ac8bb8c16a282264c379dffba707b9c998afc7506009137f3c6136888078" +dependencies = [ + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 1.0.109", +] + [[package]] name = "dlopen_derive" version = "0.1.4" @@ -1588,7 +1656,7 @@ checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f" dependencies = [ "lazy_static", "regex", - "serde 1.0.154", + "serde 1.0.159", "strsim 0.10.0", ] @@ -1611,7 +1679,7 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.154", + "serde 1.0.159", "serde_derive", "thiserror", ] @@ -1663,7 +1731,7 @@ dependencies = [ "objc", "pkg-config", "rdev", - "serde 1.0.154", + "serde 1.0.159", "serde_derive", "tfc", "unicode-segmentation", @@ -1671,10 +1739,30 @@ dependencies = [ ] [[package]] -name = "enum-map" -version = "2.4.2" +name = "enum-iterator" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c25992259941eb7e57b936157961b217a4fc8597829ddef0596d6c3cd86e1a" +checksum = "706d9e7cf1c7664859d79cd524e4e53ea2b67ea03c98cc2870c5e539695d597e" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f93763ef7b0ae1c43c4d8eccc9d5848d84ad1a1d8ce61c421d1ac85a19d05" +dependencies = [ + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "enum-map" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "988f0d17a0fa38291e5f41f71ea8d46a5d5497b9054d5a759fae2cbb819f2356" dependencies = [ "enum-map-derive", ] @@ -1685,8 +1773,8 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -1697,8 +1785,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" dependencies = [ "once_cell", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -1709,7 +1797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb" dependencies = [ "enumflags2_derive", - "serde 1.0.154", + "serde 1.0.159", ] [[package]] @@ -1718,8 +1806,8 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -1776,24 +1864,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34a887c8df3ed90498c1c437ce21f211c8e27672921a8ffa293cb8d6d4caa9e" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "rustversion", "syn 1.0.109", "synstructure", ] -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi 0.3.9", -] - [[package]] name = "errno" version = "0.3.0" @@ -1843,17 +1920,17 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "exr" -version = "1.5.3" +version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8af5ef47e2ed89d23d0ecbc1b681b30390069de70260937877514377fc24feb" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" dependencies = [ "bit_field", "flume", "half", "lebe", "miniz_oxide", + "rayon-core", "smallvec", - "threadpool", "zune-inflate", ] @@ -1868,9 +1945,9 @@ dependencies = [ [[package]] name = "fern" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a" +checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" dependencies = [ "chrono", "colored", @@ -1895,7 +1972,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "windows-sys 0.45.0", ] @@ -1937,14 +2014,14 @@ dependencies = [ "futures-sink", "nanorand", "pin-project", - "spin 0.9.5", + "spin 0.9.7", ] [[package]] name = "flutter_rust_bridge" -version = "1.68.0" +version = "1.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54f0d71ff30fc2ae7c18508b517488a89051d81e3bfc4d48d4a6cf54b5dab789" +checksum = "a85cf0e90d520425ac9bfca66e97b400782475d3091f4b75cadc210897c098c2" dependencies = [ "allo-isolate", "anyhow", @@ -1967,27 +2044,28 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_codegen" -version = "1.68.0" +version = "1.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a75a72411f0c5b480e4671417f52780172053128cf87d5614a9757d7680a0" +checksum = "77f6545fe9767e82d3817b511753273b26903cd3658970cbed03a729b409a550" dependencies = [ "anyhow", "atty", "cargo_metadata", "cbindgen", "chrono", - "clap 3.2.23", + "clap 4.2.1", "convert_case", "delegate", + "enum-iterator", "enum_dispatch", "fern", "itertools 0.10.5", "lazy_static", "log", "pathdiff", - "quote 1.0.23", + "quote 1.0.26", "regex", - "serde 1.0.154", + "serde 1.0.159", "serde_yaml", "strum_macros 0.24.3", "syn 1.0.109", @@ -1999,9 +2077,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" -version = "1.68.0" +version = "1.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6187d1635afede47c23a9979f85c3dc57e3391eb9c690b7fe95715bf945da72" +checksum = "6a65b1cb0d5cc83085f9cbccbeadd35c4e90a67166fa7d72ef984ba9a960a222" [[package]] name = "fnv" @@ -2076,9 +2154,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" dependencies = [ "futures-channel", "futures-core", @@ -2091,9 +2169,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" dependencies = [ "futures-core", "futures-sink", @@ -2101,15 +2179,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" dependencies = [ "futures-core", "futures-task", @@ -2118,9 +2196,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" [[package]] name = "futures-lite" @@ -2139,32 +2217,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" dependencies = [ "futures-channel", "futures-core", @@ -2217,7 +2295,7 @@ dependencies = [ "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] @@ -2234,7 +2312,7 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] @@ -2248,7 +2326,7 @@ dependencies = [ "gobject-sys 0.16.3", "libc", "pkg-config", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] @@ -2260,15 +2338,15 @@ dependencies = [ "gdk-sys", "glib-sys 0.16.3", "libc", - "system-deps 6.0.3", + "system-deps 6.0.4", "x11 2.21.0", ] [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -2299,9 +2377,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.11.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" dependencies = [ "color_quant", "weezl", @@ -2342,7 +2420,7 @@ dependencies = [ "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", - "system-deps 6.0.3", + "system-deps 6.0.4", "winapi 0.3.9", ] @@ -2378,7 +2456,7 @@ dependencies = [ "futures-task", "futures-util", "gio-sys", - "glib-macros 0.16.3", + "glib-macros 0.16.8", "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", @@ -2398,23 +2476,23 @@ dependencies = [ "itertools 0.9.0", "proc-macro-crate 0.1.5", "proc-macro-error", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] [[package]] name = "glib-macros" -version = "0.16.3" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" +checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" dependencies = [ "anyhow", "heck 0.4.1", "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -2435,7 +2513,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" dependencies = [ "libc", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] @@ -2463,7 +2541,7 @@ checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" dependencies = [ "glib-sys 0.16.3", "libc", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] @@ -2637,20 +2715,20 @@ dependencies = [ "gobject-sys 0.16.3", "libc", "pango-sys", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] name = "gtk3-macros" -version = "0.16.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" +checksum = "096eb63c6fedf03bafe65e5924595785eaf1bcb7200dac0f2cbe9c9738f05ad8" dependencies = [ "anyhow", "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -2718,16 +2796,16 @@ dependencies = [ "quinn", "rand 0.8.5", "regex", - "serde 1.0.154", + "serde 1.0.159", "serde_derive", - "serde_json 1.0.94", + "serde_json 1.0.95", "socket2 0.3.19", "sodiumoxide", "sysinfo", "tokio", "tokio-socks", "tokio-util", - "toml 0.7.2", + "toml 0.7.3", "winapi 0.3.9", "zstd", ] @@ -2831,16 +2909,16 @@ dependencies = [ "bindgen 0.59.2", "cc", "log", - "serde 1.0.154", + "serde 1.0.159", "serde_derive", - "serde_json 1.0.94", + "serde_json 1.0.95", ] [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" dependencies = [ "bytes", "futures-channel", @@ -2875,16 +2953,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" dependencies = [ "android_system_properties", "core-foundation-sys 0.8.3", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi 0.3.9", + "windows 0.46.0", ] [[package]] @@ -2915,9 +2993,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.5" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" dependencies = [ "bytemuck", "byteorder", @@ -2928,7 +3006,7 @@ dependencies = [ "num-rational 0.4.1", "num-traits 0.2.15", "png", - "scoped_threadpool", + "qoi", "tiff", ] @@ -2955,15 +3033,15 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", ] [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg 1.1.0", "hashbrown", @@ -3003,10 +3081,11 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" +checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" dependencies = [ + "hermit-abi 0.3.1", "libc", "windows-sys 0.45.0", ] @@ -3022,15 +3101,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", @@ -3146,7 +3225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68" dependencies = [ "bitflags", - "serde 1.0.154", + "serde 1.0.159", "unicode-segmentation", ] @@ -3194,9 +3273,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libdbus-sys" @@ -3331,9 +3410,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d" [[package]] name = "lock_api" @@ -3456,9 +3535,9 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" @@ -3558,9 +3637,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee091608fe840d80c6b25e8f838964b264ee8e02e82ae0a38b2d900464d1582" +checksum = "1a3ef954ff22d2646c21ae64171b76d6e1202d214c6d3867305489d04a06db6a" dependencies = [ "cocoa", "crossbeam-channel", @@ -3660,8 +3739,8 @@ checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ "darling", "proc-macro-crate 1.3.1", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -3700,6 +3779,55 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "netlink-packet-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e5cf0b54effda4b91615c40ff0fd12d0d4c9a6e0f5116874f03941792ff535a" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea993e32c77d87f01236c38f572ecb6c311d592e56a06262a007fd2a6e31253c" +dependencies = [ + "anyhow", + "bitflags", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-sys" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +dependencies = [ + "bytes", + "libc", + "log", +] + [[package]] name = "nix" version = "0.22.3" @@ -3807,8 +3935,8 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -3888,8 +4016,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -4010,9 +4138,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "osascript" @@ -4020,9 +4148,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38731fa859ef679f1aec66ca9562165926b442f298467f76f5990f431efe87dc" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", "serde_derive", - "serde_json 1.0.94", + "serde_json 1.0.95", ] [[package]] @@ -4054,7 +4182,7 @@ dependencies = [ "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", - "system-deps 6.0.3", + "system-deps 6.0.4", ] [[package]] @@ -4108,7 +4236,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi 0.3.9", ] @@ -4121,7 +4249,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -4203,8 +4331,8 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -4228,15 +4356,15 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "plist" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffac6a51110e97610dd3ac73e34a65b27e56a1e305df41bad1f616d8e1cb22f4" +checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" dependencies = [ "base64", "indexmap", "line-wrap", "quick-xml", - "serde 1.0.154", + "serde 1.0.159", "time 0.3.20", ] @@ -4315,8 +4443,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", "version_check", ] @@ -4327,8 +4455,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "version_check", ] @@ -4343,9 +4471,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" dependencies = [ "unicode-ident", ] @@ -4402,6 +4530,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quest" version = "0.3.0" @@ -4417,9 +4554,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.27.1" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc053f057dd768a56f62cd7e434c42c831d296968997e9ac1f76ea7c2d14c41" +checksum = "e5c1a97b1bc42b1d550bfb48d4262153fe400a12bab1511821736f7eac76d7e2" dependencies = [ "memchr", ] @@ -4444,9 +4581,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9" +checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" dependencies = [ "bytes", "rand 0.8.5", @@ -4485,11 +4622,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ - "proc-macro2 1.0.51", + "proc-macro2 1.0.54", ] [[package]] @@ -4722,6 +4859,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -4729,15 +4875,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" dependencies = [ "aho-corasick", "memchr", @@ -4746,9 +4892,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "repng" @@ -4762,9 +4908,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64", "bytes", @@ -4785,8 +4931,8 @@ dependencies = [ "pin-project-lite", "rustls", "rustls-pemfile", - "serde 1.0.154", - "serde_json 1.0.94", + "serde 1.0.159", + "serde_json 1.0.95", "serde_urlencoded", "tokio", "tokio-rustls", @@ -4897,9 +5043,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustc-hash" @@ -4922,7 +5068,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.16", + "semver 1.0.17", ] [[package]] @@ -4939,7 +5085,7 @@ dependencies = [ "cfg-if 1.0.0", "chrono", "cidr-utils", - "clap 4.1.8", + "clap 4.2.1", "clipboard", "cocoa", "core-foundation 0.9.3", @@ -4954,7 +5100,7 @@ dependencies = [ "dispatch", "dlopen", "enigo", - "errno 0.3.0", + "errno", "evdev", "flutter_rust_bridge", "flutter_rust_bridge_codegen", @@ -4989,9 +5135,9 @@ dependencies = [ "samplerate", "sciter-rs", "scrap", - "serde 1.0.154", + "serde 1.0.159", "serde_derive", - "serde_json 1.0.94", + "serde_json 1.0.95", "sha2", "shared_memory", "shutdown_hooks", @@ -5040,12 +5186,12 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.9" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "0e78cc525325c06b4a7ff02db283472f3c042b7ff0c391f96c6d5ac6f4f91b75" dependencies = [ "bitflags", - "errno 0.2.8", + "errno", "io-lifetimes", "libc", "linux-raw-sys", @@ -5147,12 +5293,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - [[package]] name = "scopeguard" version = "1.1.0" @@ -5181,8 +5321,8 @@ dependencies = [ "num_cpus", "quest", "repng", - "serde 1.0.154", - "serde_json 1.0.94", + "serde 1.0.159", + "serde_json 1.0.95", "target_build_utils", "tracing", "webm", @@ -5239,11 +5379,11 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", ] [[package]] @@ -5260,22 +5400,22 @@ checksum = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" [[package]] name = "serde" -version = "1.0.154" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.154" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 2.0.11", ] [[package]] @@ -5292,24 +5432,24 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" dependencies = [ "itoa 1.0.6", "ryu", - "serde 1.0.154", + "serde 1.0.159", ] [[package]] name = "serde_repr" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc" +checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 2.0.11", ] [[package]] @@ -5318,7 +5458,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", ] [[package]] @@ -5330,7 +5470,7 @@ dependencies = [ "form_urlencoded", "itoa 1.0.6", "ryu", - "serde 1.0.154", + "serde 1.0.159", ] [[package]] @@ -5341,7 +5481,7 @@ checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ "indexmap", "ryu", - "serde 1.0.154", + "serde 1.0.159", "yaml-rust", ] @@ -5434,9 +5574,9 @@ checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[package]] name = "simd-adler32" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a5df39617d7c8558154693a1bb8157a4aab8179209540cc0b10e5dc24e0b18" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" [[package]] name = "simple_rc" @@ -5444,7 +5584,7 @@ version = "0.1.0" dependencies = [ "confy", "hbb_common", - "serde 1.0.154", + "serde 1.0.159", "serde_derive", "walkdir", ] @@ -5519,7 +5659,7 @@ dependencies = [ "ed25519", "libc", "libsodium-sys", - "serde 1.0.154", + "serde 1.0.159", ] [[package]] @@ -5530,9 +5670,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dccf47db1b41fa1573ed27ccf5e08e3ca771cb994f776668c5ebda893b248fc" +checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef" dependencies = [ "lock_api", ] @@ -5563,9 +5703,9 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "serde 1.0.154", + "proc-macro2 1.0.54", + "quote 1.0.26", + "serde 1.0.159", "serde_derive", "syn 1.0.109", ] @@ -5577,11 +5717,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" dependencies = [ "base-x", - "proc-macro2 1.0.51", - "quote 1.0.23", - "serde 1.0.154", + "proc-macro2 1.0.54", + "quote 1.0.26", + "serde 1.0.159", "serde_derive", - "serde_json 1.0.94", + "serde_json 1.0.95", "sha1 0.6.1", "syn 1.0.109", ] @@ -5635,8 +5775,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -5647,8 +5787,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "rustversion", "syn 1.0.109", ] @@ -5670,8 +5810,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40" +dependencies = [ + "proc-macro2 1.0.54", + "quote 1.0.26", "unicode-ident", ] @@ -5681,8 +5832,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", "unicode-xid 0.2.4", ] @@ -5702,9 +5853,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.28.2" +version = "0.28.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56" +checksum = "b4c2f3ca6693feb29a89724516f016488e9aafc7f37264f898593ee4b942f31b" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys 0.8.3", @@ -5753,14 +5904,14 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.0.3" +version = "6.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +checksum = "555fc8147af6256f3931a36bb83ad0023240ce9cf2b319dec8236fd1f220b05f" dependencies = [ "cfg-expr", "heck 0.4.1", "pkg-config", - "toml 0.5.11", + "toml 0.7.3", "version-compare 0.1.1", ] @@ -5776,8 +5927,8 @@ dependencies = [ [[package]] name = "tao" -version = "0.17.0" -source = "git+https://github.com/tauri-apps/tao?branch=muda#676bd90a80286b893d8850cc4e3813a0c4a27dcf" +version = "0.18.1" +source = "git+https://github.com/tauri-apps/tao?branch=muda#0c1417884161d165b8852fbf11be5492edef9fe7" dependencies = [ "bitflags", "cairo-rs", @@ -5821,11 +5972,11 @@ dependencies = [ [[package]] name = "tao-macros" -version = "0.1.0" -source = "git+https://github.com/tauri-apps/tao?branch=muda#676bd90a80286b893d8850cc4e3813a0c4a27dcf" +version = "0.1.1" +source = "git+https://github.com/tauri-apps/tao?branch=muda#0c1417884161d165b8852fbf11be5492edef9fe7" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -5848,15 +5999,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -5906,22 +6057,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 2.0.11", ] [[package]] @@ -5962,7 +6113,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "itoa 1.0.6", - "serde 1.0.154", + "serde 1.0.159", "time-core", "time-macros", ] @@ -5999,14 +6150,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg 1.1.0", "bytes", "libc", - "memchr", "mio 0.8.6", "num_cpus", "parking_lot 0.12.1", @@ -6019,13 +6169,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", - "syn 1.0.109", + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 2.0.11", ] [[package]] @@ -6079,16 +6229,16 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", ] [[package]] name = "toml" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", "serde_spanned", "toml_datetime", "toml_edit", @@ -6100,17 +6250,17 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", ] [[package]] name = "toml_edit" -version = "0.19.4" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", - "serde 1.0.154", + "serde 1.0.159", "serde_spanned", "toml_datetime", "winnow", @@ -6146,8 +6296,8 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -6172,9 +6322,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87445e3a107818c17d87e8369db30a6fc25539bface8351efe2132b22e47dbc" +checksum = "405009de7f59e6dafff7afc04e1bff017d5e1531359bd05a1c52375b6f8b3716" dependencies = [ "cocoa", "core-graphics 0.22.3", @@ -6231,9 +6381,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" @@ -6289,9 +6439,15 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde 1.0.154", + "serde 1.0.159", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.0" @@ -6362,12 +6518,11 @@ checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi 0.3.9", "winapi-util", ] @@ -6412,8 +6567,8 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", "wasm-bindgen-shared", ] @@ -6436,7 +6591,7 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ - "quote 1.0.23", + "quote 1.0.26", "wasm-bindgen-macro-support", ] @@ -6446,8 +6601,8 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -6516,8 +6671,8 @@ version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "xml-rs", ] @@ -6598,9 +6753,9 @@ dependencies = [ [[package]] name = "whoami" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45dbc71f0cdca27dc261a9bd37ddec174e4a0af2b900b890f378460f745426e3" +checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" dependencies = [ "wasm-bindgen", "web-sys", @@ -6681,15 +6836,15 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0" +checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec" dependencies = [ - "windows_aarch64_msvc 0.30.0", - "windows_i686_gnu 0.30.0", - "windows_i686_msvc 0.30.0", - "windows_x86_64_gnu 0.30.0", - "windows_x86_64_msvc 0.30.0", + "windows_aarch64_msvc 0.32.0", + "windows_i686_gnu 0.32.0", + "windows_i686_msvc 0.32.0", + "windows_x86_64_gnu 0.32.0", + "windows_x86_64_msvc 0.32.0", ] [[package]] @@ -6729,14 +6884,23 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-implement" version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -6746,8 +6910,8 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] @@ -6829,9 +6993,9 @@ checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" [[package]] name = "windows_aarch64_msvc" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" [[package]] name = "windows_aarch64_msvc" @@ -6859,9 +7023,9 @@ checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" [[package]] name = "windows_i686_gnu" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" [[package]] name = "windows_i686_gnu" @@ -6889,9 +7053,9 @@ checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" [[package]] name = "windows_i686_msvc" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" [[package]] name = "windows_i686_msvc" @@ -6919,9 +7083,9 @@ checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" [[package]] name = "windows_x86_64_gnu" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" [[package]] name = "windows_x86_64_gnu" @@ -6955,9 +7119,9 @@ checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" [[package]] name = "windows_x86_64_msvc" -version = "0.30.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" [[package]] name = "windows_x86_64_msvc" @@ -7012,9 +7176,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" dependencies = [ "memchr", ] @@ -7149,9 +7313,9 @@ dependencies = [ [[package]] name = "zbus" -version = "3.11.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20aae5dd5b051971cd2f49f9f3b860e57b2b495ba5ba254eaec42d34ede57e97" +checksum = "3dc29e76f558b2cb94190e8605ecfe77dd40f5df8c072951714b4b71a97f5848" dependencies = [ "async-broadcast", "async-executor", @@ -7174,7 +7338,7 @@ dependencies = [ "once_cell", "ordered-stream", "rand 0.8.5", - "serde 1.0.154", + "serde 1.0.159", "serde_repr", "sha1 0.10.5", "static_assertions", @@ -7188,13 +7352,13 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.11.0" +version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9264b3a1bcf5503d4e0348b6e7efe1da58d4f92a913c15ed9e63b52de85faaa1" +checksum = "62a80fd82c011cd08459eaaf1fd83d3090c1b61e6d5284360074a7475af3a85d" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "regex", "syn 1.0.109", "zvariant_utils", @@ -7206,7 +7370,7 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" dependencies = [ - "serde 1.0.154", + "serde 1.0.159", "static_assertions", "zvariant", ] @@ -7242,9 +7406,9 @@ dependencies = [ [[package]] name = "zune-inflate" -version = "0.2.51" +version = "0.2.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01728b79fb9b7e28a8c11f715e1cd8dc2cda7416a007d66cac55cebb3a8ac6b" +checksum = "440a08fd59c6442e4b846ea9b10386c38307eae728b216e1ab2c305d1c9daaf8" dependencies = [ "simd-adler32", ] @@ -7258,7 +7422,7 @@ dependencies = [ "byteorder", "enumflags2", "libc", - "serde 1.0.154", + "serde 1.0.159", "static_assertions", "zvariant_derive", ] @@ -7270,8 +7434,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34c20260af4b28b3275d6676c7e2a6be0d4332e8e0aba4616d34007fd84e462a" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", "zvariant_utils", ] @@ -7282,7 +7446,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" dependencies = [ - "proc-macro2 1.0.51", - "quote 1.0.23", + "proc-macro2 1.0.54", + "quote 1.0.26", "syn 1.0.109", ] diff --git a/Cargo.toml b/Cargo.toml index 990ccb352..105c7da28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,18 +57,17 @@ rpassword = "7.0" base64 = "0.21" num_cpus = "1.13" bytes = { version = "1.2", features = ["serde"] } -default-net = "0.12.0" +default-net = "0.14" wol-rs = "1.0" flutter_rust_bridge = { version = "1.61.1", optional = true } errno = "0.3" rdev = { git = "https://github.com/fufesou/rdev" } url = { version = "2.1", features = ["serde"] } dlopen = "0.1" -hex = "0.4.3" - +hex = "0.4" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } -chrono = "0.4.23" -cidr-utils = "0.5.9" +chrono = "0.4" +cidr-utils = "0.5" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] cpal = "0.14" @@ -94,8 +93,8 @@ winreg = "0.10" windows-service = "0.4" virtual_display = { path = "libs/virtual_display" } impersonate_system = { git = "https://github.com/21pages/impersonate-system" } -shared_memory = "0.12.4" -shutdown_hooks = "0.1.0" +shared_memory = "0.12" +shutdown_hooks = "0.1" [target.'cfg(target_os = "macos")'.dependencies] objc = "0.2" @@ -103,10 +102,10 @@ cocoa = "0.24" dispatch = "0.2" core-foundation = "0.9" core-graphics = "0.22" -include_dir = "0.7.2" +include_dir = "0.7" dark-light = "1.0" -fruitbasket = "0.10.0" -objc_id = "0.1.1" +fruitbasket = "0.10" +objc_id = "0.1" [target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies] tray-icon = "0.4" diff --git a/res/icon.ico b/res/icon.ico index 75324b38c4b4245d8033ab08d1fb4f4ca0a7c29e..bdf42cfdb18749e37685c365808eb09d6e4daabb 120000 GIT binary patch literal 1961 zcmV;a2Uhq10096205C8B0000W0Fws*02TlM0EtjeM-2)Z3IG5A4M|8uQUCw|FaQ7m zFbDJS(6|L7-2^8#pKJ#=aA=+b9lJM0QQjoO}?Le zA9+wZX&P~10r^4l*$)z(4r2jifvk%7#}I>jm7SCbFz~0z$3Fys@md-AALI+jFEav6 zHZ1aw0(i-C$q$p4F#>Elk|G#c)&m>2sjLas9@$x_(m6w7n zvtcwpU&9DIlgP*(ACIjL54KX#A(?k#F6K_y*yU_K7{&P{t?ku2dIYxJF;0>f4w&m0-WjI*NE2I&!g(s0kpV7 zqQ;>gpq^`|cYtm&e4o-Z0zZd=Pdt@s64>1w#4oKOG0X5OGY~oE+6<^IwqZqq1rt-1qv@p7 zd*GG_b!{F@rE9jAW-5YrQ7w7T(SvK=0KO{7!lE2|`~Hdq$ideZ+3;n# zJzS);EYC_L3sS?bW}W^JUT)|@L%%;9u%*r2Xqh)2c`6c9C9wHy06jxch>!k9p9s(a6AzAUbvwZ-cP02v2Q7$({0zb7-Bj&|$g;gL?TV_VhwB&$iwz{zo z7y1HEWYiKZZoQo%&ijTR7kfjRwP1o(-VezL6tO2R(xO!$(byr*LT*ZO>%eU)WX-OSxWQE+QIsZE zyzHClV}ML5vVA^f5PATc>Jx$;?mZU}c+a^Te4m+E|* z;#g$@Gl36}0R4VWMxtzj0pGrwyy)+{>c@o`&pxl3Vnde7zFD;^+fV`Q{F8&rE8b-GB;fc; z4%zn6gYWYRSa-&YosK}{*(p>jznu_nEKXog%%Z&a9RVAD<}f`E_^uj?;JZn?tB-}y z+9%-MQUjLEFd#QonPgi(k3Ehcwl(_U^a{h6BJu=VTkq0$6VquIwjd2AgSKQ7BEV!*xl4ef z1BiP5sn!6hRCPrXB&RIu$LcH7FiuD3n-GCz`+=r*$YqcO5)(;w^$Sx_mZL9KCq&?? z6Uw``QIdFJoCybCvFVEZNCJ^=NxyAg1g!Xxqe7RURAR14A<1l-XTiF1^F1{mV*(?< zULxnjGwRJ*9G;sjP@&_n>zqKlxWq`LQo&qVV#3C`rbPGIE=GX80k4X8~$QhQKjq|SF#g|UGV_#qJjlBE%VRdYbj!5^Z0 z-yuHPXz!<9$|r Date: Wed, 29 Mar 2023 21:59:25 +0800 Subject: [PATCH 117/366] video data queue for avoid data accumulation Signed-off-by: 21pages --- Cargo.lock | 1 + Cargo.toml | 1 + src/client.rs | 20 ++++++++++++++------ src/client/io_loop.rs | 7 ++++++- src/ui_session_interface.rs | 10 ++++++---- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86bf2e7ab..1c65370be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4945,6 +4945,7 @@ dependencies = [ "core-foundation 0.9.3", "core-graphics 0.22.3", "cpal", + "crossbeam-queue", "ctrlc", "dark-light", "dasp", diff --git a/Cargo.toml b/Cargo.toml index 990ccb352..28e55b1a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ rdev = { git = "https://github.com/fufesou/rdev" } url = { version = "2.1", features = ["serde"] } dlopen = "0.1" hex = "0.4.3" +crossbeam-queue = "0.3" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } chrono = "0.4.23" diff --git a/src/client.rs b/src/client.rs index 1efd8b18e..39703e55a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -13,6 +13,7 @@ use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, Device, Host, StreamConfig, }; +use crossbeam_queue::ArrayQueue; use magnum_opus::{Channels::*, Decoder as AudioDecoder}; #[cfg(not(any(target_os = "android", target_os = "linux")))] use ringbuf::{ring_buffer::RbBase, Rb}; @@ -67,6 +68,7 @@ pub mod io_loop; pub const MILLI1: Duration = Duration::from_millis(1); pub const SEC30: Duration = Duration::from_secs(30); +pub const VIDEO_QUEUE_SIZE: usize = 120; /// Client of the remote desktop. pub struct Client; @@ -1659,7 +1661,7 @@ impl LoginConfigHandler { /// Media data. pub enum MediaData { - VideoFrame(VideoFrame), + VideoFrame, AudioFrame(AudioFrame), AudioFormat(AudioFormat), Reset, @@ -1674,11 +1676,15 @@ pub type MediaSender = mpsc::Sender; /// # Arguments /// /// * `video_callback` - The callback for video frame. Being called when a video frame is ready. -pub fn start_video_audio_threads(video_callback: F) -> (MediaSender, MediaSender) +pub fn start_video_audio_threads( + video_callback: F, +) -> (MediaSender, MediaSender, Arc>) where F: 'static + FnMut(&mut Vec) + Send, { let (video_sender, video_receiver) = mpsc::channel::(); + let video_queue = Arc::new(ArrayQueue::::new(VIDEO_QUEUE_SIZE)); + let video_queue_cloned = video_queue.clone(); let mut video_callback = video_callback; std::thread::spawn(move || { @@ -1686,9 +1692,11 @@ where loop { if let Ok(data) = video_receiver.recv() { match data { - MediaData::VideoFrame(vf) => { - if let Ok(true) = video_handler.handle_frame(vf) { - video_callback(&mut video_handler.rgb); + MediaData::VideoFrame => { + if let Some(vf) = video_queue.pop() { + if let Ok(true) = video_handler.handle_frame(vf) { + video_callback(&mut video_handler.rgb); + } } } MediaData::Reset => { @@ -1706,7 +1714,7 @@ where log::info!("Video decoder loop exits"); }); let audio_sender = start_audio_thread(); - return (video_sender, audio_sender); + return (video_sender, audio_sender, video_queue_cloned); } /// Start an audio thread diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 135e92a75..6b0e139e2 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -9,6 +9,7 @@ use std::sync::{ #[cfg(windows)] use clipboard::{cliprdr::CliprdrClientContext, ContextSend}; +use crossbeam_queue::ArrayQueue; use hbb_common::config::{PeerConfig, TransferSerde}; use hbb_common::fs::{ can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, @@ -42,6 +43,7 @@ use crate::{client::Data, client::Interface}; pub struct Remote { handler: Session, + video_queue: Arc>, video_sender: MediaSender, audio_sender: MediaSender, receiver: mpsc::UnboundedReceiver, @@ -68,6 +70,7 @@ pub struct Remote { impl Remote { pub fn new( handler: Session, + video_queue: Arc>, video_sender: MediaSender, audio_sender: MediaSender, receiver: mpsc::UnboundedReceiver, @@ -76,6 +79,7 @@ impl Remote { ) -> Self { Self { handler, + video_queue, video_sender, audio_sender, receiver, @@ -830,7 +834,8 @@ impl Remote { ..Default::default() }) }; - self.video_sender.send(MediaData::VideoFrame(vf)).ok(); + self.video_queue.force_push(vf); + self.video_sender.send(MediaData::VideoFrame).ok(); } Some(message::Union::Hash(hash)) => { self.handler diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 7ee49a3b7..f89be4879 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1149,13 +1149,15 @@ pub async fn io_loop(handler: Session) { let frame_count = Arc::new(AtomicUsize::new(0)); let frame_count_cl = frame_count.clone(); let ui_handler = handler.ui_handler.clone(); - let (video_sender, audio_sender) = start_video_audio_threads(move |data: &mut Vec| { - frame_count_cl.fetch_add(1, Ordering::Relaxed); - ui_handler.on_rgba(data); - }); + let (video_sender, audio_sender, video_queue) = + start_video_audio_threads(move |data: &mut Vec| { + frame_count_cl.fetch_add(1, Ordering::Relaxed); + ui_handler.on_rgba(data); + }); let mut remote = Remote::new( handler, + video_queue, video_sender, audio_sender, receiver, From ede048bbd00ab0ffcf8f7593d31683a5f635fa91 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 30 Mar 2023 11:47:08 +0800 Subject: [PATCH 118/366] clear video queue if receive key frame, send key frame with channel Signed-off-by: 21pages --- src/client.rs | 14 ++++++++++---- src/client/io_loop.rs | 27 ++++++++++++++++++++++++--- src/server/connection.rs | 2 +- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/client.rs b/src/client.rs index 39703e55a..2f745b70c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1661,8 +1661,9 @@ impl LoginConfigHandler { /// Media data. pub enum MediaData { - VideoFrame, - AudioFrame(AudioFrame), + VideoQueue, + VideoFrame(Box), + AudioFrame(Box), AudioFormat(AudioFormat), Reset, RecordScreen(bool, i32, i32, String), @@ -1692,7 +1693,12 @@ where loop { if let Ok(data) = video_receiver.recv() { match data { - MediaData::VideoFrame => { + MediaData::VideoFrame(vf) => { + if let Ok(true) = video_handler.handle_frame(*vf) { + video_callback(&mut video_handler.rgb); + } + } + MediaData::VideoQueue => { if let Some(vf) = video_queue.pop() { if let Ok(true) = video_handler.handle_frame(vf) { video_callback(&mut video_handler.rgb); @@ -1727,7 +1733,7 @@ pub fn start_audio_thread() -> MediaSender { if let Ok(data) = audio_receiver.recv() { match data { MediaData::AudioFrame(af) => { - audio_handler.handle_frame(af); + audio_handler.handle_frame(*af); } MediaData::AudioFormat(f) => { log::debug!("recved audio format, sample rate={}", f.sample_rate); diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 6b0e139e2..b0bddc82e 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -816,6 +816,18 @@ impl Remote { } } + fn contains_key_frame(vf: &VideoFrame) -> bool { + match &vf.union { + Some(vf) => match vf { + video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key), + video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key), + video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key), + _ => false, + }, + None => false, + } + } + async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { if let Ok(msg_in) = Message::parse_from_bytes(&data) { match msg_in.union { @@ -834,8 +846,15 @@ impl Remote { ..Default::default() }) }; - self.video_queue.force_push(vf); - self.video_sender.send(MediaData::VideoFrame).ok(); + if Self::contains_key_frame(&vf) { + while let Some(_) = self.video_queue.pop() {} + self.video_sender + .send(MediaData::VideoFrame(Box::new(vf))) + .ok(); + } else { + self.video_queue.force_push(vf); + self.video_sender.send(MediaData::VideoQueue).ok(); + } } Some(message::Union::Hash(hash)) => { self.handler @@ -1222,7 +1241,9 @@ impl Remote { } Some(message::Union::AudioFrame(frame)) => { if !self.handler.lc.read().unwrap().disable_audio.v { - self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); + self.audio_sender + .send(MediaData::AudioFrame(Box::new(frame))) + .ok(); } } Some(message::Union::FileAction(action)) => match action.union { diff --git a/src/server/connection.rs b/src/server/connection.rs index 6b0cbefc3..0c17fd176 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1669,7 +1669,7 @@ impl Connection { Some(message::Union::AudioFrame(frame)) => { if !self.disable_audio { if let Some(sender) = &self.audio_sender { - allow_err!(sender.send(MediaData::AudioFrame(frame))); + allow_err!(sender.send(MediaData::AudioFrame(Box::new(frame)))); } else { log::warn!( "Processing audio frame without the voice call audio sender." From 54a809f499d3624959f85cc87f3592454fee3a1e Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 30 Mar 2023 20:08:34 +0800 Subject: [PATCH 119/366] set hwcodec key frame interval to max Signed-off-by: 21pages --- libs/scrap/src/common/hwcodec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index e5ede45b1..2c69774fb 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -30,7 +30,7 @@ const CFG_KEY_DECODER: &str = "bestHwDecoders"; const DEFAULT_PIXFMT: AVPixelFormat = AVPixelFormat::AV_PIX_FMT_YUV420P; pub const DEFAULT_TIME_BASE: [i32; 2] = [1, 30]; -const DEFAULT_GOP: i32 = 60; +const DEFAULT_GOP: i32 = i32::MAX; const DEFAULT_HW_QUALITY: Quality = Quality_Default; const DEFAULT_RC: RateControl = RC_DEFAULT; From fb3744398c10e8953c02883c635b079390afa248 Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Thu, 30 Mar 2023 14:09:05 +0200 Subject: [PATCH 120/366] Update pl.rs --- src/lang/pl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 5866886e7..388f16597 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -478,7 +478,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Empty Password", "Puste hasło"), ("Me", "Ja"), ("identical_file_tip", "Ten plik jest identyczny jak plik na drugim komputerze."), - ("show_monitors_tip", "Pokaż monitory w zasobniku."), + ("show_monitors_tip", "Pokaż monitory w zasobniku"), ("View Mode", "Tryb widoku"), ("enter_rustdesk_passwd_tip", "Podaj hasło RustDesk."), ("remember_rustdesk_passwd_tip", "Zapamiętaj hasło RustDesk."), From d7d4935339b26508d30652f52a7881dba43356d4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 30 Mar 2023 23:46:35 +0800 Subject: [PATCH 121/366] fix icon.ico symbol link --- res/icon.ico | Bin 1961 -> 48 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/res/icon.ico b/res/icon.ico index bdf42cfdb18749e37685c365808eb09d6e4daabb..75324b38c4b4245d8033ab08d1fb4f4ca0a7c29e 120000 GIT binary patch literal 48 zcmdPX(@)DOEh$MY(l5`JS(6|L7-2^8#pKJ#=aA=+b9lJM0QQjoO}?Le zA9+wZX&P~10r^4l*$)z(4r2jifvk%7#}I>jm7SCbFz~0z$3Fys@md-AALI+jFEav6 zHZ1aw0(i-C$q$p4F#>Elk|G#c)&m>2sjLas9@$x_(m6w7n zvtcwpU&9DIlgP*(ACIjL54KX#A(?k#F6K_y*yU_K7{&P{t?ku2dIYxJF;0>f4w&m0-WjI*NE2I&!g(s0kpV7 zqQ;>gpq^`|cYtm&e4o-Z0zZd=Pdt@s64>1w#4oKOG0X5OGY~oE+6<^IwqZqq1rt-1qv@p7 zd*GG_b!{F@rE9jAW-5YrQ7w7T(SvK=0KO{7!lE2|`~Hdq$ideZ+3;n# zJzS);EYC_L3sS?bW}W^JUT)|@L%%;9u%*r2Xqh)2c`6c9C9wHy06jxch>!k9p9s(a6AzAUbvwZ-cP02v2Q7$({0zb7-Bj&|$g;gL?TV_VhwB&$iwz{zo z7y1HEWYiKZZoQo%&ijTR7kfjRwP1o(-VezL6tO2R(xO!$(byr*LT*ZO>%eU)WX-OSxWQE+QIsZE zyzHClV}ML5vVA^f5PATc>Jx$;?mZU}c+a^Te4m+E|* z;#g$@Gl36}0R4VWMxtzj0pGrwyy)+{>c@o`&pxl3Vnde7zFD;^+fV`Q{F8&rE8b-GB;fc; z4%zn6gYWYRSa-&YosK}{*(p>jznu_nEKXog%%Z&a9RVAD<}f`E_^uj?;JZn?tB-}y z+9%-MQUjLEFd#QonPgi(k3Ehcwl(_U^a{h6BJu=VTkq0$6VquIwjd2AgSKQ7BEV!*xl4ef z1BiP5sn!6hRCPrXB&RIu$LcH7FiuD3n-GCz`+=r*$YqcO5)(;w^$Sx_mZL9KCq&?? z6Uw``QIdFJoCybCvFVEZNCJ^=NxyAg1g!Xxqe7RURAR14A<1l-XTiF1^F1{mV*(?< zULxnjGwRJ*9G;sjP@&_n>zqKlxWq`LQo&qVV#3C`rbPGIE=GX80k4X8~$QhQKjq|SF#g|UGV_#qJjlBE%VRdYbj!5^Z0 z-yuHPXz!<9$|r Date: Thu, 30 Mar 2023 14:23:14 -0300 Subject: [PATCH 122/366] fix: Add missing parameter (retry_for_relay) --- flutter/web/js/gen_js_from_hbb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/web/js/gen_js_from_hbb.py b/flutter/web/js/gen_js_from_hbb.py index 8ee553b35..cfa95ffe0 100755 --- a/flutter/web/js/gen_js_from_hbb.py +++ b/flutter/web/js/gen_js_from_hbb.py @@ -59,7 +59,7 @@ def main(): b = toks[1].replace('ControlKey(ControlKey::', '').replace("Chr('", '').replace("' as _)),", '').replace(')),', '') KEY_MAP[0] += ' "%s": "%s",\n'%(a, b) print() - print('export function checkIfRetry(msgtype: string, title: string, text: string) {') + print('export function checkIfRetry(msgtype: string, title: string, text: string, retry_for_relay: boolean) {') print(' return %s'%check_if_retry[0].replace('to_lowercase', 'toLowerCase').replace('contains', 'indexOf').replace('!', '').replace('")', '") < 0')) print(';}') print() From e6e36d38a3e5cf71eb2bb2e1354075be239acd1e Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 11:48:37 +0800 Subject: [PATCH 123/366] translate mode, win, fix hotkeys Signed-off-by: fufesou --- src/keyboard.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 294e058c5..e7de62329 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -843,7 +843,7 @@ pub fn map_keyboard_mode(_peer: &str, event: &Event, mut key_event: KeyEvent) -> } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec) { +fn try_fill_unicode(peer: &str, event: &Event, key_event: &KeyEvent, events: &mut Vec) { match &event.unicode { Some(unicode_info) => { if let Some(name) = &unicode_info.name { @@ -857,11 +857,13 @@ fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec { #[cfg(target_os = "windows")] - if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { - if let Some(chr) = get_char_by_vk(event.platform_code as u32) { - let mut evt = key_event.clone(); - evt.set_seq(chr.to_string()); - events.push(evt); + if peer == OS_LOWER_LINUX { + if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { + if let Some(chr) = get_char_by_vk(event.platform_code as u32) { + let mut evt = key_event.clone(); + evt.set_seq(chr.to_string()); + events.push(evt); + } } } } @@ -886,7 +888,12 @@ fn is_hot_key_modifiers_down() -> bool { #[cfg(target_os = "windows")] pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option { let mut key_event = map_keyboard_mode(peer, event, key_event)?; - key_event.set_chr((key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16)); + let chr = if peer == OS_LOWER_WINDOWS { + (key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16) + } else { + key_event.chr() + }; + key_event.set_chr(chr); Some(key_event) } @@ -962,7 +969,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(any(target_os = "linux", target_os = "windows"))] if is_press(event) { - try_fill_unicode(event, &key_event, &mut events); + try_fill_unicode(peer, event, &key_event, &mut events); } #[cfg(target_os = "windows")] From 8cbb367b79d968b67a47c891d899d0e8cf173f71 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 12:13:50 +0800 Subject: [PATCH 124/366] fix build Signed-off-by: fufesou --- src/keyboard.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index e7de62329..93bcf7ff0 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -843,7 +843,7 @@ pub fn map_keyboard_mode(_peer: &str, event: &Event, mut key_event: KeyEvent) -> } #[cfg(not(any(target_os = "android", target_os = "ios")))] -fn try_fill_unicode(peer: &str, event: &Event, key_event: &KeyEvent, events: &mut Vec) { +fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &mut Vec) { match &event.unicode { Some(unicode_info) => { if let Some(name) = &unicode_info.name { @@ -857,7 +857,7 @@ fn try_fill_unicode(peer: &str, event: &Event, key_event: &KeyEvent, events: &mu None => { #[cfg(target_os = "windows")] - if peer == OS_LOWER_LINUX { + if _peer == OS_LOWER_LINUX { if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { if let Some(chr) = get_char_by_vk(event.platform_code as u32) { let mut evt = key_event.clone(); @@ -981,7 +981,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(target_os = "macos")] if !unsafe { IS_LEFT_OPTION_DOWN } { - try_fill_unicode(event, &key_event, &mut events); + try_fill_unicode(peer, event, &key_event, &mut events); } if events.is_empty() { From 0f72af652973f1c65c2476a24f3d9db5b62d81d3 Mon Sep 17 00:00:00 2001 From: asur4s Date: Wed, 29 Mar 2023 00:40:05 -0700 Subject: [PATCH 125/366] Release modifier when multi conn --- src/keyboard.rs | 25 ++++++++++++++++++++++ src/platform/mod.rs | 2 +- src/server/connection.rs | 42 ++++++++++++++++++++++++++++++++++++- src/server/input_service.rs | 2 +- 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 294e058c5..0e271a0b6 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -365,6 +365,21 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { } } +#[inline] +pub fn is_modifier(key: &rdev::Key) -> bool { + matches!( + key, + Key::ShiftLeft + | Key::ShiftRight + | Key::ControlLeft + | Key::ControlRight + | Key::MetaLeft + | Key::MetaRight + | Key::Alt + | Key::AltGr + ) +} + #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] fn is_numpad_key(event: &Event) -> bool { @@ -984,3 +999,13 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } events } + +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn keycode_to_rdev_key(keycode: u32) -> Key { + #[cfg(target_os = "windows")] + return rdev::win_key_from_scancode(keycode); + #[cfg(target_os = "linux")] + return rdev::linux_key_from_code(keycode); + #[cfg(target_os = "macos")] + return rdev::macos_key_from_code(keycode.try_into().unwrap_or_default()); +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index f8fe7a6dd..777de3b01 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -37,7 +37,7 @@ pub fn breakdown_callback() { #[cfg(target_os = "linux")] crate::input_service::clear_remapped_keycode(); #[cfg(not(any(target_os = "android", target_os = "ios")))] - crate::input_service::release_modifiers(); + crate::input_service::release_device_modifiers(); } // Android diff --git a/src/server/connection.rs b/src/server/connection.rs index 0c17fd176..dbda219e0 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -10,6 +10,7 @@ use crate::{ new_voice_call_request, new_voice_call_response, start_audio_thread, MediaData, MediaSender, }, common::{get_default_sound_input, set_sound_input}, + keyboard::{is_modifier, keycode_to_rdev_key}, video_service, }; #[cfg(any(target_os = "android", target_os = "ios"))] @@ -39,6 +40,7 @@ use sha2::{Digest, Sha256}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use std::sync::atomic::Ordering; use std::{ + collections::HashSet, num::NonZeroI64, sync::{atomic::AtomicI64, mpsc as std_mpsc}, }; @@ -132,6 +134,7 @@ pub struct Connection { voice_call_request_timestamp: Option, audio_input_device_before_voice_call: Option, options_in_login: Option, + pressed_modifiers: HashSet, } impl ConnInner { @@ -243,6 +246,7 @@ impl Connection { voice_call_request_timestamp: None, audio_input_device_before_voice_call: None, options_in_login: None, + pressed_modifiers: Default::default(), }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -618,7 +622,6 @@ impl Connection { } #[cfg(target_os = "linux")] clear_remapped_keycode(); - release_modifiers(); log::info!("Input thread exited"); } @@ -1366,6 +1369,28 @@ impl Connection { } else { me.press }; + + let key = match me.mode.unwrap() { + KeyboardMode::Map => Some(keycode_to_rdev_key(me.chr())), + KeyboardMode::Translate => { + if let Some(key_event::Union::Chr(code)) = me.union.clone() { + Some(keycode_to_rdev_key(code & 0x0000FFFF)) + } else { + None + } + } + _ => None, + } + .filter(is_modifier); + + if let Some(key) = key { + if is_press { + self.pressed_modifiers.insert(key); + } else { + self.pressed_modifiers.remove(&key); + } + } + if is_press { match me.union { Some(key_event::Union::Unicode(_)) @@ -2023,6 +2048,14 @@ impl Connection { }) .count(); } + + #[cfg(not(any(target_os = "android", target_os = "ios")))] + fn release_pressed_modifiers(&mut self) { + for modifier in self.pressed_modifiers.iter() { + rdev::simulate(&rdev::EventType::KeyRelease(*modifier)).ok(); + } + self.pressed_modifiers.clear(); + } } pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) { @@ -2220,3 +2253,10 @@ impl Default for PortableState { } } } + +impl Drop for Connection { + fn drop(&mut self) { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + self.release_pressed_modifiers(); + } +} diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 25b41d0ef..5bc486516 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -551,7 +551,7 @@ fn record_key_to_key(record_key: u64) -> Option { } } -pub fn release_modifiers() { +pub fn release_device_modifiers() { let mut en = ENIGO.lock().unwrap(); for modifier in [ Key::Shift, From 0312e734c06aba56ddc09bd2128da05710d0509c Mon Sep 17 00:00:00 2001 From: chiehw Date: Fri, 31 Mar 2023 14:18:55 +0800 Subject: [PATCH 126/366] fix build --- src/lang.rs | 2 +- src/server/connection.rs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lang.rs b/src/lang.rs index 972510325..4776eb89e 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -5,12 +5,12 @@ mod cn; mod cs; mod da; mod de; +mod el; mod en; mod eo; mod es; mod fa; mod fr; -mod el; mod hu; mod id; mod it; diff --git a/src/server/connection.rs b/src/server/connection.rs index dbda219e0..eecfaa20c 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -10,7 +10,6 @@ use crate::{ new_voice_call_request, new_voice_call_response, start_audio_thread, MediaData, MediaSender, }, common::{get_default_sound_input, set_sound_input}, - keyboard::{is_modifier, keycode_to_rdev_key}, video_service, }; #[cfg(any(target_os = "android", target_os = "ios"))] @@ -1371,17 +1370,17 @@ impl Connection { }; let key = match me.mode.unwrap() { - KeyboardMode::Map => Some(keycode_to_rdev_key(me.chr())), + KeyboardMode::Map => Some(crate::keyboard::keycode_to_rdev_key(me.chr())), KeyboardMode::Translate => { if let Some(key_event::Union::Chr(code)) = me.union.clone() { - Some(keycode_to_rdev_key(code & 0x0000FFFF)) + Some(crate::keyboard::keycode_to_rdev_key(code & 0x0000FFFF)) } else { None } } _ => None, } - .filter(is_modifier); + .filter(crate::keyboard::is_modifier); if let Some(key) = key { if is_press { From 0b417ac4790d6b203df10b3eb358f15b3a43d22d Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 16:42:35 +0800 Subject: [PATCH 127/366] start --server on gmd wayland, just for communications, no connections will be established Signed-off-by: fufesou --- libs/enigo/src/linux/nix_impl.rs | 2 +- libs/hbb_common/src/config.rs | 24 +- libs/hbb_common/src/platform/linux.rs | 123 +++++--- libs/scrap/src/common/mod.rs | 2 +- src/common.rs | 2 +- src/platform/linux.rs | 426 +++++++++++++++++--------- src/server/connection.rs | 2 +- src/server/service.rs | 15 + 8 files changed, 405 insertions(+), 191 deletions(-) diff --git a/libs/enigo/src/linux/nix_impl.rs b/libs/enigo/src/linux/nix_impl.rs index e7dc69ab1..c082236e3 100644 --- a/libs/enigo/src/linux/nix_impl.rs +++ b/libs/enigo/src/linux/nix_impl.rs @@ -115,7 +115,7 @@ impl Enigo { impl Default for Enigo { fn default() -> Self { - let is_x11 = "x11" == hbb_common::platform::linux::get_display_server(); + let is_x11 = hbb_common::platform::linux::is_x11_or_headless(); Self { is_x11, tfc: if is_x11 { diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 76a87153e..ff2f8fe44 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -915,15 +915,12 @@ impl PeerConfig { decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); config.password = password; store = store || store2; - if let Some(v) = config.options.get_mut("rdp_password") { - let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); - *v = password; - store = store || store2; - } - if let Some(v) = config.options.get_mut("os-password") { - let (password, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); - *v = password; - store = store || store2; + for opt in ["rdp_password", "os-password"] { + if let Some(v) = config.options.get_mut(opt) { + let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + *v = encrypted; + store = store || store2; + } } if store { config.store(id); @@ -941,12 +938,11 @@ impl PeerConfig { let _lock = CONFIG.read().unwrap(); let mut config = self.clone(); config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); - if let Some(v) = config.options.get_mut("rdp_password") { - *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION) + for opt in ["rdp_password", "os-username", "os-password"] { + if let Some(v) = config.options.get_mut(opt) { + *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION) + } } - if let Some(v) = config.options.get_mut("os-password") { - *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION) - }; if let Err(err) = store_path(Self::path(id), config) { log::error!("Failed to store config: {}", err); } diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index f6133415a..1d826ea97 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -5,6 +5,9 @@ lazy_static::lazy_static! { pub static ref DISTRO: Distro = Distro::new(); } +pub const DISPLAY_SERVER_WAYLAND: &str = "wayland"; +pub const DISPLAY_SERVER_X11: &str = "x11"; + pub struct Distro { pub name: String, pub version_id: String, @@ -12,23 +15,41 @@ pub struct Distro { impl Distro { fn new() -> Self { - let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release".to_owned()) + let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release") + .unwrap_or_default() + .trim() + .trim_matches('"') + .to_string(); + let version_id = run_cmds("awk -F'=' '/^VERSION_ID=/ {print $2}' /etc/os-release") .unwrap_or_default() .trim() .trim_matches('"') .to_string(); - let version_id = - run_cmds("awk -F'=' '/^VERSION_ID=/ {print $2}' /etc/os-release".to_owned()) - .unwrap_or_default() - .trim() - .trim_matches('"') - .to_string(); Self { name, version_id } } } +#[inline] +pub fn is_gdm_user(username: &str) -> bool { + username == "gdm" + // || username == "lightgdm" +} + +#[inline] +pub fn is_desktop_wayland() -> bool { + get_display_server() == DISPLAY_SERVER_WAYLAND +} + +#[inline] +pub fn is_x11_or_headless() -> bool { + !is_desktop_wayland() +} + +// -1 +const INVALID_SESSION: &str = "4294967295"; + pub fn get_display_server() -> String { - let mut session = get_values_of_seat0([0].to_vec())[0].clone(); + let mut session = get_values_of_seat0(&[0])[0].clone(); if session.is_empty() { // loginctl has not given the expected output. try something else. if let Ok(sid) = std::env::var("XDG_SESSION_ID") { @@ -36,14 +57,20 @@ pub fn get_display_server() -> String { session = sid; } if session.is_empty() { - session = run_cmds("cat /proc/self/sessionid".to_owned()).unwrap_or_default(); + session = run_cmds("cat /proc/self/sessionid").unwrap_or_default(); + if session == INVALID_SESSION { + session = "".to_owned(); + } } } - - get_display_server_of_session(&session) + if session.is_empty() { + "".to_owned() + } else { + get_display_server_of_session(&session) + } } -fn get_display_server_of_session(session: &str) -> String { +pub fn get_display_server_of_session(session: &str) -> String { let mut display_server = if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "Type", session])) // Check session type of the session @@ -61,7 +88,7 @@ fn get_display_server_of_session(session: &str) -> String { .replace("TTY=", "") .trim_end() .into(); - if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{tty}.\\\\+Xorg\"")) + if let Ok(xorg_results) = run_cmds(&format!("ps -e | grep \"{tty}.\\\\+Xorg\"")) // And check if Xorg is running on that tty { if xorg_results.trim_end() != "" { @@ -87,44 +114,68 @@ fn get_display_server_of_session(session: &str) -> String { display_server.to_lowercase() } -pub fn get_values_of_seat0(indices: Vec) -> Vec { +#[inline] +fn line_values(indices: &[usize], line: &str) -> Vec { + indices + .into_iter() + .map(|idx| line.split_whitespace().nth(*idx).unwrap_or("").to_owned()) + .collect::>() +} + +#[inline] +pub fn get_values_of_seat0(indices: &[usize]) -> Vec { + _get_values_of_seat0(indices, true) +} + +#[inline] +pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { + _get_values_of_seat0(indices, false) +} + +fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { if line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { - return indices - .into_iter() - .map(|idx| line.split_whitespace().nth(idx).unwrap_or("").to_owned()) - .collect::>(); + if ignore_gdm_wayland { + if is_gdm_user(line.split_whitespace().nth(2).unwrap_or("")) + && get_display_server_of_session(sid) == DISPLAY_SERVER_WAYLAND + { + continue; + } + } + return line_values(indices, line); } } } } - } - // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 - if let Ok(output) = run_loginctl(None) { + // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 for line in String::from_utf8_lossy(&output.stdout).lines() { if let Some(sid) = line.split_whitespace().next() { - let d = get_display_server_of_session(sid); - if is_active(sid) && d != "tty" { - return indices - .into_iter() - .map(|idx| line.split_whitespace().nth(idx).unwrap_or("").to_owned()) - .collect::>(); + if is_active(sid) { + let d = get_display_server_of_session(sid); + if ignore_gdm_wayland { + if is_gdm_user(line.split_whitespace().nth(2).unwrap_or("")) + && d == DISPLAY_SERVER_WAYLAND + { + continue; + } + } + if d == "tty" { + continue; + } + return line_values(indices, line); } } } } - return indices - .iter() - .map(|_x| "".to_owned()) - .collect::>(); + line_values(indices, "") } -fn is_active(sid: &str) -> bool { +pub fn is_active(sid: &str) -> bool { if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid])) { String::from_utf8_lossy(&output.stdout).contains("active") } else { @@ -132,15 +183,15 @@ fn is_active(sid: &str) -> bool { } } -pub fn run_cmds(cmds: String) -> ResultType { +pub fn run_cmds(cmds: &str) -> ResultType { let output = std::process::Command::new("sh") - .args(vec!["-c", &cmds]) + .args(vec!["-c", cmds]) .output()?; Ok(String::from_utf8_lossy(&output.stdout).to_string()) } #[cfg(not(feature = "flatpak"))] -fn run_loginctl(args: Option>) -> std::io::Result { +pub(super) fn run_loginctl(args: Option>) -> std::io::Result { let mut cmd = std::process::Command::new("loginctl"); if let Some(a) = args { return cmd.args(a).output(); @@ -149,7 +200,7 @@ fn run_loginctl(args: Option>) -> std::io::Result>) -> std::io::Result { +pub(super) fn run_loginctl(args: Option>) -> std::io::Result { let mut l_args = String::from("loginctl"); if let Some(a) = args { l_args = format!("{} {}", l_args, a.join(" ")); diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 9e15cf084..0ad158cca 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -74,7 +74,7 @@ pub trait TraitCapturer { #[cfg(x11)] #[inline] pub fn is_x11() -> bool { - "x11" == hbb_common::platform::linux::get_display_server() + hbb_common::platform::linux::is_x11_or_headless() } #[cfg(x11)] diff --git a/src/common.rs b/src/common.rs index 7bdfd9012..d07c7bbc6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -755,7 +755,7 @@ lazy_static::lazy_static! { #[cfg(target_os = "linux")] lazy_static::lazy_static! { - pub static ref IS_X11: bool = "x11" == hbb_common::platform::linux::get_display_server(); + pub static ref IS_X11: bool = hbb_common::platform::linux::is_x11_or_headless(); } pub fn make_fd_to_json(id: i32, path: String, entries: &Vec) -> String { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 6980385c8..1fdfd316d 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,4 +1,5 @@ use super::{CursorData, ResultType}; +use desktop::Desktop; pub use hbb_common::platform::linux::*; use hbb_common::{ allow_err, bail, @@ -64,6 +65,11 @@ pub struct xcb_xfixes_get_cursor_image { pub pixels: *const c_long, } +#[inline] +fn sleep_millis(millis: u64) { + std::thread::sleep(Duration::from_millis(millis)); +} + pub fn get_cursor_pos() -> Option<(i32, i32)> { let mut res = None; XDO.with(|xdo| { @@ -190,7 +196,7 @@ fn start_server(user: Option<(String, String)>, server: &mut Option) { fn stop_server(server: &mut Option) { if let Some(mut ps) = server.take() { allow_err!(ps.kill()); - std::thread::sleep(Duration::from_millis(30)); + sleep_millis(30); match ps.try_wait() { Ok(Some(_status)) => {} Ok(None) => { @@ -201,44 +207,20 @@ fn stop_server(server: &mut Option) { } } -fn set_x11_env(uid: &str) { - log::info!("uid of seat0: {}", uid); - let gdm = format!("/run/user/{}/gdm/Xauthority", uid); - let mut auth = get_env_tries("XAUTHORITY", uid, 10); - // auth is another user's when uid = 0, https://github.com/rustdesk/rustdesk/issues/2468 - if auth.is_empty() || uid == "0" { - auth = if Path::new(&gdm).exists() { - gdm - } else { - let username = get_active_username(); - if username == "root" { - format!("/{}/.Xauthority", username) - } else { - let tmp = format!("/home/{}/.Xauthority", username); - if Path::new(&tmp).exists() { - tmp - } else { - format!("/var/lib/{}/.Xauthority", username) - } - } - }; +fn set_x11_env(desktop: &Desktop) { + log::info!("DISPLAY: {}", desktop.display); + log::info!("XAUTHORITY: {}", desktop.xauth); + if !desktop.display.is_empty() { + std::env::set_var("DISPLAY", &desktop.display); } - let mut d = get_env("DISPLAY", uid); - if d.is_empty() { - d = get_display(); + if !desktop.xauth.is_empty() { + std::env::set_var("XAUTHORITY", &desktop.xauth); } - if d.is_empty() { - d = ":0".to_owned(); - } - d = d.replace(&whoami::hostname(), "").replace("localhost", ""); - log::info!("DISPLAY: {}", d); - log::info!("XAUTHORITY: {}", auth); - std::env::set_var("XAUTHORITY", auth); - std::env::set_var("DISPLAY", d); } +#[inline] fn stop_rustdesk_servers() { - let _ = run_cmds(format!( + let _ = run_cmds(&format!( r##"ps -ef | grep -E 'rustdesk +--server' | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); } @@ -246,37 +228,49 @@ fn stop_rustdesk_servers() { fn should_start_server( try_x11: bool, uid: &mut String, - cur_uid: String, + desktop: &Desktop, cm0: &mut bool, last_restart: &mut Instant, server: &mut Option, ) -> bool { let cm = get_cm(); let mut start_new = false; - if cur_uid != *uid && !cur_uid.is_empty() { - *uid = cur_uid; + let mut should_kill = false; + + if desktop.is_headless() { + if !uid.is_empty() { + // From having a monitor to not having a monitor. + *uid = "".to_owned(); + should_kill = true; + } + } else if desktop.uid != *uid && !desktop.uid.is_empty() { + *uid = desktop.uid.clone(); if try_x11 { - set_x11_env(&uid); + set_x11_env(&desktop); } - if let Some(ps) = server.as_mut() { - allow_err!(ps.kill()); - std::thread::sleep(Duration::from_millis(30)); - *last_restart = Instant::now(); - } - } else if !cm + should_kill = true; + } + + if !should_kill + && !cm && ((*cm0 && last_restart.elapsed().as_secs() > 60) || last_restart.elapsed().as_secs() > 3600) { // restart server if new connections all closed, or every one hour, // as a workaround to resolve "SpotUdp" (dns resolve) // and x server get displays failure issue + should_kill = true; + log::info!("restart server"); + } + + if should_kill { if let Some(ps) = server.as_mut() { allow_err!(ps.kill()); - std::thread::sleep(Duration::from_millis(30)); + sleep_millis(30); *last_restart = Instant::now(); - log::info!("restart server"); } } + if let Some(ps) = server.as_mut() { match ps.try_wait() { Ok(Some(_)) => { @@ -296,7 +290,7 @@ fn should_start_server( // stop_rustdesk_servers() is just a temp solution here. fn force_stop_server() { stop_rustdesk_servers(); - std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); + sleep_millis(super::SERVICE_INTERVAL); } pub fn start_os_service() { @@ -305,6 +299,8 @@ pub fn start_os_service() { let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); + let mut desktop = Desktop::default(); + let mut sid = "".to_owned(); let mut uid = "".to_owned(); let mut server: Option = None; let mut user_server: Option = None; @@ -317,31 +313,18 @@ pub fn start_os_service() { let mut cm0 = false; let mut last_restart = Instant::now(); while running.load(Ordering::SeqCst) { - let (cur_uid, cur_user) = get_active_user_id_name(); + desktop.refresh(); - // for fixing https://github.com/rustdesk/rustdesk/issues/3129 to avoid too much dbus calling, - // though duplicate logic here with should_start_server - if !(cur_uid != *uid && !cur_uid.is_empty()) { - let cm = get_cm(); - if !(!cm - && ((cm0 && last_restart.elapsed().as_secs() > 60) - || last_restart.elapsed().as_secs() > 3600)) - { - std::thread::sleep(Duration::from_millis(500)); - continue; - } - } - - let is_wayland = current_is_wayland(); - - if cur_user == "root" || !is_wayland { + // Duplicate logic here with should_start_server + // Login wayland will try to start a headless --server. + if desktop.username == "root" || !desktop.is_wayland() || desktop.is_login_wayland() { // try kill subprocess "--server" stop_server(&mut user_server); // try start subprocess "--server" if should_start_server( true, &mut uid, - cur_uid, + &desktop, &mut cm0, &mut last_restart, &mut server, @@ -349,30 +332,42 @@ pub fn start_os_service() { force_stop_server(); start_server(None, &mut server); } - } else if cur_user != "" { - if cur_user != "gdm" { - // try kill subprocess "--server" - stop_server(&mut server); + } else if desktop.username != "" { + // try kill subprocess "--server" + stop_server(&mut server); - // try start subprocess "--server" - if should_start_server( - false, - &mut uid, - cur_uid.clone(), - &mut cm0, - &mut last_restart, + // try start subprocess "--server" + if should_start_server( + false, + &mut uid, + &desktop, + &mut cm0, + &mut last_restart, + &mut user_server, + ) { + force_stop_server(); + start_server( + Some((desktop.uid.clone(), desktop.username.clone())), &mut user_server, - ) { - force_stop_server(); - start_server(Some((cur_uid, cur_user)), &mut user_server); - } + ); } } else { force_stop_server(); stop_server(&mut user_server); stop_server(&mut server); } - std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); + + let keeps_headless = sid.is_empty() && desktop.is_headless(); + let keeps_session = sid == desktop.sid; + if keeps_headless || keeps_session { + // for fixing https://github.com/rustdesk/rustdesk/issues/3129 to avoid too much dbus calling, + sleep_millis(500); + } else { + sleep_millis(super::SERVICE_INTERVAL); + } + if !desktop.is_headless() { + sid = desktop.sid.clone(); + } } if let Some(ps) = user_server.take().as_mut() { @@ -384,13 +379,15 @@ pub fn start_os_service() { log::info!("Exit"); } +#[inline] pub fn get_active_user_id_name() -> (String, String) { - let vec_id_name = get_values_of_seat0([1, 2].to_vec()); + let vec_id_name = get_values_of_seat0(&[1, 2]); (vec_id_name[0].clone(), vec_id_name[1].clone()) } +#[inline] pub fn get_active_userid() -> String { - get_values_of_seat0([1].to_vec())[0].clone() + get_values_of_seat0(&[1])[0].clone() } fn get_cm() -> bool { @@ -409,45 +406,6 @@ fn get_cm() -> bool { false } -fn get_display() -> String { - let user = get_active_username(); - log::debug!("w {}", &user); - if let Ok(output) = Command::new("w").arg(&user).output() { - for line in String::from_utf8_lossy(&output.stdout).lines() { - log::debug!(" {}", line); - let mut iter = line.split_whitespace(); - let b = iter.nth(2); - if let Some(b) = b { - if b.starts_with(":") { - return b.to_owned(); - } - } - } - } - // above not work for gdm user - log::debug!("ls -l /tmp/.X11-unix/"); - let mut last = "".to_owned(); - if let Ok(output) = Command::new("ls") - .args(vec!["-l", "/tmp/.X11-unix/"]) - .output() - { - for line in String::from_utf8_lossy(&output.stdout).lines() { - log::debug!(" {}", line); - let mut iter = line.split_whitespace(); - let user_field = iter.nth(2); - if let Some(x) = iter.last() { - if x.starts_with("X") { - last = x.replace("X", ":").to_owned(); - if user_field == Some(&user) { - return last; - } - } - } - } - } - last -} - pub fn is_login_wayland() -> bool { if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") { contents.contains("#WaylandEnable=false") || contents.contains("WaylandEnable=true") @@ -458,9 +416,9 @@ pub fn is_login_wayland() -> bool { } } +#[inline] pub fn current_is_wayland() -> bool { - let dtype = get_display_server(); - return "wayland" == dtype && unsafe { UNMODIFIED }; + return is_desktop_wayland() && unsafe { UNMODIFIED }; } // to-do: test the other display manager @@ -473,8 +431,9 @@ fn _get_display_manager() -> String { "gdm3".to_owned() } +#[inline] pub fn get_active_username() -> String { - get_values_of_seat0([2].to_vec())[0].clone() + get_values_of_seat0(&[2])[0].clone() } pub fn get_active_user_home() -> Option { @@ -488,9 +447,16 @@ pub fn get_active_user_home() -> Option { None } +pub fn get_env_var(k: &str) -> String { + match std::env::var(k) { + Ok(v) => v, + Err(_e) => "".to_owned(), + } +} + pub fn is_prelogin() -> bool { - let n = get_active_userid().len(); - n < 4 && n > 1 + let (uid, uname) = get_active_user_id_name(); + uid.len() >= 4 || uname == "root" } pub fn is_root() -> bool { @@ -498,7 +464,7 @@ pub fn is_root() -> bool { } fn is_opensuse() -> bool { - if let Ok(res) = run_cmds("cat /etc/os-release | grep opensuse".to_owned()) { + if let Ok(res) = run_cmds("cat /etc/os-release | grep opensuse") { if !res.is_empty() { return true; } @@ -512,6 +478,9 @@ pub fn run_as_user(arg: Vec<&str>, user: Option<(String, String)>) -> ResultType None => get_active_user_id_name(), }; let cmd = std::env::current_exe()?; + if uid.is_empty() { + bail!("No valid uid"); + } let xdg = &format!("XDG_RUNTIME_DIR=/run/user/{}", uid) as &str; let mut args = vec![xdg, "-u", &username, cmd.to_str().unwrap_or("")]; args.append(&mut arg.clone()); @@ -597,21 +566,31 @@ pub fn is_installed() -> bool { true } -fn get_env_tries(name: &str, uid: &str, n: usize) -> String { +pub(super) fn get_env_tries(name: &str, uid: &str, process: &str, n: usize) -> String { for _ in 0..n { - let x = get_env(name, uid); + let x = get_env(name, uid, process); if !x.is_empty() { return x; } - std::thread::sleep(Duration::from_millis(300)); + sleep_millis(300); } "".to_owned() } -fn get_env(name: &str, uid: &str) -> String { - let cmd = format!("ps -u {} -o pid= | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, name, name); - log::debug!("Run: {}", &cmd); - if let Ok(x) = run_cmds(cmd) { +#[inline] +fn get_env(name: &str, uid: &str, process: &str) -> String { + let cmd = format!("ps -u {} -f | grep '{}' | grep -v 'grep' | tail -1 | awk '{{print $2}}' | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, process, name, name); + if let Ok(x) = run_cmds(&cmd) { + x.trim_end().to_string() + } else { + "".to_owned() + } +} + +#[inline] +fn get_env_from_pid(name: &str, pid: &str) -> String { + let cmd = format!("cat /proc/{}/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", pid, name, name); + if let Ok(x) = run_cmds(&cmd) { x.trim_end().to_string() } else { "".to_owned() @@ -701,7 +680,7 @@ pub fn resolutions(name: &str) -> Vec { let connected_pat = get_xrandr_conn_pat(name); let mut v = vec![]; if let Ok(re) = Regex::new(&format!("{}{}", connected_pat, resolutions_pat)) { - match run_cmds("xrandr --query | tr -s ' '".to_owned()) { + match run_cmds("xrandr --query | tr -s ' '") { Ok(xrandr_output) => { // There'are different kinds of xrandr output. /* @@ -750,7 +729,7 @@ pub fn resolutions(name: &str) -> Vec { } pub fn current_resolution(name: &str) -> ResultType { - let xrandr_output = run_cmds("xrandr --query | tr -s ' '".to_owned())?; + let xrandr_output = run_cmds("xrandr --query | tr -s ' '")?; let re = Regex::new(&get_xrandr_conn_pat(name))?; if let Some(caps) = re.captures(&xrandr_output) { if let Some((width, height)) = get_width_height_from_captures(&caps) { @@ -775,3 +754,176 @@ pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType< .spawn()?; Ok(()) } + +mod desktop { + use super::*; + + pub const XFCE4_PANEL: &str = "xfce4-panel"; + pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary"; + + #[derive(Debug, Clone, Default)] + pub struct Desktop { + pub sid: String, + pub username: String, + pub uid: String, + pub protocal: String, + pub display: String, + pub xauth: String, + } + + impl Desktop { + #[inline] + pub fn is_wayland(&self) -> bool { + self.protocal == DISPLAY_SERVER_WAYLAND + } + + #[inline] + pub fn is_login_wayland(&self) -> bool { + super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND + } + + #[inline] + pub fn is_headless(&self) -> bool { + self.sid.is_empty() + } + + fn get_display(&mut self) { + self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10); + if self.display.is_empty() { + self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10); + } + if self.display.is_empty() { + self.display = Self::get_display_by_user(&self.username); + } + if self.display.is_empty() { + self.display = ":0".to_owned(); + } + self.display = self + .display + .replace(&whoami::hostname(), "") + .replace("localhost", ""); + } + + fn get_xauth_from_xorg(&mut self) { + if let Ok(output) = run_cmds(&format!( + "ps -u {} -f | grep 'Xorg' | grep -v 'grep'", + &self.uid + )) { + for line in output.lines() { + let mut auth_found = false; + for v in line.split_whitespace() { + if v == "-auth" { + auth_found = true; + } else if auth_found { + if std::path::Path::new(v).is_absolute() { + self.xauth = v.to_string(); + } else { + if let Some(pid) = line.split_whitespace().nth(1) { + let home_dir = get_env_from_pid("HOME", pid); + if home_dir.is_empty() { + self.xauth = format!("/home/{}/{}", self.username, v); + } else { + self.xauth = format!("{}/{}", home_dir, v); + } + } else { + // unreachable! + } + } + return; + } + } + } + } + } + + fn get_xauth(&mut self) { + self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10); + if self.xauth.is_empty() { + get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10); + } + if self.xauth.is_empty() { + self.get_xauth_from_xorg(); + } + + let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid); + if self.xauth.is_empty() { + self.xauth = if std::path::Path::new(&gdm).exists() { + gdm + } else { + let username = &self.username; + if username == "root" { + format!("/{}/.Xauthority", username) + } else { + let tmp = format!("/home/{}/.Xauthority", username); + if std::path::Path::new(&tmp).exists() { + tmp + } else { + format!("/var/lib/{}/.Xauthority", username) + } + } + }; + } + } + + fn get_display_by_user(user: &str) -> String { + // log::debug!("w {}", &user); + if let Ok(output) = std::process::Command::new("w").arg(&user).output() { + for line in String::from_utf8_lossy(&output.stdout).lines() { + let mut iter = line.split_whitespace(); + let b = iter.nth(2); + if let Some(b) = b { + if b.starts_with(":") { + return b.to_owned(); + } + } + } + } + // above not work for gdm user + //log::debug!("ls -l /tmp/.X11-unix/"); + let mut last = "".to_owned(); + if let Ok(output) = std::process::Command::new("ls") + .args(vec!["-l", "/tmp/.X11-unix/"]) + .output() + { + for line in String::from_utf8_lossy(&output.stdout).lines() { + let mut iter = line.split_whitespace(); + let user_field = iter.nth(2); + if let Some(x) = iter.last() { + if x.starts_with("X") { + last = x.replace("X", ":").to_owned(); + if user_field == Some(&user) { + return last; + } + } + } + } + } + last + } + + pub fn refresh(&mut self) { + if !self.sid.is_empty() && is_active(&self.sid) { + return; + } + + let seat0_values = get_values_of_seat0(&[0, 1, 2]); + if seat0_values[0].is_empty() { + *self = Self::default(); + return; + } + + self.sid = seat0_values[0].clone(); + self.uid = seat0_values[1].clone(); + self.username = seat0_values[2].clone(); + self.protocal = get_display_server_of_session(&self.sid).into(); + if self.is_login_wayland() { + self.display = "".to_owned(); + self.xauth = "".to_owned(); + return; + } + + self.get_display(); + self.get_xauth(); + } + } +} diff --git a/src/server/connection.rs b/src/server/connection.rs index 0c17fd176..725ff43ee 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -883,7 +883,7 @@ impl Connection { let dtype = crate::platform::linux::get_display_server(); if dtype != "x11" && dtype != "wayland" { res.set_error(format!( - "Unsupported display server type {}, x11 or wayland expected", + "Unsupported display server type \"{}\", x11 or wayland expected", dtype )); let mut msg_out = Message::new(); diff --git a/src/server/service.rs b/src/server/service.rs index 9cc1e860c..9857889cc 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -189,6 +189,17 @@ impl> ServiceTmpl { } } + #[inline] + fn wait_prelogin(&self) { + #[cfg(target_os = "linux")] + while self.active() { + if crate::platform::linux::is_prelogin() { + break; + } + thread::sleep(time::Duration::from_millis(300)); + } + } + pub fn repeat(&self, interval_ms: u64, callback: F) where F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send, @@ -198,6 +209,8 @@ impl> ServiceTmpl { let mut callback = callback; let sp = self.clone(); let thread = thread::spawn(move || { + sp.wait_prelogin(); + let mut state = S::default(); let mut may_reset = false; while sp.active() { @@ -232,6 +245,8 @@ impl> ServiceTmpl { let sp = self.clone(); let mut callback = callback; let thread = thread::spawn(move || { + sp.wait_prelogin(); + let mut error_timeout = HIBERNATE_TIMEOUT; while sp.active() { if sp.has_subscribes() { From 4d87364bd8e832d37fdfb4c28198bbdb5e67e449 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 16:54:45 +0800 Subject: [PATCH 128/366] trivial changes Signed-off-by: fufesou --- libs/hbb_common/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index ff2f8fe44..6a823c7b7 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -938,7 +938,7 @@ impl PeerConfig { let _lock = CONFIG.read().unwrap(); let mut config = self.clone(); config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); - for opt in ["rdp_password", "os-username", "os-password"] { + for opt in ["rdp_password", "os-password"] { if let Some(v) = config.options.get_mut(opt) { *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION) } From f3bb3067cdfee50fb32df88e56480431bf3b671b Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 16:58:05 +0800 Subject: [PATCH 129/366] trivial changes Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 1d826ea97..89c96799d 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -191,7 +191,7 @@ pub fn run_cmds(cmds: &str) -> ResultType { } #[cfg(not(feature = "flatpak"))] -pub(super) fn run_loginctl(args: Option>) -> std::io::Result { +fn run_loginctl(args: Option>) -> std::io::Result { let mut cmd = std::process::Command::new("loginctl"); if let Some(a) = args { return cmd.args(a).output(); @@ -200,7 +200,7 @@ pub(super) fn run_loginctl(args: Option>) -> std::io::Result>) -> std::io::Result { +fn run_loginctl(args: Option>) -> std::io::Result { let mut l_args = String::from("loginctl"); if let Some(a) = args { l_args = format!("{} {}", l_args, a.join(" ")); From 4c2169d92985ec66eb1cde76c84dbd3b52a11345 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 30 Mar 2023 10:08:05 +0800 Subject: [PATCH 130/366] feat: add dll setup/uninstall script --- src/platform/windows.rs | 91 +++++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 26 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 379cd29fb..83f5af2dd 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -10,15 +10,16 @@ use hbb_common::{ sleep, timeout, tokio, }; use std::io::prelude::*; +use std::path::Path; use std::ptr::null_mut; use std::{ + collections::HashMap, ffi::OsString, fs, io, mem, os::windows::process::CommandExt, path::PathBuf, sync::{Arc, Mutex}, time::{Duration, Instant}, - collections::HashMap }; use winapi::{ ctypes::c_void, @@ -717,7 +718,7 @@ pub fn is_share_rdp() -> bool { } pub fn set_share_rdp(enable: bool) { - let (subkey, _, _, _) = get_install_info(); + let (subkey, _, _, _, _) = get_install_info(); let cmd = format!( "reg add {} /f /v share_rdp /t REG_SZ /d \"{}\"", subkey, @@ -810,11 +811,11 @@ fn get_valid_subkey() -> String { return get_subkey(&app_name, false); } -pub fn get_install_info() -> (String, String, String, String) { +pub fn get_install_info() -> (String, String, String, String, String) { get_install_info_with_subkey(get_valid_subkey()) } -fn get_default_install_info() -> (String, String, String, String) { +fn get_default_install_info() -> (String, String, String, String, String) { get_install_info_with_subkey(get_subkey(&crate::get_app_name(), false)) } @@ -883,7 +884,7 @@ pub fn check_update_broker_process() -> ResultType<()> { Ok(()) } -fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String) { +fn get_install_info_with_subkey(subkey: String) -> (String, String, String, String, String) { let mut path = get_reg_of(&subkey, "InstallLocation"); if path.is_empty() { path = get_default_install_path(); @@ -894,26 +895,32 @@ fn get_install_info_with_subkey(subkey: String) -> (String, String, String, Stri crate::get_app_name() ); let exe = format!("{}\\{}.exe", path, crate::get_app_name()); - (subkey, path, start_menu, exe) + let dll = format!("{}\\sciter.dll", path); + (subkey, path, start_menu, exe, dll) } -pub fn copy_exe_cmd(src_exe: &str, _exe: &str, path: &str) -> String { +pub fn copy_raw_cmd(src_raw: &str, _raw: &str, _path: &str) -> String { #[cfg(feature = "flutter")] - let main_exe = format!( + let main_raw = format!( "XCOPY \"{}\" \"{}\" /Y /E /H /C /I /K /R /Z", - PathBuf::from(src_exe) + PathBuf::from(src_raw) .parent() .unwrap() .to_string_lossy() .to_string(), - path + _path ); #[cfg(not(feature = "flutter"))] - let main_exe = format!( - "copy /Y \"{src_exe}\" \"{exe}\"", - src_exe = src_exe, - exe = _exe + let main_raw = format!( + "copy /Y \"{src_raw}\" \"{raw}\"", + src_raw = src_raw, + raw = _raw ); + return main_raw; +} + +pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { + let main_exe = copy_raw_cmd(src_exe, exe, path); return format!( " @@ -929,8 +936,19 @@ pub fn copy_exe_cmd(src_exe: &str, _exe: &str, path: &str) -> String { } pub fn update_me() -> ResultType<()> { - let (_, path, _, exe) = get_install_info(); + let (_, path, _, exe, dll) = get_install_info(); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned(); + let src_dll = std::env::current_exe()? + .parent() + .unwrap_or(&Path::new(&get_default_install_path())) + .join("sciter.dll") + .to_str() + .unwrap_or("") + .to_owned(); + #[cfg(feature = "flutter")] + let copy_dll = "".to_string(); + #[cfg(not(feature = "flutter"))] + let copy_dll = copy_raw_cmd(&src_dll, &dll, &path); let cmds = format!( " chcp 65001 @@ -938,10 +956,12 @@ pub fn update_me() -> ResultType<()> { taskkill /F /IM {broker_exe} taskkill /F /IM {app_name}.exe /FI \"PID ne {cur_pid}\" {copy_exe} + {copy_dll} sc start {app_name} {lic} ", copy_exe = copy_exe_cmd(&src_exe, &exe, &path), + copy_dll = copy_dll, broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, app_name = crate::get_app_name(), lic = register_licence(), @@ -980,12 +1000,14 @@ fn get_after_install(exe: &str) -> String { pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> ResultType<()> { let uninstall_str = get_uninstall(false); let mut path = path.trim_end_matches('\\').to_owned(); - let (subkey, _path, start_menu, exe) = get_default_install_info(); + let (subkey, _path, start_menu, exe, dll) = get_default_install_info(); let mut exe = exe; + let mut dll = dll; if path.is_empty() { path = _path; } else { exe = exe.replace(&_path, &path); + dll = dll.replace(&_path, &path); } let mut version_major = "0"; let mut version_minor = "0"; @@ -1109,6 +1131,18 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} app_name = crate::get_app_name(), ); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_string(); + let src_dll = std::env::current_exe()? + .parent() + .unwrap_or(&Path::new(&get_default_install_path())) + .join("sciter.dll") + .to_str() + .unwrap_or("") + .to_owned(); + + #[cfg(feature = "flutter")] + let copy_dll = "".to_string(); + #[cfg(not(feature = "flutter"))] + let copy_dll = copy_raw_cmd(&src_dll, &dll, &path); let install_cert = if options.contains("driverCert") { format!("\"{}\" --install-cert \"RustDeskIddDriver.cer\"", src_exe) @@ -1122,6 +1156,7 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} chcp 65001 md \"{path}\" {copy_exe} +{copy_dll} reg add {subkey} /f reg add {subkey} /f /v DisplayIcon /t REG_SZ /d \"{exe}\" reg add {subkey} /f /v DisplayName /t REG_SZ /d \"{app_name}\" @@ -1181,6 +1216,7 @@ sc delete {app_name} &dels }, copy_exe = copy_exe_cmd(&src_exe, &exe, &path), + copy_dll = copy_dll ); run_cmds(cmds, debug, "install")?; std::thread::sleep(std::time::Duration::from_millis(2000)); @@ -1193,7 +1229,7 @@ sc delete {app_name} } pub fn run_after_install() -> ResultType<()> { - let (_, _, _, exe) = get_install_info(); + let (_, _, _, exe,_) = get_install_info(); run_cmds(get_after_install(&exe), true, "after_install") } @@ -1227,7 +1263,7 @@ fn get_before_uninstall(kill_self: bool) -> String { } fn get_uninstall(kill_self: bool) -> String { - let (subkey, path, start_menu, _) = get_install_info(); + let (subkey, path, start_menu, _, _) = get_install_info(); format!( " {before_uninstall} @@ -1331,7 +1367,7 @@ pub fn is_installed() -> bool { service::ServiceAccess, service_manager::{ServiceManager, ServiceManagerAccess}, }; - let (_, _, _, exe) = get_install_info(); + let (_, _, _, exe, _) = get_install_info(); if !std::fs::metadata(exe).is_ok() { return false; } @@ -1347,7 +1383,7 @@ pub fn is_installed() -> bool { } pub fn get_installed_version() -> String { - let (_, _, _, exe) = get_install_info(); + let (_, _, _, exe, _) = get_install_info(); if let Ok(output) = std::process::Command::new(exe).arg("--version").output() { for line in String::from_utf8_lossy(&output.stdout).lines() { return line.to_owned(); @@ -1357,7 +1393,7 @@ pub fn get_installed_version() -> String { } fn get_reg(name: &str) -> String { - let (subkey, _, _, _) = get_install_info(); + let (subkey, _, _, _, _) = get_install_info(); get_reg_of(&subkey, name) } @@ -1408,7 +1444,7 @@ pub fn bootstrap() { } fn register_licence() -> String { - let (subkey, _, _, _) = get_install_info(); + let (subkey, _, _, _, _) = get_install_info(); if let Ok(lic) = get_license_from_exe_name() { format!( " @@ -1765,14 +1801,17 @@ pub fn send_message_to_hnwd( pub fn create_process_with_logon(user: &str, pwd: &str, exe: &str, arg: &str) -> ResultType<()> { let last_error_table = HashMap::from([ - (ERROR_LOGON_FAILURE, "The user name or password is incorrect."), - (ERROR_ACCESS_DENIED, "Access is denied.") + ( + ERROR_LOGON_FAILURE, + "The user name or password is incorrect.", + ), + (ERROR_ACCESS_DENIED, "Access is denied."), ]); unsafe { let user_split = user.split("\\").collect::>(); let wuser = wide_string(user_split.get(1).unwrap_or(&user)); - let wpc = wide_string(user_split.get(0).unwrap_or(&"")); + let wpc = wide_string(user_split.get(0).unwrap_or(&"")); let wpwd = wide_string(pwd); let cmd = if arg.is_empty() { format!("\"{}\"", exe) @@ -1804,7 +1843,7 @@ pub fn create_process_with_logon(user: &str, pwd: &str, exe: &str, arg: &str) -> { let last_error = GetLastError(); bail!( - "CreateProcessWithLogonW failed : \"{}\", errno={}", + "CreateProcessWithLogonW failed : \"{}\", errno={}", last_error_table .get(&last_error) .unwrap_or(&"Unknown error"), From a015a9b16489bfe2854c83546454f5d8c9ef5a6f Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 31 Mar 2023 17:12:48 +0800 Subject: [PATCH 131/366] fix: remove bracket for and expression in nightly ci --- .github/workflows/flutter-nightly.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 06f6f8150..30bf10605 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -1003,7 +1003,7 @@ jobs: done - name: Publish debian 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 with: prerelease: true @@ -1176,7 +1176,7 @@ jobs: done - name: Publish debian 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 with: prerelease: true @@ -1199,7 +1199,7 @@ jobs: sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml - name: Publish appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1273,7 +1273,7 @@ jobs: # res/rustdesk*.zst - name: Publish fedora28/centos8 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 with: prerelease: true @@ -1396,7 +1396,7 @@ jobs: done - name: Publish debian 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 with: prerelease: true @@ -1452,7 +1452,7 @@ jobs: cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f - name: Publish archlinux 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 with: prerelease: true @@ -1461,7 +1461,7 @@ jobs: res/rustdesk*.zst - name: Build appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' shell: bash run: | # set-up appimage-builder @@ -1475,7 +1475,7 @@ jobs: sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml - name: Publish appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} && env.UPLOAD_ARTIFACT == 'true' + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' uses: softprops/action-gh-release@v1 with: prerelease: true @@ -1484,7 +1484,7 @@ jobs: ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage - name: Publish fedora28/centos8 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 with: prerelease: true From a06ce7e1834e846cb22b0031d9f03c5fcd611064 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 30 Mar 2023 10:54:43 +0800 Subject: [PATCH 132/366] add: x86 windows --- .github/workflows/flutter-nightly.yml | 10 ++++------ Cargo.lock | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 30bf10605..b6020460c 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -154,7 +154,7 @@ jobs: name: ${{ matrix.job.target }} (${{ matrix.job.os }}) runs-on: ${{ matrix.job.os }} # Temporarily disable this action due to additional test is needed. - if: false + # if: false strategy: fail-fast: false matrix: @@ -175,14 +175,10 @@ jobs: - name: Install Rust toolchain uses: actions-rs/toolchain@v1 with: - toolchain: stable-${{ matrix.job.target }} + toolchain: nightly-${{ matrix.job.target }} target: ${{ matrix.job.target }} override: true profile: minimal # minimal component installation (ie, no documentation) - - - name: Set Rust toolchain to the target - run: | - rustup default stable-${{ matrix.job.target }} - uses: Swatinem/rust-cache@v2 with: @@ -204,6 +200,8 @@ jobs: shell: bash run: | python3 res/inline-sciter.py + # Patch sciter x86 + sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml # Replace the link for the ico. rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico cargo build --features inline --release --bins diff --git a/Cargo.lock b/Cargo.lock index a0815f8e0..5a4cad9f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6343,7 +6343,7 @@ dependencies = [ [[package]] name = "trayicon" version = "0.1.3-1" -source = "git+https://github.com/open-trade/trayicon-rs#8d9c4489287752cc5be4a35c103198f7111112f9" +source = "git+https://github.com/open-trade/trayicon-rs#35bd01963271b45a0b6a0f65f1ce03a5f35bc691" dependencies = [ "winapi 0.3.9", "winit", From 78748271ac329a63fea837d772a7f7da4f462f89 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 31 Mar 2023 16:10:52 +0800 Subject: [PATCH 133/366] vp8 Signed-off-by: 21pages --- .../desktop/pages/desktop_setting_page.dart | 42 ++- .../lib/desktop/widgets/remote_toolbar.dart | 26 +- flutter/lib/mobile/pages/remote_page.dart | 5 +- libs/hbb_common/protos/message.proto | 16 +- libs/hbb_common/src/config.rs | 5 +- libs/scrap/examples/benchmark.rs | 46 ++- libs/scrap/src/common/android.rs | 1 + libs/scrap/src/common/codec.rs | 319 +++++++++--------- libs/scrap/src/common/hwcodec.rs | 17 +- libs/scrap/src/common/mod.rs | 53 +++ libs/scrap/src/common/record.rs | 58 ++-- libs/scrap/src/common/vpxcodec.rs | 46 +-- src/client.rs | 37 +- src/client/helper.rs | 33 +- src/client/io_loop.rs | 9 +- src/flutter_ffi.rs | 13 +- src/server/connection.rs | 34 +- src/server/video_service.rs | 85 +++-- src/ui/header.tis | 9 +- src/ui/remote.rs | 22 +- src/ui_interface.rs | 7 + src/ui_session_interface.rs | 28 +- 22 files changed, 470 insertions(+), 441 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 66ef83d31..74d51407c 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1258,9 +1258,6 @@ class _DisplayState extends State<_Display> { } Widget codec(BuildContext context) { - if (!bind.mainHasHwcodec()) { - return Offstage(); - } final key = 'codec-preference'; onChanged(String value) async { await bind.mainSetUserDefaultOption(key: key, value: value); @@ -1268,28 +1265,45 @@ class _DisplayState extends State<_Display> { } final groupValue = bind.mainGetUserDefaultOption(key: key); - + var hwRadios = []; + try { + final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings()); + final h264 = codecsJson['h264'] ?? false; + final h265 = codecsJson['h265'] ?? false; + if (h264) { + hwRadios.add(_Radio(context, + value: 'h264', + groupValue: groupValue, + label: 'H264', + onChanged: onChanged)); + } + if (h265) { + hwRadios.add(_Radio(context, + value: 'h265', + groupValue: groupValue, + label: 'H265', + onChanged: onChanged)); + } + } catch (e) { + debugPrint("failed to parse supported hwdecodings, err=$e"); + } return _Card(title: 'Default Codec', children: [ _Radio(context, value: 'auto', groupValue: groupValue, label: 'Auto', onChanged: onChanged), + _Radio(context, + value: 'vp8', + groupValue: groupValue, + label: 'VP8', + onChanged: onChanged), _Radio(context, value: 'vp9', groupValue: groupValue, label: 'VP9', onChanged: onChanged), - _Radio(context, - value: 'h264', - groupValue: groupValue, - label: 'H264', - onChanged: onChanged), - _Radio(context, - value: 'h265', - groupValue: groupValue, - label: 'H265', - onChanged: onChanged), + ...hwRadios, ]); } diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 0ef8674ef..cb4b6d644 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1349,29 +1349,30 @@ class _DisplayMenuState extends State<_DisplayMenu> { codec() { return futureBuilder(future: () async { - final supportedHwcodec = - await bind.sessionSupportedHwcodec(id: widget.id); + final alternativeCodecs = + await bind.sessionAlternativeCodecs(id: widget.id); final codecPreference = await bind.sessionGetOption(id: widget.id, arg: 'codec-preference') ?? ''; return { - 'supportedHwcodec': supportedHwcodec, + 'alternativeCodecs': alternativeCodecs, 'codecPreference': codecPreference }; }(), hasData: (data) { final List codecs = []; try { - final Map codecsJson = jsonDecode(data['supportedHwcodec']); + final Map codecsJson = jsonDecode(data['alternativeCodecs']); + final vp8 = codecsJson['vp8'] ?? false; final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { debugPrint("Show Codec Preference err=$e"); } - final visible = bind.mainHasHwcodec() && - codecs.length == 2 && - (codecs[0] || codecs[1]); + final visible = + codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2]); if (!visible) return Offstage(); final groupValue = data['codecPreference'] as String; onChanged(String? value) async { @@ -1392,6 +1393,13 @@ class _DisplayMenuState extends State<_DisplayMenu> { onChanged: onChanged, ffi: widget.ffi, ), + _RadioMenuButton( + child: Text(translate('VP8')), + value: 'vp8', + groupValue: groupValue, + onChanged: codecs[0] ? onChanged : null, + ffi: widget.ffi, + ), _RadioMenuButton( child: Text(translate('VP9')), value: 'vp9', @@ -1403,14 +1411,14 @@ class _DisplayMenuState extends State<_DisplayMenu> { child: Text(translate('H264')), value: 'h264', groupValue: groupValue, - onChanged: codecs[0] ? onChanged : null, + onChanged: codecs[1] ? onChanged : null, ffi: widget.ffi, ), _RadioMenuButton( child: Text(translate('H265')), value: 'h265', groupValue: groupValue, - onChanged: codecs[1] ? onChanged : null, + onChanged: codecs[2] ? onChanged : null, ffi: widget.ffi, ), ]); diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 083cdcd1c..74a327c27 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -973,9 +973,11 @@ void showOptions( if (hasHwcodec) { try { final Map codecsJson = - jsonDecode(await bind.sessionSupportedHwcodec(id: id)); + jsonDecode(await bind.sessionAlternativeCodecs(id: id)); + final vp8 = codecsJson['vp8'] ?? false; final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { @@ -1044,6 +1046,7 @@ void showOptions( if (hasHwcodec && codecs.length == 2 && (codecs[0] || codecs[1])) { radios.addAll([ getRadio(translate('Auto'), 'auto', codec, setCodec), + getRadio('VP8', 'vp8', codec, setCodec), getRadio('VP9', 'vp9', codec, setCodec), ]); if (codecs[0]) { diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 6295c160b..321747e3a 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -24,6 +24,7 @@ message VideoFrame { YUV yuv = 8; EncodedVideoFrames h264s = 10; EncodedVideoFrames h265s = 11; + EncodedVideoFrames vp8s = 12; } } @@ -76,6 +77,7 @@ message Features { message SupportedEncoding { bool h264 = 1; bool h265 = 2; + bool vp8 = 3; } message PeerInfo { @@ -457,18 +459,20 @@ enum ImageQuality { Best = 4; } -message VideoCodecState { +message SupportedDecoding { enum PreferCodec { Auto = 0; - VPX = 1; + VP9 = 1; H264 = 2; H265 = 3; + VP8 = 4; } - int32 score_vpx = 1; - int32 score_h264 = 2; - int32 score_h265 = 3; + int32 ability_vp9 = 1; + int32 ability_h264 = 2; + int32 ability_h265 = 3; PreferCodec prefer = 4; + int32 ability_vp8 = 5; } message OptionMessage { @@ -486,7 +490,7 @@ message OptionMessage { BoolOption disable_audio = 7; BoolOption disable_clipboard = 8; BoolOption enable_file_transfer = 9; - VideoCodecState video_codec_state = 10; + SupportedDecoding supported_decoding = 10; int32 custom_fps = 11; BoolOption disable_keyboard = 12; } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 6a823c7b7..960074a8f 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -917,7 +917,8 @@ impl PeerConfig { store = store || store2; for opt in ["rdp_password", "os-password"] { if let Some(v) = config.options.get_mut(opt) { - let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + let (encrypted, _, store2) = + decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; store = store || store2; } @@ -1356,7 +1357,7 @@ impl UserDefaultConfig { "view_style" => self.get_string(key, "original", vec!["adaptive"]), "scroll_style" => self.get_string(key, "scrollauto", vec!["scrollbar"]), "image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]), - "codec-preference" => self.get_string(key, "auto", vec!["vp9", "h264", "h265"]), + "codec-preference" => self.get_string(key, "auto", vec!["vp8", "vp9", "h264", "h265"]), "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 100.0), "custom-fps" => self.get_double_string(key, 30.0, 10.0, 120.0), _ => self diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index 003830f95..ba8dec9f2 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -3,7 +3,8 @@ use hbb_common::env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; use scrap::{ codec::{EncoderApi, EncoderCfg}, Capturer, Display, TraitCapturer, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, - VpxVideoCodecId, STRIDE_ALIGN, + VpxVideoCodecId::{self, *}, + STRIDE_ALIGN, }; use std::{io::Write, time::Instant}; @@ -49,7 +50,7 @@ fn main() { "benchmark {}x{} bitrate:{}k hw_pixfmt:{:?}", width, height, bitrate_k, args.flag_hw_pixfmt ); - test_vp9(&yuvs, width, height, bitrate_k, yuv_count); + [VP8, VP9].map(|c| test_vpx(c, &yuvs, width, height, bitrate_k, yuv_count)); #[cfg(feature = "hwcodec")] { use hwcodec::AVPixelFormat; @@ -57,7 +58,7 @@ fn main() { Pixfmt::I420 => AVPixelFormat::AV_PIX_FMT_YUV420P, Pixfmt::NV12 => AVPixelFormat::AV_PIX_FMT_NV12, }; - let yuvs = hw::vp9_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); + let yuvs = hw::vpx_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); hw::test(&yuvs, width, height, bitrate_k, yuv_count, hw_pixfmt); } } @@ -87,13 +88,20 @@ fn capture_yuv(yuv_count: usize) -> (Vec>, usize, usize) { } } -fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, yuv_count: usize) { +fn test_vpx( + codec_id: VpxVideoCodecId, + yuvs: &Vec>, + width: usize, + height: usize, + bitrate_k: usize, + yuv_count: usize, +) { let config = EncoderCfg::VPX(VpxEncoderConfig { width: width as _, height: height as _, timebase: [1, 1000], bitrate: bitrate_k as _, - codec: VpxVideoCodecId::VP9, + codec: codec_id, num_threads: (num_cpus::get() / 2) as _, }); let mut encoder = VpxEncoder::new(config).unwrap(); @@ -104,35 +112,43 @@ fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, .unwrap(); let _ = encoder.flush().unwrap(); } - println!("vp9 encode: {:?}", start.elapsed() / yuv_count as _); + println!( + "{:?} encode: {:?}", + codec_id, + start.elapsed() / yuv_count as _ + ); // prepare data separately - let mut vp9s = vec![]; + let mut vpxs = vec![]; let start = Instant::now(); for yuv in yuvs { for ref frame in encoder .encode(start.elapsed().as_millis() as _, yuv, STRIDE_ALIGN) .unwrap() { - vp9s.push(frame.data.to_vec()); + vpxs.push(frame.data.to_vec()); } for ref frame in encoder.flush().unwrap() { - vp9s.push(frame.data.to_vec()); + vpxs.push(frame.data.to_vec()); } } - assert_eq!(vp9s.len(), yuv_count); + assert_eq!(vpxs.len(), yuv_count); let mut decoder = VpxDecoder::new(VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, + codec: codec_id, num_threads: (num_cpus::get() / 2) as _, }) .unwrap(); let start = Instant::now(); - for vp9 in vp9s { - let _ = decoder.decode(&vp9); + for vpx in vpxs { + let _ = decoder.decode(&vpx); let _ = decoder.flush(); } - println!("vp9 decode: {:?}", start.elapsed() / yuv_count as _); + println!( + "{:?} decode: {:?}", + codec_id, + start.elapsed() / yuv_count as _ + ); } #[cfg(feature = "hwcodec")] @@ -267,7 +283,7 @@ mod hw { Some(info.clone()) == best.h264 || Some(info.clone()) == best.h265 } - pub fn vp9_yuv_to_hw_yuv( + pub fn vpx_yuv_to_hw_yuv( yuvs: Vec>, width: usize, height: usize, diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index 8daf8e4bb..36d6a8a9b 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -50,6 +50,7 @@ impl crate::TraitCapturer for Capturer { pub enum Frame<'a> { RAW(&'a [u8]), + VP8(&'a [u8]), VP9(&'a [u8]), Empty, } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 9e4b6fce4..3209933b4 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -1,7 +1,6 @@ -use std::ops::{Deref, DerefMut}; -#[cfg(feature = "hwcodec")] use std::{ collections::HashMap, + ops::{Deref, DerefMut}, sync::{Arc, Mutex}, }; @@ -11,30 +10,31 @@ use crate::hwcodec::*; use crate::mediacodec::{ MediaCodecDecoder, MediaCodecDecoders, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT, }; -use crate::{vpxcodec::*, ImageFormat}; +use crate::{vpxcodec::*, CodecName, ImageFormat}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::sysinfo::{System, SystemExt}; use hbb_common::{ anyhow::anyhow, + config::PeerConfig, log, - message_proto::{video_frame, EncodedVideoFrames, Message, VideoCodecState}, + message_proto::{ + supported_decoding::PreferCodec, video_frame, EncodedVideoFrames, Message, + SupportedDecoding, SupportedEncoding, + }, ResultType, }; #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] -use hbb_common::{ - config::{Config2, PeerConfig}, - lazy_static, - message_proto::video_codec_state::PreferCodec, -}; +use hbb_common::{config::Config2, lazy_static}; -#[cfg(feature = "hwcodec")] lazy_static::lazy_static! { - static ref PEER_DECODER_STATES: Arc>> = Default::default(); + static ref PEER_DECODINGS: Arc>> = Default::default(); + static ref CODEC_NAME: Arc> = Arc::new(Mutex::new(CodecName::VP9)); } -const SCORE_VPX: i32 = 90; #[derive(Debug, Clone)] pub struct HwEncoderConfig { - pub codec_name: String, + pub name: String, pub width: usize, pub height: usize, pub bitrate: i32, @@ -58,10 +58,6 @@ pub trait EncoderApi { fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()>; } -pub struct DecoderCfg { - pub vpx: VpxDecoderConfig, -} - pub struct Encoder { pub codec: Box, } @@ -81,7 +77,8 @@ impl DerefMut for Encoder { } pub struct Decoder { - vpx: VpxDecoder, + vp8: VpxDecoder, + vp9: VpxDecoder, #[cfg(feature = "hwcodec")] hw: HwDecoders, #[cfg(feature = "hwcodec")] @@ -91,10 +88,10 @@ pub struct Decoder { } #[derive(Debug, Clone)] -pub enum EncoderUpdate { - State(VideoCodecState), +pub enum EncodingUpdate { + New(SupportedDecoding), Remove, - DisableHwIfNotExist, + NewOnlyVP9, } impl Encoder { @@ -120,172 +117,156 @@ impl Encoder { } } - // TODO - pub fn update_video_encoder(id: i32, update: EncoderUpdate) { + pub fn update(id: i32, update: EncodingUpdate) { + let mut decodings = PEER_DECODINGS.lock().unwrap(); + match update { + EncodingUpdate::New(decoding) => { + decodings.insert(id, decoding); + } + EncodingUpdate::Remove => { + decodings.remove(&id); + } + EncodingUpdate::NewOnlyVP9 => { + decodings.insert( + id, + SupportedDecoding { + ability_vp9: 1, + ..Default::default() + }, + ); + } + } + + let vp8_useable = decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_vp8 > 0); + #[allow(unused_mut)] + let mut h264_name = None; + #[allow(unused_mut)] + let mut h265_name = None; #[cfg(feature = "hwcodec")] { - let mut states = PEER_DECODER_STATES.lock().unwrap(); - match update { - EncoderUpdate::State(state) => { - states.insert(id, state); - } - EncoderUpdate::Remove => { - states.remove(&id); - } - EncoderUpdate::DisableHwIfNotExist => { - if !states.contains_key(&id) { - states.insert(id, VideoCodecState::default()); - } - } + let best = HwEncoder::best(); + let h264_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); + let h265_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); + if h264_useable { + h264_name = best.h264.map_or(None, |c| Some(c.name)); } - let name = HwEncoder::current_name(); - if states.len() > 0 { - let best = HwEncoder::best(); - let enabled_h264 = best.h264.is_some() - && states.len() > 0 - && states.iter().all(|(_, s)| s.score_h264 > 0); - let enabled_h265 = best.h265.is_some() - && states.len() > 0 - && states.iter().all(|(_, s)| s.score_h265 > 0); - - // Preference first - let mut preference = PreferCodec::Auto; - let preferences: Vec<_> = states - .iter() - .filter(|(_, s)| { - s.prefer == PreferCodec::VPX.into() - || s.prefer == PreferCodec::H264.into() && enabled_h264 - || s.prefer == PreferCodec::H265.into() && enabled_h265 - }) - .map(|(_, s)| s.prefer) - .collect(); - if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { - preference = preferences[0].enum_value_or(PreferCodec::Auto); - } - - match preference { - PreferCodec::VPX => *name.lock().unwrap() = None, - PreferCodec::H264 => { - *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)) - } - PreferCodec::H265 => { - *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)) - } - PreferCodec::Auto => { - // score encoder - let mut score_vpx = SCORE_VPX; - let mut score_h264 = best.h264.as_ref().map_or(0, |c| c.score); - let mut score_h265 = best.h265.as_ref().map_or(0, |c| c.score); - - // score decoder - score_vpx += states.iter().map(|s| s.1.score_vpx).sum::(); - if enabled_h264 { - score_h264 += states.iter().map(|s| s.1.score_h264).sum::(); - } - if enabled_h265 { - score_h265 += states.iter().map(|s| s.1.score_h265).sum::(); - } - - if enabled_h265 && score_h265 >= score_vpx && score_h265 >= score_h264 { - *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)); - } else if enabled_h264 - && score_h264 >= score_vpx - && score_h264 >= score_h265 - { - *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)); - } else { - *name.lock().unwrap() = None; - } - } - } - - log::info!( - "connection count:{}, used preference:{:?}, encoder:{:?}", - states.len(), - preference, - name.lock().unwrap() - ) - } else { - *name.lock().unwrap() = None; + if h265_useable { + h265_name = best.h265.map_or(None, |c| Some(c.name)); } } - #[cfg(not(feature = "hwcodec"))] - { - let _ = id; - let _ = update; + + let mut name = CODEC_NAME.lock().unwrap(); + let mut preference = PreferCodec::Auto; + let preferences: Vec<_> = decodings + .iter() + .filter(|(_, s)| { + s.prefer == PreferCodec::VP9.into() + || s.prefer == PreferCodec::VP8.into() && vp8_useable + || s.prefer == PreferCodec::H264.into() && h264_name.is_some() + || s.prefer == PreferCodec::H265.into() && h265_name.is_some() + }) + .map(|(_, s)| s.prefer) + .collect(); + if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { + preference = preferences[0].enum_value_or(PreferCodec::Auto); } + + #[allow(unused_mut)] + let mut auto_codec = CodecName::VP9; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if vp8_useable && System::new_all().total_memory() <= 4 * 1024 * 1024 * 1024 { + // 4 Gb + auto_codec = CodecName::VP8 + } + + match preference { + PreferCodec::VP8 => *name = CodecName::VP8, + PreferCodec::VP9 => *name = CodecName::VP9, + PreferCodec::H264 => *name = h264_name.map_or(auto_codec, |c| CodecName::H264(c)), + PreferCodec::H265 => *name = h265_name.map_or(auto_codec, |c| CodecName::H265(c)), + PreferCodec::Auto => *name = auto_codec, + } + + log::info!( + "connection count:{}, used preference:{:?}, encoder:{:?}", + decodings.len(), + preference, + *name + ) } + #[inline] - pub fn current_hw_encoder_name() -> Option { - #[cfg(feature = "hwcodec")] - if enable_hwcodec_option() { - return HwEncoder::current_name().lock().unwrap().clone(); - } else { - return None; - } - #[cfg(not(feature = "hwcodec"))] - return None; + pub fn negotiated_codec() -> CodecName { + CODEC_NAME.lock().unwrap().clone() } - pub fn supported_encoding() -> (bool, bool) { + pub fn supported_encoding() -> SupportedEncoding { + #[allow(unused_mut)] + let mut encoding = SupportedEncoding { + vp8: true, + ..Default::default() + }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwEncoder::best(); - ( - best.h264.as_ref().map_or(false, |c| c.score > 0), - best.h265.as_ref().map_or(false, |c| c.score > 0), - ) - } else { - (false, false) + encoding.h264 = best.h264.is_some(); + encoding.h265 = best.h265.is_some(); } - #[cfg(not(feature = "hwcodec"))] - (false, false) + encoding } } impl Decoder { - pub fn video_codec_state(_id: &str) -> VideoCodecState { + pub fn supported_decodings(id_for_perfer: Option<&str>) -> SupportedDecoding { + #[allow(unused_mut)] + let mut decoding = SupportedDecoding { + ability_vp8: 1, + ability_vp9: 1, + prefer: id_for_perfer + .map_or(PreferCodec::Auto, |id| Self::codec_preference(id)) + .into(), + ..Default::default() + }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwDecoder::best(); - return VideoCodecState { - score_vpx: SCORE_VPX, - score_h264: best.h264.map_or(0, |c| c.score), - score_h265: best.h265.map_or(0, |c| c.score), - prefer: Self::codec_preference(_id).into(), - ..Default::default() - }; + decoding.ability_h264 = if best.h264.is_some() { 1 } else { 0 }; + decoding.ability_h265 = if best.h265.is_some() { 1 } else { 0 }; } #[cfg(feature = "mediacodec")] if enable_hwcodec_option() { - let score_h264 = if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 92 - } else { - 0 - }; - let score_h265 = if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 94 - } else { - 0 - }; - return VideoCodecState { - score_vpx: SCORE_VPX, - score_h264, - score_h265, - prefer: Self::codec_preference(_id).into(), - ..Default::default() - }; - } - VideoCodecState { - score_vpx: SCORE_VPX, - ..Default::default() + decoding.ability_h264 = + if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 1 + } else { + 0 + }; + decoding.ability_h265 = + if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 1 + } else { + 0 + }; } + decoding } - pub fn new(config: DecoderCfg) -> Decoder { - let vpx = VpxDecoder::new(config.vpx).unwrap(); + pub fn new() -> Decoder { + let vp8 = VpxDecoder::new(VpxDecoderConfig { + codec: VpxVideoCodecId::VP8, + num_threads: (num_cpus::get() / 2) as _, + }) + .unwrap(); + let vp9 = VpxDecoder::new(VpxDecoderConfig { + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }) + .unwrap(); Decoder { - vpx, + vp8, + vp9, #[cfg(feature = "hwcodec")] hw: if enable_hwcodec_option() { HwDecoder::new_decoders() @@ -310,8 +291,11 @@ impl Decoder { rgb: &mut Vec, ) -> ResultType { match frame { + video_frame::Union::Vp8s(vp8s) => { + Decoder::handle_vpxs_video_frame(&mut self.vp8, vp8s, fmt, rgb) + } video_frame::Union::Vp9s(vp9s) => { - Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, fmt, rgb) + Decoder::handle_vpxs_video_frame(&mut self.vp9, vp9s, fmt, rgb) } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { @@ -349,15 +333,15 @@ impl Decoder { } } - fn handle_vp9s_video_frame( + fn handle_vpxs_video_frame( decoder: &mut VpxDecoder, - vp9s: &EncodedVideoFrames, + vpxs: &EncodedVideoFrames, fmt: (ImageFormat, usize), rgb: &mut Vec, ) -> ResultType { let mut last_frame = Image::new(); - for vp9 in vp9s.frames.iter() { - for frame in decoder.decode(&vp9.data)? { + for vpx in vpxs.frames.iter() { + for frame in decoder.decode(&vpx.data)? { drop(last_frame); last_frame = frame; } @@ -408,14 +392,15 @@ impl Decoder { return Ok(false); } - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] fn codec_preference(id: &str) -> PreferCodec { let codec = PeerConfig::load(id) .options .get("codec-preference") .map_or("".to_owned(), |c| c.to_owned()); - if codec == "vp9" { - PreferCodec::VPX + if codec == "vp8" { + PreferCodec::VP8 + } else if codec == "vp9" { + PreferCodec::VP9 } else if codec == "h264" { PreferCodec::H264 } else if codec == "h265" { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 2c69774fb..f4de4bf84 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -7,7 +7,7 @@ use hbb_common::{ anyhow::{anyhow, Context}, bytes::Bytes, config::HwCodecConfig, - lazy_static, log, + log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}, ResultType, }; @@ -19,11 +19,6 @@ use hwcodec::{ Quality::{self, *}, RateControl::{self, *}, }; -use std::sync::{Arc, Mutex}; - -lazy_static::lazy_static! { - static ref HW_ENCODER_NAME: Arc>> = Default::default(); -} const CFG_KEY_ENCODER: &str = "bestHwEncoders"; const CFG_KEY_DECODER: &str = "bestHwDecoders"; @@ -49,7 +44,7 @@ impl EncoderApi for HwEncoder { match cfg { EncoderCfg::HW(config) => { let ctx = EncodeContext { - name: config.codec_name.clone(), + name: config.name.clone(), width: config.width as _, height: config.height as _, pixfmt: DEFAULT_PIXFMT, @@ -60,12 +55,12 @@ impl EncoderApi for HwEncoder { quality: DEFAULT_HW_QUALITY, rc: DEFAULT_RC, }; - let format = match Encoder::format_from_name(config.codec_name.clone()) { + let format = match Encoder::format_from_name(config.name.clone()) { Ok(format) => format, Err(_) => { return Err(anyhow!(format!( "failed to get format from name:{}", - config.codec_name + config.name ))) } }; @@ -133,10 +128,6 @@ impl HwEncoder { }) } - pub fn current_name() -> Arc>> { - HW_ENCODER_NAME.clone() - } - pub fn encode(&mut self, bgra: &[u8]) -> ResultType> { match self.pixfmt { AVPixelFormat::AV_PIX_FMT_YUV420P => hw::hw_bgra_to_i420( diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 0ad158cca..26b946401 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -1,4 +1,5 @@ pub use self::vpxcodec::*; +use hbb_common::message_proto::{video_frame, VideoFrame}; cfg_if! { if #[cfg(quartz)] { @@ -92,3 +93,55 @@ pub fn is_cursor_embedded() -> bool { pub fn is_cursor_embedded() -> bool { false } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CodecName { + VP8, + VP9, + H264(String), + H265(String), +} + +#[derive(PartialEq, Debug, Clone)] +pub enum CodecFormat { + VP8, + VP9, + H264, + H265, + Unknown, +} + +impl From<&VideoFrame> for CodecFormat { + fn from(it: &VideoFrame) -> Self { + match it.union { + Some(video_frame::Union::Vp8s(_)) => CodecFormat::VP8, + Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, + Some(video_frame::Union::H264s(_)) => CodecFormat::H264, + Some(video_frame::Union::H265s(_)) => CodecFormat::H265, + _ => CodecFormat::Unknown, + } + } +} + +impl From<&CodecName> for CodecFormat { + fn from(value: &CodecName) -> Self { + match value { + CodecName::VP8 => Self::VP8, + CodecName::VP9 => Self::VP9, + CodecName::H264(_) => Self::H264, + CodecName::H265(_) => Self::H265, + } + } +} + +impl ToString for CodecFormat { + fn to_string(&self) -> String { + match self { + CodecFormat::VP8 => "VP8".into(), + CodecFormat::VP9 => "VP9".into(), + CodecFormat::H264 => "H264".into(), + CodecFormat::H265 => "H265".into(), + CodecFormat::Unknown => "Unknow".into(), + } + } +} diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 9f38f2d6a..9de70ae14 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -1,3 +1,4 @@ +use crate::CodecFormat; #[cfg(feature = "hwcodec")] use hbb_common::anyhow::anyhow; use hbb_common::{ @@ -21,13 +22,6 @@ use webm::mux::{self, Segment, Track, VideoTrack, Writer}; const MIN_SECS: u64 = 1; -#[derive(Debug, Clone, PartialEq)] -pub enum RecordCodecID { - VP9, - H264, - H265, -} - #[derive(Debug, Clone)] pub struct RecorderContext { pub server: bool, @@ -36,7 +30,7 @@ pub struct RecorderContext { pub filename: String, pub width: usize, pub height: usize, - pub codec_id: RecordCodecID, + pub format: CodecFormat, pub tx: Option>, } @@ -55,8 +49,9 @@ impl RecorderContext { } let file = if self.server { "s" } else { "c" }.to_string() + &self.id.clone() - + &chrono::Local::now().format("_%Y%m%d%H%M%S").to_string() - + if self.codec_id == RecordCodecID::VP9 { + + &chrono::Local::now().format("_%Y%m%d%H%M%S_").to_string() + + &self.format.to_string() + + if self.format == CodecFormat::VP9 || self.format == CodecFormat::VP8 { ".webm" } else { ".mp4" @@ -107,8 +102,8 @@ impl DerefMut for Recorder { impl Recorder { pub fn new(mut ctx: RecorderContext) -> ResultType { ctx.set_filename()?; - let recorder = match ctx.codec_id { - RecordCodecID::VP9 => Recorder { + let recorder = match ctx.format { + CodecFormat::VP8 | CodecFormat::VP9 => Recorder { inner: Box::new(WebmRecorder::new(ctx.clone())?), ctx, }, @@ -126,8 +121,8 @@ impl Recorder { fn change(&mut self, mut ctx: RecorderContext) -> ResultType<()> { ctx.set_filename()?; - self.inner = match ctx.codec_id { - RecordCodecID::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), + self.inner = match ctx.format { + CodecFormat::VP8 | CodecFormat::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), #[cfg(feature = "hwcodec")] _ => Box::new(HwRecorder::new(ctx.clone())?), #[cfg(not(feature = "hwcodec"))] @@ -148,10 +143,19 @@ impl Recorder { pub fn write_frame(&mut self, frame: &video_frame::Union) -> ResultType<()> { match frame { - video_frame::Union::Vp9s(vp9s) => { - if self.ctx.codec_id != RecordCodecID::VP9 { + video_frame::Union::Vp8s(vp8s) => { + if self.ctx.format != CodecFormat::VP8 { self.change(RecorderContext { - codec_id: RecordCodecID::VP9, + format: CodecFormat::VP8, + ..self.ctx.clone() + })?; + } + vp8s.frames.iter().map(|f| self.write_video(f)).count(); + } + video_frame::Union::Vp9s(vp9s) => { + if self.ctx.format != CodecFormat::VP9 { + self.change(RecorderContext { + format: CodecFormat::VP9, ..self.ctx.clone() })?; } @@ -159,25 +163,25 @@ impl Recorder { } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { - if self.ctx.codec_id != RecordCodecID::H264 { + if self.ctx.format != CodecFormat::H264 { self.change(RecorderContext { - codec_id: RecordCodecID::H264, + format: CodecFormat::H264, ..self.ctx.clone() })?; } - if self.ctx.codec_id == RecordCodecID::H264 { + if self.ctx.format == CodecFormat::H264 { h264s.frames.iter().map(|f| self.write_video(f)).count(); } } #[cfg(feature = "hwcodec")] video_frame::Union::H265s(h265s) => { - if self.ctx.codec_id != RecordCodecID::H265 { + if self.ctx.format != CodecFormat::H265 { self.change(RecorderContext { - codec_id: RecordCodecID::H265, + format: CodecFormat::H265, ..self.ctx.clone() })?; } - if self.ctx.codec_id == RecordCodecID::H265 { + if self.ctx.format == CodecFormat::H265 { h265s.frames.iter().map(|f| self.write_video(f)).count(); } } @@ -221,7 +225,11 @@ impl RecorderApi for WebmRecorder { ctx.width as _, ctx.height as _, None, - mux::VideoCodecId::VP9, + if ctx.format == CodecFormat::VP9 { + mux::VideoCodecId::VP9 + } else { + mux::VideoCodecId::VP8 + }, ); Ok(WebmRecorder { vt, @@ -279,7 +287,7 @@ impl RecorderApi for HwRecorder { filename: ctx.filename.clone(), width: ctx.width, height: ctx.height, - is265: ctx.codec_id == RecordCodecID::H265, + is265: ctx.format == CodecFormat::H265, framerate: crate::hwcodec::DEFAULT_TIME_BASE[1] as _, }) .map_err(|_| anyhow!("Failed to create hardware muxer"))?; diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 3df9c0461..4820ea171 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -30,6 +30,7 @@ pub struct VpxEncoder { ctx: vpx_codec_ctx_t, width: usize, height: usize, + id: VpxVideoCodecId, } pub struct VpxDecoder { @@ -97,15 +98,10 @@ impl EncoderApi for VpxEncoder { { match cfg { crate::codec::EncoderCfg::VPX(config) => { - let i; - if cfg!(feature = "VP8") { - i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), - }; - } else { - i = call_vpx_ptr!(vpx_codec_vp9_cx()); - } + let i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), + }; let mut c = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; call_vpx!(vpx_codec_enc_config_default(i, &mut c, 0)); @@ -187,12 +183,17 @@ impl EncoderApi for VpxEncoder { VP9E_SET_TILE_COLUMNS as _, 4 as c_int )); + } else if config.codec == VpxVideoCodecId::VP8 { + // https://github.com/webmproject/libvpx/blob/972149cafeb71d6f08df89e91a0130d6a38c4b15/vpx/vp8cx.h#L172 + // https://groups.google.com/a/webmproject.org/g/webm-discuss/c/DJhSrmfQ61M + call_vpx!(vpx_codec_control_(&mut ctx, VP8E_SET_CPUUSED as _, 12,)); } Ok(Self { ctx, width: config.width as _, height: config.height as _, + id: config.codec, }) } _ => Err(anyhow!("encoder type mismatch")), @@ -213,7 +214,7 @@ impl EncoderApi for VpxEncoder { // to-do: flush periodically, e.g. 1 second if frames.len() > 0 { - Ok(VpxEncoder::create_msg(frames)) + Ok(VpxEncoder::create_msg(self.id, frames)) } else { Err(anyhow!("no valid frame")) } @@ -280,13 +281,17 @@ impl VpxEncoder { } #[inline] - fn create_msg(vp9s: Vec) -> Message { + pub fn create_msg(codec_id: VpxVideoCodecId, frames: Vec) -> Message { let mut msg_out = Message::new(); let mut vf = VideoFrame::new(); - vf.set_vp9s(EncodedVideoFrames { - frames: vp9s.into(), + let vpxs = EncodedVideoFrames { + frames: frames.into(), ..Default::default() - }); + }; + match codec_id { + VpxVideoCodecId::VP8 => vf.set_vp8s(vpxs), + VpxVideoCodecId::VP9 => vf.set_vp9s(vpxs), + } msg_out.set_video_frame(vf); msg_out } @@ -382,15 +387,10 @@ impl VpxDecoder { pub fn new(config: VpxDecoderConfig) -> Result { // This is sound because `vpx_codec_ctx` is a repr(C) struct without any field that can // cause UB if uninitialized. - let i; - if cfg!(feature = "VP8") { - i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), - }; - } else { - i = call_vpx_ptr!(vpx_codec_vp9_dx()); - } + let i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), + }; let mut ctx = Default::default(); let cfg = vpx_codec_dec_cfg_t { threads: if config.num_threads == 0 { diff --git a/src/client.rs b/src/client.rs index 2f745b70c..ae93ccfcf 100644 --- a/src/client.rs +++ b/src/client.rs @@ -44,9 +44,9 @@ use hbb_common::{ }; pub use helper::*; use scrap::{ - codec::{Decoder, DecoderCfg}, + codec::Decoder, record::{Recorder, RecorderContext}, - ImageFormat, VpxDecoderConfig, VpxVideoCodecId, + ImageFormat, }; use crate::{ @@ -917,12 +917,7 @@ impl VideoHandler { /// Create a new video handler. pub fn new() -> Self { VideoHandler { - decoder: Decoder::new(DecoderCfg { - vpx: VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }, - }), + decoder: Decoder::new(), rgb: Default::default(), recorder: Default::default(), record: false, @@ -954,12 +949,7 @@ impl VideoHandler { /// Reset the decoder. pub fn reset(&mut self) { - self.decoder = Decoder::new(DecoderCfg { - vpx: VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: 1, - }, - }); + self.decoder = Decoder::new(); } /// Start or stop screen record. @@ -973,7 +963,7 @@ impl VideoHandler { filename: "".to_owned(), width: w as _, height: h as _, - codec_id: scrap::record::RecordCodecID::VP9, + format: scrap::CodecFormat::VP9, tx: None, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))); @@ -999,7 +989,7 @@ pub struct LoginConfigHandler { pub conn_id: i32, features: Option, session_id: u64, - pub supported_encoding: Option<(bool, bool)>, + pub supported_encoding: SupportedEncoding, pub restarting_remote_device: bool, pub force_relay: bool, pub direct: Option, @@ -1047,7 +1037,7 @@ impl LoginConfigHandler { self.remember = !config.password.is_empty(); self.config = config; self.session_id = rand::random(); - self.supported_encoding = None; + self.supported_encoding = Default::default(); self.restarting_remote_device = false; self.force_relay = !self.get_option("force-always-relay").is_empty() || force_relay; self.direct = None; @@ -1331,8 +1321,8 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } - let state = Decoder::video_codec_state(&self.id); - msg.video_codec_state = hbb_common::protobuf::MessageField::some(state); + msg.supported_decoding = + hbb_common::protobuf::MessageField::some(Decoder::supported_decodings(Some(&self.id))); n += 1; if n > 0 { @@ -1565,10 +1555,7 @@ impl LoginConfigHandler { self.conn_id = pi.conn_id; // no matter if change, for update file time self.save_config(config); - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - { - self.supported_encoding = Some((pi.encoding.h264, pi.encoding.h265)); - } + self.supported_encoding = pi.encoding.clone().unwrap_or_default(); } pub fn get_remote_dir(&self) -> String { @@ -1626,10 +1613,10 @@ impl LoginConfigHandler { } pub fn change_prefer_codec(&self) -> Message { - let state = scrap::codec::Decoder::video_codec_state(&self.id); + let decoding = scrap::codec::Decoder::supported_decodings(Some(&self.id)); let mut misc = Misc::new(); misc.set_option(OptionMessage { - video_codec_state: hbb_common::protobuf::MessageField::some(state), + supported_decoding: hbb_common::protobuf::MessageField::some(decoding), ..Default::default() }); let mut msg_out = Message::new(); diff --git a/src/client/helper.rs b/src/client/helper.rs index a9696a8e8..61844d908 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -1,37 +1,8 @@ use hbb_common::{ get_time, - message_proto::{video_frame, Message, VideoFrame, VoiceCallRequest, VoiceCallResponse}, + message_proto::{Message, VoiceCallRequest, VoiceCallResponse}, }; - -#[derive(PartialEq, Debug, Clone)] -pub enum CodecFormat { - VP9, - H264, - H265, - Unknown, -} - -impl From<&VideoFrame> for CodecFormat { - fn from(it: &VideoFrame) -> Self { - match it.union { - Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, - Some(video_frame::Union::H264s(_)) => CodecFormat::H264, - Some(video_frame::Union::H265s(_)) => CodecFormat::H265, - _ => CodecFormat::Unknown, - } - } -} - -impl ToString for CodecFormat { - fn to_string(&self) -> String { - match self { - CodecFormat::VP9 => "VP9".into(), - CodecFormat::H264 => "H264".into(), - CodecFormat::H265 => "H265".into(), - CodecFormat::Unknown => "Unknow".into(), - } - } -} +use scrap::CodecFormat; #[derive(Debug, Default)] pub struct QualityStatus { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b0bddc82e..0f662e015 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -29,10 +29,10 @@ use hbb_common::tokio::{ time::{self, Duration, Instant, Interval}, }; use hbb_common::{allow_err, fs, get_time, log, message_proto::*, Stream}; +use scrap::CodecFormat; use crate::client::{ - new_voice_call_request, Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, - SEC30, + new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{self, update_clipboard}; @@ -817,11 +817,10 @@ impl Remote { } fn contains_key_frame(vf: &VideoFrame) -> bool { + use video_frame::Union::*; match &vf.union { Some(vf) => match vf { - video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key), - video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key), - video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key), + Vp8s(f) | Vp9s(f) | H264s(f) | H265s(f) => f.frames.iter().any(|e| e.key), _ => false, }, None => false, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index aee486b94..1d965ebc5 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -969,6 +969,13 @@ pub fn main_has_hwcodec() -> SyncReturn { SyncReturn(has_hwcodec()) } +pub fn main_supported_hwdecodings() -> SyncReturn { + let decoding = supported_hwdecodings(); + let msg = HashMap::from([("h264", decoding.0), ("h265", decoding.1)]); + + SyncReturn(serde_json::ser::to_string(&msg).unwrap_or("".to_owned())) +} + pub fn main_is_root() -> bool { is_root() } @@ -1054,10 +1061,10 @@ pub fn session_send_note(id: String, note: String) { } } -pub fn session_supported_hwcodec(id: String) -> String { +pub fn session_alternative_codecs(id: String) -> String { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - let (h264, h265) = session.supported_hwcodec(); - let msg = HashMap::from([("h264", h264), ("h265", h265)]); + let (vp8, h264, h265) = session.alternative_codecs(); + let msg = HashMap::from([("vp8", vp8), ("h264", h264), ("h265", h265)]); serde_json::ser::to_string(&msg).unwrap_or("".to_owned()) } else { String::new() diff --git a/src/server/connection.rs b/src/server/connection.rs index 725ff43ee..539d96ddd 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -533,7 +533,7 @@ impl Connection { let _ = privacy_mode::turn_off_privacy(0); } video_service::notify_video_frame_fetched(id, None); - scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); + scrap::codec::Encoder::update(id, scrap::codec::EncodingUpdate::Remove); video_service::VIDEO_QOS.lock().unwrap().reset(); if conn.authorized { password::update_temporary_password(); @@ -860,16 +860,7 @@ impl Connection { } } - #[cfg(feature = "hwcodec")] - { - let (h264, h265) = scrap::codec::Encoder::supported_encoding(); - pi.encoding = Some(SupportedEncoding { - h264, - h265, - ..Default::default() - }) - .into(); - } + pi.encoding = Some(scrap::codec::Encoder::supported_encoding()).into(); if self.port_forward_socket.is_some() { let mut msg_out = Message::new(); @@ -1145,21 +1136,21 @@ impl Connection { self.lr = lr.clone(); if let Some(o) = lr.option.as_ref() { self.options_in_login = Some(o.clone()); - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( + if let Some(q) = o.supported_decoding.clone().take() { + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::State(q), + scrap::codec::EncodingUpdate::New(q), ); } else { - scrap::codec::Encoder::update_video_encoder( + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, + scrap::codec::EncodingUpdate::NewOnlyVP9, ); } } else { - scrap::codec::Encoder::update_video_encoder( + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, + scrap::codec::EncodingUpdate::NewOnlyVP9, ); } self.video_ack_required = lr.video_ack_required; @@ -1758,11 +1749,8 @@ impl Connection { .unwrap() .update_user_fps(o.custom_fps as _); } - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( - self.inner.id(), - scrap::codec::EncoderUpdate::State(q), - ); + if let Some(q) = o.supported_decoding.clone().take() { + scrap::codec::Encoder::update(self.inner.id(), scrap::codec::EncodingUpdate::New(q)); } if let Ok(q) = o.lock_after_session_end.enum_value() { if q != BoolOption::NotSet { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 205d0584c..691ca4abe 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,7 +31,7 @@ use scrap::{ codec::{Encoder, EncoderCfg, HwEncoderConfig}, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - Display, TraitCapturer, + CodecName, Display, TraitCapturer, }; #[cfg(windows)] use std::sync::Once; @@ -468,21 +468,29 @@ fn run(sp: GenericService) -> ResultType<()> { drop(video_qos); log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); - let encoder_cfg = match Encoder::current_hw_encoder_name() { - Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { - codec_name, - width: c.width, - height: c.height, - bitrate: bitrate as _, - }), - None => EncoderCfg::VPX(VpxEncoderConfig { - width: c.width as _, - height: c.height as _, - timebase: [1, 1000], // Output timestamp precision - bitrate, - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }), + let encoder_cfg = match Encoder::negotiated_codec() { + scrap::CodecName::H264(name) | scrap::CodecName::H265(name) => { + EncoderCfg::HW(HwEncoderConfig { + name, + width: c.width, + height: c.height, + bitrate: bitrate as _, + }) + } + name @ (scrap::CodecName::VP8 | scrap::CodecName::VP9) => { + EncoderCfg::VPX(VpxEncoderConfig { + width: c.width as _, + height: c.height as _, + timebase: [1, 1000], // Output timestamp precision + bitrate, + codec: if name == scrap::CodecName::VP8 { + VpxVideoCodecId::VP8 + } else { + VpxVideoCodecId::VP9 + }, + num_threads: (num_cpus::get() / 2) as _, + }) + } }; let mut encoder; @@ -526,7 +534,7 @@ fn run(sp: GenericService) -> ResultType<()> { let mut try_gdi = 1; #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); - let codec_name = Encoder::current_hw_encoder_name(); + let codec_name = Encoder::negotiated_codec(); let recorder = get_recorder(c.width, c.height, &codec_name); #[cfg(windows)] start_uac_elevation_check(); @@ -557,7 +565,7 @@ fn run(sp: GenericService) -> ResultType<()> { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } - if codec_name != Encoder::current_hw_encoder_name() { + if codec_name != Encoder::negotiated_codec() { bail!("SWITCH"); } #[cfg(windows)] @@ -603,8 +611,14 @@ fn run(sp: GenericService) -> ResultType<()> { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; match frame { + scrap::Frame::VP8(data) => { + let send_conn_ids = + handle_one_frame_encoded(VpxVideoCodecId::VP8, &sp, data, ms)?; + frame_controller.set_send(now, send_conn_ids); + } scrap::Frame::VP9(data) => { - let send_conn_ids = handle_one_frame_encoded(&sp, data, ms)?; + let send_conn_ids = + handle_one_frame_encoded(VpxVideoCodecId::VP9, &sp, data, ms)?; frame_controller.set_send(now, send_conn_ids); } scrap::Frame::RAW(data) => { @@ -717,12 +731,11 @@ fn run(sp: GenericService) -> ResultType<()> { fn get_recorder( width: usize, height: usize, - codec_name: &Option, + codec_name: &CodecName, ) -> Arc>> { #[cfg(not(target_os = "ios"))] let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { use crate::hbbs_http::record_upload; - use scrap::record::RecordCodecID::*; let tx = if record_upload::is_enable() { let (tx, rx) = std::sync::mpsc::channel(); @@ -731,16 +744,6 @@ fn get_recorder( } else { None }; - let codec_id = match codec_name { - Some(name) => { - if name.contains("264") { - H264 - } else { - H265 - } - } - None => VP9, - }; Recorder::new(RecorderContext { server: true, id: Config::get_id(), @@ -748,7 +751,7 @@ fn get_recorder( filename: "".to_owned(), width, height, - codec_id, + format: codec_name.into(), tx, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))) @@ -775,19 +778,6 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu Ok(()) } -#[inline] -#[cfg(any(target_os = "android", target_os = "ios"))] -fn create_msg(vp9s: Vec) -> Message { - let mut msg_out = Message::new(); - let mut vf = VideoFrame::new(); - vf.set_vp9s(EncodedVideoFrames { - frames: vp9s.into(), - ..Default::default() - }); - msg_out.set_video_frame(vf); - msg_out -} - #[inline] fn handle_one_frame( sp: &GenericService, @@ -820,6 +810,7 @@ fn handle_one_frame( #[inline] #[cfg(any(target_os = "android", target_os = "ios"))] pub fn handle_one_frame_encoded( + codec: VpxVideoCodecId, sp: &GenericService, frame: &[u8], ms: i64, @@ -831,13 +822,13 @@ pub fn handle_one_frame_encoded( } Ok(()) })?; - let vp9_frame = EncodedVideoFrame { + let vpx_frame = EncodedVideoFrame { data: frame.to_vec().into(), key: true, pts: ms, ..Default::default() }; - let send_conn_ids = sp.send_video_frame(create_msg(vec![vp9_frame])); + let send_conn_ids = sp.send_video_frame(scrap::VpxEncoder::create_msg(codec, vec![vpx_frame])); Ok(send_conn_ids) } diff --git a/src/ui/header.tis b/src/ui/header.tis index 257ba417e..af4f1e349 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -161,8 +161,8 @@ class Header: Reactor.Component { } function renderDisplayPop() { - var codecs = handler.supported_hwcodec(); - var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]); + var codecs = handler.alternative_codecs(); + var show_codec = codecs[0] || codecs[1] || codecs[2]; var cursor_embedded = false; if ((pi.displays || []).length > 0) { @@ -186,9 +186,10 @@ class Header: Reactor.Component { {show_codec ?

  • {svg_checkmark}Auto
  • + {codecs[0] ?
  • {svg_checkmark}VP8
  • : ""}
  • {svg_checkmark}VP9
  • - {codecs[0] ?
  • {svg_checkmark}H264
  • : ""} - {codecs[1] ?
  • {svg_checkmark}H265
  • : ""} + {codecs[1] ?
  • {svg_checkmark}H264
  • : ""} + {codecs[2] ?
  • {svg_checkmark}H265
  • : ""}
    : ""}
    {!cursor_embedded &&
  • {svg_checkmark}{translate('Show remote cursor')}
  • } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 68decf955..fc878cf1f 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -20,7 +20,6 @@ use hbb_common::{ use crate::{ client::*, - ui_interface::has_hwcodec, ui_session_interface::{InvokeUiSession, Session}, }; @@ -197,7 +196,14 @@ impl InvokeUiSession for SciterHandler { self.call("confirmDeleteFiles", &make_args!(id, i, name)); } - fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) { + fn override_file_confirm( + &self, + id: i32, + file_num: i32, + to: String, + is_upload: bool, + is_identical: bool, + ) { self.call( "overrideFileConfirm", &make_args!(id, file_num, to, is_upload, is_identical), @@ -451,8 +457,7 @@ impl sciter::EventHandler for SciterSession { fn set_write_override(i32, i32, bool, bool, bool); fn get_keyboard_mode(); fn save_keyboard_mode(String); - fn has_hwcodec(); - fn supported_hwcodec(); + fn alternative_codecs(); fn change_prefer_codec(); fn restart_remote_device(); fn request_voice_call(); @@ -504,10 +509,6 @@ impl SciterSession { v } - fn has_hwcodec(&self) -> bool { - has_hwcodec() - } - pub fn t(&self, name: String) -> String { crate::client::translate(name) } @@ -516,9 +517,10 @@ impl SciterSession { super::get_icon() } - fn supported_hwcodec(&self) -> Value { - let (h264, h265) = self.0.supported_hwcodec(); + fn alternative_codecs(&self) -> Value { + let (vp8, h264, h265) = self.0.alternative_codecs(); let mut v = Value::array(0); + v.push(vp8); v.push(h264); v.push(h265); v diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 11357be4a..26d470fe0 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -752,6 +752,13 @@ pub fn has_hwcodec() -> bool { return true; } +#[cfg(feature = "flutter")] +#[inline] +pub fn supported_hwdecodings() -> (bool, bool) { + let decoding = scrap::codec::Decoder::supported_decodings(None); + (decoding.ability_h264 > 0, decoding.ability_h265 > 0) +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_root() -> bool { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f89be4879..3dee89a6e 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -216,24 +216,16 @@ impl Session { true } - pub fn supported_hwcodec(&self) -> (bool, bool) { - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - { - let decoder = scrap::codec::Decoder::video_codec_state(&self.id); - let mut h264 = decoder.score_h264 > 0; - let mut h265 = decoder.score_h265 > 0; - let (encoding_264, encoding_265) = self - .lc - .read() - .unwrap() - .supported_encoding - .unwrap_or_default(); - h264 = h264 && encoding_264; - h265 = h265 && encoding_265; - return (h264, h265); - } - #[allow(unreachable_code)] - (false, false) + pub fn alternative_codecs(&self) -> (bool, bool, bool) { + let decoder = scrap::codec::Decoder::supported_decodings(None); + let mut vp8 = decoder.ability_vp8 > 0; + let mut h264 = decoder.ability_h264 > 0; + let mut h265 = decoder.ability_h265 > 0; + let enc = &self.lc.read().unwrap().supported_encoding; + vp8 = vp8 && enc.vp8; + h264 = h264 && enc.h264; + h265 = h265 && enc.h265; + (vp8, h264, h265) } pub fn change_prefer_codec(&self) { From 3b289f54c94afc6eb49b6c3c4c489c6ed2edd796 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 31 Mar 2023 23:13:27 +0800 Subject: [PATCH 134/366] opt: use our nightly vcpkg --- .github/workflows/flutter-nightly.yml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index b6020460c..e5c2c2752 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -87,18 +87,14 @@ jobs: 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 - + cd C:\ + git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 + - name: Build rustdesk + env: + VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver - name: Sign rustdesk files @@ -192,12 +188,14 @@ jobs: - name: Install vcpkg dependencies run: | - $VCPKG_ROOT/vcpkg install libvpx:x86-windows-static libyuv:x86-windows-static opus:x86-windows-static - shell: bash + cd C:\ + git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 - name: Build rustdesk id: build shell: bash + env: + VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg run: | python3 res/inline-sciter.py # Patch sciter x86 From 726884f1f3890cc6072da5c6d4611b56f6e8ca48 Mon Sep 17 00:00:00 2001 From: chiehw Date: Sat, 1 Apr 2023 01:12:41 +0800 Subject: [PATCH 135/366] Fix review --- src/server/connection.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index eecfaa20c..b25c5b1e7 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1369,10 +1369,12 @@ impl Connection { me.press }; - let key = match me.mode.unwrap() { - KeyboardMode::Map => Some(crate::keyboard::keycode_to_rdev_key(me.chr())), + let key = match me.mode.enum_value_or_default() { + KeyboardMode::Map => { + Some(crate::keyboard::keycode_to_rdev_key(me.chr())) + } KeyboardMode::Translate => { - if let Some(key_event::Union::Chr(code)) = me.union.clone() { + if let Some(key_event::Union::Chr(code)) = me.union { Some(crate::keyboard::keycode_to_rdev_key(code & 0x0000FFFF)) } else { None From c0aa343757f49ed97467d4d677f548b61a1defb4 Mon Sep 17 00:00:00 2001 From: jimmyGALLAND <64364019+jimmyGALLAND@users.noreply.github.com> Date: Fri, 31 Mar 2023 21:57:05 +0200 Subject: [PATCH 136/366] Update fr.rs --- src/lang/fr.rs | 79 +++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/src/lang/fr.rs b/src/lang/fr.rs index bf3853d96..3ec3f08d2 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -213,7 +213,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Closed manually by the peer", "Fermé manuellement par le pair"), ("Enable remote configuration modification", "Autoriser la modification de la configuration à distance"), ("Run without install", "Exécuter sans installer"), - ("Connect via relay", ""), + ("Connect via relay", "Connexion via relais"), ("Always connect via relay", "Forcer la connexion relais"), ("whitelist_tip", "Seule une IP de la liste blanche peut accéder à mon appareil"), ("Login", "Connexion"), @@ -288,8 +288,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "L'activation de la capture d'écran démarrera automatiquement le service, permettant à d'autres appareils de demander une connexion à partir de cet appareil."), ("android_stop_service_tip", "La fermeture du service fermera automatiquement toutes les connexions établies."), ("android_version_audio_tip", "La version actuelle d'Android ne prend pas en charge la capture audio, veuillez passer à Android 10 ou supérieur."), - ("android_start_service_tip", ""), - ("android_permission_may_not_change_tip", ""), + ("android_start_service_tip", "Appuyez sur [Démarrer le service] ou activez l'autorisation [Capture d'écran] pour démarrer le service de partage d'écran."), + ("android_permission_may_not_change_tip", "Les autorisations pour les connexions établies peuvent ne pas être prisent en compte instantanément ou avant la reconnection."), ("Account", "Compte"), ("Overwrite", "Écraser"), ("This file exists, skip or overwrite this file?", "Ce fichier existe, ignorer ou écraser ce fichier ?"), @@ -311,8 +311,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Gardez le service RustDesk en arrière plan"), ("Ignore Battery Optimizations", "Ignorer les optimisations batterie"), ("android_open_battery_optimizations_tip", "Conseil android d'optimisation de batterie"), - ("Start on Boot", ""), - ("Start the screen sharing service on boot, requires special permissions", ""), + ("Start on Boot", "Lancer au démarrage"), + ("Start the screen sharing service on boot, requires special permissions", "Lancer le service de partage d'écran au démarrage, nécessite des autorisations spéciales"), ("Connection not allowed", "Connexion non autorisée"), ("Legacy mode", "Mode hérité"), ("Map mode", ""), @@ -348,7 +348,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Security", "Sécurité"), ("Theme", "Thème"), ("Dark Theme", "Thème somble"), - ("Light Theme", ""), + ("Light Theme", "Thème clair"), ("Dark", "Sombre"), ("Light", "Clair"), ("Follow System", "Suivi système"), @@ -421,7 +421,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("software_render_tip", "Si vous avez une carte graphique NVIDIA et que la fenêtre distante se ferme immédiatement après la connexion, l'installation du pilote Nouveau et le choix d'utiliser le rendu du logiciel peuvent aider. Un redémarrage du logiciel est requis."), ("Always use software rendering", "Utiliser toujours le rendu logiciel"), ("config_input", "Afin de contrôler le bureau à distance avec le clavier, vous devez accorder à RustDesk l'autorisation \"Surveillance de l’entrée\"."), - ("config_microphone", ""), + ("config_microphone", "Pour discuter à distance, vous devez accorder à RustDesk les autorisations « Enregistrer l'audio »."), ("request_elevation_tip", "Vous pouvez également demander une augmentation des privilèges s'il y a quelqu'un du côté distant."), ("Wait", "En cours"), ("Elevation Error", "Erreur d'augmentation des privilèges"), @@ -451,37 +451,38 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("FPS", "FPS"), ("Auto", "Auto"), ("Other Default Options", "Autres options par défaut"), - ("Voice call", ""), - ("Text chat", ""), - ("Stop voice call", ""), - ("relay_hint_tip", ""), - ("Reconnect", ""), - ("Codec", ""), - ("Resolution", ""), - ("No transfers in progress", ""), - ("Set one-time password length", ""), - ("idd_driver_tip", ""), - ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), - ("empty_recent_tip", ""), - ("empty_favorite_tip", ""), - ("empty_lan_tip", ""), - ("empty_address_book_tip", ""), - ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), - ("Me", ""), - ("identical_file_tip", ""), - ("show_monitors_tip", ""), - ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), - ("login_linux_tip", ""), + ("Voice call", "Appel voix"), + ("Text chat", "Conversation textuelfle"), + ("Stop voice call", "Stopper l'appel voix"), + ("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\ » à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\ » dans la fiche pair."), + ("Reconnect", "Se reconnecter"), + ("Codec", "Codec"), + ("Resolution", "Résolution"), + ("No transfers in progress", "Pas de transfert en cours"), + ("Set one-time password length", "Définir la longueur du mot de passe à usage unique"), + ("idd_driver_tip", "Installez le pilote d'affichage virtuel pour être utilisé lorsque vous n'avez pas d'affichage physique."), + ("confirm_idd_driver_tip", "L'option d'installation du pilote d'affichage virtuel est cochée. Notez qu'un certificat de test sera installé pour faire confiance au pilote d'affichage virtuel. Ce certificat de test ne sera utilisé que pour faire confiance aux pilotes Rustdesk."), + ("RDP Settings", "Configuration RDP"), + ("Sort by", "Trier par"), + ("New Connection", "Nouvelle connexion"), + ("Restore", "Restaurer"), + ("Minimize", "Minimiser"), + ("Maximize", "Maximiser"), + ("Your Device", "Votre appareil"), + ("empty_recent_tip", "Oups, pas de sessions récentes!\nIl est temps d'en prévoir une nouvelle."), + ("empty_favorite_tip", "Vous n'avez pas encore de pairs favoris?\nTrouvons quelqu'un avec qui vous connecter et ajoutez-le à vos favoris!"), + ("empty_lan_tip", "Oh non, il semble que nous n'ayons pas encore de pairs découverts."), + ("empty_address_book_tip", "Ouh là là! il semble qu'il n'y ait actuellement aucun pair répertorié dans votre carnet d'adresses."), + ("eg: admin", "ex: admin"), + ("Empty Username", "Nom d'utilisation non spécifié"), + ("Empty Password", "Mot de passe non spécifié"), + ("Me", "Moi"), + ("identical_file_tip", "Ce fichier est identique à celui du pair."), + ("show_monitors_tip", "Afficher les moniteurs dans la barre d'outils."), + ("View Mode", "Mode vue"), + ("enter_rustdesk_passwd_tip", "Saisissez le mot de passe RustDesk."), + ("remember_rustdesk_passwd_tip", "Se rappeler du mot de passe RustDesk."), + ("login_linux_tip", "Se connecter au compte Linux distant"), + ("login_linux_tooltip_tip", "Vous devez vous connecter à un compte Linux distant pour activer une session de bureau X."), ].iter().cloned().collect(); } From e4fc46a8f3a2256575de7b6e37baea275849243f Mon Sep 17 00:00:00 2001 From: jimmyGALLAND <64364019+jimmyGALLAND@users.noreply.github.com> Date: Fri, 31 Mar 2023 22:32:39 +0200 Subject: [PATCH 137/366] Update fr.rs --- src/lang/fr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 3ec3f08d2..3279b9175 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -454,7 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Voice call", "Appel voix"), ("Text chat", "Conversation textuelfle"), ("Stop voice call", "Stopper l'appel voix"), - ("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\ » à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\ » dans la fiche pair."), + ("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\" à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\" dans la fiche pair."), ("Reconnect", "Se reconnecter"), ("Codec", "Codec"), ("Resolution", "Résolution"), From b542acff2353e7c6e4a0a312d2322f32633d80b4 Mon Sep 17 00:00:00 2001 From: Tomasz Boguszewski Date: Sat, 1 Apr 2023 03:39:34 +0200 Subject: [PATCH 138/366] Use match statement in msgbox function Signed-off-by: Tomasz Boguszewski --- src/cli.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 40ab21188..454eec1ee 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -48,18 +48,24 @@ impl Interface for Session { } fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str) { - if msgtype == "input-password" { - self.sender - .send(Data::Login((self.password.clone(), true))) - .ok(); - } else if msgtype == "re-input-password" { - log::error!("{}: {}", title, text); - let pass = rpassword::prompt_password("Enter password: ").unwrap(); - self.sender.send(Data::Login((pass, true))).ok(); - } else if msgtype.contains("error") { - log::error!("{}: {}: {}", msgtype, title, text); - } else { - log::info!("{}: {}: {}", msgtype, title, text); + match msgtype { + "input-password" => { + self.sender + .send(Data::Login((self.password.clone(), true))) + .ok(); + } + "re-input-password" => { + log::error!("{}: {}", title, text); + let password = rpassword::prompt_password("Enter password: ").unwrap(); + let login_data = Data::Login((password, true)); + self.sender.send(login_data).ok(); + } + msg if msg.contains("error") => { + log::error!("{}: {}: {}", msgtype, title, text); + } + _ => { + log::info!("{}: {}: {}", msgtype, title, text); + } } } From 6372c96722b5415e379515302040a624af6ae553 Mon Sep 17 00:00:00 2001 From: Tomasz Boguszewski Date: Sat, 1 Apr 2023 04:09:35 +0200 Subject: [PATCH 139/366] Simplify gen_name function Signed-off-by: Tomasz Boguszewski --- src/naming.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/naming.rs b/src/naming.rs index 53e675d92..d805ee462 100644 --- a/src/naming.rs +++ b/src/naming.rs @@ -4,10 +4,8 @@ use hbb_common::ResultType; use license::*; fn gen_name(lic: &License) -> ResultType { - let tmp = serde_json::to_vec::(lic)?; - let tmp = URL_SAFE_NO_PAD.encode(&tmp); - let tmp: String = tmp.chars().rev().collect(); - Ok(tmp) + let tmp = URL_SAFE_NO_PAD.encode(&serde_json::to_vec(lic)?); + Ok(tmp.chars().rev().collect()) } fn main() { From 83b7518897e6979ce25dd3f84ac48090a76b6036 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 1 Apr 2023 10:13:39 +0800 Subject: [PATCH 140/366] Revert "vp8" --- .../desktop/pages/desktop_setting_page.dart | 42 +-- .../lib/desktop/widgets/remote_toolbar.dart | 26 +- flutter/lib/mobile/pages/remote_page.dart | 5 +- libs/hbb_common/protos/message.proto | 16 +- libs/hbb_common/src/config.rs | 5 +- libs/scrap/examples/benchmark.rs | 46 +-- libs/scrap/src/common/android.rs | 1 - libs/scrap/src/common/codec.rs | 319 +++++++++--------- libs/scrap/src/common/hwcodec.rs | 17 +- libs/scrap/src/common/mod.rs | 53 --- libs/scrap/src/common/record.rs | 56 ++- libs/scrap/src/common/vpxcodec.rs | 46 +-- src/client.rs | 37 +- src/client/helper.rs | 33 +- src/client/io_loop.rs | 9 +- src/flutter_ffi.rs | 13 +- src/server/connection.rs | 34 +- src/server/video_service.rs | 85 ++--- src/ui/header.tis | 9 +- src/ui/remote.rs | 22 +- src/ui_interface.rs | 7 - src/ui_session_interface.rs | 28 +- 22 files changed, 440 insertions(+), 469 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 74d51407c..66ef83d31 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1258,6 +1258,9 @@ class _DisplayState extends State<_Display> { } Widget codec(BuildContext context) { + if (!bind.mainHasHwcodec()) { + return Offstage(); + } final key = 'codec-preference'; onChanged(String value) async { await bind.mainSetUserDefaultOption(key: key, value: value); @@ -1265,45 +1268,28 @@ class _DisplayState extends State<_Display> { } final groupValue = bind.mainGetUserDefaultOption(key: key); - var hwRadios = []; - try { - final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings()); - final h264 = codecsJson['h264'] ?? false; - final h265 = codecsJson['h265'] ?? false; - if (h264) { - hwRadios.add(_Radio(context, - value: 'h264', - groupValue: groupValue, - label: 'H264', - onChanged: onChanged)); - } - if (h265) { - hwRadios.add(_Radio(context, - value: 'h265', - groupValue: groupValue, - label: 'H265', - onChanged: onChanged)); - } - } catch (e) { - debugPrint("failed to parse supported hwdecodings, err=$e"); - } + return _Card(title: 'Default Codec', children: [ _Radio(context, value: 'auto', groupValue: groupValue, label: 'Auto', onChanged: onChanged), - _Radio(context, - value: 'vp8', - groupValue: groupValue, - label: 'VP8', - onChanged: onChanged), _Radio(context, value: 'vp9', groupValue: groupValue, label: 'VP9', onChanged: onChanged), - ...hwRadios, + _Radio(context, + value: 'h264', + groupValue: groupValue, + label: 'H264', + onChanged: onChanged), + _Radio(context, + value: 'h265', + groupValue: groupValue, + label: 'H265', + onChanged: onChanged), ]); } diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index cb4b6d644..0ef8674ef 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1349,30 +1349,29 @@ class _DisplayMenuState extends State<_DisplayMenu> { codec() { return futureBuilder(future: () async { - final alternativeCodecs = - await bind.sessionAlternativeCodecs(id: widget.id); + final supportedHwcodec = + await bind.sessionSupportedHwcodec(id: widget.id); final codecPreference = await bind.sessionGetOption(id: widget.id, arg: 'codec-preference') ?? ''; return { - 'alternativeCodecs': alternativeCodecs, + 'supportedHwcodec': supportedHwcodec, 'codecPreference': codecPreference }; }(), hasData: (data) { final List codecs = []; try { - final Map codecsJson = jsonDecode(data['alternativeCodecs']); - final vp8 = codecsJson['vp8'] ?? false; + final Map codecsJson = jsonDecode(data['supportedHwcodec']); final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; - codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { debugPrint("Show Codec Preference err=$e"); } - final visible = - codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2]); + final visible = bind.mainHasHwcodec() && + codecs.length == 2 && + (codecs[0] || codecs[1]); if (!visible) return Offstage(); final groupValue = data['codecPreference'] as String; onChanged(String? value) async { @@ -1393,13 +1392,6 @@ class _DisplayMenuState extends State<_DisplayMenu> { onChanged: onChanged, ffi: widget.ffi, ), - _RadioMenuButton( - child: Text(translate('VP8')), - value: 'vp8', - groupValue: groupValue, - onChanged: codecs[0] ? onChanged : null, - ffi: widget.ffi, - ), _RadioMenuButton( child: Text(translate('VP9')), value: 'vp9', @@ -1411,14 +1403,14 @@ class _DisplayMenuState extends State<_DisplayMenu> { child: Text(translate('H264')), value: 'h264', groupValue: groupValue, - onChanged: codecs[1] ? onChanged : null, + onChanged: codecs[0] ? onChanged : null, ffi: widget.ffi, ), _RadioMenuButton( child: Text(translate('H265')), value: 'h265', groupValue: groupValue, - onChanged: codecs[2] ? onChanged : null, + onChanged: codecs[1] ? onChanged : null, ffi: widget.ffi, ), ]); diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 74a327c27..083cdcd1c 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -973,11 +973,9 @@ void showOptions( if (hasHwcodec) { try { final Map codecsJson = - jsonDecode(await bind.sessionAlternativeCodecs(id: id)); - final vp8 = codecsJson['vp8'] ?? false; + jsonDecode(await bind.sessionSupportedHwcodec(id: id)); final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; - codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { @@ -1046,7 +1044,6 @@ void showOptions( if (hasHwcodec && codecs.length == 2 && (codecs[0] || codecs[1])) { radios.addAll([ getRadio(translate('Auto'), 'auto', codec, setCodec), - getRadio('VP8', 'vp8', codec, setCodec), getRadio('VP9', 'vp9', codec, setCodec), ]); if (codecs[0]) { diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 321747e3a..6295c160b 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -24,7 +24,6 @@ message VideoFrame { YUV yuv = 8; EncodedVideoFrames h264s = 10; EncodedVideoFrames h265s = 11; - EncodedVideoFrames vp8s = 12; } } @@ -77,7 +76,6 @@ message Features { message SupportedEncoding { bool h264 = 1; bool h265 = 2; - bool vp8 = 3; } message PeerInfo { @@ -459,20 +457,18 @@ enum ImageQuality { Best = 4; } -message SupportedDecoding { +message VideoCodecState { enum PreferCodec { Auto = 0; - VP9 = 1; + VPX = 1; H264 = 2; H265 = 3; - VP8 = 4; } - int32 ability_vp9 = 1; - int32 ability_h264 = 2; - int32 ability_h265 = 3; + int32 score_vpx = 1; + int32 score_h264 = 2; + int32 score_h265 = 3; PreferCodec prefer = 4; - int32 ability_vp8 = 5; } message OptionMessage { @@ -490,7 +486,7 @@ message OptionMessage { BoolOption disable_audio = 7; BoolOption disable_clipboard = 8; BoolOption enable_file_transfer = 9; - SupportedDecoding supported_decoding = 10; + VideoCodecState video_codec_state = 10; int32 custom_fps = 11; BoolOption disable_keyboard = 12; } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 960074a8f..6a823c7b7 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -917,8 +917,7 @@ impl PeerConfig { store = store || store2; for opt in ["rdp_password", "os-password"] { if let Some(v) = config.options.get_mut(opt) { - let (encrypted, _, store2) = - decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; store = store || store2; } @@ -1357,7 +1356,7 @@ impl UserDefaultConfig { "view_style" => self.get_string(key, "original", vec!["adaptive"]), "scroll_style" => self.get_string(key, "scrollauto", vec!["scrollbar"]), "image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]), - "codec-preference" => self.get_string(key, "auto", vec!["vp8", "vp9", "h264", "h265"]), + "codec-preference" => self.get_string(key, "auto", vec!["vp9", "h264", "h265"]), "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 100.0), "custom-fps" => self.get_double_string(key, 30.0, 10.0, 120.0), _ => self diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index ba8dec9f2..003830f95 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -3,8 +3,7 @@ use hbb_common::env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; use scrap::{ codec::{EncoderApi, EncoderCfg}, Capturer, Display, TraitCapturer, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, - VpxVideoCodecId::{self, *}, - STRIDE_ALIGN, + VpxVideoCodecId, STRIDE_ALIGN, }; use std::{io::Write, time::Instant}; @@ -50,7 +49,7 @@ fn main() { "benchmark {}x{} bitrate:{}k hw_pixfmt:{:?}", width, height, bitrate_k, args.flag_hw_pixfmt ); - [VP8, VP9].map(|c| test_vpx(c, &yuvs, width, height, bitrate_k, yuv_count)); + test_vp9(&yuvs, width, height, bitrate_k, yuv_count); #[cfg(feature = "hwcodec")] { use hwcodec::AVPixelFormat; @@ -58,7 +57,7 @@ fn main() { Pixfmt::I420 => AVPixelFormat::AV_PIX_FMT_YUV420P, Pixfmt::NV12 => AVPixelFormat::AV_PIX_FMT_NV12, }; - let yuvs = hw::vpx_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); + let yuvs = hw::vp9_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); hw::test(&yuvs, width, height, bitrate_k, yuv_count, hw_pixfmt); } } @@ -88,20 +87,13 @@ fn capture_yuv(yuv_count: usize) -> (Vec>, usize, usize) { } } -fn test_vpx( - codec_id: VpxVideoCodecId, - yuvs: &Vec>, - width: usize, - height: usize, - bitrate_k: usize, - yuv_count: usize, -) { +fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, yuv_count: usize) { let config = EncoderCfg::VPX(VpxEncoderConfig { width: width as _, height: height as _, timebase: [1, 1000], bitrate: bitrate_k as _, - codec: codec_id, + codec: VpxVideoCodecId::VP9, num_threads: (num_cpus::get() / 2) as _, }); let mut encoder = VpxEncoder::new(config).unwrap(); @@ -112,43 +104,35 @@ fn test_vpx( .unwrap(); let _ = encoder.flush().unwrap(); } - println!( - "{:?} encode: {:?}", - codec_id, - start.elapsed() / yuv_count as _ - ); + println!("vp9 encode: {:?}", start.elapsed() / yuv_count as _); // prepare data separately - let mut vpxs = vec![]; + let mut vp9s = vec![]; let start = Instant::now(); for yuv in yuvs { for ref frame in encoder .encode(start.elapsed().as_millis() as _, yuv, STRIDE_ALIGN) .unwrap() { - vpxs.push(frame.data.to_vec()); + vp9s.push(frame.data.to_vec()); } for ref frame in encoder.flush().unwrap() { - vpxs.push(frame.data.to_vec()); + vp9s.push(frame.data.to_vec()); } } - assert_eq!(vpxs.len(), yuv_count); + assert_eq!(vp9s.len(), yuv_count); let mut decoder = VpxDecoder::new(VpxDecoderConfig { - codec: codec_id, + codec: VpxVideoCodecId::VP9, num_threads: (num_cpus::get() / 2) as _, }) .unwrap(); let start = Instant::now(); - for vpx in vpxs { - let _ = decoder.decode(&vpx); + for vp9 in vp9s { + let _ = decoder.decode(&vp9); let _ = decoder.flush(); } - println!( - "{:?} decode: {:?}", - codec_id, - start.elapsed() / yuv_count as _ - ); + println!("vp9 decode: {:?}", start.elapsed() / yuv_count as _); } #[cfg(feature = "hwcodec")] @@ -283,7 +267,7 @@ mod hw { Some(info.clone()) == best.h264 || Some(info.clone()) == best.h265 } - pub fn vpx_yuv_to_hw_yuv( + pub fn vp9_yuv_to_hw_yuv( yuvs: Vec>, width: usize, height: usize, diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index 36d6a8a9b..8daf8e4bb 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -50,7 +50,6 @@ impl crate::TraitCapturer for Capturer { pub enum Frame<'a> { RAW(&'a [u8]), - VP8(&'a [u8]), VP9(&'a [u8]), Empty, } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 3209933b4..9e4b6fce4 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -1,6 +1,7 @@ +use std::ops::{Deref, DerefMut}; +#[cfg(feature = "hwcodec")] use std::{ collections::HashMap, - ops::{Deref, DerefMut}, sync::{Arc, Mutex}, }; @@ -10,31 +11,30 @@ use crate::hwcodec::*; use crate::mediacodec::{ MediaCodecDecoder, MediaCodecDecoders, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT, }; -use crate::{vpxcodec::*, CodecName, ImageFormat}; +use crate::{vpxcodec::*, ImageFormat}; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use hbb_common::sysinfo::{System, SystemExt}; use hbb_common::{ anyhow::anyhow, - config::PeerConfig, log, - message_proto::{ - supported_decoding::PreferCodec, video_frame, EncodedVideoFrames, Message, - SupportedDecoding, SupportedEncoding, - }, + message_proto::{video_frame, EncodedVideoFrames, Message, VideoCodecState}, ResultType, }; #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] -use hbb_common::{config::Config2, lazy_static}; +use hbb_common::{ + config::{Config2, PeerConfig}, + lazy_static, + message_proto::video_codec_state::PreferCodec, +}; +#[cfg(feature = "hwcodec")] lazy_static::lazy_static! { - static ref PEER_DECODINGS: Arc>> = Default::default(); - static ref CODEC_NAME: Arc> = Arc::new(Mutex::new(CodecName::VP9)); + static ref PEER_DECODER_STATES: Arc>> = Default::default(); } +const SCORE_VPX: i32 = 90; #[derive(Debug, Clone)] pub struct HwEncoderConfig { - pub name: String, + pub codec_name: String, pub width: usize, pub height: usize, pub bitrate: i32, @@ -58,6 +58,10 @@ pub trait EncoderApi { fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()>; } +pub struct DecoderCfg { + pub vpx: VpxDecoderConfig, +} + pub struct Encoder { pub codec: Box, } @@ -77,8 +81,7 @@ impl DerefMut for Encoder { } pub struct Decoder { - vp8: VpxDecoder, - vp9: VpxDecoder, + vpx: VpxDecoder, #[cfg(feature = "hwcodec")] hw: HwDecoders, #[cfg(feature = "hwcodec")] @@ -88,10 +91,10 @@ pub struct Decoder { } #[derive(Debug, Clone)] -pub enum EncodingUpdate { - New(SupportedDecoding), +pub enum EncoderUpdate { + State(VideoCodecState), Remove, - NewOnlyVP9, + DisableHwIfNotExist, } impl Encoder { @@ -117,156 +120,172 @@ impl Encoder { } } - pub fn update(id: i32, update: EncodingUpdate) { - let mut decodings = PEER_DECODINGS.lock().unwrap(); - match update { - EncodingUpdate::New(decoding) => { - decodings.insert(id, decoding); - } - EncodingUpdate::Remove => { - decodings.remove(&id); - } - EncodingUpdate::NewOnlyVP9 => { - decodings.insert( - id, - SupportedDecoding { - ability_vp9: 1, - ..Default::default() - }, - ); - } - } - - let vp8_useable = decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_vp8 > 0); - #[allow(unused_mut)] - let mut h264_name = None; - #[allow(unused_mut)] - let mut h265_name = None; + // TODO + pub fn update_video_encoder(id: i32, update: EncoderUpdate) { #[cfg(feature = "hwcodec")] { - let best = HwEncoder::best(); - let h264_useable = - decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); - let h265_useable = - decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); - if h264_useable { - h264_name = best.h264.map_or(None, |c| Some(c.name)); + let mut states = PEER_DECODER_STATES.lock().unwrap(); + match update { + EncoderUpdate::State(state) => { + states.insert(id, state); + } + EncoderUpdate::Remove => { + states.remove(&id); + } + EncoderUpdate::DisableHwIfNotExist => { + if !states.contains_key(&id) { + states.insert(id, VideoCodecState::default()); + } + } } - if h265_useable { - h265_name = best.h265.map_or(None, |c| Some(c.name)); + let name = HwEncoder::current_name(); + if states.len() > 0 { + let best = HwEncoder::best(); + let enabled_h264 = best.h264.is_some() + && states.len() > 0 + && states.iter().all(|(_, s)| s.score_h264 > 0); + let enabled_h265 = best.h265.is_some() + && states.len() > 0 + && states.iter().all(|(_, s)| s.score_h265 > 0); + + // Preference first + let mut preference = PreferCodec::Auto; + let preferences: Vec<_> = states + .iter() + .filter(|(_, s)| { + s.prefer == PreferCodec::VPX.into() + || s.prefer == PreferCodec::H264.into() && enabled_h264 + || s.prefer == PreferCodec::H265.into() && enabled_h265 + }) + .map(|(_, s)| s.prefer) + .collect(); + if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { + preference = preferences[0].enum_value_or(PreferCodec::Auto); + } + + match preference { + PreferCodec::VPX => *name.lock().unwrap() = None, + PreferCodec::H264 => { + *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)) + } + PreferCodec::H265 => { + *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)) + } + PreferCodec::Auto => { + // score encoder + let mut score_vpx = SCORE_VPX; + let mut score_h264 = best.h264.as_ref().map_or(0, |c| c.score); + let mut score_h265 = best.h265.as_ref().map_or(0, |c| c.score); + + // score decoder + score_vpx += states.iter().map(|s| s.1.score_vpx).sum::(); + if enabled_h264 { + score_h264 += states.iter().map(|s| s.1.score_h264).sum::(); + } + if enabled_h265 { + score_h265 += states.iter().map(|s| s.1.score_h265).sum::(); + } + + if enabled_h265 && score_h265 >= score_vpx && score_h265 >= score_h264 { + *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)); + } else if enabled_h264 + && score_h264 >= score_vpx + && score_h264 >= score_h265 + { + *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)); + } else { + *name.lock().unwrap() = None; + } + } + } + + log::info!( + "connection count:{}, used preference:{:?}, encoder:{:?}", + states.len(), + preference, + name.lock().unwrap() + ) + } else { + *name.lock().unwrap() = None; } } - - let mut name = CODEC_NAME.lock().unwrap(); - let mut preference = PreferCodec::Auto; - let preferences: Vec<_> = decodings - .iter() - .filter(|(_, s)| { - s.prefer == PreferCodec::VP9.into() - || s.prefer == PreferCodec::VP8.into() && vp8_useable - || s.prefer == PreferCodec::H264.into() && h264_name.is_some() - || s.prefer == PreferCodec::H265.into() && h265_name.is_some() - }) - .map(|(_, s)| s.prefer) - .collect(); - if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { - preference = preferences[0].enum_value_or(PreferCodec::Auto); + #[cfg(not(feature = "hwcodec"))] + { + let _ = id; + let _ = update; } - - #[allow(unused_mut)] - let mut auto_codec = CodecName::VP9; - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if vp8_useable && System::new_all().total_memory() <= 4 * 1024 * 1024 * 1024 { - // 4 Gb - auto_codec = CodecName::VP8 - } - - match preference { - PreferCodec::VP8 => *name = CodecName::VP8, - PreferCodec::VP9 => *name = CodecName::VP9, - PreferCodec::H264 => *name = h264_name.map_or(auto_codec, |c| CodecName::H264(c)), - PreferCodec::H265 => *name = h265_name.map_or(auto_codec, |c| CodecName::H265(c)), - PreferCodec::Auto => *name = auto_codec, - } - - log::info!( - "connection count:{}, used preference:{:?}, encoder:{:?}", - decodings.len(), - preference, - *name - ) } - #[inline] - pub fn negotiated_codec() -> CodecName { - CODEC_NAME.lock().unwrap().clone() + pub fn current_hw_encoder_name() -> Option { + #[cfg(feature = "hwcodec")] + if enable_hwcodec_option() { + return HwEncoder::current_name().lock().unwrap().clone(); + } else { + return None; + } + #[cfg(not(feature = "hwcodec"))] + return None; } - pub fn supported_encoding() -> SupportedEncoding { - #[allow(unused_mut)] - let mut encoding = SupportedEncoding { - vp8: true, - ..Default::default() - }; + pub fn supported_encoding() -> (bool, bool) { #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwEncoder::best(); - encoding.h264 = best.h264.is_some(); - encoding.h265 = best.h265.is_some(); + ( + best.h264.as_ref().map_or(false, |c| c.score > 0), + best.h265.as_ref().map_or(false, |c| c.score > 0), + ) + } else { + (false, false) } - encoding + #[cfg(not(feature = "hwcodec"))] + (false, false) } } impl Decoder { - pub fn supported_decodings(id_for_perfer: Option<&str>) -> SupportedDecoding { - #[allow(unused_mut)] - let mut decoding = SupportedDecoding { - ability_vp8: 1, - ability_vp9: 1, - prefer: id_for_perfer - .map_or(PreferCodec::Auto, |id| Self::codec_preference(id)) - .into(), - ..Default::default() - }; + pub fn video_codec_state(_id: &str) -> VideoCodecState { #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwDecoder::best(); - decoding.ability_h264 = if best.h264.is_some() { 1 } else { 0 }; - decoding.ability_h265 = if best.h265.is_some() { 1 } else { 0 }; + return VideoCodecState { + score_vpx: SCORE_VPX, + score_h264: best.h264.map_or(0, |c| c.score), + score_h265: best.h265.map_or(0, |c| c.score), + prefer: Self::codec_preference(_id).into(), + ..Default::default() + }; } #[cfg(feature = "mediacodec")] if enable_hwcodec_option() { - decoding.ability_h264 = - if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 1 - } else { - 0 - }; - decoding.ability_h265 = - if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 1 - } else { - 0 - }; + let score_h264 = if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 92 + } else { + 0 + }; + let score_h265 = if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 94 + } else { + 0 + }; + return VideoCodecState { + score_vpx: SCORE_VPX, + score_h264, + score_h265, + prefer: Self::codec_preference(_id).into(), + ..Default::default() + }; + } + VideoCodecState { + score_vpx: SCORE_VPX, + ..Default::default() } - decoding } - pub fn new() -> Decoder { - let vp8 = VpxDecoder::new(VpxDecoderConfig { - codec: VpxVideoCodecId::VP8, - num_threads: (num_cpus::get() / 2) as _, - }) - .unwrap(); - let vp9 = VpxDecoder::new(VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }) - .unwrap(); + pub fn new(config: DecoderCfg) -> Decoder { + let vpx = VpxDecoder::new(config.vpx).unwrap(); Decoder { - vp8, - vp9, + vpx, #[cfg(feature = "hwcodec")] hw: if enable_hwcodec_option() { HwDecoder::new_decoders() @@ -291,11 +310,8 @@ impl Decoder { rgb: &mut Vec, ) -> ResultType { match frame { - video_frame::Union::Vp8s(vp8s) => { - Decoder::handle_vpxs_video_frame(&mut self.vp8, vp8s, fmt, rgb) - } video_frame::Union::Vp9s(vp9s) => { - Decoder::handle_vpxs_video_frame(&mut self.vp9, vp9s, fmt, rgb) + Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, fmt, rgb) } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { @@ -333,15 +349,15 @@ impl Decoder { } } - fn handle_vpxs_video_frame( + fn handle_vp9s_video_frame( decoder: &mut VpxDecoder, - vpxs: &EncodedVideoFrames, + vp9s: &EncodedVideoFrames, fmt: (ImageFormat, usize), rgb: &mut Vec, ) -> ResultType { let mut last_frame = Image::new(); - for vpx in vpxs.frames.iter() { - for frame in decoder.decode(&vpx.data)? { + for vp9 in vp9s.frames.iter() { + for frame in decoder.decode(&vp9.data)? { drop(last_frame); last_frame = frame; } @@ -392,15 +408,14 @@ impl Decoder { return Ok(false); } + #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] fn codec_preference(id: &str) -> PreferCodec { let codec = PeerConfig::load(id) .options .get("codec-preference") .map_or("".to_owned(), |c| c.to_owned()); - if codec == "vp8" { - PreferCodec::VP8 - } else if codec == "vp9" { - PreferCodec::VP9 + if codec == "vp9" { + PreferCodec::VPX } else if codec == "h264" { PreferCodec::H264 } else if codec == "h265" { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index f4de4bf84..2c69774fb 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -7,7 +7,7 @@ use hbb_common::{ anyhow::{anyhow, Context}, bytes::Bytes, config::HwCodecConfig, - log, + lazy_static, log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}, ResultType, }; @@ -19,6 +19,11 @@ use hwcodec::{ Quality::{self, *}, RateControl::{self, *}, }; +use std::sync::{Arc, Mutex}; + +lazy_static::lazy_static! { + static ref HW_ENCODER_NAME: Arc>> = Default::default(); +} const CFG_KEY_ENCODER: &str = "bestHwEncoders"; const CFG_KEY_DECODER: &str = "bestHwDecoders"; @@ -44,7 +49,7 @@ impl EncoderApi for HwEncoder { match cfg { EncoderCfg::HW(config) => { let ctx = EncodeContext { - name: config.name.clone(), + name: config.codec_name.clone(), width: config.width as _, height: config.height as _, pixfmt: DEFAULT_PIXFMT, @@ -55,12 +60,12 @@ impl EncoderApi for HwEncoder { quality: DEFAULT_HW_QUALITY, rc: DEFAULT_RC, }; - let format = match Encoder::format_from_name(config.name.clone()) { + let format = match Encoder::format_from_name(config.codec_name.clone()) { Ok(format) => format, Err(_) => { return Err(anyhow!(format!( "failed to get format from name:{}", - config.name + config.codec_name ))) } }; @@ -128,6 +133,10 @@ impl HwEncoder { }) } + pub fn current_name() -> Arc>> { + HW_ENCODER_NAME.clone() + } + pub fn encode(&mut self, bgra: &[u8]) -> ResultType> { match self.pixfmt { AVPixelFormat::AV_PIX_FMT_YUV420P => hw::hw_bgra_to_i420( diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 26b946401..0ad158cca 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -1,5 +1,4 @@ pub use self::vpxcodec::*; -use hbb_common::message_proto::{video_frame, VideoFrame}; cfg_if! { if #[cfg(quartz)] { @@ -93,55 +92,3 @@ pub fn is_cursor_embedded() -> bool { pub fn is_cursor_embedded() -> bool { false } - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CodecName { - VP8, - VP9, - H264(String), - H265(String), -} - -#[derive(PartialEq, Debug, Clone)] -pub enum CodecFormat { - VP8, - VP9, - H264, - H265, - Unknown, -} - -impl From<&VideoFrame> for CodecFormat { - fn from(it: &VideoFrame) -> Self { - match it.union { - Some(video_frame::Union::Vp8s(_)) => CodecFormat::VP8, - Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, - Some(video_frame::Union::H264s(_)) => CodecFormat::H264, - Some(video_frame::Union::H265s(_)) => CodecFormat::H265, - _ => CodecFormat::Unknown, - } - } -} - -impl From<&CodecName> for CodecFormat { - fn from(value: &CodecName) -> Self { - match value { - CodecName::VP8 => Self::VP8, - CodecName::VP9 => Self::VP9, - CodecName::H264(_) => Self::H264, - CodecName::H265(_) => Self::H265, - } - } -} - -impl ToString for CodecFormat { - fn to_string(&self) -> String { - match self { - CodecFormat::VP8 => "VP8".into(), - CodecFormat::VP9 => "VP9".into(), - CodecFormat::H264 => "H264".into(), - CodecFormat::H265 => "H265".into(), - CodecFormat::Unknown => "Unknow".into(), - } - } -} diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 9de70ae14..9f38f2d6a 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -1,4 +1,3 @@ -use crate::CodecFormat; #[cfg(feature = "hwcodec")] use hbb_common::anyhow::anyhow; use hbb_common::{ @@ -22,6 +21,13 @@ use webm::mux::{self, Segment, Track, VideoTrack, Writer}; const MIN_SECS: u64 = 1; +#[derive(Debug, Clone, PartialEq)] +pub enum RecordCodecID { + VP9, + H264, + H265, +} + #[derive(Debug, Clone)] pub struct RecorderContext { pub server: bool, @@ -30,7 +36,7 @@ pub struct RecorderContext { pub filename: String, pub width: usize, pub height: usize, - pub format: CodecFormat, + pub codec_id: RecordCodecID, pub tx: Option>, } @@ -49,9 +55,8 @@ impl RecorderContext { } let file = if self.server { "s" } else { "c" }.to_string() + &self.id.clone() - + &chrono::Local::now().format("_%Y%m%d%H%M%S_").to_string() - + &self.format.to_string() - + if self.format == CodecFormat::VP9 || self.format == CodecFormat::VP8 { + + &chrono::Local::now().format("_%Y%m%d%H%M%S").to_string() + + if self.codec_id == RecordCodecID::VP9 { ".webm" } else { ".mp4" @@ -102,8 +107,8 @@ impl DerefMut for Recorder { impl Recorder { pub fn new(mut ctx: RecorderContext) -> ResultType { ctx.set_filename()?; - let recorder = match ctx.format { - CodecFormat::VP8 | CodecFormat::VP9 => Recorder { + let recorder = match ctx.codec_id { + RecordCodecID::VP9 => Recorder { inner: Box::new(WebmRecorder::new(ctx.clone())?), ctx, }, @@ -121,8 +126,8 @@ impl Recorder { fn change(&mut self, mut ctx: RecorderContext) -> ResultType<()> { ctx.set_filename()?; - self.inner = match ctx.format { - CodecFormat::VP8 | CodecFormat::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), + self.inner = match ctx.codec_id { + RecordCodecID::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), #[cfg(feature = "hwcodec")] _ => Box::new(HwRecorder::new(ctx.clone())?), #[cfg(not(feature = "hwcodec"))] @@ -143,19 +148,10 @@ impl Recorder { pub fn write_frame(&mut self, frame: &video_frame::Union) -> ResultType<()> { match frame { - video_frame::Union::Vp8s(vp8s) => { - if self.ctx.format != CodecFormat::VP8 { - self.change(RecorderContext { - format: CodecFormat::VP8, - ..self.ctx.clone() - })?; - } - vp8s.frames.iter().map(|f| self.write_video(f)).count(); - } video_frame::Union::Vp9s(vp9s) => { - if self.ctx.format != CodecFormat::VP9 { + if self.ctx.codec_id != RecordCodecID::VP9 { self.change(RecorderContext { - format: CodecFormat::VP9, + codec_id: RecordCodecID::VP9, ..self.ctx.clone() })?; } @@ -163,25 +159,25 @@ impl Recorder { } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { - if self.ctx.format != CodecFormat::H264 { + if self.ctx.codec_id != RecordCodecID::H264 { self.change(RecorderContext { - format: CodecFormat::H264, + codec_id: RecordCodecID::H264, ..self.ctx.clone() })?; } - if self.ctx.format == CodecFormat::H264 { + if self.ctx.codec_id == RecordCodecID::H264 { h264s.frames.iter().map(|f| self.write_video(f)).count(); } } #[cfg(feature = "hwcodec")] video_frame::Union::H265s(h265s) => { - if self.ctx.format != CodecFormat::H265 { + if self.ctx.codec_id != RecordCodecID::H265 { self.change(RecorderContext { - format: CodecFormat::H265, + codec_id: RecordCodecID::H265, ..self.ctx.clone() })?; } - if self.ctx.format == CodecFormat::H265 { + if self.ctx.codec_id == RecordCodecID::H265 { h265s.frames.iter().map(|f| self.write_video(f)).count(); } } @@ -225,11 +221,7 @@ impl RecorderApi for WebmRecorder { ctx.width as _, ctx.height as _, None, - if ctx.format == CodecFormat::VP9 { - mux::VideoCodecId::VP9 - } else { - mux::VideoCodecId::VP8 - }, + mux::VideoCodecId::VP9, ); Ok(WebmRecorder { vt, @@ -287,7 +279,7 @@ impl RecorderApi for HwRecorder { filename: ctx.filename.clone(), width: ctx.width, height: ctx.height, - is265: ctx.format == CodecFormat::H265, + is265: ctx.codec_id == RecordCodecID::H265, framerate: crate::hwcodec::DEFAULT_TIME_BASE[1] as _, }) .map_err(|_| anyhow!("Failed to create hardware muxer"))?; diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 4820ea171..3df9c0461 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -30,7 +30,6 @@ pub struct VpxEncoder { ctx: vpx_codec_ctx_t, width: usize, height: usize, - id: VpxVideoCodecId, } pub struct VpxDecoder { @@ -98,10 +97,15 @@ impl EncoderApi for VpxEncoder { { match cfg { crate::codec::EncoderCfg::VPX(config) => { - let i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), - }; + let i; + if cfg!(feature = "VP8") { + i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), + }; + } else { + i = call_vpx_ptr!(vpx_codec_vp9_cx()); + } let mut c = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; call_vpx!(vpx_codec_enc_config_default(i, &mut c, 0)); @@ -183,17 +187,12 @@ impl EncoderApi for VpxEncoder { VP9E_SET_TILE_COLUMNS as _, 4 as c_int )); - } else if config.codec == VpxVideoCodecId::VP8 { - // https://github.com/webmproject/libvpx/blob/972149cafeb71d6f08df89e91a0130d6a38c4b15/vpx/vp8cx.h#L172 - // https://groups.google.com/a/webmproject.org/g/webm-discuss/c/DJhSrmfQ61M - call_vpx!(vpx_codec_control_(&mut ctx, VP8E_SET_CPUUSED as _, 12,)); } Ok(Self { ctx, width: config.width as _, height: config.height as _, - id: config.codec, }) } _ => Err(anyhow!("encoder type mismatch")), @@ -214,7 +213,7 @@ impl EncoderApi for VpxEncoder { // to-do: flush periodically, e.g. 1 second if frames.len() > 0 { - Ok(VpxEncoder::create_msg(self.id, frames)) + Ok(VpxEncoder::create_msg(frames)) } else { Err(anyhow!("no valid frame")) } @@ -281,17 +280,13 @@ impl VpxEncoder { } #[inline] - pub fn create_msg(codec_id: VpxVideoCodecId, frames: Vec) -> Message { + fn create_msg(vp9s: Vec) -> Message { let mut msg_out = Message::new(); let mut vf = VideoFrame::new(); - let vpxs = EncodedVideoFrames { - frames: frames.into(), + vf.set_vp9s(EncodedVideoFrames { + frames: vp9s.into(), ..Default::default() - }; - match codec_id { - VpxVideoCodecId::VP8 => vf.set_vp8s(vpxs), - VpxVideoCodecId::VP9 => vf.set_vp9s(vpxs), - } + }); msg_out.set_video_frame(vf); msg_out } @@ -387,10 +382,15 @@ impl VpxDecoder { pub fn new(config: VpxDecoderConfig) -> Result { // This is sound because `vpx_codec_ctx` is a repr(C) struct without any field that can // cause UB if uninitialized. - let i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), - }; + let i; + if cfg!(feature = "VP8") { + i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), + }; + } else { + i = call_vpx_ptr!(vpx_codec_vp9_dx()); + } let mut ctx = Default::default(); let cfg = vpx_codec_dec_cfg_t { threads: if config.num_threads == 0 { diff --git a/src/client.rs b/src/client.rs index ae93ccfcf..2f745b70c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -44,9 +44,9 @@ use hbb_common::{ }; pub use helper::*; use scrap::{ - codec::Decoder, + codec::{Decoder, DecoderCfg}, record::{Recorder, RecorderContext}, - ImageFormat, + ImageFormat, VpxDecoderConfig, VpxVideoCodecId, }; use crate::{ @@ -917,7 +917,12 @@ impl VideoHandler { /// Create a new video handler. pub fn new() -> Self { VideoHandler { - decoder: Decoder::new(), + decoder: Decoder::new(DecoderCfg { + vpx: VpxDecoderConfig { + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }, + }), rgb: Default::default(), recorder: Default::default(), record: false, @@ -949,7 +954,12 @@ impl VideoHandler { /// Reset the decoder. pub fn reset(&mut self) { - self.decoder = Decoder::new(); + self.decoder = Decoder::new(DecoderCfg { + vpx: VpxDecoderConfig { + codec: VpxVideoCodecId::VP9, + num_threads: 1, + }, + }); } /// Start or stop screen record. @@ -963,7 +973,7 @@ impl VideoHandler { filename: "".to_owned(), width: w as _, height: h as _, - format: scrap::CodecFormat::VP9, + codec_id: scrap::record::RecordCodecID::VP9, tx: None, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))); @@ -989,7 +999,7 @@ pub struct LoginConfigHandler { pub conn_id: i32, features: Option, session_id: u64, - pub supported_encoding: SupportedEncoding, + pub supported_encoding: Option<(bool, bool)>, pub restarting_remote_device: bool, pub force_relay: bool, pub direct: Option, @@ -1037,7 +1047,7 @@ impl LoginConfigHandler { self.remember = !config.password.is_empty(); self.config = config; self.session_id = rand::random(); - self.supported_encoding = Default::default(); + self.supported_encoding = None; self.restarting_remote_device = false; self.force_relay = !self.get_option("force-always-relay").is_empty() || force_relay; self.direct = None; @@ -1321,8 +1331,8 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } - msg.supported_decoding = - hbb_common::protobuf::MessageField::some(Decoder::supported_decodings(Some(&self.id))); + let state = Decoder::video_codec_state(&self.id); + msg.video_codec_state = hbb_common::protobuf::MessageField::some(state); n += 1; if n > 0 { @@ -1555,7 +1565,10 @@ impl LoginConfigHandler { self.conn_id = pi.conn_id; // no matter if change, for update file time self.save_config(config); - self.supported_encoding = pi.encoding.clone().unwrap_or_default(); + #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] + { + self.supported_encoding = Some((pi.encoding.h264, pi.encoding.h265)); + } } pub fn get_remote_dir(&self) -> String { @@ -1613,10 +1626,10 @@ impl LoginConfigHandler { } pub fn change_prefer_codec(&self) -> Message { - let decoding = scrap::codec::Decoder::supported_decodings(Some(&self.id)); + let state = scrap::codec::Decoder::video_codec_state(&self.id); let mut misc = Misc::new(); misc.set_option(OptionMessage { - supported_decoding: hbb_common::protobuf::MessageField::some(decoding), + video_codec_state: hbb_common::protobuf::MessageField::some(state), ..Default::default() }); let mut msg_out = Message::new(); diff --git a/src/client/helper.rs b/src/client/helper.rs index 61844d908..a9696a8e8 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -1,8 +1,37 @@ use hbb_common::{ get_time, - message_proto::{Message, VoiceCallRequest, VoiceCallResponse}, + message_proto::{video_frame, Message, VideoFrame, VoiceCallRequest, VoiceCallResponse}, }; -use scrap::CodecFormat; + +#[derive(PartialEq, Debug, Clone)] +pub enum CodecFormat { + VP9, + H264, + H265, + Unknown, +} + +impl From<&VideoFrame> for CodecFormat { + fn from(it: &VideoFrame) -> Self { + match it.union { + Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, + Some(video_frame::Union::H264s(_)) => CodecFormat::H264, + Some(video_frame::Union::H265s(_)) => CodecFormat::H265, + _ => CodecFormat::Unknown, + } + } +} + +impl ToString for CodecFormat { + fn to_string(&self) -> String { + match self { + CodecFormat::VP9 => "VP9".into(), + CodecFormat::H264 => "H264".into(), + CodecFormat::H265 => "H265".into(), + CodecFormat::Unknown => "Unknow".into(), + } + } +} #[derive(Debug, Default)] pub struct QualityStatus { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 0f662e015..b0bddc82e 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -29,10 +29,10 @@ use hbb_common::tokio::{ time::{self, Duration, Instant, Interval}, }; use hbb_common::{allow_err, fs, get_time, log, message_proto::*, Stream}; -use scrap::CodecFormat; use crate::client::{ - new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, + new_voice_call_request, Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, + SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{self, update_clipboard}; @@ -817,10 +817,11 @@ impl Remote { } fn contains_key_frame(vf: &VideoFrame) -> bool { - use video_frame::Union::*; match &vf.union { Some(vf) => match vf { - Vp8s(f) | Vp9s(f) | H264s(f) | H265s(f) => f.frames.iter().any(|e| e.key), + video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key), + video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key), + video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key), _ => false, }, None => false, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 1d965ebc5..aee486b94 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -969,13 +969,6 @@ pub fn main_has_hwcodec() -> SyncReturn { SyncReturn(has_hwcodec()) } -pub fn main_supported_hwdecodings() -> SyncReturn { - let decoding = supported_hwdecodings(); - let msg = HashMap::from([("h264", decoding.0), ("h265", decoding.1)]); - - SyncReturn(serde_json::ser::to_string(&msg).unwrap_or("".to_owned())) -} - pub fn main_is_root() -> bool { is_root() } @@ -1061,10 +1054,10 @@ pub fn session_send_note(id: String, note: String) { } } -pub fn session_alternative_codecs(id: String) -> String { +pub fn session_supported_hwcodec(id: String) -> String { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - let (vp8, h264, h265) = session.alternative_codecs(); - let msg = HashMap::from([("vp8", vp8), ("h264", h264), ("h265", h265)]); + let (h264, h265) = session.supported_hwcodec(); + let msg = HashMap::from([("h264", h264), ("h265", h265)]); serde_json::ser::to_string(&msg).unwrap_or("".to_owned()) } else { String::new() diff --git a/src/server/connection.rs b/src/server/connection.rs index d1ae4d6a3..23e166fcf 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -536,7 +536,7 @@ impl Connection { let _ = privacy_mode::turn_off_privacy(0); } video_service::notify_video_frame_fetched(id, None); - scrap::codec::Encoder::update(id, scrap::codec::EncodingUpdate::Remove); + scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); video_service::VIDEO_QOS.lock().unwrap().reset(); if conn.authorized { password::update_temporary_password(); @@ -862,7 +862,16 @@ impl Connection { } } - pi.encoding = Some(scrap::codec::Encoder::supported_encoding()).into(); + #[cfg(feature = "hwcodec")] + { + let (h264, h265) = scrap::codec::Encoder::supported_encoding(); + pi.encoding = Some(SupportedEncoding { + h264, + h265, + ..Default::default() + }) + .into(); + } if self.port_forward_socket.is_some() { let mut msg_out = Message::new(); @@ -1138,21 +1147,21 @@ impl Connection { self.lr = lr.clone(); if let Some(o) = lr.option.as_ref() { self.options_in_login = Some(o.clone()); - if let Some(q) = o.supported_decoding.clone().take() { - scrap::codec::Encoder::update( + if let Some(q) = o.video_codec_state.clone().take() { + scrap::codec::Encoder::update_video_encoder( self.inner.id(), - scrap::codec::EncodingUpdate::New(q), + scrap::codec::EncoderUpdate::State(q), ); } else { - scrap::codec::Encoder::update( + scrap::codec::Encoder::update_video_encoder( self.inner.id(), - scrap::codec::EncodingUpdate::NewOnlyVP9, + scrap::codec::EncoderUpdate::DisableHwIfNotExist, ); } } else { - scrap::codec::Encoder::update( + scrap::codec::Encoder::update_video_encoder( self.inner.id(), - scrap::codec::EncodingUpdate::NewOnlyVP9, + scrap::codec::EncoderUpdate::DisableHwIfNotExist, ); } self.video_ack_required = lr.video_ack_required; @@ -1775,8 +1784,11 @@ impl Connection { .unwrap() .update_user_fps(o.custom_fps as _); } - if let Some(q) = o.supported_decoding.clone().take() { - scrap::codec::Encoder::update(self.inner.id(), scrap::codec::EncodingUpdate::New(q)); + if let Some(q) = o.video_codec_state.clone().take() { + scrap::codec::Encoder::update_video_encoder( + self.inner.id(), + scrap::codec::EncoderUpdate::State(q), + ); } if let Ok(q) = o.lock_after_session_end.enum_value() { if q != BoolOption::NotSet { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 691ca4abe..205d0584c 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,7 +31,7 @@ use scrap::{ codec::{Encoder, EncoderCfg, HwEncoderConfig}, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - CodecName, Display, TraitCapturer, + Display, TraitCapturer, }; #[cfg(windows)] use std::sync::Once; @@ -468,29 +468,21 @@ fn run(sp: GenericService) -> ResultType<()> { drop(video_qos); log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); - let encoder_cfg = match Encoder::negotiated_codec() { - scrap::CodecName::H264(name) | scrap::CodecName::H265(name) => { - EncoderCfg::HW(HwEncoderConfig { - name, - width: c.width, - height: c.height, - bitrate: bitrate as _, - }) - } - name @ (scrap::CodecName::VP8 | scrap::CodecName::VP9) => { - EncoderCfg::VPX(VpxEncoderConfig { - width: c.width as _, - height: c.height as _, - timebase: [1, 1000], // Output timestamp precision - bitrate, - codec: if name == scrap::CodecName::VP8 { - VpxVideoCodecId::VP8 - } else { - VpxVideoCodecId::VP9 - }, - num_threads: (num_cpus::get() / 2) as _, - }) - } + let encoder_cfg = match Encoder::current_hw_encoder_name() { + Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { + codec_name, + width: c.width, + height: c.height, + bitrate: bitrate as _, + }), + None => EncoderCfg::VPX(VpxEncoderConfig { + width: c.width as _, + height: c.height as _, + timebase: [1, 1000], // Output timestamp precision + bitrate, + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }), }; let mut encoder; @@ -534,7 +526,7 @@ fn run(sp: GenericService) -> ResultType<()> { let mut try_gdi = 1; #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); - let codec_name = Encoder::negotiated_codec(); + let codec_name = Encoder::current_hw_encoder_name(); let recorder = get_recorder(c.width, c.height, &codec_name); #[cfg(windows)] start_uac_elevation_check(); @@ -565,7 +557,7 @@ fn run(sp: GenericService) -> ResultType<()> { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } - if codec_name != Encoder::negotiated_codec() { + if codec_name != Encoder::current_hw_encoder_name() { bail!("SWITCH"); } #[cfg(windows)] @@ -611,14 +603,8 @@ fn run(sp: GenericService) -> ResultType<()> { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; match frame { - scrap::Frame::VP8(data) => { - let send_conn_ids = - handle_one_frame_encoded(VpxVideoCodecId::VP8, &sp, data, ms)?; - frame_controller.set_send(now, send_conn_ids); - } scrap::Frame::VP9(data) => { - let send_conn_ids = - handle_one_frame_encoded(VpxVideoCodecId::VP9, &sp, data, ms)?; + let send_conn_ids = handle_one_frame_encoded(&sp, data, ms)?; frame_controller.set_send(now, send_conn_ids); } scrap::Frame::RAW(data) => { @@ -731,11 +717,12 @@ fn run(sp: GenericService) -> ResultType<()> { fn get_recorder( width: usize, height: usize, - codec_name: &CodecName, + codec_name: &Option, ) -> Arc>> { #[cfg(not(target_os = "ios"))] let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { use crate::hbbs_http::record_upload; + use scrap::record::RecordCodecID::*; let tx = if record_upload::is_enable() { let (tx, rx) = std::sync::mpsc::channel(); @@ -744,6 +731,16 @@ fn get_recorder( } else { None }; + let codec_id = match codec_name { + Some(name) => { + if name.contains("264") { + H264 + } else { + H265 + } + } + None => VP9, + }; Recorder::new(RecorderContext { server: true, id: Config::get_id(), @@ -751,7 +748,7 @@ fn get_recorder( filename: "".to_owned(), width, height, - format: codec_name.into(), + codec_id, tx, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))) @@ -778,6 +775,19 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu Ok(()) } +#[inline] +#[cfg(any(target_os = "android", target_os = "ios"))] +fn create_msg(vp9s: Vec) -> Message { + let mut msg_out = Message::new(); + let mut vf = VideoFrame::new(); + vf.set_vp9s(EncodedVideoFrames { + frames: vp9s.into(), + ..Default::default() + }); + msg_out.set_video_frame(vf); + msg_out +} + #[inline] fn handle_one_frame( sp: &GenericService, @@ -810,7 +820,6 @@ fn handle_one_frame( #[inline] #[cfg(any(target_os = "android", target_os = "ios"))] pub fn handle_one_frame_encoded( - codec: VpxVideoCodecId, sp: &GenericService, frame: &[u8], ms: i64, @@ -822,13 +831,13 @@ pub fn handle_one_frame_encoded( } Ok(()) })?; - let vpx_frame = EncodedVideoFrame { + let vp9_frame = EncodedVideoFrame { data: frame.to_vec().into(), key: true, pts: ms, ..Default::default() }; - let send_conn_ids = sp.send_video_frame(scrap::VpxEncoder::create_msg(codec, vec![vpx_frame])); + let send_conn_ids = sp.send_video_frame(create_msg(vec![vp9_frame])); Ok(send_conn_ids) } diff --git a/src/ui/header.tis b/src/ui/header.tis index af4f1e349..257ba417e 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -161,8 +161,8 @@ class Header: Reactor.Component { } function renderDisplayPop() { - var codecs = handler.alternative_codecs(); - var show_codec = codecs[0] || codecs[1] || codecs[2]; + var codecs = handler.supported_hwcodec(); + var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]); var cursor_embedded = false; if ((pi.displays || []).length > 0) { @@ -186,10 +186,9 @@ class Header: Reactor.Component { {show_codec ?
  • {svg_checkmark}Auto
  • - {codecs[0] ?
  • {svg_checkmark}VP8
  • : ""}
  • {svg_checkmark}VP9
  • - {codecs[1] ?
  • {svg_checkmark}H264
  • : ""} - {codecs[2] ?
  • {svg_checkmark}H265
  • : ""} + {codecs[0] ?
  • {svg_checkmark}H264
  • : ""} + {codecs[1] ?
  • {svg_checkmark}H265
  • : ""}
    : ""}
    {!cursor_embedded &&
  • {svg_checkmark}{translate('Show remote cursor')}
  • } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index fc878cf1f..68decf955 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -20,6 +20,7 @@ use hbb_common::{ use crate::{ client::*, + ui_interface::has_hwcodec, ui_session_interface::{InvokeUiSession, Session}, }; @@ -196,14 +197,7 @@ impl InvokeUiSession for SciterHandler { self.call("confirmDeleteFiles", &make_args!(id, i, name)); } - fn override_file_confirm( - &self, - id: i32, - file_num: i32, - to: String, - is_upload: bool, - is_identical: bool, - ) { + fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) { self.call( "overrideFileConfirm", &make_args!(id, file_num, to, is_upload, is_identical), @@ -457,7 +451,8 @@ impl sciter::EventHandler for SciterSession { fn set_write_override(i32, i32, bool, bool, bool); fn get_keyboard_mode(); fn save_keyboard_mode(String); - fn alternative_codecs(); + fn has_hwcodec(); + fn supported_hwcodec(); fn change_prefer_codec(); fn restart_remote_device(); fn request_voice_call(); @@ -509,6 +504,10 @@ impl SciterSession { v } + fn has_hwcodec(&self) -> bool { + has_hwcodec() + } + pub fn t(&self, name: String) -> String { crate::client::translate(name) } @@ -517,10 +516,9 @@ impl SciterSession { super::get_icon() } - fn alternative_codecs(&self) -> Value { - let (vp8, h264, h265) = self.0.alternative_codecs(); + fn supported_hwcodec(&self) -> Value { + let (h264, h265) = self.0.supported_hwcodec(); let mut v = Value::array(0); - v.push(vp8); v.push(h264); v.push(h265); v diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 26d470fe0..11357be4a 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -752,13 +752,6 @@ pub fn has_hwcodec() -> bool { return true; } -#[cfg(feature = "flutter")] -#[inline] -pub fn supported_hwdecodings() -> (bool, bool) { - let decoding = scrap::codec::Decoder::supported_decodings(None); - (decoding.ability_h264 > 0, decoding.ability_h265 > 0) -} - #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_root() -> bool { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 3dee89a6e..f89be4879 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -216,16 +216,24 @@ impl Session { true } - pub fn alternative_codecs(&self) -> (bool, bool, bool) { - let decoder = scrap::codec::Decoder::supported_decodings(None); - let mut vp8 = decoder.ability_vp8 > 0; - let mut h264 = decoder.ability_h264 > 0; - let mut h265 = decoder.ability_h265 > 0; - let enc = &self.lc.read().unwrap().supported_encoding; - vp8 = vp8 && enc.vp8; - h264 = h264 && enc.h264; - h265 = h265 && enc.h265; - (vp8, h264, h265) + pub fn supported_hwcodec(&self) -> (bool, bool) { + #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] + { + let decoder = scrap::codec::Decoder::video_codec_state(&self.id); + let mut h264 = decoder.score_h264 > 0; + let mut h265 = decoder.score_h265 > 0; + let (encoding_264, encoding_265) = self + .lc + .read() + .unwrap() + .supported_encoding + .unwrap_or_default(); + h264 = h264 && encoding_264; + h265 = h265 && encoding_265; + return (h264, h265); + } + #[allow(unreachable_code)] + (false, false) } pub fn change_prefer_codec(&self) { From afa00df941fb4eb333138649ac2c56e99bb8ab57 Mon Sep 17 00:00:00 2001 From: Tomasz Boguszewski Date: Sat, 1 Apr 2023 04:23:25 +0200 Subject: [PATCH 141/366] Improve argument parsing Signed-off-by: Tomasz Boguszewski --- src/naming.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/naming.rs b/src/naming.rs index d805ee462..7a8d0cecc 100644 --- a/src/naming.rs +++ b/src/naming.rs @@ -9,19 +9,8 @@ fn gen_name(lic: &License) -> ResultType { } fn main() { - let mut args = Vec::new(); - let mut i = 0; - for arg in std::env::args() { - if i > 0 { - args.push(arg); - } - i += 1; - } - let api = if args.len() < 3 { - "".to_owned() - } else { - args[2].clone() - }; + let args: Vec<_> = std::env::args().skip(1).collect(); + let api = args.get(2).cloned().unwrap_or_default(); if args.len() >= 2 { println!( "rustdesk-licensed-{}.exe", From 3ab73bdf361d4df4621a87f8e87d21704e2c602a Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 10:26:55 +0800 Subject: [PATCH 142/366] fix x11 login screen Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 6 ++++++ src/server/service.rs | 9 ++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 89c96799d..4da6d7436 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -40,6 +40,12 @@ pub fn is_desktop_wayland() -> bool { get_display_server() == DISPLAY_SERVER_WAYLAND } +#[inline] +pub fn is_x11_wayland() -> bool { + let ds = get_display_server(); + ds == DISPLAY_SERVER_X11 || ds == DISPLAY_SERVER_WAYLAND +} + #[inline] pub fn is_x11_or_headless() -> bool { !is_desktop_wayland() diff --git a/src/server/service.rs b/src/server/service.rs index 9857889cc..fbe21105d 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -190,12 +190,15 @@ impl> ServiceTmpl { } #[inline] - fn wait_prelogin(&self) { + fn wait_prelogin_or_x11gdm(&self) { #[cfg(target_os = "linux")] while self.active() { if crate::platform::linux::is_prelogin() { break; } + if crate::platform::linux::is_x11_wayland() { + break; + } thread::sleep(time::Duration::from_millis(300)); } } @@ -209,7 +212,7 @@ impl> ServiceTmpl { let mut callback = callback; let sp = self.clone(); let thread = thread::spawn(move || { - sp.wait_prelogin(); + sp.wait_prelogin_or_x11gdm(); let mut state = S::default(); let mut may_reset = false; @@ -245,7 +248,7 @@ impl> ServiceTmpl { let sp = self.clone(); let mut callback = callback; let thread = thread::spawn(move || { - sp.wait_prelogin(); + sp.wait_prelogin_or_x11gdm(); let mut error_timeout = HIBERNATE_TIMEOUT; while sp.active() { From de485ca3a494bdc905555384cc40e894a884e79a Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 10:57:40 +0800 Subject: [PATCH 143/366] remove wait prelogin in service Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 6 ------ src/server/service.rs | 18 ------------------ 2 files changed, 24 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 4da6d7436..89c96799d 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -40,12 +40,6 @@ pub fn is_desktop_wayland() -> bool { get_display_server() == DISPLAY_SERVER_WAYLAND } -#[inline] -pub fn is_x11_wayland() -> bool { - let ds = get_display_server(); - ds == DISPLAY_SERVER_X11 || ds == DISPLAY_SERVER_WAYLAND -} - #[inline] pub fn is_x11_or_headless() -> bool { !is_desktop_wayland() diff --git a/src/server/service.rs b/src/server/service.rs index fbe21105d..9cc1e860c 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -189,20 +189,6 @@ impl> ServiceTmpl { } } - #[inline] - fn wait_prelogin_or_x11gdm(&self) { - #[cfg(target_os = "linux")] - while self.active() { - if crate::platform::linux::is_prelogin() { - break; - } - if crate::platform::linux::is_x11_wayland() { - break; - } - thread::sleep(time::Duration::from_millis(300)); - } - } - pub fn repeat(&self, interval_ms: u64, callback: F) where F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send, @@ -212,8 +198,6 @@ impl> ServiceTmpl { let mut callback = callback; let sp = self.clone(); let thread = thread::spawn(move || { - sp.wait_prelogin_or_x11gdm(); - let mut state = S::default(); let mut may_reset = false; while sp.active() { @@ -248,8 +232,6 @@ impl> ServiceTmpl { let sp = self.clone(); let mut callback = callback; let thread = thread::spawn(move || { - sp.wait_prelogin_or_x11gdm(); - let mut error_timeout = HIBERNATE_TIMEOUT; while sp.active() { if sp.has_subscribes() { From 820b01ab41ba45ed71276c7052f7c263397013e1 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 11:42:10 +0800 Subject: [PATCH 144/366] add translate mode ref link Signed-off-by: fufesou --- src/keyboard.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/keyboard.rs b/src/keyboard.rs index cdedc4fdb..86735b8f6 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -942,6 +942,7 @@ fn is_press(event: &Event) -> bool { matches!(event.event_type, EventType::KeyPress(_)) } +// https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); if let Some(unicode_info) = &event.unicode { From 40204077db1a025815ebe233c4cacea5b933ee0d Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 13:18:01 +0800 Subject: [PATCH 145/366] revert linux is_prelogin Signed-off-by: fufesou --- src/platform/linux.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 1fdfd316d..dda4b115b 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -455,8 +455,8 @@ pub fn get_env_var(k: &str) -> String { } pub fn is_prelogin() -> bool { - let (uid, uname) = get_active_user_id_name(); - uid.len() >= 4 || uname == "root" + let n = get_active_userid().len(); + n < 4 && n > 1 } pub fn is_root() -> bool { From ba8ab75a8080732c26f86fafadbaa0387f2867d5 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 15:51:42 +0800 Subject: [PATCH 146/366] fix remote toolbar autohide Signed-off-by: fufesou --- flutter/lib/desktop/widgets/remote_toolbar.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 0ef8674ef..703ed6db9 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -320,6 +320,8 @@ class _RemoteMenubarState extends State { PeerInfo get pi => widget.ffi.ffiModel.pi; FfiModel get ffiModel => widget.ffi.ffiModel; + triggerAutoHide() => _debouncerHide.value = _debouncerHide.value + 1; + @override initState() { super.initState(); @@ -332,7 +334,7 @@ class _RemoteMenubarState extends State { widget.onEnterOrLeaveImageSetter((enter) { if (enter) { - _debouncerHide.value = 0; + triggerAutoHide(); _isCursorOverImage = true; } else { _isCursorOverImage = false; @@ -367,7 +369,7 @@ class _RemoteMenubarState extends State { Widget _buildDraggableShowHide(BuildContext context) { return Obx(() { if (show.isTrue && _dragging.isFalse) { - _debouncerHide.value = 1; + triggerAutoHide(); } return Align( alignment: FractionalOffset(_fractionX.value, 0), From ae53ec877b7da292725eff8d891621bd0c8d9ebb Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 18:09:53 +0800 Subject: [PATCH 147/366] translate mode, win2win, Send both unicode and virtual keycode to remote side Signed-off-by: fufesou --- libs/hbb_common/protos/message.proto | 6 ++- src/keyboard.rs | 57 +++++++++++++++++----------- src/platform/windows.rs | 42 +++++++++++--------- src/server/input_service.rs | 43 +++++++++++++++++++++ 4 files changed, 105 insertions(+), 43 deletions(-) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 6295c160b..a481ae6c4 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -203,11 +203,13 @@ message KeyEvent { bool press = 2; oneof union { ControlKey control_key = 3; - // high word, sym key code. win: virtual-key code, linux: keysym ?, macos: - // low word, position key code. win: scancode, linux: key code, macos: key code + // position key code. win: scancode, linux: key code, macos: key code uint32 chr = 4; uint32 unicode = 5; string seq = 6; + // high word. virtual keycode + // low word. unicode + uint32 win2win_hotkey = 7; } repeated ControlKey modifiers = 8; KeyboardMode mode = 9; diff --git a/src/keyboard.rs b/src/keyboard.rs index 86735b8f6..a2d17bc14 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -4,7 +4,7 @@ use crate::common::GrabState; #[cfg(feature = "flutter")] use crate::flutter::{CUR_SESSION_ID, SESSIONS}; #[cfg(target_os = "windows")] -use crate::platform::windows::get_char_by_vk; +use crate::platform::windows::{get_char_from_vk, get_unicode_from_vk}; #[cfg(not(any(feature = "flutter", feature = "cli")))] use crate::ui::CUR_SESSION; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -874,7 +874,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m #[cfg(target_os = "windows")] if _peer == OS_LOWER_LINUX { if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { - if let Some(chr) = get_char_by_vk(event.platform_code as u32) { + if let Some(chr) = get_char_from_vk(event.platform_code as u32) { let mut evt = key_event.clone(); evt.set_seq(chr.to_string()); events.push(evt); @@ -885,6 +885,33 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m } } +#[cfg(target_os = "windows")] +fn try_file_win2win_hotkey( + peer: &str, + event: &Event, + key_event: &KeyEvent, + events: &mut Vec, +) { + if peer == OS_LOWER_WINDOWS && is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { + let win2win_hotkey = match event.event_type { + EventType::KeyPress(..) => { + if let Some(unicode) = get_unicode_from_vk(event.platform_code as u32) { + Some((unicode as u32 & 0x0000FFFF) | (event.platform_code << 16)) + } else { + None + } + } + EventType::KeyRelease(..) => Some(event.platform_code << 16), + _ => None, + }; + if let Some(code) = win2win_hotkey { + let mut evt = key_event.clone(); + evt.set_win2win_hotkey(code); + events.push(evt); + } + } +} + #[cfg(target_os = "windows")] fn is_hot_key_modifiers_down() -> bool { if rdev::get_modifier(Key::ControlLeft) || rdev::get_modifier(Key::ControlRight) { @@ -899,25 +926,6 @@ fn is_hot_key_modifiers_down() -> bool { return false; } -#[inline] -#[cfg(target_os = "windows")] -pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option { - let mut key_event = map_keyboard_mode(peer, event, key_event)?; - let chr = if peer == OS_LOWER_WINDOWS { - (key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16) - } else { - key_event.chr() - }; - key_event.set_chr(chr); - Some(key_event) -} - -#[inline] -#[cfg(not(target_os = "windows"))] -pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option { - map_keyboard_mode(peer, event, key_event) -} - #[inline] #[cfg(any(target_os = "linux", target_os = "windows"))] fn is_altgr(event: &Event) -> bool { @@ -961,7 +969,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - #[cfg(not(any(target_os = "android", target_os = "ios")))] if is_numpad_key(&event) { - if let Some(evt) = translate_key_code(peer, event, key_event) { + if let Some(evt) = map_keyboard_mode(peer, event, key_event) { events.push(evt); } return events; @@ -995,13 +1003,16 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } + #[cfg(target_os = "windows")] + try_file_win2win_hotkey(peer, event, &key_event, &mut events); + #[cfg(target_os = "macos")] if !unsafe { IS_LEFT_OPTION_DOWN } { try_fill_unicode(peer, event, &key_event, &mut events); } if events.is_empty() { - if let Some(evt) = translate_key_code(peer, event, key_event) { + if let Some(evt) = map_keyboard_mode(peer, event, key_event) { events.push(evt); } } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 83f5af2dd..76b06c490 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1229,7 +1229,7 @@ sc delete {app_name} } pub fn run_after_install() -> ResultType<()> { - let (_, _, _, exe,_) = get_install_info(); + let (_, _, _, exe, _) = get_install_info(); run_cmds(get_after_install(&exe), true, "after_install") } @@ -2130,7 +2130,25 @@ mod cert { } } -pub fn get_char_by_vk(vk: u32) -> Option { +#[inline] +pub fn get_char_from_vk(vk: u32) -> Option { + get_char_from_unicode(get_unicode_from_vk(vk)?) +} + +pub fn get_char_from_unicode(unicode: u16) -> Option { + let buff = [unicode]; + if let Some(chr) = String::from_utf16(&buff[..1]).ok()?.chars().next() { + if chr.is_control() { + return None; + } else { + Some(chr) + } + } else { + None + } +} + +pub fn get_unicode_from_vk(vk: u32) -> Option { const BUF_LEN: i32 = 32; let mut buff = [0_u16; BUF_LEN as usize]; let buff_ptr = buff.as_mut_ptr(); @@ -2155,19 +2173,7 @@ pub fn get_char_by_vk(vk: u32) -> Option { ToUnicodeEx(vk, 0x00, &state as _, buff_ptr, BUF_LEN, 0, layout) }; if len == 1 { - if let Some(chr) = String::from_utf16(&buff[..len as usize]) - .ok()? - .chars() - .next() - { - if chr.is_control() { - return None; - } else { - Some(chr) - } - } else { - None - } + Some(buff[0]) } else { None } @@ -2190,10 +2196,10 @@ mod tests { } #[test] - fn test_get_char_by_vk() { - let chr = get_char_by_vk(0x41); // VK_A + fn test_get_unicode_char_by_vk() { + let chr = get_char_from_vk(0x41); // VK_A assert_eq!(chr, Some('a')); - let chr = get_char_by_vk(VK_ESCAPE as u32); // VK_ESC + let chr = get_char_from_vk(VK_ESCAPE as u32); // VK_ESC assert_eq!(chr, None) } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 5bc486516..38467fa32 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1,6 +1,8 @@ use super::*; #[cfg(target_os = "linux")] use crate::common::IS_X11; +#[cfg(target_os = "windows")] +use crate::platform::windows::get_char_from_unicode; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; @@ -1280,12 +1282,53 @@ fn translate_keyboard_mode(evt: &KeyEvent) { Some(key_event::Union::Unicode(..)) => { // Do not handle unicode for now. } + #[cfg(target_os = "windows")] + Some(key_event::Union::Win2winHotkey(code)) => { + simulate_win2win_hotkey(*code, evt.down); + } _ => { log::debug!("Unreachable. Unexpected key event {:?}", &evt); } } } +#[cfg(target_os = "windows")] +fn simulate_win2win_hotkey(code: u32, down: bool) { + let mut simulated = false; + + let unicode: u16 = (code & 0x0000FFFF) as u16; + if unicode != 0 { + // Try convert unicode to virtual keycode first. + // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw + let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; + if res as u16 != 0xFFFF { + let vk = res & 0x00FF; + let flag = res >> 8; + let modifiers = [rdev::Key::ShiftLeft, rdev::Key::ControlLeft, rdev::Key::Alt]; + let mod_len = modifiers.len(); + for pos in 0..mod_len { + if flag & (0x0001 << pos) != 0 { + allow_err!(rdev::simulate(&EventType::KeyPress(modifiers[pos]))); + } + } + allow_err!(rdev::simulate_code(Some(vk as _), None, true)); + allow_err!(rdev::simulate_code(Some(vk as _), None, false)); + for pos in 0..mod_len { + let rpos = mod_len - 1 - pos; + if flag & (0x0001 << rpos) != 0 { + allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos]))); + } + } + simulated = true; + } + } + + if simulated { + let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; + allow_err!(rdev::simulate_code(Some(keycode), None, down)); + } +} + pub fn handle_key_(evt: &KeyEvent) { if EXITING.load(Ordering::SeqCst) { return; From 021939a6a6c16b2ec62aefb5bf2d6eddb4dd22bd Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 19:30:22 +0800 Subject: [PATCH 148/366] tmp commit, for debug Signed-off-by: fufesou --- src/keyboard.rs | 8 ++++++++ src/server/input_service.rs | 16 ++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index a2d17bc14..e8186ad45 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -517,6 +517,11 @@ pub fn event_to_key_events( } }; + println!( + "REMOVE ME ==================================== key_events {:?}", + &key_events + ); + #[cfg(not(any(target_os = "android", target_os = "ios")))] if keyboard_mode != KeyboardMode::Translate { let is_numpad_key = is_numpad_key(&event); @@ -893,8 +898,10 @@ fn try_file_win2win_hotkey( events: &mut Vec, ) { if peer == OS_LOWER_WINDOWS && is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } { + let mut down = false; let win2win_hotkey = match event.event_type { EventType::KeyPress(..) => { + down = true; if let Some(unicode) = get_unicode_from_vk(event.platform_code as u32) { Some((unicode as u32 & 0x0000FFFF) | (event.platform_code << 16)) } else { @@ -907,6 +914,7 @@ fn try_file_win2win_hotkey( if let Some(code) = win2win_hotkey { let mut evt = key_event.clone(); evt.set_win2win_hotkey(code); + evt.down = down; events.push(evt); } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 38467fa32..d85848314 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1,8 +1,6 @@ use super::*; #[cfg(target_os = "linux")] use crate::common::IS_X11; -#[cfg(target_os = "windows")] -use crate::platform::windows::get_char_from_unicode; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; @@ -1294,13 +1292,12 @@ fn translate_keyboard_mode(evt: &KeyEvent) { #[cfg(target_os = "windows")] fn simulate_win2win_hotkey(code: u32, down: bool) { - let mut simulated = false; - let unicode: u16 = (code & 0x0000FFFF) as u16; - if unicode != 0 { + if down { // Try convert unicode to virtual keycode first. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; + println!("REMOVE ME =============================== VkKeyScanW {} {}", unicode, res); if res as u16 != 0xFFFF { let vk = res & 0x00FF; let flag = res >> 8; @@ -1319,14 +1316,13 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos]))); } } - simulated = true; + return; } } - if simulated { - let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; - allow_err!(rdev::simulate_code(Some(keycode), None, down)); - } + let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; + println!("REMOVE ME =============================== simulate_win2win_hotkey down {} {},{}", down, unicode, keycode); + allow_err!(rdev::simulate_code(Some(keycode), None, down)); } pub fn handle_key_(evt: &KeyEvent) { From ed4016a77aa62fb6bb41730ae8a28906b0b35d2b Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 20:56:03 +0800 Subject: [PATCH 149/366] debug done Signed-off-by: fufesou --- src/keyboard.rs | 5 ----- src/server/input_service.rs | 2 -- 2 files changed, 7 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index e8186ad45..b0eb68aa5 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -517,11 +517,6 @@ pub fn event_to_key_events( } }; - println!( - "REMOVE ME ==================================== key_events {:?}", - &key_events - ); - #[cfg(not(any(target_os = "android", target_os = "ios")))] if keyboard_mode != KeyboardMode::Translate { let is_numpad_key = is_numpad_key(&event); diff --git a/src/server/input_service.rs b/src/server/input_service.rs index d85848314..0def3aa90 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1297,7 +1297,6 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { // Try convert unicode to virtual keycode first. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; - println!("REMOVE ME =============================== VkKeyScanW {} {}", unicode, res); if res as u16 != 0xFFFF { let vk = res & 0x00FF; let flag = res >> 8; @@ -1321,7 +1320,6 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; - println!("REMOVE ME =============================== simulate_win2win_hotkey down {} {},{}", down, unicode, keycode); allow_err!(rdev::simulate_code(Some(keycode), None, down)); } From 83249f0f957770c5757386aefe0c9f132be7542a Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 21:35:59 +0800 Subject: [PATCH 150/366] debug done Signed-off-by: fufesou --- src/keyboard.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index b0eb68aa5..7f1f7e5f8 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -865,6 +865,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m if name.len() > 0 { let mut evt = key_event.clone(); evt.set_seq(name.to_string()); + evt.down = true; events.push(evt); } } @@ -877,6 +878,7 @@ fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &m if let Some(chr) = get_char_from_vk(event.platform_code as u32) { let mut evt = key_event.clone(); evt.set_seq(chr.to_string()); + evt.down = true; events.push(evt); } } @@ -956,6 +958,7 @@ fn is_press(event: &Event) -> bool { // https://github.com/fufesou/rustdesk/wiki/Keyboard-mode----Translate-Mode pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -> Vec { let mut events: Vec = Vec::new(); + if let Some(unicode_info) = &event.unicode { if unicode_info.is_dead { #[cfg(target_os = "macos")] @@ -994,11 +997,15 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - return events; } + #[cfg(target_os = "windows")] + try_file_win2win_hotkey(peer, event, &key_event, &mut events); + #[cfg(any(target_os = "linux", target_os = "windows"))] - if is_press(event) { + if events.is_empty() && is_press(event) { try_fill_unicode(peer, event, &key_event, &mut events); } + // If AltGr is down, no need to send events other than unicode. #[cfg(target_os = "windows")] unsafe { if IS_0X021D_DOWN { @@ -1006,9 +1013,6 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) - } } - #[cfg(target_os = "windows")] - try_file_win2win_hotkey(peer, event, &key_event, &mut events); - #[cfg(target_os = "macos")] if !unsafe { IS_LEFT_OPTION_DOWN } { try_fill_unicode(peer, event, &key_event, &mut events); From 51ff2d35b55e8a9742caeacb3713afb28b868120 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 2 Apr 2023 14:31:30 +0800 Subject: [PATCH 151/366] translate mode win, update input layout, just a temporary workaround Signed-off-by: fufesou --- src/flutter.rs | 2 +- src/server/input_service.rs | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/flutter.rs b/src/flutter.rs index fde9ce7cd..b293059d0 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -218,7 +218,7 @@ impl VideoRenderer { } pub fn on_rgba(&self, rgba: &Vec) { - if self.ptr == usize::default() { + if self.ptr == usize::default() || self.width == 0 || self.height == 0 { return; } if let Some(func) = &self.on_rgba_func { diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 0def3aa90..c5011a856 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -16,9 +16,19 @@ use std::{ thread, time::{self, Instant}, }; +#[cfg(target_os = "windows")] +use winapi::um::winuser::{ + ActivateKeyboardLayout, GetForegroundWindow, GetKeyboardLayout, GetWindowThreadProcessId, + VkKeyScanW, +}; const INVALID_CURSOR_POS: i32 = i32::MIN; +#[cfg(target_os = "windows")] +lazy_static::lazy_static! { + static ref LAST_HKL: Arc> = Arc::new(Mutex::new(0)); +} + #[derive(Default)] struct StateCursor { hcursor: u64, @@ -1242,7 +1252,32 @@ fn translate_process_code(code: u32, down: bool) { }; } +#[cfg(target_os = "windows")] +fn check_update_input_layout() { + unsafe { + let foreground_thread_id = + GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()); + let layout = GetKeyboardLayout(foreground_thread_id); + let layout_u32 = layout as u32; + let mut last_layout_lock = LAST_HKL.lock().unwrap(); + if *last_layout_lock == 0 || *last_layout_lock != layout_u32 { + let res = ActivateKeyboardLayout(layout, 0); + if res == layout { + *last_layout_lock = layout_u32; + } else { + log::error!("Failed to call ActivateKeyboardLayout, {}", layout_u32); + } + } + } +} + fn translate_keyboard_mode(evt: &KeyEvent) { + // --server could not detect the input layout change. + // This is a temporary workaround. + // There may be a better way to detect and handle the input layout change. + #[cfg(target_os = "windows")] + check_update_input_layout(); + match &evt.union { Some(key_event::Union::Seq(seq)) => { // Fr -> US @@ -1296,7 +1331,7 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { if down { // Try convert unicode to virtual keycode first. // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw - let res = unsafe { winapi::um::winuser::VkKeyScanW(unicode) }; + let res = unsafe { VkKeyScanW(unicode) }; if res as u16 != 0xFFFF { let vk = res & 0x00FF; let flag = res >> 8; From e0667833d5394be7f66da63440c15708818802d3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 2 Apr 2023 14:39:54 +0800 Subject: [PATCH 152/366] comment Signed-off-by: fufesou --- src/server/input_service.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index c5011a856..7d3229c12 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1274,7 +1274,20 @@ fn check_update_input_layout() { fn translate_keyboard_mode(evt: &KeyEvent) { // --server could not detect the input layout change. // This is a temporary workaround. + // // There may be a better way to detect and handle the input layout change. + // while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) + // { + // ... + // if (msg.message == WM_INPUTLANGCHANGE) + // { + // // handle WM_INPUTLANGCHANGE message here + // check_update_input_layout(); + // } + // TranslateMessage(&msg); + // DispatchMessage(&msg); + // ... + // } #[cfg(target_os = "windows")] check_update_input_layout(); From 156ee78ebb4ad81c5e26d337f2e6c303730787be Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 3 Apr 2023 00:16:09 +0800 Subject: [PATCH 153/366] remove compile warn --- src/platform/windows.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 76b06c490..383259a32 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -9,15 +9,15 @@ use hbb_common::{ message_proto::Resolution, sleep, timeout, tokio, }; -use std::io::prelude::*; -use std::path::Path; -use std::ptr::null_mut; use std::{ collections::HashMap, ffi::OsString, - fs, io, mem, + fs, io, + io::prelude::*, + mem, os::windows::process::CommandExt, - path::PathBuf, + path::*, + ptr::null_mut, sync::{Arc, Mutex}, time::{Duration, Instant}, }; @@ -936,8 +936,9 @@ pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { } pub fn update_me() -> ResultType<()> { - let (_, path, _, exe, dll) = get_install_info(); + let (_, path, _, exe, _dll) = get_install_info(); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_owned(); + #[cfg(not(feature = "flutter"))] let src_dll = std::env::current_exe()? .parent() .unwrap_or(&Path::new(&get_default_install_path())) @@ -948,7 +949,7 @@ pub fn update_me() -> ResultType<()> { #[cfg(feature = "flutter")] let copy_dll = "".to_string(); #[cfg(not(feature = "flutter"))] - let copy_dll = copy_raw_cmd(&src_dll, &dll, &path); + let copy_dll = copy_raw_cmd(&src_dll, &_dll, &path); let cmds = format!( " chcp 65001 @@ -1002,12 +1003,12 @@ pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> Res let mut path = path.trim_end_matches('\\').to_owned(); let (subkey, _path, start_menu, exe, dll) = get_default_install_info(); let mut exe = exe; - let mut dll = dll; + let mut _dll = dll; if path.is_empty() { path = _path; } else { exe = exe.replace(&_path, &path); - dll = dll.replace(&_path, &path); + _dll = _dll.replace(&_path, &path); } let mut version_major = "0"; let mut version_minor = "0"; @@ -1131,6 +1132,7 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} app_name = crate::get_app_name(), ); let src_exe = std::env::current_exe()?.to_str().unwrap_or("").to_string(); + #[cfg(not(feature = "flutter"))] let src_dll = std::env::current_exe()? .parent() .unwrap_or(&Path::new(&get_default_install_path())) @@ -1138,11 +1140,10 @@ if exist \"{tmp_path}\\{app_name} Tray.lnk\" del /f /q \"{tmp_path}\\{app_name} .to_str() .unwrap_or("") .to_owned(); - #[cfg(feature = "flutter")] let copy_dll = "".to_string(); #[cfg(not(feature = "flutter"))] - let copy_dll = copy_raw_cmd(&src_dll, &dll, &path); + let copy_dll = copy_raw_cmd(&src_dll, &_dll, &path); let install_cert = if options.contains("driverCert") { format!("\"{}\" --install-cert \"RustDeskIddDriver.cer\"", src_exe) From 8b1fb742b421c182c7d14efd69fb1eb633191e98 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 3 Apr 2023 00:24:39 +0800 Subject: [PATCH 154/366] Icons.list_rounded looks bad on Windows. https://github.com/rustdesk/rustdesk/issues/3885#issuecomment-1493356168 --- flutter/lib/common/widgets/peer_tab_page.dart | 2 +- flutter/pubspec.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 2d36d9150..0c8ac9329 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -261,7 +261,7 @@ class _PeerTabPageState extends State }, child: Icon( peerCardUiType.value == PeerUiType.grid - ? Icons.list_rounded + ? Icons.list : Icons.grid_view_rounded, size: 18, color: textColor, diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 6bd19d01a..639ee04de 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -1236,10 +1236,10 @@ packages: dependency: "direct main" description: name: texture_rgba_renderer - sha256: "52bc9f217b7b07a760ee837d5a17329ad1f78ae8ed1e3fa612c6f1bed3c77f79" + sha256: cb048abdd800468ca40749ca10d1db9d1e6a055d1cde6234c05191293f0c7d61 url: "https://pub.dev" source: hosted - version: "0.0.13" + version: "0.0.16" timing: dependency: transitive description: From 88d82749979eba69147a35f717da0dbc6fc1668e Mon Sep 17 00:00:00 2001 From: Kingtous Date: Sun, 2 Apr 2023 22:41:16 +0800 Subject: [PATCH 155/366] fix: nightly build needs to upload artifact --- .github/workflows/flutter-build.yml | 1608 ++++++++++++++++++++++++ .github/workflows/flutter-ci.yml | 2 +- .github/workflows/flutter-nightly.yml | 1613 +------------------------ 3 files changed, 1614 insertions(+), 1609 deletions(-) create mode 100644 .github/workflows/flutter-build.yml diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml new file mode 100644 index 000000000..97a4073af --- /dev/null +++ b/.github/workflows/flutter-build.yml @@ -0,0 +1,1608 @@ +name: Build the flutter version of the RustDesk + +on: + workflow_call: + inputs: + upload-artifact: + type: boolean + default: true + +env: + LLVM_VERSION: "15.0.6" + FLUTTER_VERSION: "3.7.0" + TAG_NAME: "nightly" + # vcpkg version: 2022.05.10 + # for multiarch gcc compatibility + VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44" + VERSION: "1.2.0" + NDK_VERSION: "r23" + #signing keys env variable checks + ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}' + MACOS_P12_BASE64: '${{ secrets.MACOS_P12_BASE64 }}' + # To make a custom build with your own servers set the below secret values + RS_PUB_KEY: '${{ secrets.RS_PUB_KEY }}' + RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}' + UPLOAD_ARTIFACT: "${{ inputs.upload-artifact }}" + +jobs: + build-for-windows-flutter: + 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 } + # - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Install LLVM and Clang + uses: KyleMayes/install-llvm-action@v1 + with: + version: ${{ env.LLVM_VERSION }} + + - name: Install flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + cache: true + + - name: Replace engine with rustdesk custom flutter engine + run: | + flutter doctor -v + flutter precache --windows + Invoke-WebRequest -Uri https://github.com/Kingtous/engine/releases/download/v3.7.0-rustdesk/windows-x64-release-flutter.zip -OutFile windows-x64-flutter-release.zip + Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine + mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/ + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }} + + - name: Install flutter rust bridge deps + run: | + cargo install flutter_rust_bridge_codegen + Push-Location flutter ; flutter pub get ; Pop-Location + ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart + + - name: Install vcpkg dependencies + run: | + cd C:\ + git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 + + - name: Build rustdesk + env: + VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg + run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver + + - name: Sign rustdesk files + uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.CERTNAME }}' + folder: './flutter/build/windows/runner/Release/' + recursive: true + + - name: Build self-extracted executable + shell: bash + if: env.UPLOAD_ARTIFACT == 'true' + run: | + pushd ./libs/portable + python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe + popd + mkdir -p ./SignOutput + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe + + # - name: Rename rustdesk + # shell: bash + # run: | + # for name in rustdesk*??-install.exe; do + # mv "$name" ./SignOutput/"${name%%-install.exe}-${{ matrix.job.target }}.exe" + # done + + - name: Sign rustdesk self-extracted file + uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' + folder: './SignOutput' + recursive: false + + - name: Publish Release + uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./SignOutput/rustdesk-*.exe + + # The fallback for the flutter version, we use Sciter for 32bit Windows. + build-for-windows-sciter: + name: ${{ matrix.job.target }} (${{ matrix.job.os }}) + runs-on: ${{ matrix.job.os }} + # Temporarily disable this action due to additional test is needed. + # if: false + strategy: + fail-fast: false + matrix: + job: + # - { target: i686-pc-windows-msvc , os: windows-2019 } + # - { target: x86_64-pc-windows-gnu , os: windows-2019 } + - { target: i686-pc-windows-msvc, os: windows-2019 } + # - { target: aarch64-pc-windows-msvc, os: windows-2019 } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Install LLVM and Clang + uses: Kingtous/install-llvm-action-32bit@master + with: + version: ${{ env.LLVM_VERSION }} + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-${{ matrix.job.target }} + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }}-sciter + + - name: Restore from cache and install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + cd C:\ + git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 + + - name: Build rustdesk + id: build + shell: bash + env: + VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg + run: | + python3 res/inline-sciter.py + # Patch sciter x86 + sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml + # Replace the link for the ico. + rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico + cargo build --features inline --release --bins + mkdir -p ./Release + mv ./target/release/rustdesk.exe ./Release/rustdesk.exe + curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll + echo "output_folder=./Release" >> $GITHUB_OUTPUT + + - name: Sign rustdesk files + uses: GermanBluefox/code-sign-action@v7 + if: env.UPLOAD_ARTIFACT == 'true' + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.CERTNAME }}' + folder: './Release/' + recursive: true + + - name: Build self-extracted executable + shell: bash + run: | + pushd ./libs/portable + pip3 install -r requirements.txt + python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe + popd + mkdir -p ./SignOutput + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-sciter.exe + + - name: Sign rustdesk self-extracted file + uses: GermanBluefox/code-sign-action@v7 + with: + certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' + password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' + certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' + # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' + folder: './SignOutput' + recursive: false + + - name: Publish Release + uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./SignOutput/rustdesk-*.exe + + build-for-macOS: + name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { + target: x86_64-apple-darwin, + os: macos-latest, + extra-build-args: "", + } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Import the codesign cert + if: env.MACOS_P12_BASE64 != null + uses: apple-actions/import-codesign-certs@v1 + with: + p12-file-base64: ${{ secrets.MACOS_P12_BASE64 }} + p12-password: ${{ secrets.MACOS_P12_PASSWORD }} + keychain: rustdesk + + - name: Check sign and import sign key + if: env.MACOS_P12_BASE64 != null + run: | + security default-keychain -s rustdesk.keychain + security find-identity -v + + - name: Import notarize key + if: env.MACOS_P12_BASE64 != null + uses: timheuer/base64-to-file@v1.2 + with: + # https://gregoryszorc.com/docs/apple-codesign/stable/apple_codesign_rcodesign.html#notarizing-and-stapling + fileName: rustdesk.json + fileDir: ${{ github.workspace }} + encodedString: ${{ secrets.MACOS_NOTARIZE_JSON }} + + - name: Install rcodesign tool + if: env.MACOS_P12_BASE64 != null + shell: bash + run: | + pushd /tmp + wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz + tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz + mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin + popd + + - name: Install build runtime + run: | + brew install llvm create-dmg nasm yasm cmake gcc wget ninja pkg-config + + - name: Install flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: ${{ matrix.job.os }} + + - name: Install flutter rust bridge deps + shell: bash + run: | + cargo install flutter_rust_bridge_codegen + pushd flutter && flutter pub get && popd + ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart + + - name: Restore from cache and install vcpkg + uses: lukka/run-vcpkg@v7 + with: + setupOnly: true + vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} + + - name: Install vcpkg dependencies + run: | + $VCPKG_ROOT/vcpkg install libvpx libyuv opus + + - name: Show version information (Rust, cargo, Clang) + shell: bash + run: | + clang --version || true + rustup -V + rustup toolchain list + rustup default + cargo -V + rustc -V + + - name: Build rustdesk + run: | + # --hwcodec not supported on macos yet + ./build.py --flutter ${{ matrix.job.extra-build-args }} + + - name: Codesign app and create signed dmg + if: env.MACOS_P12_BASE64 != null + run: | + security default-keychain -s rustdesk.keychain + security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain + # start sign the rustdesk.app and dmg + rm rustdesk-${{ env.VERSION }}.dmg || true + codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict ./flutter/build/macos/Build/Products/Release/RustDesk.app -vvv + create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app + codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict rustdesk-${{ env.VERSION }}.dmg -vvv + # notarize the rustdesk-${{ env.VERSION }}.dmg + rcodesign notary-submit --api-key-path ${{ github.workspace }}/rustdesk.json --staple rustdesk-${{ env.VERSION }}.dmg + + - name: Rename rustdesk + run: | + for name in rustdesk*??.dmg; do + mv "$name" "${name%%.dmg}-${{ matrix.job.target }}.dmg" + done + + - name: Publish DMG package + if: env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk*-${{ matrix.job.target }}.dmg + + build-vcpkg-deps-linux: + uses: ./.github/workflows/vcpkg-deps-linux.yml + + generate-bridge-linux: + uses: ./.github/workflows/bridge.yml + + build-rustdesk-android: + needs: [generate-bridge-linux] + name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { + arch: x86_64, + target: aarch64-linux-android, + os: ubuntu-20.04, + extra-build-features: "", + openssl-arch: android-arm64 + } + - { + arch: x86_64, + target: armv7-linux-androideabi, + os: ubuntu-18.04, + extra-build-features: "", + openssl-arch: android-arm + } + steps: + - name: Install dependencies + run: | + sudo apt update + sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless + - name: Checkout source code + uses: actions/checkout@v3 + - name: Install flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: ${{ env.NDK_VERSION }} + add-to-path: true + + - name: Clone deps + shell: bash + run: | + pushd /opt + git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + + - name: Disable rust bridge build + run: | + sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs + + - name: Build rustdesk lib + env: + ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} + ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} + VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg + run: | + rustup target add ${{ matrix.job.target }} + cargo install cargo-ndk + case ${{ matrix.job.target }} in + aarch64-linux-android) + ./flutter/ndk_arm64.sh + mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a + cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so + ;; + armv7-linux-androideabi) + ./flutter/ndk_arm.sh + mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a + cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so + ;; + esac + + - name: Build rustdesk + shell: bash + env: + JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64 + run: | + export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH + # temporary use debug sign config + sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle + case ${{ matrix.job.target }} in + aarch64-linux-android) + mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a + cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/arm64-v8a/*.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ + cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so + # build flutter + pushd flutter + flutter build apk --release --target-platform android-arm64 --split-per-abi + mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + ;; + armv7-linux-androideabi) + mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a + cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/armeabi-v7a/*.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ + cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so + # build flutter + pushd flutter + flutter build apk --release --target-platform android-arm --split-per-abi + mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + ;; + esac + popd + mkdir -p signed-apk; pushd signed-apk + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk . + + - uses: r0adkll/sign-android-release@v1 + name: Sign app APK + if: env.ANDROID_SIGNING_KEY != null + id: sign-rustdesk + with: + releaseDirectory: ./signed-apk + signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }} + alias: ${{ secrets.ANDROID_ALIAS }} + keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }} + keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }} + env: + # override default build-tools version (29.0.3) -- optional + BUILD_TOOLS_VERSION: "30.0.2" + + - name: Upload Artifacts + if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' + uses: actions/upload-artifact@master + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk + path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} + + - name: Publish signed apk package + if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ${{steps.sign-rustdesk.outputs.signedReleaseFile}} + + - name: Publish unsigned apk package + if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + + build-rustdesk-lib-linux-amd64: + needs: [generate-bridge-linux, build-vcpkg-deps-linux] + name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + # use a high level qemu-user-static + job: + # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } + # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-20.04, + extra-build-features: "", + } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-20.04, + extra-build-features: "flatpak", + } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-20.04, + extra-build-features: "appimage", + } + # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + steps: + - name: Maximize build space + run: | + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo apt update -y + sudo apt install qemu-user-static + + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 12 + + - name: Free Space + run: | + df + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + cache-directories: "/opt/rust-registry" + + - name: Install local registry + run: | + mkdir -p /opt/rust-registry + cargo install cargo-local-registry + + - name: Build local registry + uses: nick-fields/retry@v2 + id: build-local-registry + continue-on-error: true + with: + max_attempts: 3 + timeout_minutes: 15 + retry_on: error + command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry + + - name: Disable rust bridge build + run: | + sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs + # only build cdylib + sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Restore vcpkg files + uses: actions/download-artifact@master + with: + name: vcpkg-artifact-${{ matrix.job.arch }} + path: /opt/artifacts/vcpkg/installed + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk library for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04 + # not ready yet + # distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + ls -l /opt/artifacts/vcpkg/installed + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/rust-registry:/opt/rust-registry" + shell: /bin/bash + install: | + apt update -y + echo -e "installing deps" + apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null + # we have libopus compiled by us. + apt remove -y libopus-dev || true + # output devs + ls -l ./ + tree -L 3 /opt/artifacts/vcpkg/installed + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # rust + pushd /opt + wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz + tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz + cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh + rm -rf rust-1.64.0-${{ matrix.job.target }} + # edit config + mkdir -p ~/.cargo/ + echo """ + [source.crates-io] + registry = 'https://github.com/rust-lang/crates.io-index' + replace-with = 'local-registry' + + [source.local-registry] + local-registry = '/opt/rust-registry/' + """ > ~/.cargo/config + cat ~/.cargo/config + # start build + pushd /workspace + # mock + case "${{ matrix.job.arch }}" in + x86_64) + # no need mock on x86_64 + export VCPKG_ROOT=/opt/artifacts/vcpkg + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release + ;; + esac + + - name: Upload Artifacts + uses: actions/upload-artifact@master + with: + name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so + path: target/release/liblibrustdesk.so + + build-rustdesk-lib-linux-arm: + needs: [generate-bridge-linux, build-vcpkg-deps-linux] + name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + # use a high level qemu-user-static + job: + - { + arch: aarch64, + target: aarch64-unknown-linux-gnu, + os: ubuntu-20.04, + use-cross: true, + extra-build-features: "", + } + - { + arch: aarch64, + target: aarch64-unknown-linux-gnu, + os: ubuntu-18.04, # just for naming package, not running host + use-cross: true, + extra-build-features: "appimage", + } + # - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } + # - { + # arch: armv7, + # target: armv7-unknown-linux-gnueabihf, + # os: ubuntu-20.04, + # use-cross: true, + # extra-build-features: "", + # } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } + # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } + steps: + - name: Maximize build space + run: | + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo apt update -y + sudo apt install qemu-user-static + + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 12 + + - name: Free Space + run: | + df + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + cache-directories: "/opt/rust-registry" + + - name: Install local registry + run: | + mkdir -p /opt/rust-registry + cargo install cargo-local-registry + + - name: Build local registry + uses: nick-fields/retry@v2 + id: build-local-registry + continue-on-error: true + with: + max_attempts: 3 + timeout_minutes: 15 + retry_on: error + command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry + + - name: Disable rust bridge build + run: | + sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs + # only build cdylib + sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Restore vcpkg files + uses: actions/download-artifact@master + with: + name: vcpkg-artifact-${{ matrix.job.arch }} + path: /opt/artifacts/vcpkg/installed + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk library for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + ls -l /opt/artifacts/vcpkg/installed + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/rust-registry:/opt/rust-registry" + shell: /bin/bash + install: | + apt update -y + echo -e "installing deps" + apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null + # we have libopus compiled by us. + apt remove -y libopus-dev || true + # output devs + ls -l ./ + tree -L 3 /opt/artifacts/vcpkg/installed + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # rust + pushd /opt + wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz + tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz + cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh + rm -rf rust-1.64.0-${{ matrix.job.target }} + # edit config + mkdir -p ~/.cargo/ + echo """ + [source.crates-io] + registry = 'https://github.com/rust-lang/crates.io-index' + replace-with = 'local-registry' + + [source.local-registry] + local-registry = '/opt/rust-registry/' + """ > ~/.cargo/config + cat ~/.cargo/config + # start build + pushd /workspace + export VCPKG_ROOT=/opt/artifacts/vcpkg + cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release + + - name: Upload Artifacts + uses: actions/upload-artifact@master + with: + name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so + path: target/release/liblibrustdesk.so + + build-rustdesk-sciter-arm: + needs: [build-vcpkg-deps-linux] + name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + # use a high level qemu-user-static + job: + - { + arch: armv7, + target: armv7-unknown-linux-gnueabihf, + deb-arch: armhf, + os: ubuntu-latest, + use-cross: true, + extra-build-features: "", + } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } + # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } + steps: + + - name: Maximize build space + run: | + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet + sudo apt update -y + sudo apt install qemu-user-static + + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Set Swap Space + uses: pierotofy/set-swap-space@master + with: + swap-size-gb: 12 + + - name: Free Space + run: | + df + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + cache-directories: "/opt/rust-registry" + + - name: Install local registry + run: | + mkdir -p /opt/rust-registry + cargo install cargo-local-registry + + - name: Build local registry + uses: nick-fields/retry@v2 + id: build-local-registry + continue-on-error: true + with: + max_attempts: 3 + timeout_minutes: 15 + retry_on: error + command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry + + - name: Restore vcpkg files + uses: actions/download-artifact@master + with: + name: vcpkg-artifact-${{ matrix.job.arch }} + path: /opt/artifacts/vcpkg/installed + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk sciter binary for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/rust-registry:/opt/rust-registry" + shell: /bin/bash + install: | + apt update -y + apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm libclang-dev + apt-get -qq install -y libdbus-1-dev pkg-config nasm yasm libglib2.0-dev libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev + apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # rust + pushd /opt + wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz + tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz + cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh + rm -rf rust-1.64.0-${{ matrix.job.target }} + # edit config + mkdir -p ~/.cargo/ + echo """ + [source.crates-io] + registry = 'https://github.com/rust-lang/crates.io-index' + replace-with = 'local-registry' + + [source.local-registry] + local-registry = '/opt/rust-registry/' + """ > ~/.cargo/config + cat ~/.cargo/config + + # build + pushd /workspace + python3 ./res/inline-sciter.py + export VCPKG_ROOT=/opt/artifacts/vcpkg + export ARCH=armhf + cargo build --features inline --release --bins + # package + mkdir -p ./Release + mv ./target/release/rustdesk ./Release/rustdesk + wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/arm32/libsciter-gtk.so + ./build.py --package ./Release + + - name: Rename rustdesk + shell: bash + run: | + for name in rustdesk*??.deb; do + # use cp to duplicate deb files to fit other packages. + cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb" + done + + - name: Publish debian package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + + - name: Upload Artifact + uses: actions/upload-artifact@master + if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + + build-rustdesk-linux-arm: + needs: [build-rustdesk-lib-linux-arm] + name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ubuntu-20.04 # 20.04 has more performance on arm build + strategy: + fail-fast: false + matrix: + job: + - { + arch: aarch64, + target: aarch64-unknown-linux-gnu, + os: ubuntu-18.04, # just for naming package, not running host + use-cross: true, + extra-build-features: "", + } + - { + arch: aarch64, + target: aarch64-unknown-linux-gnu, + os: ubuntu-18.04, # just for naming package, not running host + use-cross: true, + extra-build-features: "appimage", + } + # - { + # arch: aarch64, + # target: aarch64-unknown-linux-gnu, + # os: ubuntu-18.04, # just for naming package, not running host + # use-cross: true, + # extra-build-features: "flatpak", + # } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" } + # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } + # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Prepare env + run: | + sudo apt update -y + sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools + mkdir -p ./target/release/ + + - name: Restore the rustdesk lib file + uses: actions/download-artifact@master + with: + name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so + path: ./target/release/ + + - name: Download Flutter + shell: bash + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + pushd /opt + # clone repo and reset to flutter 3.7.0 + git clone https://github.com/sony/flutter-elinux.git || true + pushd flutter-elinux + # reset to flutter 3.7.0 + git fetch + git reset --hard 51a1d685901f79fbac51665a967c3a1a789ecee5 + popd + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk binary for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04-rustdesk + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + --volume "/opt/flutter-elinux:/opt/flutter-elinux" + shell: /bin/bash + install: | + apt update -y + apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + pushd /workspace + # we use flutter-elinux to build our rustdesk + export PATH=/opt/flutter-elinux/bin:$PATH + sed -i "s/flutter build linux --release/flutter-elinux build linux/g" ./build.py + # Setup flutter-elinux. Run doctor to check if issues here. + flutter-elinux doctor -v + # Patch arm64 engine for flutter 3.6.0+ + flutter-elinux precache --linux + pushd /tmp + curl -O https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.7.0-stable.tar.xz + tar -xvf flutter_linux_3.7.0-stable.tar.xz flutter/bin/cache/artifacts/engine/linux-x64/shader_lib + cp -R flutter/bin/cache/artifacts/engine/linux-x64/shader_lib /opt/flutter-elinux/flutter/bin/cache/artifacts/engine/linux-arm64 + popd + # edit to corresponding arch + case ${{ matrix.job.arch }} in + aarch64) + sed -i "s/Architecture: amd64/Architecture: arm64/g" ./build.py + sed -i "s/x64\/release/arm64\/release/g" ./build.py + ;; + armv7) + sed -i "s/Architecture: amd64/Architecture: arm/g" ./build.py + sed -i "s/x64\/release/arm\/release/g" ./build.py + ;; + esac + python3 ./build.py --flutter --hwcodec --skip-cargo + # rpm package + echo -e "start packaging fedora package" + pushd /workspace + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/64bit/32bit/g" ./res/rpm-flutter.spec + sed -i "s/linux\/x64/linux\/arm/g" ./res/rpm-flutter.spec + ;; + aarch64) + sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter.spec + ;; + esac + HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb + pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} + mkdir -p /opt/artifacts/rpm + for name in rustdesk*??.rpm; do + mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" + done + # rpm suse package + echo -e "start packaging suse package" + pushd /workspace + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/64bit/32bit/g" ./res/rpm-flutter-suse.spec + sed -i "s/linux\/x64/linux\/arm/g" ./res/rpm-flutter-suse.spec + ;; + aarch64) + sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter-suse.spec + ;; + esac + HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb + pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} + mkdir -p /opt/artifacts/rpm + for name in rustdesk*??.rpm; do + mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-suse.rpm" + done + + - name: Rename rustdesk + shell: bash + run: | + for name in rustdesk*??.deb; do + cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + done + + - name: Publish debian package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Build appimage package + if: ${{ matrix.job.extra-build-features == 'appimage' }} + shell: bash + run: | + # set-up appimage-builder + pushd /tmp + wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage + chmod +x appimage-builder-x86_64.AppImage + sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder + popd + # run appimage-builder + pushd appimage + sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml + + - name: Publish appimage package + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage + + - name: Upload Artifact + uses: actions/upload-artifact@master + if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Patch archlinux PKGBUILD + if: ${{ matrix.job.extra-build-features == '' }} + run: | + sed -i "s/arch=('x86_64')/arch=('${{ matrix.job.arch }}')/g" res/PKGBUILD + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/linux\/x64/linux\/arm/g" ./res/PKGBUILD + ;; + aarch64) + sed -i "s/linux\/x64/linux\/arm64/g" ./res/PKGBUILD + ;; + esac + + # Temporary disable for there is no many archlinux arm hosts + # - name: Build archlinux package + # if: ${{ matrix.job.extra-build-features == '' }} + # uses: vufa/arch-makepkg-action@master + # with: + # packages: > + # llvm + # clang + # libva + # libvdpau + # rust + # gstreamer + # unzip + # git + # cmake + # gcc + # curl + # wget + # yasm + # nasm + # zip + # make + # pkg-config + # clang + # gtk3 + # xdotool + # libxcb + # libxfixes + # alsa-lib + # pipewire + # python + # ttf-arphic-uming + # libappindicator-gtk3 + # scripts: | + # cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f + + # - name: Publish archlinux package + # if: ${{ matrix.job.extra-build-features == '' }} + # uses: softprops/action-gh-release@v1 + # with: + # prerelease: true + # tag_name: ${{ env.TAG_NAME }} + # files: | + # res/rustdesk*.zst + + - name: Publish fedora28/centos8 package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + /opt/artifacts/rpm/*.rpm + + build-rustdesk-linux-amd64: + needs: [build-rustdesk-lib-linux-amd64] + name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + job: + # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } + # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-18.04, + extra-build-features: "", + } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-18.04, + extra-build-features: "flatpak", + } + - { + arch: x86_64, + target: x86_64-unknown-linux-gnu, + os: ubuntu-18.04, + extra-build-features: "appimage", + } + # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Prepare env + run: | + sudo apt update -y + sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools + mkdir -p ./target/release/ + + - name: Restore the rustdesk lib file + uses: actions/download-artifact@master + with: + name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so + path: ./target/release/ + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk binary for ${{ matrix.job.arch }} + id: vcpkg + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04 + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + --volume "/opt/artifacts:/opt/artifacts" + shell: /bin/bash + install: | + apt update -y + apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + # Setup Flutter + pushd /opt + wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz + tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz + ls -l . + export PATH=/opt/flutter/bin:$PATH + flutter doctor -v + pushd /workspace + python3 ./build.py --flutter --hwcodec --skip-cargo + # rpm package + pushd /workspace + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/64bit/32bit/g" ./res/rpm-flutter.spec + ;; + esac + HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb + pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} + mkdir -p /opt/artifacts/rpm + for name in rustdesk*??.rpm; do + mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" + done + # rpm suse package + pushd /workspace + case ${{ matrix.job.arch }} in + armv7) + sed -i "s/64bit/32bit/g" ./res/rpm-flutter-suse.spec + ;; + esac + HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb + pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} + mkdir -p /opt/artifacts/rpm + for name in rustdesk*??.rpm; do + mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-suse.rpm" + done + + - name: Rename rustdesk + shell: bash + run: | + for name in rustdesk*??.deb; do + # use cp to duplicate deb files to fit other packages. + cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + done + + - name: Publish debian package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Upload Artifact + uses: actions/upload-artifact@master + if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + + - name: Patch archlinux PKGBUILD + if: ${{ matrix.job.extra-build-features == '' }} + run: | + sed -i "s/arch=('x86_64')/arch=('${{ matrix.job.arch }}')/g" res/PKGBUILD + + - name: Build archlinux package + if: ${{ matrix.job.extra-build-features == '' }} + uses: vufa/arch-makepkg-action@master + with: + packages: > + llvm + clang + libva + libvdpau + rust + gstreamer + unzip + git + cmake + gcc + curl + wget + yasm + nasm + zip + make + pkg-config + clang + gtk3 + xdotool + libxcb + libxfixes + alsa-lib + pipewire + python + ttf-arphic-uming + libappindicator-gtk3 + scripts: | + cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f + + - name: Publish archlinux package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + res/rustdesk*.zst + + - name: Build appimage package + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' + shell: bash + run: | + # set-up appimage-builder + pushd /tmp + wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage + chmod +x appimage-builder-x86_64.AppImage + sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder + popd + # run appimage-builder + pushd appimage + sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml + + - name: Publish appimage package + if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage + + - name: Publish fedora28/centos8 package + if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + /opt/artifacts/rpm/*.rpm + + # Temporary disable flatpak arm build + # + # build-flatpak-arm: + # name: Build Flatpak + # needs: [build-rustdesk-linux-arm] + # runs-on: ${{ matrix.job.os }} + # strategy: + # fail-fast: false + # matrix: + # job: + # # - { target: aarch64-unknown-linux-gnu , os: ubuntu-18.04, arch: arm64 } + # - { target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, arch: arm64 } + # steps: + # - name: Checkout source code + # uses: actions/checkout@v3 + + # - name: Download Binary + # uses: actions/download-artifact@master + # with: + # name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + # path: . + + # - name: Rename Binary + # run: | + # mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb + + # - uses: Kingtous/run-on-arch-action@amd64-support + # name: Build rustdesk flatpak package for ${{ matrix.job.arch }} + # id: rpm + # with: + # arch: ${{ matrix.job.arch }} + # distro: ubuntu18.04 + # githubToken: ${{ github.token }} + # setup: | + # ls -l "${PWD}" + # dockerRunArgs: | + # --volume "${PWD}:/workspace" + # shell: /bin/bash + # install: | + # apt update -y + # apt install -y rpm + # run: | + # pushd /workspace + # # install + # apt update -y + # apt install -y flatpak flatpak-builder cmake g++ gcc git curl wget nasm yasm libgtk-3-dev git + # # flatpak deps + # flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + # flatpak --user install -y flathub org.freedesktop.Platform/${{ matrix.job.arch }}/21.08 + # flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/21.08 + # # package + # pushd flatpak + # git clone https://github.com/flathub/shared-modules.git --depth=1 + # flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json + # flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak org.rustdesk.rustdesk + + # - name: Publish flatpak package + # uses: softprops/action-gh-release@v1 + # with: + # prerelease: true + # tag_name: ${{ env.TAG_NAME }} + # files: | + # flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak + + build-flatpak-amd64: + name: Build Flatpak + needs: [build-rustdesk-linux-amd64] + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + job: + - { target: x86_64-unknown-linux-gnu, os: ubuntu-18.04, arch: x86_64 } + steps: + - name: Checkout source code + uses: actions/checkout@v3 + + - name: Download Binary + uses: actions/download-artifact@master + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + path: . + + - name: Rename Binary + run: | + mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb + + - uses: Kingtous/run-on-arch-action@amd64-support + name: Build rustdesk flatpak package for ${{ matrix.job.arch }} + id: rpm + with: + arch: ${{ matrix.job.arch }} + distro: ubuntu18.04 + githubToken: ${{ github.token }} + setup: | + ls -l "${PWD}" + dockerRunArgs: | + --volume "${PWD}:/workspace" + shell: /bin/bash + install: | + apt update -y + apt install -y rpm git wget curl + run: | + # disable git safe.directory + git config --global --add safe.directory "*" + pushd /workspace + # install + apt update -y + apt install -y flatpak flatpak-builder cmake g++ gcc git curl wget nasm yasm libgtk-3-dev git + # flatpak deps + flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo + flatpak --user install -y flathub org.freedesktop.Platform/${{ matrix.job.arch }}/21.08 + flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/21.08 + # package + pushd flatpak + git clone https://github.com/flathub/shared-modules.git --depth=1 + flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json + flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak com.rustdesk.RustDesk + + - name: Publish flatpak package + uses: softprops/action-gh-release@v1 + if: env.UPLOAD_ARTIFACT == 'true' + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml index 14027f9f8..d40d6f736 100644 --- a/.github/workflows/flutter-ci.yml +++ b/.github/workflows/flutter-ci.yml @@ -18,7 +18,7 @@ on: jobs: run-ci: - uses: ./.github/workflows/flutter-nightly.yml + uses: ./.github/workflows/flutter-build.yml with: upload-artifact: false \ No newline at end of file diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index e5c2c2752..ab625e431 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -5,1613 +5,10 @@ on: # schedule build every night - cron: "0 0 * * *" workflow_dispatch: - inputs: - upload-artifact: - description: "Upload the artifact produced by this workflow to the Release page." - type: boolean - default: true - workflow_call: - inputs: - upload-artifact: - type: boolean - default: true - -env: - LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.7.0" - TAG_NAME: "nightly" - # vcpkg version: 2022.05.10 - # for multiarch gcc compatibility - VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44" - VERSION: "1.2.0" - NDK_VERSION: "r23" - #signing keys env variable checks - ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}' - MACOS_P12_BASE64: '${{ secrets.MACOS_P12_BASE64 }}' - # To make a custom build with your own servers set the below secret values - RS_PUB_KEY: '${{ secrets.RS_PUB_KEY }}' - RENDEZVOUS_SERVER: '${{ secrets.RENDEZVOUS_SERVER }}' - UPLOAD_ARTIFACT: '${{ inputs.upload-artifact }}' jobs: - build-for-windows-flutter: - 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 } - # - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install LLVM and Clang - uses: KyleMayes/install-llvm-action@v1 - with: - version: ${{ env.LLVM_VERSION }} - - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - cache: true - - - name: Replace engine with rustdesk custom flutter engine - run: | - flutter doctor -v - flutter precache --windows - Invoke-WebRequest -Uri https://github.com/Kingtous/engine/releases/download/v3.7.0-rustdesk/windows-x64-release-flutter.zip -OutFile windows-x64-flutter-release.zip - Expand-Archive windows-x64-flutter-release.zip -DestinationPath engine - mv -Force engine/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/ - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }} - - - name: Install flutter rust bridge deps - run: | - cargo install flutter_rust_bridge_codegen - Push-Location flutter ; flutter pub get ; Pop-Location - ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - - name: Install vcpkg dependencies - run: | - cd C:\ - git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 - - - name: Build rustdesk - env: - VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg - run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver - - - name: Sign rustdesk files - uses: GermanBluefox/code-sign-action@v7 - if: env.UPLOAD_ARTIFACT == 'true' - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.CERTNAME }}' - folder: './flutter/build/windows/runner/Release/' - recursive: true - - - name: Build self-extracted executable - shell: bash - if: env.UPLOAD_ARTIFACT == 'true' - run: | - pushd ./libs/portable - python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe - popd - mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.exe - - # - name: Rename rustdesk - # shell: bash - # run: | - # for name in rustdesk*??-install.exe; do - # mv "$name" ./SignOutput/"${name%%-install.exe}-${{ matrix.job.target }}.exe" - # done - - - name: Sign rustdesk self-extracted file - uses: GermanBluefox/code-sign-action@v7 - if: env.UPLOAD_ARTIFACT == 'true' - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' - folder: './SignOutput' - recursive: false - - - name: Publish Release - uses: softprops/action-gh-release@v1 - if: env.UPLOAD_ARTIFACT == 'true' - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./SignOutput/rustdesk-*.exe - - # The fallback for the flutter version, we use Sciter for 32bit Windows. - build-for-windows-sciter: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) - runs-on: ${{ matrix.job.os }} - # Temporarily disable this action due to additional test is needed. - # if: false - strategy: - fail-fast: false - matrix: - job: - # - { target: i686-pc-windows-msvc , os: windows-2019 } - # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: i686-pc-windows-msvc, os: windows-2019 } - # - { target: aarch64-pc-windows-msvc, os: windows-2019 } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Install LLVM and Clang - uses: Kingtous/install-llvm-action-32bit@master - with: - version: ${{ env.LLVM_VERSION }} - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly-${{ matrix.job.target }} - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }}-sciter - - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - cd C:\ - git clone https://github.com/Kingtous/rustdesk_thirdpary_lib --depth=1 - - - name: Build rustdesk - id: build - shell: bash - env: - VCPKG_ROOT: C:\rustdesk_thirdpary_lib\vcpkg - run: | - python3 res/inline-sciter.py - # Patch sciter x86 - sed -i 's/branch = "dyn"/branch = "dyn_x86"/g' ./Cargo.toml - # Replace the link for the ico. - rm res/icon.ico && cp flutter/windows/runner/resources/app_icon.ico res/icon.ico - cargo build --features inline --release --bins - mkdir -p ./Release - mv ./target/release/rustdesk.exe ./Release/rustdesk.exe - curl -LJ -o ./Release/sciter.dll https://github.com/c-smile/sciter-sdk/raw/master/bin.win/x32/sciter.dll - echo "output_folder=./Release" >> $GITHUB_OUTPUT - - - name: Sign rustdesk files - uses: GermanBluefox/code-sign-action@v7 - if: env.UPLOAD_ARTIFACT == 'true' - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.CERTNAME }}' - folder: './Release/' - recursive: true - - - name: Build self-extracted executable - shell: bash - run: | - pushd ./libs/portable - pip3 install -r requirements.txt - python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe - popd - mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-sciter.exe - - - name: Sign rustdesk self-extracted file - uses: GermanBluefox/code-sign-action@v7 - with: - certificate: '${{ secrets.WINDOWS_PFX_BASE64 }}' - password: '${{ secrets.WINDOWS_PFX_PASSWORD }}' - certificatesha1: '${{ secrets.WINDOWS_PFX_SHA1_THUMBPRINT }}' - # certificatename: '${{ secrets.WINDOWS_PFX_NAME }}' - folder: './SignOutput' - recursive: false - - - name: Publish Release - uses: softprops/action-gh-release@v1 - if: env.UPLOAD_ARTIFACT == 'true' - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./SignOutput/rustdesk-*.exe - - build-for-macOS: - name: ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-args }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { - target: x86_64-apple-darwin, - os: macos-latest, - extra-build-args: "", - } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Import the codesign cert - if: env.MACOS_P12_BASE64 != null - uses: apple-actions/import-codesign-certs@v1 - with: - p12-file-base64: ${{ secrets.MACOS_P12_BASE64 }} - p12-password: ${{ secrets.MACOS_P12_PASSWORD }} - keychain: rustdesk - - - name: Check sign and import sign key - if: env.MACOS_P12_BASE64 != null - run: | - security default-keychain -s rustdesk.keychain - security find-identity -v - - - name: Import notarize key - if: env.MACOS_P12_BASE64 != null - uses: timheuer/base64-to-file@v1.2 - with: - # https://gregoryszorc.com/docs/apple-codesign/stable/apple_codesign_rcodesign.html#notarizing-and-stapling - fileName: rustdesk.json - fileDir: ${{ github.workspace }} - encodedString: ${{ secrets.MACOS_NOTARIZE_JSON }} - - - name: Install rcodesign tool - if: env.MACOS_P12_BASE64 != null - shell: bash - run: | - pushd /tmp - wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz - tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz - mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin - popd - - - name: Install build runtime - run: | - brew install llvm create-dmg nasm yasm cmake gcc wget ninja pkg-config - - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: ${{ matrix.job.os }} - - - name: Install flutter rust bridge deps - shell: bash - run: | - cargo install flutter_rust_bridge_codegen - pushd flutter && flutter pub get && popd - ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart - - - name: Restore from cache and install vcpkg - uses: lukka/run-vcpkg@v7 - with: - setupOnly: true - vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }} - - - name: Install vcpkg dependencies - run: | - $VCPKG_ROOT/vcpkg install libvpx libyuv opus - - - name: Show version information (Rust, cargo, Clang) - shell: bash - run: | - clang --version || true - rustup -V - rustup toolchain list - rustup default - cargo -V - rustc -V - - - name: Build rustdesk - run: | - # --hwcodec not supported on macos yet - ./build.py --flutter ${{ matrix.job.extra-build-args }} - - - name: Codesign app and create signed dmg - if: env.MACOS_P12_BASE64 != null - run: | - security default-keychain -s rustdesk.keychain - security unlock-keychain -p ${{ secrets.MACOS_P12_PASSWORD }} rustdesk.keychain - # start sign the rustdesk.app and dmg - rm rustdesk-${{ env.VERSION }}.dmg || true - codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict ./flutter/build/macos/Build/Products/Release/RustDesk.app -vvv - create-dmg --icon "RustDesk.app" 200 190 --hide-extension "RustDesk.app" --window-size 800 400 --app-drop-link 600 185 rustdesk-${{ env.VERSION }}.dmg ./flutter/build/macos/Build/Products/Release/RustDesk.app - codesign --force --options runtime -s ${{ secrets.MACOS_CODESIGN_IDENTITY }} --deep --strict rustdesk-${{ env.VERSION }}.dmg -vvv - # notarize the rustdesk-${{ env.VERSION }}.dmg - rcodesign notary-submit --api-key-path ${{ github.workspace }}/rustdesk.json --staple rustdesk-${{ env.VERSION }}.dmg - - - name: Rename rustdesk - run: | - for name in rustdesk*??.dmg; do - mv "$name" "${name%%.dmg}-${{ matrix.job.target }}.dmg" - done - - - name: Publish DMG package - if: env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - rustdesk*-${{ matrix.job.target }}.dmg - - build-vcpkg-deps-linux: - uses: ./.github/workflows/vcpkg-deps-linux.yml - - generate-bridge-linux: - uses: ./.github/workflows/bridge.yml - - build-rustdesk-android: - needs: [generate-bridge-linux] - name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - job: - - { - arch: x86_64, - target: aarch64-linux-android, - os: ubuntu-20.04, - extra-build-features: "", - openssl-arch: android-arm64 - } - - { - arch: x86_64, - target: armv7-linux-androideabi, - os: ubuntu-18.04, - extra-build-features: "", - openssl-arch: android-arm - } - steps: - - name: Install dependencies - run: | - sudo apt update - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless - - name: Checkout source code - uses: actions/checkout@v3 - - name: Install flutter - uses: subosito/flutter-action@v2 - with: - channel: "stable" - flutter-version: ${{ env.FLUTTER_VERSION }} - - uses: nttld/setup-ndk@v1 - id: setup-ndk - with: - ndk-version: ${{ env.NDK_VERSION }} - add-to-path: true - - - name: Clone deps - shell: bash - run: | - pushd /opt - git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - - - name: Build rustdesk lib - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }} - VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg - run: | - rustup target add ${{ matrix.job.target }} - cargo install cargo-ndk - case ${{ matrix.job.target }} in - aarch64-linux-android) - ./flutter/ndk_arm64.sh - mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - ;; - armv7-linux-androideabi) - ./flutter/ndk_arm.sh - mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - ;; - esac - - - name: Build rustdesk - shell: bash - env: - JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64 - run: | - export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH - # temporary use debug sign config - sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle - case ${{ matrix.job.target }} in - aarch64-linux-android) - mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a - cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/arm64-v8a/*.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - # build flutter - pushd flutter - flutter build apk --release --target-platform android-arm64 --split-per-abi - mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk - ;; - armv7-linux-androideabi) - mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a - cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/armeabi-v7a/*.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ - cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - # build flutter - pushd flutter - flutter build apk --release --target-platform android-arm --split-per-abi - mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk - ;; - esac - popd - mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk . - - - uses: r0adkll/sign-android-release@v1 - name: Sign app APK - if: env.ANDROID_SIGNING_KEY != null - id: sign-rustdesk - with: - releaseDirectory: ./signed-apk - signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }} - alias: ${{ secrets.ANDROID_ALIAS }} - keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }} - keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }} - env: - # override default build-tools version (29.0.3) -- optional - BUILD_TOOLS_VERSION: "30.0.2" - - - name: Upload Artifacts - if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' - uses: actions/upload-artifact@master - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk - path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} - - - name: Publish signed apk package - if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ${{steps.sign-rustdesk.outputs.signedReleaseFile}} - - - name: Publish unsigned apk package - if: env.ANDROID_SIGNING_KEY == null && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk - - build-rustdesk-lib-linux-amd64: - needs: [generate-bridge-linux, build-vcpkg-deps-linux] - name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - # use a high level qemu-user-static - job: - # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "flatpak", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-20.04, - extra-build-features: "appimage", - } - # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - steps: - - name: Maximize build space - run: | - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/share/dotnet - sudo apt update -y - sudo apt install qemu-user-static - - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 12 - - - name: Free Space - run: | - df - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - cache-directories: "/opt/rust-registry" - - - name: Install local registry - run: | - mkdir -p /opt/rust-registry - cargo install cargo-local-registry - - - name: Build local registry - uses: nick-fields/retry@v2 - id: build-local-registry - continue-on-error: true - with: - max_attempts: 3 - timeout_minutes: 15 - retry_on: error - command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - # only build cdylib - sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Restore vcpkg files - uses: actions/download-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: /opt/artifacts/vcpkg/installed - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk library for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - # not ready yet - # distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - ls -l /opt/artifacts/vcpkg/installed - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/rust-registry:/opt/rust-registry" - shell: /bin/bash - install: | - apt update -y - echo -e "installing deps" - apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null - # we have libopus compiled by us. - apt remove -y libopus-dev || true - # output devs - ls -l ./ - tree -L 3 /opt/artifacts/vcpkg/installed - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # rust - pushd /opt - wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz - tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz - cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh - rm -rf rust-1.64.0-${{ matrix.job.target }} - # edit config - mkdir -p ~/.cargo/ - echo """ - [source.crates-io] - registry = 'https://github.com/rust-lang/crates.io-index' - replace-with = 'local-registry' - - [source.local-registry] - local-registry = '/opt/rust-registry/' - """ > ~/.cargo/config - cat ~/.cargo/config - # start build - pushd /workspace - # mock - case "${{ matrix.job.arch }}" in - x86_64) - # no need mock on x86_64 - export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release - ;; - esac - - - name: Upload Artifacts - uses: actions/upload-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: target/release/liblibrustdesk.so - - build-rustdesk-lib-linux-arm: - needs: [generate-bridge-linux, build-vcpkg-deps-linux] - name: build-rust-lib ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - # use a high level qemu-user-static - job: - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-20.04, - use-cross: true, - extra-build-features: "", - } - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "appimage", - } - # - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } - # - { - # arch: armv7, - # target: armv7-unknown-linux-gnueabihf, - # os: ubuntu-20.04, - # use-cross: true, - # extra-build-features: "", - # } - # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } - # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - steps: - - name: Maximize build space - run: | - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/share/dotnet - sudo apt update -y - sudo apt install qemu-user-static - - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 12 - - - name: Free Space - run: | - df - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - cache-directories: "/opt/rust-registry" - - - name: Install local registry - run: | - mkdir -p /opt/rust-registry - cargo install cargo-local-registry - - - name: Build local registry - uses: nick-fields/retry@v2 - id: build-local-registry - continue-on-error: true - with: - max_attempts: 3 - timeout_minutes: 15 - retry_on: error - command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry - - - name: Disable rust bridge build - run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs - # only build cdylib - sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Restore vcpkg files - uses: actions/download-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: /opt/artifacts/vcpkg/installed - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk library for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - ls -l /opt/artifacts/vcpkg/installed - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/rust-registry:/opt/rust-registry" - shell: /bin/bash - install: | - apt update -y - echo -e "installing deps" - apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null - # we have libopus compiled by us. - apt remove -y libopus-dev || true - # output devs - ls -l ./ - tree -L 3 /opt/artifacts/vcpkg/installed - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # rust - pushd /opt - wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz - tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz - cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh - rm -rf rust-1.64.0-${{ matrix.job.target }} - # edit config - mkdir -p ~/.cargo/ - echo """ - [source.crates-io] - registry = 'https://github.com/rust-lang/crates.io-index' - replace-with = 'local-registry' - - [source.local-registry] - local-registry = '/opt/rust-registry/' - """ > ~/.cargo/config - cat ~/.cargo/config - # start build - pushd /workspace - export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release - - - name: Upload Artifacts - uses: actions/upload-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: target/release/liblibrustdesk.so - - build-rustdesk-sciter-arm: - needs: [build-vcpkg-deps-linux] - name: build-rustdesk(sciter) ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ${{ matrix.job.os }} - strategy: - fail-fast: false - matrix: - # use a high level qemu-user-static - job: - - { - arch: armv7, - target: armv7-unknown-linux-gnueabihf, - deb-arch: armhf, - os: ubuntu-latest, - use-cross: true, - extra-build-features: "", - } - # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } - # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - steps: - - - name: Maximize build space - run: | - sudo rm -rf /opt/ghc - sudo rm -rf /usr/local/lib/android - sudo rm -rf /usr/share/dotnet - sudo apt update -y - sudo apt install qemu-user-static - - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Set Swap Space - uses: pierotofy/set-swap-space@master - with: - swap-size-gb: 12 - - - name: Free Space - run: | - df - - - name: Install Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - target: ${{ matrix.job.target }} - override: true - profile: minimal # minimal component installation (ie, no documentation) - - - uses: Swatinem/rust-cache@v2 - with: - prefix-key: rustdesk-lib-cache - key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - cache-directories: "/opt/rust-registry" - - - name: Install local registry - run: | - mkdir -p /opt/rust-registry - cargo install cargo-local-registry - - - name: Build local registry - uses: nick-fields/retry@v2 - id: build-local-registry - continue-on-error: true - with: - max_attempts: 3 - timeout_minutes: 15 - retry_on: error - command: cargo local-registry --sync ./Cargo.lock /opt/rust-registry - - - name: Restore vcpkg files - uses: actions/download-artifact@master - with: - name: vcpkg-artifact-${{ matrix.job.arch }} - path: /opt/artifacts/vcpkg/installed - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk sciter binary for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/rust-registry:/opt/rust-registry" - shell: /bin/bash - install: | - apt update -y - apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm libclang-dev - apt-get -qq install -y libdbus-1-dev pkg-config nasm yasm libglib2.0-dev libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev - apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # rust - pushd /opt - wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-1.64.0-${{ matrix.job.target }}.tar.gz - tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz - cd rust-1.64.0-${{ matrix.job.target }} && ./install.sh - rm -rf rust-1.64.0-${{ matrix.job.target }} - # edit config - mkdir -p ~/.cargo/ - echo """ - [source.crates-io] - registry = 'https://github.com/rust-lang/crates.io-index' - replace-with = 'local-registry' - - [source.local-registry] - local-registry = '/opt/rust-registry/' - """ > ~/.cargo/config - cat ~/.cargo/config - - # build - pushd /workspace - python3 ./res/inline-sciter.py - export VCPKG_ROOT=/opt/artifacts/vcpkg - export ARCH=armhf - cargo build --features inline --release --bins - # package - mkdir -p ./Release - mv ./target/release/rustdesk ./Release/rustdesk - wget -O ./Release/libsciter-gtk.so https://github.com/c-smile/sciter-sdk/raw/master/bin.lnx/arm32/libsciter-gtk.so - ./build.py --package ./Release - - - name: Rename rustdesk - shell: bash - run: | - for name in rustdesk*??.deb; do - # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb" - done - - - name: Publish debian package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - - - name: Upload Artifact - uses: actions/upload-artifact@master - if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - - build-rustdesk-linux-arm: - needs: [build-rustdesk-lib-linux-arm] - name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ubuntu-20.04 # 20.04 has more performance on arm build - strategy: - fail-fast: false - matrix: - job: - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "", - } - - { - arch: aarch64, - target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host - use-cross: true, - extra-build-features: "appimage", - } - # - { - # arch: aarch64, - # target: aarch64-unknown-linux-gnu, - # os: ubuntu-18.04, # just for naming package, not running host - # use-cross: true, - # extra-build-features: "flatpak", - # } - # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "" } - # - { arch: armv7, target: armv7-unknown-linux-gnueabihf , os: ubuntu-20.04, use-cross: true, extra-build-features: "appimage" } - # - { target: arm-unknown-linux-musleabihf, os: ubuntu-20.04, use-cross: true } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Prepare env - run: | - sudo apt update -y - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools - mkdir -p ./target/release/ - - - name: Restore the rustdesk lib file - uses: actions/download-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: ./target/release/ - - - name: Download Flutter - shell: bash - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /opt - # clone repo and reset to flutter 3.7.0 - git clone https://github.com/sony/flutter-elinux.git || true - pushd flutter-elinux - # reset to flutter 3.7.0 - git fetch - git reset --hard 51a1d685901f79fbac51665a967c3a1a789ecee5 - popd - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk binary for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04-rustdesk - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - --volume "/opt/flutter-elinux:/opt/flutter-elinux" - shell: /bin/bash - install: | - apt update -y - apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /workspace - # we use flutter-elinux to build our rustdesk - export PATH=/opt/flutter-elinux/bin:$PATH - sed -i "s/flutter build linux --release/flutter-elinux build linux/g" ./build.py - # Setup flutter-elinux. Run doctor to check if issues here. - flutter-elinux doctor -v - # Patch arm64 engine for flutter 3.6.0+ - flutter-elinux precache --linux - pushd /tmp - curl -O https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.7.0-stable.tar.xz - tar -xvf flutter_linux_3.7.0-stable.tar.xz flutter/bin/cache/artifacts/engine/linux-x64/shader_lib - cp -R flutter/bin/cache/artifacts/engine/linux-x64/shader_lib /opt/flutter-elinux/flutter/bin/cache/artifacts/engine/linux-arm64 - popd - # edit to corresponding arch - case ${{ matrix.job.arch }} in - aarch64) - sed -i "s/Architecture: amd64/Architecture: arm64/g" ./build.py - sed -i "s/x64\/release/arm64\/release/g" ./build.py - ;; - armv7) - sed -i "s/Architecture: amd64/Architecture: arm/g" ./build.py - sed -i "s/x64\/release/arm\/release/g" ./build.py - ;; - esac - python3 ./build.py --flutter --hwcodec --skip-cargo - # rpm package - echo -e "start packaging fedora package" - pushd /workspace - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/64bit/32bit/g" ./res/rpm-flutter.spec - sed -i "s/linux\/x64/linux\/arm/g" ./res/rpm-flutter.spec - ;; - aarch64) - sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter.spec - ;; - esac - HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb - pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} - mkdir -p /opt/artifacts/rpm - for name in rustdesk*??.rpm; do - mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" - done - # rpm suse package - echo -e "start packaging suse package" - pushd /workspace - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/64bit/32bit/g" ./res/rpm-flutter-suse.spec - sed -i "s/linux\/x64/linux\/arm/g" ./res/rpm-flutter-suse.spec - ;; - aarch64) - sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter-suse.spec - ;; - esac - HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb - pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} - mkdir -p /opt/artifacts/rpm - for name in rustdesk*??.rpm; do - mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-suse.rpm" - done - - - name: Rename rustdesk - shell: bash - run: | - for name in rustdesk*??.deb; do - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" - done - - - name: Publish debian package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - - - name: Build appimage package - if: ${{ matrix.job.extra-build-features == 'appimage' }} - shell: bash - run: | - # set-up appimage-builder - pushd /tmp - wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage - chmod +x appimage-builder-x86_64.AppImage - sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder - popd - # run appimage-builder - pushd appimage - sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml - - - name: Publish appimage package - if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage - - - name: Upload Artifact - uses: actions/upload-artifact@master - if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - - - name: Patch archlinux PKGBUILD - if: ${{ matrix.job.extra-build-features == '' }} - run: | - sed -i "s/arch=('x86_64')/arch=('${{ matrix.job.arch }}')/g" res/PKGBUILD - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/linux\/x64/linux\/arm/g" ./res/PKGBUILD - ;; - aarch64) - sed -i "s/linux\/x64/linux\/arm64/g" ./res/PKGBUILD - ;; - esac - - # Temporary disable for there is no many archlinux arm hosts - # - name: Build archlinux package - # if: ${{ matrix.job.extra-build-features == '' }} - # uses: vufa/arch-makepkg-action@master - # with: - # packages: > - # llvm - # clang - # libva - # libvdpau - # rust - # gstreamer - # unzip - # git - # cmake - # gcc - # curl - # wget - # yasm - # nasm - # zip - # make - # pkg-config - # clang - # gtk3 - # xdotool - # libxcb - # libxfixes - # alsa-lib - # pipewire - # python - # ttf-arphic-uming - # libappindicator-gtk3 - # scripts: | - # cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f - - # - name: Publish archlinux package - # if: ${{ matrix.job.extra-build-features == '' }} - # uses: softprops/action-gh-release@v1 - # with: - # prerelease: true - # tag_name: ${{ env.TAG_NAME }} - # files: | - # res/rustdesk*.zst - - - name: Publish fedora28/centos8 package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - /opt/artifacts/rpm/*.rpm - - build-rustdesk-linux-amd64: - needs: [build-rustdesk-lib-linux-amd64] - name: build-rustdesk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - job: - # - { target: i686-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true } - # - { target: i686-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, - extra-build-features: "", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, - extra-build-features: "flatpak", - } - - { - arch: x86_64, - target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, - extra-build-features: "appimage", - } - # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Restore bridge files - uses: actions/download-artifact@master - with: - name: bridge-artifact - path: ./ - - - name: Prepare env - run: | - sudo apt update -y - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev libarchive-tools - mkdir -p ./target/release/ - - - name: Restore the rustdesk lib file - uses: actions/download-artifact@master - with: - name: librustdesk-${{ matrix.job.arch }}-${{ matrix.job.extra-build-features }}.so - path: ./target/release/ - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk binary for ${{ matrix.job.arch }} - id: vcpkg - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - --volume "/opt/artifacts:/opt/artifacts" - shell: /bin/bash - install: | - apt update -y - apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - # Setup Flutter - pushd /opt - wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz - tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz - ls -l . - export PATH=/opt/flutter/bin:$PATH - flutter doctor -v - pushd /workspace - python3 ./build.py --flutter --hwcodec --skip-cargo - # rpm package - pushd /workspace - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/64bit/32bit/g" ./res/rpm-flutter.spec - ;; - esac - HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb - pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} - mkdir -p /opt/artifacts/rpm - for name in rustdesk*??.rpm; do - mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-fedora28-centos8.rpm" - done - # rpm suse package - pushd /workspace - case ${{ matrix.job.arch }} in - armv7) - sed -i "s/64bit/32bit/g" ./res/rpm-flutter-suse.spec - ;; - esac - HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb - pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} - mkdir -p /opt/artifacts/rpm - for name in rustdesk*??.rpm; do - mv "$name" "/opt/artifacts/rpm/${name%%.rpm}-suse.rpm" - done - - - name: Rename rustdesk - shell: bash - run: | - for name in rustdesk*??.deb; do - # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" - done - - - name: Publish debian package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - - - name: Upload Artifact - uses: actions/upload-artifact@master - if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - - - name: Patch archlinux PKGBUILD - if: ${{ matrix.job.extra-build-features == '' }} - run: | - sed -i "s/arch=('x86_64')/arch=('${{ matrix.job.arch }}')/g" res/PKGBUILD - - - name: Build archlinux package - if: ${{ matrix.job.extra-build-features == '' }} - uses: vufa/arch-makepkg-action@master - with: - packages: > - llvm - clang - libva - libvdpau - rust - gstreamer - unzip - git - cmake - gcc - curl - wget - yasm - nasm - zip - make - pkg-config - clang - gtk3 - xdotool - libxcb - libxfixes - alsa-lib - pipewire - python - ttf-arphic-uming - libappindicator-gtk3 - scripts: | - cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f - - - name: Publish archlinux package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - res/rustdesk*.zst - - - name: Build appimage package - if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' - shell: bash - run: | - # set-up appimage-builder - pushd /tmp - wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage - chmod +x appimage-builder-x86_64.AppImage - sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder - popd - # run appimage-builder - pushd appimage - sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-x86_64.yml - - - name: Publish appimage package - if: matrix.job.extra-build-features == 'appimage' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - ./appimage/rustdesk-${{ env.VERSION }}-*.AppImage - - - name: Publish fedora28/centos8 package - if: matrix.job.extra-build-features == '' && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - /opt/artifacts/rpm/*.rpm - - # Temporary disable flatpak arm build - # - # build-flatpak-arm: - # name: Build Flatpak - # needs: [build-rustdesk-linux-arm] - # runs-on: ${{ matrix.job.os }} - # strategy: - # fail-fast: false - # matrix: - # job: - # # - { target: aarch64-unknown-linux-gnu , os: ubuntu-18.04, arch: arm64 } - # - { target: aarch64-unknown-linux-gnu, os: ubuntu-20.04, arch: arm64 } - # steps: - # - name: Checkout source code - # uses: actions/checkout@v3 - - # - name: Download Binary - # uses: actions/download-artifact@master - # with: - # name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - # path: . - - # - name: Rename Binary - # run: | - # mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb - - # - uses: Kingtous/run-on-arch-action@amd64-support - # name: Build rustdesk flatpak package for ${{ matrix.job.arch }} - # id: rpm - # with: - # arch: ${{ matrix.job.arch }} - # distro: ubuntu18.04 - # githubToken: ${{ github.token }} - # setup: | - # ls -l "${PWD}" - # dockerRunArgs: | - # --volume "${PWD}:/workspace" - # shell: /bin/bash - # install: | - # apt update -y - # apt install -y rpm - # run: | - # pushd /workspace - # # install - # apt update -y - # apt install -y flatpak flatpak-builder cmake g++ gcc git curl wget nasm yasm libgtk-3-dev git - # # flatpak deps - # flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - # flatpak --user install -y flathub org.freedesktop.Platform/${{ matrix.job.arch }}/21.08 - # flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/21.08 - # # package - # pushd flatpak - # git clone https://github.com/flathub/shared-modules.git --depth=1 - # flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json - # flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak org.rustdesk.rustdesk - - # - name: Publish flatpak package - # uses: softprops/action-gh-release@v1 - # with: - # prerelease: true - # tag_name: ${{ env.TAG_NAME }} - # files: | - # flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak - - build-flatpak-amd64: - name: Build Flatpak - needs: [build-rustdesk-linux-amd64] - runs-on: ubuntu-20.04 - strategy: - fail-fast: false - matrix: - job: - - { target: x86_64-unknown-linux-gnu, os: ubuntu-18.04, arch: x86_64 } - steps: - - name: Checkout source code - uses: actions/checkout@v3 - - - name: Download Binary - uses: actions/download-artifact@master - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: . - - - name: Rename Binary - run: | - mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb - - - uses: Kingtous/run-on-arch-action@amd64-support - name: Build rustdesk flatpak package for ${{ matrix.job.arch }} - id: rpm - with: - arch: ${{ matrix.job.arch }} - distro: ubuntu18.04 - githubToken: ${{ github.token }} - setup: | - ls -l "${PWD}" - dockerRunArgs: | - --volume "${PWD}:/workspace" - shell: /bin/bash - install: | - apt update -y - apt install -y rpm git wget curl - run: | - # disable git safe.directory - git config --global --add safe.directory "*" - pushd /workspace - # install - apt update -y - apt install -y flatpak flatpak-builder cmake g++ gcc git curl wget nasm yasm libgtk-3-dev git - # flatpak deps - flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - flatpak --user install -y flathub org.freedesktop.Platform/${{ matrix.job.arch }}/21.08 - flatpak --user install -y flathub org.freedesktop.Sdk/${{ matrix.job.arch }}/21.08 - # package - pushd flatpak - git clone https://github.com/flathub/shared-modules.git --depth=1 - flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json - flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak com.rustdesk.RustDesk - - - name: Publish flatpak package - uses: softprops/action-gh-release@v1 - if: env.UPLOAD_ARTIFACT == 'true' - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak + run-flutter-nightly-build: + uses: ./.github/workflows/flutter-build.yml + with: + upload-artifact: true + \ No newline at end of file From ddd0d6eafc56611be4c45e3e4a60b9ee0fbc9482 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 3 Apr 2023 11:03:21 +0800 Subject: [PATCH 156/366] remove disable-vp8 --- flutter/build_android_deps.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/flutter/build_android_deps.sh b/flutter/build_android_deps.sh index a30abd154..2f876ca6e 100755 --- a/flutter/build_android_deps.sh +++ b/flutter/build_android_deps.sh @@ -58,13 +58,12 @@ function build { fi make clean ./configure --target=$LIBVPX_TARGET \ - --enable-pic --disable-vp8 \ + --enable-pic --disable-webm-io \ --disable-unit-tests \ --disable-examples \ --disable-libyuv \ --disable-postproc \ - --disable-vp8 \ --disable-tools \ --disable-docs \ --prefix=$PREFIX @@ -122,4 +121,4 @@ build arm64-v8a arm64-android aarch64-linux-android arm64-android-gcc build armeabi-v7a arm-android arm-linux-androideabi armv7-android-gcc # rm -rf build/libvpx -# rm -rf build/oboe \ No newline at end of file +# rm -rf build/oboe From c842650c11b04dbd8eedeaff5bcc9ffea92b8b31 Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 31 Mar 2023 16:10:52 +0800 Subject: [PATCH 157/366] vp8 Signed-off-by: 21pages --- .../desktop/pages/desktop_setting_page.dart | 42 ++- .../lib/desktop/widgets/remote_toolbar.dart | 26 +- flutter/lib/mobile/pages/remote_page.dart | 35 +- libs/hbb_common/protos/message.proto | 16 +- libs/hbb_common/src/config.rs | 5 +- libs/scrap/examples/benchmark.rs | 46 ++- libs/scrap/src/common/android.rs | 1 + libs/scrap/src/common/codec.rs | 319 +++++++++--------- libs/scrap/src/common/hwcodec.rs | 17 +- libs/scrap/src/common/mod.rs | 53 +++ libs/scrap/src/common/record.rs | 58 ++-- libs/scrap/src/common/vpxcodec.rs | 46 +-- src/client.rs | 37 +- src/client/helper.rs | 33 +- src/client/io_loop.rs | 9 +- src/flutter_ffi.rs | 13 +- src/server/connection.rs | 34 +- src/server/video_service.rs | 85 +++-- src/ui/header.tis | 9 +- src/ui/remote.rs | 22 +- src/ui_interface.rs | 7 + src/ui_session_interface.rs | 28 +- 22 files changed, 484 insertions(+), 457 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 66ef83d31..74d51407c 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1258,9 +1258,6 @@ class _DisplayState extends State<_Display> { } Widget codec(BuildContext context) { - if (!bind.mainHasHwcodec()) { - return Offstage(); - } final key = 'codec-preference'; onChanged(String value) async { await bind.mainSetUserDefaultOption(key: key, value: value); @@ -1268,28 +1265,45 @@ class _DisplayState extends State<_Display> { } final groupValue = bind.mainGetUserDefaultOption(key: key); - + var hwRadios = []; + try { + final Map codecsJson = jsonDecode(bind.mainSupportedHwdecodings()); + final h264 = codecsJson['h264'] ?? false; + final h265 = codecsJson['h265'] ?? false; + if (h264) { + hwRadios.add(_Radio(context, + value: 'h264', + groupValue: groupValue, + label: 'H264', + onChanged: onChanged)); + } + if (h265) { + hwRadios.add(_Radio(context, + value: 'h265', + groupValue: groupValue, + label: 'H265', + onChanged: onChanged)); + } + } catch (e) { + debugPrint("failed to parse supported hwdecodings, err=$e"); + } return _Card(title: 'Default Codec', children: [ _Radio(context, value: 'auto', groupValue: groupValue, label: 'Auto', onChanged: onChanged), + _Radio(context, + value: 'vp8', + groupValue: groupValue, + label: 'VP8', + onChanged: onChanged), _Radio(context, value: 'vp9', groupValue: groupValue, label: 'VP9', onChanged: onChanged), - _Radio(context, - value: 'h264', - groupValue: groupValue, - label: 'H264', - onChanged: onChanged), - _Radio(context, - value: 'h265', - groupValue: groupValue, - label: 'H265', - onChanged: onChanged), + ...hwRadios, ]); } diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 703ed6db9..cdbf8be27 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1351,29 +1351,30 @@ class _DisplayMenuState extends State<_DisplayMenu> { codec() { return futureBuilder(future: () async { - final supportedHwcodec = - await bind.sessionSupportedHwcodec(id: widget.id); + final alternativeCodecs = + await bind.sessionAlternativeCodecs(id: widget.id); final codecPreference = await bind.sessionGetOption(id: widget.id, arg: 'codec-preference') ?? ''; return { - 'supportedHwcodec': supportedHwcodec, + 'alternativeCodecs': alternativeCodecs, 'codecPreference': codecPreference }; }(), hasData: (data) { final List codecs = []; try { - final Map codecsJson = jsonDecode(data['supportedHwcodec']); + final Map codecsJson = jsonDecode(data['alternativeCodecs']); + final vp8 = codecsJson['vp8'] ?? false; final h264 = codecsJson['h264'] ?? false; final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); codecs.add(h264); codecs.add(h265); } catch (e) { debugPrint("Show Codec Preference err=$e"); } - final visible = bind.mainHasHwcodec() && - codecs.length == 2 && - (codecs[0] || codecs[1]); + final visible = + codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2]); if (!visible) return Offstage(); final groupValue = data['codecPreference'] as String; onChanged(String? value) async { @@ -1394,6 +1395,13 @@ class _DisplayMenuState extends State<_DisplayMenu> { onChanged: onChanged, ffi: widget.ffi, ), + _RadioMenuButton( + child: Text(translate('VP8')), + value: 'vp8', + groupValue: groupValue, + onChanged: codecs[0] ? onChanged : null, + ffi: widget.ffi, + ), _RadioMenuButton( child: Text(translate('VP9')), value: 'vp9', @@ -1405,14 +1413,14 @@ class _DisplayMenuState extends State<_DisplayMenu> { child: Text(translate('H264')), value: 'h264', groupValue: groupValue, - onChanged: codecs[0] ? onChanged : null, + onChanged: codecs[1] ? onChanged : null, ffi: widget.ffi, ), _RadioMenuButton( child: Text(translate('H265')), value: 'h265', groupValue: groupValue, - onChanged: codecs[1] ? onChanged : null, + onChanged: codecs[2] ? onChanged : null, ffi: widget.ffi, ), ]); diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 083cdcd1c..35959f407 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -970,17 +970,17 @@ void showOptions( final perms = gFFI.ffiModel.permissions; final hasHwcodec = bind.mainHasHwcodec(); final List codecs = []; - if (hasHwcodec) { - try { - final Map codecsJson = - jsonDecode(await bind.sessionSupportedHwcodec(id: id)); - final h264 = codecsJson['h264'] ?? false; - final h265 = codecsJson['h265'] ?? false; - codecs.add(h264); - codecs.add(h265); - } catch (e) { - debugPrint("Show Codec Preference err=$e"); - } + try { + final Map codecsJson = + jsonDecode(await bind.sessionAlternativeCodecs(id: id)); + final vp8 = codecsJson['vp8'] ?? false; + final h264 = codecsJson['h264'] ?? false; + final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); + codecs.add(h264); + codecs.add(h265); + } catch (e) { + debugPrint("Show Codec Preference err=$e"); } dialogManager.show((setState, close) { @@ -1041,15 +1041,16 @@ void showOptions( const Divider(color: MyTheme.border) ]; - if (hasHwcodec && codecs.length == 2 && (codecs[0] || codecs[1])) { - radios.addAll([ - getRadio(translate('Auto'), 'auto', codec, setCodec), - getRadio('VP9', 'vp9', codec, setCodec), - ]); + if (codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2])) { + radios.add(getRadio(translate('Auto'), 'auto', codec, setCodec)); if (codecs[0]) { + radios.add(getRadio('VP8', 'vp8', codec, setCodec)); + } + radios.add(getRadio('VP9', 'vp9', codec, setCodec)); + if (codecs[1]) { radios.add(getRadio('H264', 'h264', codec, setCodec)); } - if (codecs[1]) { + if (codecs[2]) { radios.add(getRadio('H265', 'h265', codec, setCodec)); } radios.add(const Divider(color: MyTheme.border)); diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index a481ae6c4..cd86111ec 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -24,6 +24,7 @@ message VideoFrame { YUV yuv = 8; EncodedVideoFrames h264s = 10; EncodedVideoFrames h265s = 11; + EncodedVideoFrames vp8s = 12; } } @@ -76,6 +77,7 @@ message Features { message SupportedEncoding { bool h264 = 1; bool h265 = 2; + bool vp8 = 3; } message PeerInfo { @@ -459,18 +461,20 @@ enum ImageQuality { Best = 4; } -message VideoCodecState { +message SupportedDecoding { enum PreferCodec { Auto = 0; - VPX = 1; + VP9 = 1; H264 = 2; H265 = 3; + VP8 = 4; } - int32 score_vpx = 1; - int32 score_h264 = 2; - int32 score_h265 = 3; + int32 ability_vp9 = 1; + int32 ability_h264 = 2; + int32 ability_h265 = 3; PreferCodec prefer = 4; + int32 ability_vp8 = 5; } message OptionMessage { @@ -488,7 +492,7 @@ message OptionMessage { BoolOption disable_audio = 7; BoolOption disable_clipboard = 8; BoolOption enable_file_transfer = 9; - VideoCodecState video_codec_state = 10; + SupportedDecoding supported_decoding = 10; int32 custom_fps = 11; BoolOption disable_keyboard = 12; } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 6a823c7b7..960074a8f 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -917,7 +917,8 @@ impl PeerConfig { store = store || store2; for opt in ["rdp_password", "os-password"] { if let Some(v) = config.options.get_mut(opt) { - let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + let (encrypted, _, store2) = + decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; store = store || store2; } @@ -1356,7 +1357,7 @@ impl UserDefaultConfig { "view_style" => self.get_string(key, "original", vec!["adaptive"]), "scroll_style" => self.get_string(key, "scrollauto", vec!["scrollbar"]), "image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]), - "codec-preference" => self.get_string(key, "auto", vec!["vp9", "h264", "h265"]), + "codec-preference" => self.get_string(key, "auto", vec!["vp8", "vp9", "h264", "h265"]), "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 100.0), "custom-fps" => self.get_double_string(key, 30.0, 10.0, 120.0), _ => self diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index 003830f95..ba8dec9f2 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -3,7 +3,8 @@ use hbb_common::env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; use scrap::{ codec::{EncoderApi, EncoderCfg}, Capturer, Display, TraitCapturer, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, - VpxVideoCodecId, STRIDE_ALIGN, + VpxVideoCodecId::{self, *}, + STRIDE_ALIGN, }; use std::{io::Write, time::Instant}; @@ -49,7 +50,7 @@ fn main() { "benchmark {}x{} bitrate:{}k hw_pixfmt:{:?}", width, height, bitrate_k, args.flag_hw_pixfmt ); - test_vp9(&yuvs, width, height, bitrate_k, yuv_count); + [VP8, VP9].map(|c| test_vpx(c, &yuvs, width, height, bitrate_k, yuv_count)); #[cfg(feature = "hwcodec")] { use hwcodec::AVPixelFormat; @@ -57,7 +58,7 @@ fn main() { Pixfmt::I420 => AVPixelFormat::AV_PIX_FMT_YUV420P, Pixfmt::NV12 => AVPixelFormat::AV_PIX_FMT_NV12, }; - let yuvs = hw::vp9_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); + let yuvs = hw::vpx_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); hw::test(&yuvs, width, height, bitrate_k, yuv_count, hw_pixfmt); } } @@ -87,13 +88,20 @@ fn capture_yuv(yuv_count: usize) -> (Vec>, usize, usize) { } } -fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, yuv_count: usize) { +fn test_vpx( + codec_id: VpxVideoCodecId, + yuvs: &Vec>, + width: usize, + height: usize, + bitrate_k: usize, + yuv_count: usize, +) { let config = EncoderCfg::VPX(VpxEncoderConfig { width: width as _, height: height as _, timebase: [1, 1000], bitrate: bitrate_k as _, - codec: VpxVideoCodecId::VP9, + codec: codec_id, num_threads: (num_cpus::get() / 2) as _, }); let mut encoder = VpxEncoder::new(config).unwrap(); @@ -104,35 +112,43 @@ fn test_vp9(yuvs: &Vec>, width: usize, height: usize, bitrate_k: usize, .unwrap(); let _ = encoder.flush().unwrap(); } - println!("vp9 encode: {:?}", start.elapsed() / yuv_count as _); + println!( + "{:?} encode: {:?}", + codec_id, + start.elapsed() / yuv_count as _ + ); // prepare data separately - let mut vp9s = vec![]; + let mut vpxs = vec![]; let start = Instant::now(); for yuv in yuvs { for ref frame in encoder .encode(start.elapsed().as_millis() as _, yuv, STRIDE_ALIGN) .unwrap() { - vp9s.push(frame.data.to_vec()); + vpxs.push(frame.data.to_vec()); } for ref frame in encoder.flush().unwrap() { - vp9s.push(frame.data.to_vec()); + vpxs.push(frame.data.to_vec()); } } - assert_eq!(vp9s.len(), yuv_count); + assert_eq!(vpxs.len(), yuv_count); let mut decoder = VpxDecoder::new(VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, + codec: codec_id, num_threads: (num_cpus::get() / 2) as _, }) .unwrap(); let start = Instant::now(); - for vp9 in vp9s { - let _ = decoder.decode(&vp9); + for vpx in vpxs { + let _ = decoder.decode(&vpx); let _ = decoder.flush(); } - println!("vp9 decode: {:?}", start.elapsed() / yuv_count as _); + println!( + "{:?} decode: {:?}", + codec_id, + start.elapsed() / yuv_count as _ + ); } #[cfg(feature = "hwcodec")] @@ -267,7 +283,7 @@ mod hw { Some(info.clone()) == best.h264 || Some(info.clone()) == best.h265 } - pub fn vp9_yuv_to_hw_yuv( + pub fn vpx_yuv_to_hw_yuv( yuvs: Vec>, width: usize, height: usize, diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index 8daf8e4bb..36d6a8a9b 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -50,6 +50,7 @@ impl crate::TraitCapturer for Capturer { pub enum Frame<'a> { RAW(&'a [u8]), + VP8(&'a [u8]), VP9(&'a [u8]), Empty, } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 9e4b6fce4..3209933b4 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -1,7 +1,6 @@ -use std::ops::{Deref, DerefMut}; -#[cfg(feature = "hwcodec")] use std::{ collections::HashMap, + ops::{Deref, DerefMut}, sync::{Arc, Mutex}, }; @@ -11,30 +10,31 @@ use crate::hwcodec::*; use crate::mediacodec::{ MediaCodecDecoder, MediaCodecDecoders, H264_DECODER_SUPPORT, H265_DECODER_SUPPORT, }; -use crate::{vpxcodec::*, ImageFormat}; +use crate::{vpxcodec::*, CodecName, ImageFormat}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::sysinfo::{System, SystemExt}; use hbb_common::{ anyhow::anyhow, + config::PeerConfig, log, - message_proto::{video_frame, EncodedVideoFrames, Message, VideoCodecState}, + message_proto::{ + supported_decoding::PreferCodec, video_frame, EncodedVideoFrames, Message, + SupportedDecoding, SupportedEncoding, + }, ResultType, }; #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] -use hbb_common::{ - config::{Config2, PeerConfig}, - lazy_static, - message_proto::video_codec_state::PreferCodec, -}; +use hbb_common::{config::Config2, lazy_static}; -#[cfg(feature = "hwcodec")] lazy_static::lazy_static! { - static ref PEER_DECODER_STATES: Arc>> = Default::default(); + static ref PEER_DECODINGS: Arc>> = Default::default(); + static ref CODEC_NAME: Arc> = Arc::new(Mutex::new(CodecName::VP9)); } -const SCORE_VPX: i32 = 90; #[derive(Debug, Clone)] pub struct HwEncoderConfig { - pub codec_name: String, + pub name: String, pub width: usize, pub height: usize, pub bitrate: i32, @@ -58,10 +58,6 @@ pub trait EncoderApi { fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()>; } -pub struct DecoderCfg { - pub vpx: VpxDecoderConfig, -} - pub struct Encoder { pub codec: Box, } @@ -81,7 +77,8 @@ impl DerefMut for Encoder { } pub struct Decoder { - vpx: VpxDecoder, + vp8: VpxDecoder, + vp9: VpxDecoder, #[cfg(feature = "hwcodec")] hw: HwDecoders, #[cfg(feature = "hwcodec")] @@ -91,10 +88,10 @@ pub struct Decoder { } #[derive(Debug, Clone)] -pub enum EncoderUpdate { - State(VideoCodecState), +pub enum EncodingUpdate { + New(SupportedDecoding), Remove, - DisableHwIfNotExist, + NewOnlyVP9, } impl Encoder { @@ -120,172 +117,156 @@ impl Encoder { } } - // TODO - pub fn update_video_encoder(id: i32, update: EncoderUpdate) { + pub fn update(id: i32, update: EncodingUpdate) { + let mut decodings = PEER_DECODINGS.lock().unwrap(); + match update { + EncodingUpdate::New(decoding) => { + decodings.insert(id, decoding); + } + EncodingUpdate::Remove => { + decodings.remove(&id); + } + EncodingUpdate::NewOnlyVP9 => { + decodings.insert( + id, + SupportedDecoding { + ability_vp9: 1, + ..Default::default() + }, + ); + } + } + + let vp8_useable = decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_vp8 > 0); + #[allow(unused_mut)] + let mut h264_name = None; + #[allow(unused_mut)] + let mut h265_name = None; #[cfg(feature = "hwcodec")] { - let mut states = PEER_DECODER_STATES.lock().unwrap(); - match update { - EncoderUpdate::State(state) => { - states.insert(id, state); - } - EncoderUpdate::Remove => { - states.remove(&id); - } - EncoderUpdate::DisableHwIfNotExist => { - if !states.contains_key(&id) { - states.insert(id, VideoCodecState::default()); - } - } + let best = HwEncoder::best(); + let h264_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); + let h265_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); + if h264_useable { + h264_name = best.h264.map_or(None, |c| Some(c.name)); } - let name = HwEncoder::current_name(); - if states.len() > 0 { - let best = HwEncoder::best(); - let enabled_h264 = best.h264.is_some() - && states.len() > 0 - && states.iter().all(|(_, s)| s.score_h264 > 0); - let enabled_h265 = best.h265.is_some() - && states.len() > 0 - && states.iter().all(|(_, s)| s.score_h265 > 0); - - // Preference first - let mut preference = PreferCodec::Auto; - let preferences: Vec<_> = states - .iter() - .filter(|(_, s)| { - s.prefer == PreferCodec::VPX.into() - || s.prefer == PreferCodec::H264.into() && enabled_h264 - || s.prefer == PreferCodec::H265.into() && enabled_h265 - }) - .map(|(_, s)| s.prefer) - .collect(); - if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { - preference = preferences[0].enum_value_or(PreferCodec::Auto); - } - - match preference { - PreferCodec::VPX => *name.lock().unwrap() = None, - PreferCodec::H264 => { - *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)) - } - PreferCodec::H265 => { - *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)) - } - PreferCodec::Auto => { - // score encoder - let mut score_vpx = SCORE_VPX; - let mut score_h264 = best.h264.as_ref().map_or(0, |c| c.score); - let mut score_h265 = best.h265.as_ref().map_or(0, |c| c.score); - - // score decoder - score_vpx += states.iter().map(|s| s.1.score_vpx).sum::(); - if enabled_h264 { - score_h264 += states.iter().map(|s| s.1.score_h264).sum::(); - } - if enabled_h265 { - score_h265 += states.iter().map(|s| s.1.score_h265).sum::(); - } - - if enabled_h265 && score_h265 >= score_vpx && score_h265 >= score_h264 { - *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)); - } else if enabled_h264 - && score_h264 >= score_vpx - && score_h264 >= score_h265 - { - *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)); - } else { - *name.lock().unwrap() = None; - } - } - } - - log::info!( - "connection count:{}, used preference:{:?}, encoder:{:?}", - states.len(), - preference, - name.lock().unwrap() - ) - } else { - *name.lock().unwrap() = None; + if h265_useable { + h265_name = best.h265.map_or(None, |c| Some(c.name)); } } - #[cfg(not(feature = "hwcodec"))] - { - let _ = id; - let _ = update; + + let mut name = CODEC_NAME.lock().unwrap(); + let mut preference = PreferCodec::Auto; + let preferences: Vec<_> = decodings + .iter() + .filter(|(_, s)| { + s.prefer == PreferCodec::VP9.into() + || s.prefer == PreferCodec::VP8.into() && vp8_useable + || s.prefer == PreferCodec::H264.into() && h264_name.is_some() + || s.prefer == PreferCodec::H265.into() && h265_name.is_some() + }) + .map(|(_, s)| s.prefer) + .collect(); + if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { + preference = preferences[0].enum_value_or(PreferCodec::Auto); } + + #[allow(unused_mut)] + let mut auto_codec = CodecName::VP9; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if vp8_useable && System::new_all().total_memory() <= 4 * 1024 * 1024 * 1024 { + // 4 Gb + auto_codec = CodecName::VP8 + } + + match preference { + PreferCodec::VP8 => *name = CodecName::VP8, + PreferCodec::VP9 => *name = CodecName::VP9, + PreferCodec::H264 => *name = h264_name.map_or(auto_codec, |c| CodecName::H264(c)), + PreferCodec::H265 => *name = h265_name.map_or(auto_codec, |c| CodecName::H265(c)), + PreferCodec::Auto => *name = auto_codec, + } + + log::info!( + "connection count:{}, used preference:{:?}, encoder:{:?}", + decodings.len(), + preference, + *name + ) } + #[inline] - pub fn current_hw_encoder_name() -> Option { - #[cfg(feature = "hwcodec")] - if enable_hwcodec_option() { - return HwEncoder::current_name().lock().unwrap().clone(); - } else { - return None; - } - #[cfg(not(feature = "hwcodec"))] - return None; + pub fn negotiated_codec() -> CodecName { + CODEC_NAME.lock().unwrap().clone() } - pub fn supported_encoding() -> (bool, bool) { + pub fn supported_encoding() -> SupportedEncoding { + #[allow(unused_mut)] + let mut encoding = SupportedEncoding { + vp8: true, + ..Default::default() + }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwEncoder::best(); - ( - best.h264.as_ref().map_or(false, |c| c.score > 0), - best.h265.as_ref().map_or(false, |c| c.score > 0), - ) - } else { - (false, false) + encoding.h264 = best.h264.is_some(); + encoding.h265 = best.h265.is_some(); } - #[cfg(not(feature = "hwcodec"))] - (false, false) + encoding } } impl Decoder { - pub fn video_codec_state(_id: &str) -> VideoCodecState { + pub fn supported_decodings(id_for_perfer: Option<&str>) -> SupportedDecoding { + #[allow(unused_mut)] + let mut decoding = SupportedDecoding { + ability_vp8: 1, + ability_vp9: 1, + prefer: id_for_perfer + .map_or(PreferCodec::Auto, |id| Self::codec_preference(id)) + .into(), + ..Default::default() + }; #[cfg(feature = "hwcodec")] if enable_hwcodec_option() { let best = HwDecoder::best(); - return VideoCodecState { - score_vpx: SCORE_VPX, - score_h264: best.h264.map_or(0, |c| c.score), - score_h265: best.h265.map_or(0, |c| c.score), - prefer: Self::codec_preference(_id).into(), - ..Default::default() - }; + decoding.ability_h264 = if best.h264.is_some() { 1 } else { 0 }; + decoding.ability_h265 = if best.h265.is_some() { 1 } else { 0 }; } #[cfg(feature = "mediacodec")] if enable_hwcodec_option() { - let score_h264 = if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 92 - } else { - 0 - }; - let score_h265 = if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { - 94 - } else { - 0 - }; - return VideoCodecState { - score_vpx: SCORE_VPX, - score_h264, - score_h265, - prefer: Self::codec_preference(_id).into(), - ..Default::default() - }; - } - VideoCodecState { - score_vpx: SCORE_VPX, - ..Default::default() + decoding.ability_h264 = + if H264_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 1 + } else { + 0 + }; + decoding.ability_h265 = + if H265_DECODER_SUPPORT.load(std::sync::atomic::Ordering::SeqCst) { + 1 + } else { + 0 + }; } + decoding } - pub fn new(config: DecoderCfg) -> Decoder { - let vpx = VpxDecoder::new(config.vpx).unwrap(); + pub fn new() -> Decoder { + let vp8 = VpxDecoder::new(VpxDecoderConfig { + codec: VpxVideoCodecId::VP8, + num_threads: (num_cpus::get() / 2) as _, + }) + .unwrap(); + let vp9 = VpxDecoder::new(VpxDecoderConfig { + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }) + .unwrap(); Decoder { - vpx, + vp8, + vp9, #[cfg(feature = "hwcodec")] hw: if enable_hwcodec_option() { HwDecoder::new_decoders() @@ -310,8 +291,11 @@ impl Decoder { rgb: &mut Vec, ) -> ResultType { match frame { + video_frame::Union::Vp8s(vp8s) => { + Decoder::handle_vpxs_video_frame(&mut self.vp8, vp8s, fmt, rgb) + } video_frame::Union::Vp9s(vp9s) => { - Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, fmt, rgb) + Decoder::handle_vpxs_video_frame(&mut self.vp9, vp9s, fmt, rgb) } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { @@ -349,15 +333,15 @@ impl Decoder { } } - fn handle_vp9s_video_frame( + fn handle_vpxs_video_frame( decoder: &mut VpxDecoder, - vp9s: &EncodedVideoFrames, + vpxs: &EncodedVideoFrames, fmt: (ImageFormat, usize), rgb: &mut Vec, ) -> ResultType { let mut last_frame = Image::new(); - for vp9 in vp9s.frames.iter() { - for frame in decoder.decode(&vp9.data)? { + for vpx in vpxs.frames.iter() { + for frame in decoder.decode(&vpx.data)? { drop(last_frame); last_frame = frame; } @@ -408,14 +392,15 @@ impl Decoder { return Ok(false); } - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] fn codec_preference(id: &str) -> PreferCodec { let codec = PeerConfig::load(id) .options .get("codec-preference") .map_or("".to_owned(), |c| c.to_owned()); - if codec == "vp9" { - PreferCodec::VPX + if codec == "vp8" { + PreferCodec::VP8 + } else if codec == "vp9" { + PreferCodec::VP9 } else if codec == "h264" { PreferCodec::H264 } else if codec == "h265" { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index 2c69774fb..f4de4bf84 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -7,7 +7,7 @@ use hbb_common::{ anyhow::{anyhow, Context}, bytes::Bytes, config::HwCodecConfig, - lazy_static, log, + log, message_proto::{EncodedVideoFrame, EncodedVideoFrames, Message, VideoFrame}, ResultType, }; @@ -19,11 +19,6 @@ use hwcodec::{ Quality::{self, *}, RateControl::{self, *}, }; -use std::sync::{Arc, Mutex}; - -lazy_static::lazy_static! { - static ref HW_ENCODER_NAME: Arc>> = Default::default(); -} const CFG_KEY_ENCODER: &str = "bestHwEncoders"; const CFG_KEY_DECODER: &str = "bestHwDecoders"; @@ -49,7 +44,7 @@ impl EncoderApi for HwEncoder { match cfg { EncoderCfg::HW(config) => { let ctx = EncodeContext { - name: config.codec_name.clone(), + name: config.name.clone(), width: config.width as _, height: config.height as _, pixfmt: DEFAULT_PIXFMT, @@ -60,12 +55,12 @@ impl EncoderApi for HwEncoder { quality: DEFAULT_HW_QUALITY, rc: DEFAULT_RC, }; - let format = match Encoder::format_from_name(config.codec_name.clone()) { + let format = match Encoder::format_from_name(config.name.clone()) { Ok(format) => format, Err(_) => { return Err(anyhow!(format!( "failed to get format from name:{}", - config.codec_name + config.name ))) } }; @@ -133,10 +128,6 @@ impl HwEncoder { }) } - pub fn current_name() -> Arc>> { - HW_ENCODER_NAME.clone() - } - pub fn encode(&mut self, bgra: &[u8]) -> ResultType> { match self.pixfmt { AVPixelFormat::AV_PIX_FMT_YUV420P => hw::hw_bgra_to_i420( diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 0ad158cca..26b946401 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -1,4 +1,5 @@ pub use self::vpxcodec::*; +use hbb_common::message_proto::{video_frame, VideoFrame}; cfg_if! { if #[cfg(quartz)] { @@ -92,3 +93,55 @@ pub fn is_cursor_embedded() -> bool { pub fn is_cursor_embedded() -> bool { false } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum CodecName { + VP8, + VP9, + H264(String), + H265(String), +} + +#[derive(PartialEq, Debug, Clone)] +pub enum CodecFormat { + VP8, + VP9, + H264, + H265, + Unknown, +} + +impl From<&VideoFrame> for CodecFormat { + fn from(it: &VideoFrame) -> Self { + match it.union { + Some(video_frame::Union::Vp8s(_)) => CodecFormat::VP8, + Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, + Some(video_frame::Union::H264s(_)) => CodecFormat::H264, + Some(video_frame::Union::H265s(_)) => CodecFormat::H265, + _ => CodecFormat::Unknown, + } + } +} + +impl From<&CodecName> for CodecFormat { + fn from(value: &CodecName) -> Self { + match value { + CodecName::VP8 => Self::VP8, + CodecName::VP9 => Self::VP9, + CodecName::H264(_) => Self::H264, + CodecName::H265(_) => Self::H265, + } + } +} + +impl ToString for CodecFormat { + fn to_string(&self) -> String { + match self { + CodecFormat::VP8 => "VP8".into(), + CodecFormat::VP9 => "VP9".into(), + CodecFormat::H264 => "H264".into(), + CodecFormat::H265 => "H265".into(), + CodecFormat::Unknown => "Unknow".into(), + } + } +} diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 9f38f2d6a..9de70ae14 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -1,3 +1,4 @@ +use crate::CodecFormat; #[cfg(feature = "hwcodec")] use hbb_common::anyhow::anyhow; use hbb_common::{ @@ -21,13 +22,6 @@ use webm::mux::{self, Segment, Track, VideoTrack, Writer}; const MIN_SECS: u64 = 1; -#[derive(Debug, Clone, PartialEq)] -pub enum RecordCodecID { - VP9, - H264, - H265, -} - #[derive(Debug, Clone)] pub struct RecorderContext { pub server: bool, @@ -36,7 +30,7 @@ pub struct RecorderContext { pub filename: String, pub width: usize, pub height: usize, - pub codec_id: RecordCodecID, + pub format: CodecFormat, pub tx: Option>, } @@ -55,8 +49,9 @@ impl RecorderContext { } let file = if self.server { "s" } else { "c" }.to_string() + &self.id.clone() - + &chrono::Local::now().format("_%Y%m%d%H%M%S").to_string() - + if self.codec_id == RecordCodecID::VP9 { + + &chrono::Local::now().format("_%Y%m%d%H%M%S_").to_string() + + &self.format.to_string() + + if self.format == CodecFormat::VP9 || self.format == CodecFormat::VP8 { ".webm" } else { ".mp4" @@ -107,8 +102,8 @@ impl DerefMut for Recorder { impl Recorder { pub fn new(mut ctx: RecorderContext) -> ResultType { ctx.set_filename()?; - let recorder = match ctx.codec_id { - RecordCodecID::VP9 => Recorder { + let recorder = match ctx.format { + CodecFormat::VP8 | CodecFormat::VP9 => Recorder { inner: Box::new(WebmRecorder::new(ctx.clone())?), ctx, }, @@ -126,8 +121,8 @@ impl Recorder { fn change(&mut self, mut ctx: RecorderContext) -> ResultType<()> { ctx.set_filename()?; - self.inner = match ctx.codec_id { - RecordCodecID::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), + self.inner = match ctx.format { + CodecFormat::VP8 | CodecFormat::VP9 => Box::new(WebmRecorder::new(ctx.clone())?), #[cfg(feature = "hwcodec")] _ => Box::new(HwRecorder::new(ctx.clone())?), #[cfg(not(feature = "hwcodec"))] @@ -148,10 +143,19 @@ impl Recorder { pub fn write_frame(&mut self, frame: &video_frame::Union) -> ResultType<()> { match frame { - video_frame::Union::Vp9s(vp9s) => { - if self.ctx.codec_id != RecordCodecID::VP9 { + video_frame::Union::Vp8s(vp8s) => { + if self.ctx.format != CodecFormat::VP8 { self.change(RecorderContext { - codec_id: RecordCodecID::VP9, + format: CodecFormat::VP8, + ..self.ctx.clone() + })?; + } + vp8s.frames.iter().map(|f| self.write_video(f)).count(); + } + video_frame::Union::Vp9s(vp9s) => { + if self.ctx.format != CodecFormat::VP9 { + self.change(RecorderContext { + format: CodecFormat::VP9, ..self.ctx.clone() })?; } @@ -159,25 +163,25 @@ impl Recorder { } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { - if self.ctx.codec_id != RecordCodecID::H264 { + if self.ctx.format != CodecFormat::H264 { self.change(RecorderContext { - codec_id: RecordCodecID::H264, + format: CodecFormat::H264, ..self.ctx.clone() })?; } - if self.ctx.codec_id == RecordCodecID::H264 { + if self.ctx.format == CodecFormat::H264 { h264s.frames.iter().map(|f| self.write_video(f)).count(); } } #[cfg(feature = "hwcodec")] video_frame::Union::H265s(h265s) => { - if self.ctx.codec_id != RecordCodecID::H265 { + if self.ctx.format != CodecFormat::H265 { self.change(RecorderContext { - codec_id: RecordCodecID::H265, + format: CodecFormat::H265, ..self.ctx.clone() })?; } - if self.ctx.codec_id == RecordCodecID::H265 { + if self.ctx.format == CodecFormat::H265 { h265s.frames.iter().map(|f| self.write_video(f)).count(); } } @@ -221,7 +225,11 @@ impl RecorderApi for WebmRecorder { ctx.width as _, ctx.height as _, None, - mux::VideoCodecId::VP9, + if ctx.format == CodecFormat::VP9 { + mux::VideoCodecId::VP9 + } else { + mux::VideoCodecId::VP8 + }, ); Ok(WebmRecorder { vt, @@ -279,7 +287,7 @@ impl RecorderApi for HwRecorder { filename: ctx.filename.clone(), width: ctx.width, height: ctx.height, - is265: ctx.codec_id == RecordCodecID::H265, + is265: ctx.format == CodecFormat::H265, framerate: crate::hwcodec::DEFAULT_TIME_BASE[1] as _, }) .map_err(|_| anyhow!("Failed to create hardware muxer"))?; diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 3df9c0461..4820ea171 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -30,6 +30,7 @@ pub struct VpxEncoder { ctx: vpx_codec_ctx_t, width: usize, height: usize, + id: VpxVideoCodecId, } pub struct VpxDecoder { @@ -97,15 +98,10 @@ impl EncoderApi for VpxEncoder { { match cfg { crate::codec::EncoderCfg::VPX(config) => { - let i; - if cfg!(feature = "VP8") { - i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), - }; - } else { - i = call_vpx_ptr!(vpx_codec_vp9_cx()); - } + let i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_cx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_cx()), + }; let mut c = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; call_vpx!(vpx_codec_enc_config_default(i, &mut c, 0)); @@ -187,12 +183,17 @@ impl EncoderApi for VpxEncoder { VP9E_SET_TILE_COLUMNS as _, 4 as c_int )); + } else if config.codec == VpxVideoCodecId::VP8 { + // https://github.com/webmproject/libvpx/blob/972149cafeb71d6f08df89e91a0130d6a38c4b15/vpx/vp8cx.h#L172 + // https://groups.google.com/a/webmproject.org/g/webm-discuss/c/DJhSrmfQ61M + call_vpx!(vpx_codec_control_(&mut ctx, VP8E_SET_CPUUSED as _, 12,)); } Ok(Self { ctx, width: config.width as _, height: config.height as _, + id: config.codec, }) } _ => Err(anyhow!("encoder type mismatch")), @@ -213,7 +214,7 @@ impl EncoderApi for VpxEncoder { // to-do: flush periodically, e.g. 1 second if frames.len() > 0 { - Ok(VpxEncoder::create_msg(frames)) + Ok(VpxEncoder::create_msg(self.id, frames)) } else { Err(anyhow!("no valid frame")) } @@ -280,13 +281,17 @@ impl VpxEncoder { } #[inline] - fn create_msg(vp9s: Vec) -> Message { + pub fn create_msg(codec_id: VpxVideoCodecId, frames: Vec) -> Message { let mut msg_out = Message::new(); let mut vf = VideoFrame::new(); - vf.set_vp9s(EncodedVideoFrames { - frames: vp9s.into(), + let vpxs = EncodedVideoFrames { + frames: frames.into(), ..Default::default() - }); + }; + match codec_id { + VpxVideoCodecId::VP8 => vf.set_vp8s(vpxs), + VpxVideoCodecId::VP9 => vf.set_vp9s(vpxs), + } msg_out.set_video_frame(vf); msg_out } @@ -382,15 +387,10 @@ impl VpxDecoder { pub fn new(config: VpxDecoderConfig) -> Result { // This is sound because `vpx_codec_ctx` is a repr(C) struct without any field that can // cause UB if uninitialized. - let i; - if cfg!(feature = "VP8") { - i = match config.codec { - VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), - VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), - }; - } else { - i = call_vpx_ptr!(vpx_codec_vp9_dx()); - } + let i = match config.codec { + VpxVideoCodecId::VP8 => call_vpx_ptr!(vpx_codec_vp8_dx()), + VpxVideoCodecId::VP9 => call_vpx_ptr!(vpx_codec_vp9_dx()), + }; let mut ctx = Default::default(); let cfg = vpx_codec_dec_cfg_t { threads: if config.num_threads == 0 { diff --git a/src/client.rs b/src/client.rs index 2f745b70c..ae93ccfcf 100644 --- a/src/client.rs +++ b/src/client.rs @@ -44,9 +44,9 @@ use hbb_common::{ }; pub use helper::*; use scrap::{ - codec::{Decoder, DecoderCfg}, + codec::Decoder, record::{Recorder, RecorderContext}, - ImageFormat, VpxDecoderConfig, VpxVideoCodecId, + ImageFormat, }; use crate::{ @@ -917,12 +917,7 @@ impl VideoHandler { /// Create a new video handler. pub fn new() -> Self { VideoHandler { - decoder: Decoder::new(DecoderCfg { - vpx: VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }, - }), + decoder: Decoder::new(), rgb: Default::default(), recorder: Default::default(), record: false, @@ -954,12 +949,7 @@ impl VideoHandler { /// Reset the decoder. pub fn reset(&mut self) { - self.decoder = Decoder::new(DecoderCfg { - vpx: VpxDecoderConfig { - codec: VpxVideoCodecId::VP9, - num_threads: 1, - }, - }); + self.decoder = Decoder::new(); } /// Start or stop screen record. @@ -973,7 +963,7 @@ impl VideoHandler { filename: "".to_owned(), width: w as _, height: h as _, - codec_id: scrap::record::RecordCodecID::VP9, + format: scrap::CodecFormat::VP9, tx: None, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))); @@ -999,7 +989,7 @@ pub struct LoginConfigHandler { pub conn_id: i32, features: Option, session_id: u64, - pub supported_encoding: Option<(bool, bool)>, + pub supported_encoding: SupportedEncoding, pub restarting_remote_device: bool, pub force_relay: bool, pub direct: Option, @@ -1047,7 +1037,7 @@ impl LoginConfigHandler { self.remember = !config.password.is_empty(); self.config = config; self.session_id = rand::random(); - self.supported_encoding = None; + self.supported_encoding = Default::default(); self.restarting_remote_device = false; self.force_relay = !self.get_option("force-always-relay").is_empty() || force_relay; self.direct = None; @@ -1331,8 +1321,8 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } - let state = Decoder::video_codec_state(&self.id); - msg.video_codec_state = hbb_common::protobuf::MessageField::some(state); + msg.supported_decoding = + hbb_common::protobuf::MessageField::some(Decoder::supported_decodings(Some(&self.id))); n += 1; if n > 0 { @@ -1565,10 +1555,7 @@ impl LoginConfigHandler { self.conn_id = pi.conn_id; // no matter if change, for update file time self.save_config(config); - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - { - self.supported_encoding = Some((pi.encoding.h264, pi.encoding.h265)); - } + self.supported_encoding = pi.encoding.clone().unwrap_or_default(); } pub fn get_remote_dir(&self) -> String { @@ -1626,10 +1613,10 @@ impl LoginConfigHandler { } pub fn change_prefer_codec(&self) -> Message { - let state = scrap::codec::Decoder::video_codec_state(&self.id); + let decoding = scrap::codec::Decoder::supported_decodings(Some(&self.id)); let mut misc = Misc::new(); misc.set_option(OptionMessage { - video_codec_state: hbb_common::protobuf::MessageField::some(state), + supported_decoding: hbb_common::protobuf::MessageField::some(decoding), ..Default::default() }); let mut msg_out = Message::new(); diff --git a/src/client/helper.rs b/src/client/helper.rs index a9696a8e8..61844d908 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -1,37 +1,8 @@ use hbb_common::{ get_time, - message_proto::{video_frame, Message, VideoFrame, VoiceCallRequest, VoiceCallResponse}, + message_proto::{Message, VoiceCallRequest, VoiceCallResponse}, }; - -#[derive(PartialEq, Debug, Clone)] -pub enum CodecFormat { - VP9, - H264, - H265, - Unknown, -} - -impl From<&VideoFrame> for CodecFormat { - fn from(it: &VideoFrame) -> Self { - match it.union { - Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, - Some(video_frame::Union::H264s(_)) => CodecFormat::H264, - Some(video_frame::Union::H265s(_)) => CodecFormat::H265, - _ => CodecFormat::Unknown, - } - } -} - -impl ToString for CodecFormat { - fn to_string(&self) -> String { - match self { - CodecFormat::VP9 => "VP9".into(), - CodecFormat::H264 => "H264".into(), - CodecFormat::H265 => "H265".into(), - CodecFormat::Unknown => "Unknow".into(), - } - } -} +use scrap::CodecFormat; #[derive(Debug, Default)] pub struct QualityStatus { diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b0bddc82e..0f662e015 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -29,10 +29,10 @@ use hbb_common::tokio::{ time::{self, Duration, Instant, Interval}, }; use hbb_common::{allow_err, fs, get_time, log, message_proto::*, Stream}; +use scrap::CodecFormat; use crate::client::{ - new_voice_call_request, Client, CodecFormat, MediaData, MediaSender, QualityStatus, MILLI1, - SEC30, + new_voice_call_request, Client, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{self, update_clipboard}; @@ -817,11 +817,10 @@ impl Remote { } fn contains_key_frame(vf: &VideoFrame) -> bool { + use video_frame::Union::*; match &vf.union { Some(vf) => match vf { - video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key), - video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key), - video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key), + Vp8s(f) | Vp9s(f) | H264s(f) | H265s(f) => f.frames.iter().any(|e| e.key), _ => false, }, None => false, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index aee486b94..1d965ebc5 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -969,6 +969,13 @@ pub fn main_has_hwcodec() -> SyncReturn { SyncReturn(has_hwcodec()) } +pub fn main_supported_hwdecodings() -> SyncReturn { + let decoding = supported_hwdecodings(); + let msg = HashMap::from([("h264", decoding.0), ("h265", decoding.1)]); + + SyncReturn(serde_json::ser::to_string(&msg).unwrap_or("".to_owned())) +} + pub fn main_is_root() -> bool { is_root() } @@ -1054,10 +1061,10 @@ pub fn session_send_note(id: String, note: String) { } } -pub fn session_supported_hwcodec(id: String) -> String { +pub fn session_alternative_codecs(id: String) -> String { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - let (h264, h265) = session.supported_hwcodec(); - let msg = HashMap::from([("h264", h264), ("h265", h265)]); + let (vp8, h264, h265) = session.alternative_codecs(); + let msg = HashMap::from([("vp8", vp8), ("h264", h264), ("h265", h265)]); serde_json::ser::to_string(&msg).unwrap_or("".to_owned()) } else { String::new() diff --git a/src/server/connection.rs b/src/server/connection.rs index 23e166fcf..d1ae4d6a3 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -536,7 +536,7 @@ impl Connection { let _ = privacy_mode::turn_off_privacy(0); } video_service::notify_video_frame_fetched(id, None); - scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); + scrap::codec::Encoder::update(id, scrap::codec::EncodingUpdate::Remove); video_service::VIDEO_QOS.lock().unwrap().reset(); if conn.authorized { password::update_temporary_password(); @@ -862,16 +862,7 @@ impl Connection { } } - #[cfg(feature = "hwcodec")] - { - let (h264, h265) = scrap::codec::Encoder::supported_encoding(); - pi.encoding = Some(SupportedEncoding { - h264, - h265, - ..Default::default() - }) - .into(); - } + pi.encoding = Some(scrap::codec::Encoder::supported_encoding()).into(); if self.port_forward_socket.is_some() { let mut msg_out = Message::new(); @@ -1147,21 +1138,21 @@ impl Connection { self.lr = lr.clone(); if let Some(o) = lr.option.as_ref() { self.options_in_login = Some(o.clone()); - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( + if let Some(q) = o.supported_decoding.clone().take() { + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::State(q), + scrap::codec::EncodingUpdate::New(q), ); } else { - scrap::codec::Encoder::update_video_encoder( + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, + scrap::codec::EncodingUpdate::NewOnlyVP9, ); } } else { - scrap::codec::Encoder::update_video_encoder( + scrap::codec::Encoder::update( self.inner.id(), - scrap::codec::EncoderUpdate::DisableHwIfNotExist, + scrap::codec::EncodingUpdate::NewOnlyVP9, ); } self.video_ack_required = lr.video_ack_required; @@ -1784,11 +1775,8 @@ impl Connection { .unwrap() .update_user_fps(o.custom_fps as _); } - if let Some(q) = o.video_codec_state.clone().take() { - scrap::codec::Encoder::update_video_encoder( - self.inner.id(), - scrap::codec::EncoderUpdate::State(q), - ); + if let Some(q) = o.supported_decoding.clone().take() { + scrap::codec::Encoder::update(self.inner.id(), scrap::codec::EncodingUpdate::New(q)); } if let Ok(q) = o.lock_after_session_end.enum_value() { if q != BoolOption::NotSet { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 205d0584c..691ca4abe 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,7 +31,7 @@ use scrap::{ codec::{Encoder, EncoderCfg, HwEncoderConfig}, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - Display, TraitCapturer, + CodecName, Display, TraitCapturer, }; #[cfg(windows)] use std::sync::Once; @@ -468,21 +468,29 @@ fn run(sp: GenericService) -> ResultType<()> { drop(video_qos); log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); - let encoder_cfg = match Encoder::current_hw_encoder_name() { - Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { - codec_name, - width: c.width, - height: c.height, - bitrate: bitrate as _, - }), - None => EncoderCfg::VPX(VpxEncoderConfig { - width: c.width as _, - height: c.height as _, - timebase: [1, 1000], // Output timestamp precision - bitrate, - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }), + let encoder_cfg = match Encoder::negotiated_codec() { + scrap::CodecName::H264(name) | scrap::CodecName::H265(name) => { + EncoderCfg::HW(HwEncoderConfig { + name, + width: c.width, + height: c.height, + bitrate: bitrate as _, + }) + } + name @ (scrap::CodecName::VP8 | scrap::CodecName::VP9) => { + EncoderCfg::VPX(VpxEncoderConfig { + width: c.width as _, + height: c.height as _, + timebase: [1, 1000], // Output timestamp precision + bitrate, + codec: if name == scrap::CodecName::VP8 { + VpxVideoCodecId::VP8 + } else { + VpxVideoCodecId::VP9 + }, + num_threads: (num_cpus::get() / 2) as _, + }) + } }; let mut encoder; @@ -526,7 +534,7 @@ fn run(sp: GenericService) -> ResultType<()> { let mut try_gdi = 1; #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); - let codec_name = Encoder::current_hw_encoder_name(); + let codec_name = Encoder::negotiated_codec(); let recorder = get_recorder(c.width, c.height, &codec_name); #[cfg(windows)] start_uac_elevation_check(); @@ -557,7 +565,7 @@ fn run(sp: GenericService) -> ResultType<()> { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } - if codec_name != Encoder::current_hw_encoder_name() { + if codec_name != Encoder::negotiated_codec() { bail!("SWITCH"); } #[cfg(windows)] @@ -603,8 +611,14 @@ fn run(sp: GenericService) -> ResultType<()> { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; match frame { + scrap::Frame::VP8(data) => { + let send_conn_ids = + handle_one_frame_encoded(VpxVideoCodecId::VP8, &sp, data, ms)?; + frame_controller.set_send(now, send_conn_ids); + } scrap::Frame::VP9(data) => { - let send_conn_ids = handle_one_frame_encoded(&sp, data, ms)?; + let send_conn_ids = + handle_one_frame_encoded(VpxVideoCodecId::VP9, &sp, data, ms)?; frame_controller.set_send(now, send_conn_ids); } scrap::Frame::RAW(data) => { @@ -717,12 +731,11 @@ fn run(sp: GenericService) -> ResultType<()> { fn get_recorder( width: usize, height: usize, - codec_name: &Option, + codec_name: &CodecName, ) -> Arc>> { #[cfg(not(target_os = "ios"))] let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() { use crate::hbbs_http::record_upload; - use scrap::record::RecordCodecID::*; let tx = if record_upload::is_enable() { let (tx, rx) = std::sync::mpsc::channel(); @@ -731,16 +744,6 @@ fn get_recorder( } else { None }; - let codec_id = match codec_name { - Some(name) => { - if name.contains("264") { - H264 - } else { - H265 - } - } - None => VP9, - }; Recorder::new(RecorderContext { server: true, id: Config::get_id(), @@ -748,7 +751,7 @@ fn get_recorder( filename: "".to_owned(), width, height, - codec_id, + format: codec_name.into(), tx, }) .map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r)))) @@ -775,19 +778,6 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu Ok(()) } -#[inline] -#[cfg(any(target_os = "android", target_os = "ios"))] -fn create_msg(vp9s: Vec) -> Message { - let mut msg_out = Message::new(); - let mut vf = VideoFrame::new(); - vf.set_vp9s(EncodedVideoFrames { - frames: vp9s.into(), - ..Default::default() - }); - msg_out.set_video_frame(vf); - msg_out -} - #[inline] fn handle_one_frame( sp: &GenericService, @@ -820,6 +810,7 @@ fn handle_one_frame( #[inline] #[cfg(any(target_os = "android", target_os = "ios"))] pub fn handle_one_frame_encoded( + codec: VpxVideoCodecId, sp: &GenericService, frame: &[u8], ms: i64, @@ -831,13 +822,13 @@ pub fn handle_one_frame_encoded( } Ok(()) })?; - let vp9_frame = EncodedVideoFrame { + let vpx_frame = EncodedVideoFrame { data: frame.to_vec().into(), key: true, pts: ms, ..Default::default() }; - let send_conn_ids = sp.send_video_frame(create_msg(vec![vp9_frame])); + let send_conn_ids = sp.send_video_frame(scrap::VpxEncoder::create_msg(codec, vec![vpx_frame])); Ok(send_conn_ids) } diff --git a/src/ui/header.tis b/src/ui/header.tis index 257ba417e..af4f1e349 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -161,8 +161,8 @@ class Header: Reactor.Component { } function renderDisplayPop() { - var codecs = handler.supported_hwcodec(); - var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]); + var codecs = handler.alternative_codecs(); + var show_codec = codecs[0] || codecs[1] || codecs[2]; var cursor_embedded = false; if ((pi.displays || []).length > 0) { @@ -186,9 +186,10 @@ class Header: Reactor.Component { {show_codec ?
  • {svg_checkmark}Auto
  • + {codecs[0] ?
  • {svg_checkmark}VP8
  • : ""}
  • {svg_checkmark}VP9
  • - {codecs[0] ?
  • {svg_checkmark}H264
  • : ""} - {codecs[1] ?
  • {svg_checkmark}H265
  • : ""} + {codecs[1] ?
  • {svg_checkmark}H264
  • : ""} + {codecs[2] ?
  • {svg_checkmark}H265
  • : ""}
    : ""}
    {!cursor_embedded &&
  • {svg_checkmark}{translate('Show remote cursor')}
  • } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 68decf955..fc878cf1f 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -20,7 +20,6 @@ use hbb_common::{ use crate::{ client::*, - ui_interface::has_hwcodec, ui_session_interface::{InvokeUiSession, Session}, }; @@ -197,7 +196,14 @@ impl InvokeUiSession for SciterHandler { self.call("confirmDeleteFiles", &make_args!(id, i, name)); } - fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool, is_identical: bool) { + fn override_file_confirm( + &self, + id: i32, + file_num: i32, + to: String, + is_upload: bool, + is_identical: bool, + ) { self.call( "overrideFileConfirm", &make_args!(id, file_num, to, is_upload, is_identical), @@ -451,8 +457,7 @@ impl sciter::EventHandler for SciterSession { fn set_write_override(i32, i32, bool, bool, bool); fn get_keyboard_mode(); fn save_keyboard_mode(String); - fn has_hwcodec(); - fn supported_hwcodec(); + fn alternative_codecs(); fn change_prefer_codec(); fn restart_remote_device(); fn request_voice_call(); @@ -504,10 +509,6 @@ impl SciterSession { v } - fn has_hwcodec(&self) -> bool { - has_hwcodec() - } - pub fn t(&self, name: String) -> String { crate::client::translate(name) } @@ -516,9 +517,10 @@ impl SciterSession { super::get_icon() } - fn supported_hwcodec(&self) -> Value { - let (h264, h265) = self.0.supported_hwcodec(); + fn alternative_codecs(&self) -> Value { + let (vp8, h264, h265) = self.0.alternative_codecs(); let mut v = Value::array(0); + v.push(vp8); v.push(h264); v.push(h265); v diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 11357be4a..26d470fe0 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -752,6 +752,13 @@ pub fn has_hwcodec() -> bool { return true; } +#[cfg(feature = "flutter")] +#[inline] +pub fn supported_hwdecodings() -> (bool, bool) { + let decoding = scrap::codec::Decoder::supported_decodings(None); + (decoding.ability_h264 > 0, decoding.ability_h265 > 0) +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] #[inline] pub fn is_root() -> bool { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f89be4879..3dee89a6e 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -216,24 +216,16 @@ impl Session { true } - pub fn supported_hwcodec(&self) -> (bool, bool) { - #[cfg(any(feature = "hwcodec", feature = "mediacodec"))] - { - let decoder = scrap::codec::Decoder::video_codec_state(&self.id); - let mut h264 = decoder.score_h264 > 0; - let mut h265 = decoder.score_h265 > 0; - let (encoding_264, encoding_265) = self - .lc - .read() - .unwrap() - .supported_encoding - .unwrap_or_default(); - h264 = h264 && encoding_264; - h265 = h265 && encoding_265; - return (h264, h265); - } - #[allow(unreachable_code)] - (false, false) + pub fn alternative_codecs(&self) -> (bool, bool, bool) { + let decoder = scrap::codec::Decoder::supported_decodings(None); + let mut vp8 = decoder.ability_vp8 > 0; + let mut h264 = decoder.ability_h264 > 0; + let mut h265 = decoder.ability_h265 > 0; + let enc = &self.lc.read().unwrap().supported_encoding; + vp8 = vp8 && enc.vp8; + h264 = h264 && enc.h264; + h265 = h265 && enc.h265; + (vp8, h264, h265) } pub fn change_prefer_codec(&self) { From 10eddc139c3254d9183ede0c945b6ad8cf7af5f6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 22 Mar 2023 17:01:11 +0800 Subject: [PATCH 158/366] linux virtual display, init commit Signed-off-by: fufesou --- Cargo.lock | 54 ++ Cargo.toml | 2 + libs/hbb_common/protos/message.proto | 6 + libs/hbb_common/src/platform/linux.rs | 4 +- res/pam.d/rustdesk.debian | 5 + res/pam.d/rustdesk.suse | 5 + res/startwm.sh | 130 ++++ res/xorg.conf | 30 + src/cli.rs | 4 +- src/client.rs | 124 +++- src/client/io_loop.rs | 4 +- src/platform/linux.rs | 1 + src/platform/linux_desktop.rs | 951 ++++++++++++++++++++++++++ src/platform/mod.rs | 3 + src/port_forward.rs | 4 +- src/rendezvous_mediator.rs | 9 +- src/server.rs | 34 + src/server/connection.rs | 103 ++- src/server/service.rs | 2 + src/ui/common.css | 4 + src/ui/common.tis | 26 +- src/ui/msgbox.tis | 62 +- src/ui/remote.rs | 2 +- src/ui_session_interface.rs | 29 +- 24 files changed, 1544 insertions(+), 54 deletions(-) create mode 100644 res/pam.d/rustdesk.debian create mode 100644 res/pam.d/rustdesk.suse create mode 100755 res/startwm.sh create mode 100644 res/xorg.conf create mode 100644 src/platform/linux_desktop.rs diff --git a/Cargo.lock b/Cargo.lock index 5a4cad9f8..9d2984abe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4159,6 +4159,38 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "pam" +version = "0.7.0" +source = "git+https://github.com/fufesou/pam#10da2cbbabe32cbc9de22a66abe44738e7ec0ea0" +dependencies = [ + "libc", + "pam-macros", + "pam-sys", + "users 0.10.0", +] + +[[package]] +name = "pam-macros" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94f3b9b97df3c6d4e51a14916639b24e02c7d15d1dba686ce9b1118277cb811" +dependencies = [ + "proc-macro2 1.0.54", + "quote 1.0.26", + "syn 1.0.109", +] + +[[package]] +name = "pam-sys" +version = "1.0.0-alpha4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9dfd42858f6a6bb1081079fd9dc259ca3e2aaece6cb689fd36b1058046c969" +dependencies = [ + "bindgen 0.59.2", + "libc", +] + [[package]] name = "pango" version = "0.16.5" @@ -5124,6 +5156,7 @@ dependencies = [ "objc", "objc_id", "os-version", + "pam", "parity-tokio-ipc", "rdev", "repng", @@ -5149,6 +5182,7 @@ dependencies = [ "tray-icon", "trayicon", "url", + "users 0.11.0", "uuid", "virtual_display", "whoami", @@ -6443,6 +6477,26 @@ dependencies = [ "serde 1.0.159", ] +[[package]] +name = "users" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "users" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" +dependencies = [ + "libc", + "log", +] + [[package]] name = "utf8parse" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index a01415837..10f90f387 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,6 +122,8 @@ mouce = { git="https://github.com/fufesou/mouce.git" } evdev = { git="https://github.com/fufesou/evdev" } dbus = "0.9" dbus-crossroads = "0.5" +pam = { git="https://github.com/fufesou/pam" } +users = "0.11.0" [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index a481ae6c4..c49f163a8 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -52,6 +52,11 @@ message FileTransfer { bool show_hidden = 2; } +message OSLogin { + string username = 1; + string password = 2; +} + message LoginRequest { string username = 1; bytes password = 2; @@ -65,6 +70,7 @@ message LoginRequest { bool video_ack_required = 9; uint64 session_id = 10; string version = 11; + OSLogin os_login = 12; } message ChatMessage { string text = 1; } diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 89c96799d..1d826ea97 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -191,7 +191,7 @@ pub fn run_cmds(cmds: &str) -> ResultType { } #[cfg(not(feature = "flatpak"))] -fn run_loginctl(args: Option>) -> std::io::Result { +pub(super) fn run_loginctl(args: Option>) -> std::io::Result { let mut cmd = std::process::Command::new("loginctl"); if let Some(a) = args { return cmd.args(a).output(); @@ -200,7 +200,7 @@ fn run_loginctl(args: Option>) -> std::io::Result>) -> std::io::Result { +pub(super) fn run_loginctl(args: Option>) -> std::io::Result { let mut l_args = String::from("loginctl"); if let Some(a) = args { l_args = format!("{} {}", l_args, a.join(" ")); diff --git a/res/pam.d/rustdesk.debian b/res/pam.d/rustdesk.debian new file mode 100644 index 000000000..789ce8f7c --- /dev/null +++ b/res/pam.d/rustdesk.debian @@ -0,0 +1,5 @@ +#%PAM-1.0 +@include common-auth +@include common-account +@include common-session +@include common-password diff --git a/res/pam.d/rustdesk.suse b/res/pam.d/rustdesk.suse new file mode 100644 index 000000000..a7c7836ce --- /dev/null +++ b/res/pam.d/rustdesk.suse @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth include common-auth +account include common-account +session include common-session +password include common-password diff --git a/res/startwm.sh b/res/startwm.sh new file mode 100755 index 000000000..7cdaf07ce --- /dev/null +++ b/res/startwm.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash + +# This script is derived from https://github.com/neutrinolabs/xrdp/sesman/startwm.sh. + +# +# This script is an example. You might need to edit this script +# depending on your distro if it doesn't work for you. +# +# Uncomment the following line for debug: +# exec xterm + + +# Execution sequence for interactive login shell - pseudocode +# +# IF /etc/profile is readable THEN +# execute ~/.bash_profile +# END IF +# IF ~/.bash_profile is readable THEN +# execute ~/.bash_profile +# ELSE +# IF ~/.bash_login is readable THEN +# execute ~/.bash_login +# ELSE +# IF ~/.profile is readable THEN +# execute ~/.profile +# END IF +# END IF +# END IF +pre_start() +{ + if [ -r /etc/profile ]; then + . /etc/profile + fi + if [ -r ~/.bash_profile ]; then + . ~/.bash_profile + else + if [ -r ~/.bash_login ]; then + . ~/.bash_login + else + if [ -r ~/.profile ]; then + . ~/.profile + fi + fi + fi + return 0 +} + +# When loging out from the interactive shell, the execution sequence is: +# +# IF ~/.bash_logout exists THEN +# execute ~/.bash_logout +# END IF +post_start() +{ + if [ -r ~/.bash_logout ]; then + . ~/.bash_logout + fi + return 0 +} + +#start the window manager +wm_start() +{ + if [ -r /etc/default/locale ]; then + . /etc/default/locale + export LANG LANGUAGE + fi + + # debian + if [ -r /etc/X11/Xsession ]; then + pre_start + . /etc/X11/Xsession + post_start + exit 0 + fi + + # alpine + # Don't use /etc/X11/xinit/Xsession - it doesn't work + if [ -f /etc/alpine-release ]; then + if [ -f /etc/X11/xinit/xinitrc ]; then + pre_start + /etc/X11/xinit/xinitrc + post_start + else + echo "** xinit package isn't installed" >&2 + exit 1 + fi + fi + + # el + if [ -r /etc/X11/xinit/Xsession ]; then + pre_start + . /etc/X11/xinit/Xsession + post_start + exit 0 + fi + + # suse + if [ -r /etc/X11/xdm/Xsession ]; then + # since the following script run a user login shell, + # do not execute the pseudo login shell scripts + . /etc/X11/xdm/Xsession + exit 0 + elif [ -r /usr/etc/X11/xdm/Xsession ]; then + . /usr/etc/X11/xdm/Xsession + exit 0 + fi + + pre_start + xterm + post_start +} + +#. /etc/environment +#export PATH=$PATH +#export LANG=$LANG + +# change PATH to be what your environment needs usually what is in +# /etc/environment +#PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games" +#export PATH=$PATH + +# for PATH and LANG from /etc/environment +# pam will auto process the environment file if /etc/pam.d/xrdp-sesman +# includes +# auth required pam_env.so readenv=1 + +wm_start + +exit 1 diff --git a/res/xorg.conf b/res/xorg.conf new file mode 100644 index 000000000..fe1539995 --- /dev/null +++ b/res/xorg.conf @@ -0,0 +1,30 @@ +Section "Monitor" + Identifier "Dummy Monitor" + + # Default HorizSync 31.50 - 48.00 kHz + HorizSync 5.0 - 150.0 + # Default VertRefresh 50.00 - 70.00 Hz + VertRefresh 5.0 - 100.0 + + # Taken from https://www.xpra.org/xorg.conf + Modeline "1920x1080" 23.53 1920 1952 2040 2072 1080 1106 1108 1135 + Modeline "1280x720" 27.41 1280 1312 1416 1448 720 737 740 757 +EndSection + +Section "Device" + Identifier "Dummy VideoCard" + Driver "dummy" + # Default VideoRam 4096 + # (1920 * 1080 * 4) / 1024 = 8100 + VideoRam 8100 +EndSection + +Section "Screen" + Identifier "Dummy Screen" + Device "Dummy VideoCard" + Monitor "Dummy Monitor" + SubSection "Display" + Depth 24 + Modes "1920x1080" "1280x720" + EndSubSection +EndSection \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 454eec1ee..13e70987b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -85,8 +85,8 @@ impl Interface for Session { handle_hash(self.lc.clone(), &pass, hash, self, peer).await; } - async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { - handle_login_from_ui(self.lc.clone(), password, remember, peer).await; + async fn handle_login_from_ui(&mut self, os_username: String, os_password: String, password: String, remember: bool, peer: &mut Stream) { + handle_login_from_ui(self.lc.clone(), os_username, os_password, password, remember, peer).await; } async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { diff --git a/src/client.rs b/src/client.rs index 2f745b70c..f03cb0e55 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1591,7 +1591,12 @@ impl LoginConfigHandler { } /// Create a [`Message`] for login. - fn create_login_msg(&self, password: Vec) -> Message { + fn create_login_msg( + &self, + os_username: String, + os_password: String, + password: Vec, + ) -> Message { #[cfg(any(target_os = "android", target_os = "ios"))] let my_id = Config::get_id_or(crate::common::DEVICE_ID.lock().unwrap().clone()); #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -1604,6 +1609,12 @@ impl LoginConfigHandler { option: self.get_option_message(true).into(), session_id: self.session_id, version: crate::VERSION.to_string(), + os_login: Some(OSLogin { + username: os_username, + password: os_password, + ..Default::default() + }) + .into(), ..Default::default() }; match self.conn_type { @@ -1908,10 +1919,46 @@ pub fn handle_login_error( err: &str, interface: &impl Interface, ) -> bool { - if err == "Wrong Password" { + if err == crate::server::LOGIN_MSG_PASSWORD_EMPTY { + lc.write().unwrap().password = Default::default(); + interface.msgbox("input-password", "Password Required", "", ""); + true + } else if err == crate::server::LOGIN_MSG_PASSWORD_WRONG { lc.write().unwrap().password = Default::default(); interface.msgbox("re-input-password", err, "Do you want to enter again?", ""); true + } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY { + interface.msgbox( + "xsession-login", + "xsession is unready", + "Input linux user/password", + "", + ); + true + } else if err == crate::server::LOGIN_MSG_XSESSION_FAILED { + interface.msgbox( + "xsession-re-login", + "xsession username/password is wrong", + "Do you want to enter again?", + "", + ); + true + } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY { + interface.msgbox( + "xsession-login-password", + "xsession is unready", + "Input connection password and linux user/password", + "", + ); + true + } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG { + interface.msgbox( + "xsession-login-re-password", + "xsession is unready and password is wrong", + "Do you want to enter again?", + "", + ); + true } else if err == "No Password Access" { lc.write().unwrap().password = Default::default(); interface.msgbox( @@ -1944,7 +1991,7 @@ pub async fn handle_hash( lc: Arc>, password_preset: &str, hash: Hash, - interface: &impl Interface, + _interface: &impl Interface, peer: &mut Stream, ) { lc.write().unwrap().hash = hash.clone(); @@ -1970,13 +2017,19 @@ pub async fn handle_hash( } if password.is_empty() { // login without password, the remote side can click accept - send_login(lc.clone(), Vec::new(), peer).await; - interface.msgbox("input-password", "Password Required", "", ""); + send_login(lc.clone(), "".to_owned(), "".to_owned(), Vec::new(), peer).await; } else { let mut hasher = Sha256::new(); hasher.update(&password); hasher.update(&hash.challenge); - send_login(lc.clone(), hasher.finalize()[..].into(), peer).await; + send_login( + lc.clone(), + "".to_owned(), + "".to_owned(), + hasher.finalize()[..].into(), + peer, + ) + .await; } lc.write().unwrap().hash = hash; } @@ -1986,10 +2039,21 @@ pub async fn handle_hash( /// # Arguments /// /// * `lc` - Login config. +/// * `os_username` - OS username. +/// * `os_password` - OS password. /// * `password` - Password. /// * `peer` - [`Stream`] for communicating with peer. -async fn send_login(lc: Arc>, password: Vec, peer: &mut Stream) { - let msg_out = lc.read().unwrap().create_login_msg(password); +async fn send_login( + lc: Arc>, + os_username: String, + os_password: String, + password: Vec, + peer: &mut Stream, +) { + let msg_out = lc + .read() + .unwrap() + .create_login_msg(os_username, os_password, password); allow_err!(peer.send(&msg_out).await); } @@ -1998,25 +2062,40 @@ async fn send_login(lc: Arc>, password: Vec, peer /// # Arguments /// /// * `lc` - Login config. +/// * `os_username` - OS username. +/// * `os_password` - OS password. /// * `password` - Password. /// * `remember` - Whether to remember password. /// * `peer` - [`Stream`] for communicating with peer. pub async fn handle_login_from_ui( lc: Arc>, + os_username: String, + os_password: String, password: String, remember: bool, peer: &mut Stream, ) { - let mut hasher = Sha256::new(); - hasher.update(password); - hasher.update(&lc.read().unwrap().hash.salt); - let res = hasher.finalize(); - lc.write().unwrap().remember = remember; - lc.write().unwrap().password = res[..].into(); + let mut hash_password = if password.is_empty() { + let mut password2 = lc.read().unwrap().password.clone(); + if password2.is_empty() { + password2 = lc.read().unwrap().config.password.clone(); + } + password2 + } else { + let mut hasher = Sha256::new(); + hasher.update(password); + hasher.update(&lc.read().unwrap().hash.salt); + let res = hasher.finalize(); + lc.write().unwrap().remember = remember; + lc.write().unwrap().password = res[..].into(); + res[..].into() + }; let mut hasher2 = Sha256::new(); - hasher2.update(&res[..]); + hasher2.update(&hash_password[..]); hasher2.update(&lc.read().unwrap().hash.challenge); - send_login(lc.clone(), hasher2.finalize()[..].into(), peer).await; + hash_password = hasher2.finalize()[..].to_vec(); + + send_login(lc.clone(), os_username, os_password, hash_password, peer).await; } async fn send_switch_login_request( @@ -2030,7 +2109,7 @@ async fn send_switch_login_request( lr: hbb_common::protobuf::MessageField::some( lc.read() .unwrap() - .create_login_msg(vec![]) + .create_login_msg("".to_owned(), "".to_owned(), vec![]) .login_request() .to_owned(), ), @@ -2051,7 +2130,14 @@ pub trait Interface: Send + Clone + 'static + Sized { self.msgbox("error", "Error", err, ""); } async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream); - async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream); + async fn handle_login_from_ui( + &mut self, + os_username: String, + os_password: String, + password: String, + remember: bool, + peer: &mut Stream, + ); async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream); fn get_login_config_handler(&self) -> Arc>; @@ -2071,7 +2157,7 @@ pub trait Interface: Send + Clone + 'static + Sized { #[derive(Clone)] pub enum Data { Close, - Login((String, bool)), + Login((String, String, String, bool)), Message(Message), SendFiles((i32, String, String, i32, bool, bool)), RemoveDirAll((i32, String, bool, bool)), diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b0bddc82e..c1c38af40 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -360,9 +360,9 @@ impl Remote { allow_err!(peer.send(&msg).await); return false; } - Data::Login((password, remember)) => { + Data::Login((os_username, os_password, password, remember)) => { self.handler - .handle_login_from_ui(password, remember, peer) + .handle_login_from_ui(os_username, os_password, password, remember, peer) .await; } Data::ToggleClipboardFile => { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index dda4b115b..c1da431a4 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,3 +1,4 @@ +use super::linux_desktop::GNOME_SESSION_BINARY; use super::{CursorData, ResultType}; use desktop::Desktop; pub use hbb_common::platform::linux::*; diff --git a/src/platform/linux_desktop.rs b/src/platform/linux_desktop.rs new file mode 100644 index 000000000..bf9f465d0 --- /dev/null +++ b/src/platform/linux_desktop.rs @@ -0,0 +1,951 @@ +use super::{linux::*, ResultType}; +use hbb_common::{allow_err, bail, log, rand::prelude::*, tokio::time}; +use pam; +use std::{ + collections::HashMap, + os::unix::process::CommandExt, + path::Path, + process::{Child, Command}, + sync::{ + atomic::{AtomicBool, Ordering}, + mpsc::{sync_channel, SyncSender}, + Arc, Mutex, + }, + time::{Duration, Instant}, +}; +use users::{get_user_by_name, os::unix::UserExt, User}; + +lazy_static::lazy_static! { + static ref DESKTOP_RUNNING: Arc = Arc::new(AtomicBool::new(false)); + static ref DESKTOP_INST: Arc>> = Arc::new(Mutex::new(None)); +} + +pub const VIRTUAL_X11_DESKTOP: &str = "xfce4"; +pub const VIRTUAL_X11_DESKTOP_START: &str = "startxfce4"; +pub const XFCE4_PANEL: &str = "xfce4-panel"; +pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary"; +pub const ENV_DESKTOP_PROTOCAL: &str = "RUSTDESK_PROTOCAL"; +pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland"; +pub const ENV_DESKTOP_PROTOCAL__X11: &str = "x11"; +pub const ENV_DESKTOP_PROTOCAL_UNKNOWN: &str = "unknown"; + +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Protocal { + Wayland, + X11, // Xorg + Unknown, +} + +#[derive(Debug, Clone)] +pub struct DesktopEnv { + pub protocal: Protocal, + pub username: String, + pub uid: String, + pub display: String, + pub xauth: String, +} + +#[derive(Debug)] +pub struct Desktop { + env: DesktopEnv, + child_exit: Arc, + is_child_running: Arc, +} + +fn check_update_env() { + let mut inst = DESKTOP_INST.lock().unwrap(); + if let Some(inst) = &mut (*inst) { + if !inst.is_child_running.load(Ordering::SeqCst) { + inst.child_exit.store(true, Ordering::SeqCst); + let old_env = inst.env.clone(); + allow_err!(inst.env.refresh()); + if !inst.env.is_same_env(&old_env) { + inst.env.update_env(); + log::debug!("desktop env changed, {:?}", &inst.env); + } + } + } +} + +pub fn start_xdesktop() { + std::thread::spawn(|| { + if wait_xdesktop(20) { + log::info!("Wait desktop: default"); + } else { + log::info!("Wait desktop: none"); + } + *DESKTOP_INST.lock().unwrap() = Some(Desktop::new()); + + let interval = time::Duration::from_millis(super::SERVICE_INTERVAL); + DESKTOP_RUNNING.store(true, Ordering::SeqCst); + while DESKTOP_RUNNING.load(Ordering::SeqCst) { + check_update_env(); + std::thread::sleep(interval); + } + log::info!("xdesktop update thread exit"); + }); +} + +pub fn stop_xdesktop() { + DESKTOP_RUNNING.store(false, Ordering::SeqCst); +} + +pub fn get_desktop_env() -> Option { + match &*DESKTOP_INST.lock().unwrap() { + Some(inst) => Some(inst.env.clone()), + None => None, + } +} + +pub fn try_start_x_session(username: &str, password: &str) -> ResultType { + let mut inst = DESKTOP_INST.lock().unwrap(); + if let Some(inst) = &mut (*inst) { + let _ = inst.try_start_x_session(username, password)?; + log::debug!("try_start_x_session, username: {}, {:?}", &username, &inst); + Ok(inst.env.clone()) + } else { + bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED); + } +} + +fn wait_xdesktop(timeout_secs: u64) -> bool { + let wait_begin = Instant::now(); + while wait_begin.elapsed().as_secs() < timeout_secs { + let seat0 = get_values_of_seat0(&[0]); + if !seat0[0].is_empty() { + return true; + } + + if let Ok(output) = run_cmds(format!( + "ps -ef | grep -v 'grep' | grep -E 'gnome-session-binary|{}'", + XFCE4_PANEL + )) { + if !output.is_empty() { + log::info!("wait xdesktop: find xclient {}", &output); + return true; + } + } + + std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); + } + + false +} + +impl DesktopEnv { + pub fn new() -> Self { + let xauth = get_env_var("XAUTHORITY"); + + Self { + protocal: Protocal::Unknown, + username: "".to_owned(), + uid: "".to_owned(), + display: "".to_owned(), + xauth: if xauth.is_empty() { + "/tmp/.Xauthority".to_owned() + } else { + xauth + }, + } + } + + fn update_env(&self) { + if self.is_ready() { + std::env::set_var("DISPLAY", &self.display); + std::env::set_var("XAUTHORITY", &self.xauth); + std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); + } else { + std::env::set_var("DISPLAY", ""); + std::env::set_var("XAUTHORITY", ""); + std::env::set_var(ENV_DESKTOP_PROTOCAL, &Protocal::Unknown.to_string()); + } + } + + pub fn is_same_env(&self, other: &Self) -> bool { + self.protocal == other.protocal + && self.uid == other.uid + && self.display == other.display + && self.xauth == other.xauth + } + + #[inline(always)] + pub fn is_ready(&self) -> bool { + self.protocal == Protocal::X11 + } + + // The logic mainly fron https://github.com/neutrinolabs/xrdp/blob/34fe9b60ebaea59e8814bbc3ca5383cabaa1b869/sesman/session.c#L334. + fn get_avail_display() -> ResultType { + let display_range = 0..51; + for i in display_range.clone() { + if Self::is_x_server_running(i) { + continue; + } + return Ok(i); + } + bail!("No avaliable display found in range {:?}", display_range) + } + + fn is_x_server_running(display: u32) -> bool { + Path::new(&format!("/tmp/.X11-unix/X{}", display)).exists() + || Path::new(&format!("/tmp/.X{}-lock", display)).exists() + } + + fn get_display(&mut self) { + self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10); + if self.display.is_empty() { + self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10); + } + if self.display.is_empty() { + self.display = Self::get_display_by_user(&self.username); + } + if self.display.is_empty() { + self.display = ":0".to_owned(); + } + self.display = self + .display + .replace(&whoami::hostname(), "") + .replace("localhost", ""); + } + + fn get_xauth(&mut self) { + self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10); + if self.xauth.is_empty() { + get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10); + } + + let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid); + if self.xauth.is_empty() { + self.xauth = if std::path::Path::new(&gdm).exists() { + gdm + } else { + let username = &self.username; + if username == "root" { + format!("/{}/.Xauthority", username) + } else { + let tmp = format!("/home/{}/.Xauthority", username); + if std::path::Path::new(&tmp).exists() { + tmp + } else { + format!("/var/lib/{}/.Xauthority", username) + } + } + }; + } + } + + // fixme: reduce loginctl + fn get_env_seat0(&mut self) -> ResultType { + let output = Command::new("loginctl").output()?; + for line in String::from_utf8_lossy(&output.stdout).lines() { + if line.contains("seat0") { + if let Some(sid) = line.split_whitespace().nth(0) { + if Self::is_active(sid)? { + if let Some(uid) = line.split_whitespace().nth(1) { + self.uid = uid.to_owned(); + } + if let Some(u) = line.split_whitespace().nth(2) { + self.username = u.to_owned(); + } + + self.protocal = Protocal::Unknown; + let type_output = Command::new("loginctl") + .args(vec!["show-session", "-p", "Type", sid]) + .output()?; + let type_stdout = String::from_utf8_lossy(&type_output.stdout); + + if type_stdout.contains("x11") { + self.protocal = Protocal::X11; + break; + } else if type_stdout.contains("wayland") { + self.protocal = Protocal::Wayland; + } + } + } + } + } + Ok(self.is_ready()) + } + + // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 + fn get_env_active(&mut self) -> ResultType { + let output = Command::new("loginctl").output()?; + + // set active Xorg session + for line in String::from_utf8_lossy(&output.stdout).lines() { + if line.contains("sessions listed.") { + continue; + } + if let Some(sid) = line.split_whitespace().nth(0) { + if Self::is_active(sid)? { + if Self::get_display_server_of_session(sid) == ENV_DESKTOP_PROTOCAL__X11 { + if let Some(uid) = line.split_whitespace().nth(1) { + self.uid = uid.to_owned(); + } + if let Some(u) = line.split_whitespace().nth(2) { + self.username = u.to_owned(); + } + + self.protocal = Protocal::X11; + } + } + } + } + // // set active xfce4 session + // for line in String::from_utf8_lossy(&output.stdout).lines() { + // if let Some(sid) = line.split_whitespace().nth(0) { + // if Self::is_active(sid)? { + // let tty_output = Command::new("loginctl") + // .args(vec!["show-session", "-p", "TTY", sid]) + // .output()?; + // let tty: String = String::from_utf8_lossy(&tty_output.stdout) + // .replace("TTY=", "") + // .trim_end() + // .into(); + + // let xfce_panel_info = + // run_cmds(format!("ps -e | grep \"{}.\\\\+{}\"", tty, XFCE4_PANEL))?; + // if xfce_panel_info.trim_end().to_string() != "" { + // if let Some(uid) = line.split_whitespace().nth(1) { + // self.uid = uid.to_owned(); + // } + // if let Some(u) = line.split_whitespace().nth(2) { + // self.username = u.to_owned(); + // } + // } + // } + // } + // } + Ok(self.is_ready()) + } + + // fixme: dup + fn get_display_server_of_session(session: &str) -> String { + if let Ok(output) = Command::new("loginctl") + .args(vec!["show-session", "-p", "Type", session]) + .output() + // Check session type of the session + { + let display_server = String::from_utf8_lossy(&output.stdout) + .replace("Type=", "") + .trim_end() + .into(); + if display_server == "tty" { + // If the type is tty... + if let Ok(output) = Command::new("loginctl") + .args(vec!["show-session", "-p", "TTY", session]) + .output() + // Get the tty number + { + let tty: String = String::from_utf8_lossy(&output.stdout) + .replace("TTY=", "") + .trim_end() + .into(); + if let Ok(xorg_results) = + run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty)) + // And check if Xorg is running on that tty + { + if xorg_results.trim_end().to_string() != "" { + // If it is, manually return "x11", otherwise return tty + ENV_DESKTOP_PROTOCAL__X11.to_owned() + } else { + display_server + } + } else { + // If any of these commands fail just fall back to the display server + display_server + } + } else { + display_server + } + } else { + // If the session is not a tty, then just return the type as usual + display_server + } + } else { + "".to_owned() + } + } + + // fixme: remove + fn is_active(sid: &str) -> ResultType { + let output = Command::new("loginctl") + .args(vec!["show-session", "-p", "State", sid]) + .output()?; + + Ok(String::from_utf8_lossy(&output.stdout).contains("active")) + } + + fn get_display_by_user(user: &str) -> String { + // log::debug!("w {}", &user); + if let Ok(output) = std::process::Command::new("w").arg(&user).output() { + for line in String::from_utf8_lossy(&output.stdout).lines() { + let mut iter = line.split_whitespace(); + let b = iter.nth(2); + if let Some(b) = b { + if b.starts_with(":") { + return b.to_owned(); + } + } + } + } + // above not work for gdm user + //log::debug!("ls -l /tmp/.X11-unix/"); + let mut last = "".to_owned(); + if let Ok(output) = std::process::Command::new("ls") + .args(vec!["-l", "/tmp/.X11-unix/"]) + .output() + { + for line in String::from_utf8_lossy(&output.stdout).lines() { + let mut iter = line.split_whitespace(); + let user_field = iter.nth(2); + if let Some(x) = iter.last() { + if x.starts_with("X") { + last = x.replace("X", ":").to_owned(); + if user_field == Some(&user) { + return last; + } + } + } + } + } + last + } + + fn add_xauth_cookie( + file: &str, + display: &str, + uid: u32, + gid: u32, + envs: &HashMap<&str, String>, + ) -> ResultType<()> { + let randstr = (0..16) + .map(|_| format!("{:02x}", random::())) + .collect::(); + let output = Command::new("xauth") + .uid(uid) + .gid(gid) + .envs(envs) + .args(vec!["-q", "-f", file, "add", display, ".", &randstr]) + .output()?; + // xauth run success, even the following error occurs. + // Ok(Output { status: ExitStatus(unix_wait_status(0)), stdout: "", stderr: "xauth: file .Xauthority does not exist\n" }) + let errmsg = String::from_utf8_lossy(&output.stderr).to_string(); + if !errmsg.is_empty() { + if !errmsg.contains("does not exist") { + bail!("Failed to launch xauth, {}", errmsg) + } + } + Ok(()) + } + + fn wait_x_server_running(pid: u32, display_num: u32, max_wait_secs: u64) -> ResultType<()> { + let wait_begin = Instant::now(); + loop { + if run_cmds(format!("ls /proc/{}", pid))?.is_empty() { + bail!("X server exit"); + } + + if Self::is_x_server_running(display_num) { + return Ok(()); + } + if wait_begin.elapsed().as_secs() > max_wait_secs { + bail!("Failed to wait xserver after {} seconds", max_wait_secs); + } + std::thread::sleep(Duration::from_millis(300)); + } + } + + fn refresh(&mut self) -> ResultType { + *self = Self::new(); + if self.get_env_seat0()? || self.get_env_active()? { + self.get_display(); + self.get_xauth(); + Ok(true) + } else { + Ok(false) + } + } +} + +impl Drop for Desktop { + fn drop(&mut self) { + self.stop_children(); + } +} + +impl Desktop { + fn fatal_exit() { + std::process::exit(0); + } + + pub fn new() -> Self { + Self { + env: DesktopEnv::new(), + child_exit: Arc::new(AtomicBool::new(true)), + is_child_running: Arc::new(AtomicBool::new(false)), + } + } + + fn try_start_x_session(&mut self, username: &str, password: &str) -> ResultType<()> { + match get_user_by_name(username) { + Some(userinfo) => { + let mut client = pam::Client::with_password(pam_get_service_name())?; + client + .conversation_mut() + .set_credentials(username, password); + match client.authenticate() { + Ok(_) => { + if self.env.is_ready() && self.env.username == username { + return Ok(()); + } + + self.env.username = username.to_string(); + self.env.uid = userinfo.uid().to_string(); + self.env.protocal = Protocal::Unknown; + match self.start_x_session(&userinfo, password) { + Ok(_) => { + log::info!("Succeeded to start x11, update env {:?}", &self.env); + self.env.update_env(); + Ok(()) + } + Err(e) => { + self.env = DesktopEnv::new(); + self.env.update_env(); + bail!("failed to start x session, {}", e); + } + } + } + Err(e) => { + bail!("failed to check user pass for {}, {}", username, e); + } + } + } + None => { + bail!("failed to get userinfo of {}", username); + } + } + } + + fn start_x_session(&mut self, userinfo: &User, password: &str) -> ResultType<()> { + self.stop_children(); + + let display_num = DesktopEnv::get_avail_display()?; + // "xServer_ip:display_num.screen_num" + self.env.display = format!(":{}", display_num); + + let uid = userinfo.uid(); + let gid = userinfo.primary_group_id(); + let envs = HashMap::from([ + ("SHELL", userinfo.shell().to_string_lossy().to_string()), + ("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()), + ("USER", self.env.username.clone()), + ("UID", userinfo.uid().to_string()), + ("HOME", userinfo.home_dir().to_string_lossy().to_string()), + ( + "XDG_RUNTIME_DIR", + format!("/run/user/{}", userinfo.uid().to_string()), + ), + // ("DISPLAY", self.display.clone()), + // ("XAUTHORITY", self.xauth.clone()), + // (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()), + ]); + let env = self.env.clone(); + self.child_exit.store(false, Ordering::SeqCst); + let is_child_running = self.is_child_running.clone(); + + let (tx_res, rx_res) = sync_channel(1); + let password = password.to_string(); + // start x11 + std::thread::spawn(move || { + match Self::start_x_session_thread( + tx_res.clone(), + is_child_running, + env, + uid, + gid, + display_num, + password, + envs, + ) { + Ok(_) => {} + Err(e) => { + log::error!("Failed to start x session thread"); + allow_err!(tx_res.send(format!("Failed to start x session thread, {}", e))); + } + } + }); + + // wait x11 + match rx_res.recv_timeout(Duration::from_millis(10_000)) { + Ok(res) => { + if res == "" { + self.env.protocal = Protocal::X11; + Ok(()) + } else { + bail!(res) + } + } + Err(e) => { + bail!("Failed to recv x11 result {}", e) + } + } + } + + fn start_x_session_thread( + tx_res: SyncSender, + is_child_running: Arc, + env: DesktopEnv, + uid: u32, + gid: u32, + display_num: u32, + password: String, + envs: HashMap<&str, String>, + ) -> ResultType<()> { + let mut client = pam::Client::with_password(pam_get_service_name())?; + client + .conversation_mut() + .set_credentials(&env.username, &password); + client.authenticate()?; + + client.set_item(pam::PamItemType::TTY, &env.display)?; + client.open_session()?; + + // fixme: FreeBSD kernel needs to login here. + // see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556 + + let (child_xorg, child_wm) = Self::start_x11(&env, uid, gid, display_num, &envs)?; + is_child_running.store(true, Ordering::SeqCst); + + log::info!("Start xorg and wm done, notify and wait xtop x11"); + allow_err!(tx_res.send("".to_owned())); + + Self::wait_stop_x11(child_xorg, child_wm); + log::info!("Wait x11 stop done"); + Ok(()) + } + + fn wait_xorg_exit(child_xorg: &mut Child) -> ResultType { + if let Ok(_) = child_xorg.kill() { + for _ in 0..3 { + match child_xorg.try_wait() { + Ok(Some(status)) => return Ok(format!("Xorg exit with {}", status)), + Ok(None) => {} + Err(e) => { + // fatal error + log::error!("Failed to wait xorg process, {}", e); + bail!("Failed to wait xorg process, {}", e) + } + } + std::thread::sleep(std::time::Duration::from_millis(1_000)); + } + log::error!("Failed to wait xorg process, not exit"); + bail!("Failed to wait xorg process, not exit") + } else { + Ok("Xorg is already exited".to_owned()) + } + } + + fn start_x11( + env: &DesktopEnv, + uid: u32, + gid: u32, + display_num: u32, + envs: &HashMap<&str, String>, + ) -> ResultType<(Child, Child)> { + log::debug!("envs of user {}: {:?}", &env.username, &envs); + + DesktopEnv::add_xauth_cookie(&env.xauth, &env.display, uid, gid, &envs)?; + + // Start Xorg + let mut child_xorg = Self::start_x_server(&env.xauth, &env.display, uid, gid, &envs)?; + + log::info!("xorg started, wait 10 secs to ensuer x server is running"); + + let max_wait_secs = 10; + // wait x server running + if let Err(e) = + DesktopEnv::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) + { + match Self::wait_xorg_exit(&mut child_xorg) { + Ok(msg) => log::info!("{}", msg), + Err(e) => { + log::error!("{}", e); + Self::fatal_exit(); + } + } + bail!(e) + } + + log::info!( + "xorg is running, start x window manager with DISPLAY: {}, XAUTHORITY: {}", + &env.display, + &env.xauth + ); + + std::env::set_var("DISPLAY", &env.display); + std::env::set_var("XAUTHORITY", &env.xauth); + // start window manager (startwm.sh) + let child_wm = match Self::start_x_window_manager(uid, gid, &envs) { + Ok(c) => c, + Err(e) => { + match Self::wait_xorg_exit(&mut child_xorg) { + Ok(msg) => log::info!("{}", msg), + Err(e) => { + log::error!("{}", e); + Self::fatal_exit(); + } + } + bail!(e) + } + }; + log::info!("x window manager is started"); + + Ok((child_xorg, child_wm)) + } + + fn try_wait_x11_child_exit(child_xorg: &mut Child, child_wm: &mut Child) -> bool { + match child_xorg.try_wait() { + Ok(Some(status)) => { + println!( + "=============================MYDEBUG Xorg exit with {}", + status + ); + log::info!("Xorg exit with {}", status); + return true; + } + Ok(None) => {} + Err(e) => log::error!("Failed to wait xorg process, {}", e), + } + + match child_wm.try_wait() { + Ok(Some(status)) => { + println!( + "=============================MYDEBUG: wm exit with {}", + status + ); + log::info!("wm exit with {}", status); + return true; + } + Ok(None) => {} + Err(e) => log::error!("Failed to wait xorg process, {}", e), + } + false + } + + fn wait_x11_children_exit(child_xorg: &mut Child, child_wm: &mut Child) { + log::debug!("Try kill child process xorg"); + if let Ok(_) = child_xorg.kill() { + let mut exited = false; + for _ in 0..2 { + match child_xorg.try_wait() { + Ok(Some(status)) => { + println!( + "=============================MYDEBUG Xorg exit with {}", + status + ); + log::info!("Xorg exit with {}", status); + exited = true; + break; + } + Ok(None) => {} + Err(e) => { + println!( + "=============================MYDEBUG Failed to wait xorg process, {}", + &e + ); + log::error!("Failed to wait xorg process, {}", e); + Self::fatal_exit(); + } + } + std::thread::sleep(std::time::Duration::from_millis(1_000)); + } + if !exited { + println!( + "=============================MYDEBUG Failed to wait child xorg, after kill()" + ); + log::error!("Failed to wait child xorg, after kill()"); + // try kill -9? + } + } + log::debug!("Try kill child process wm"); + if let Ok(_) = child_wm.kill() { + let mut exited = false; + for _ in 0..2 { + match child_wm.try_wait() { + Ok(Some(status)) => { + println!( + "=============================MYDEBUG wm exit with {}", + status + ); + log::info!("wm exit with {}", status); + exited = true; + } + Ok(None) => {} + Err(e) => { + println!( + "=============================MYDEBUG Failed to wait wm process, {}", + &e + ); + log::error!("Failed to wait wm process, {}", e); + Self::fatal_exit(); + } + } + std::thread::sleep(std::time::Duration::from_millis(1_000)); + } + if !exited { + println!( + "=============================MYDEBUG Failed to wait child xorg, after kill()" + ); + log::error!("Failed to wait child xorg, after kill()"); + // try kill -9? + } + } + } + + fn try_wait_stop_x11(child_xorg: &mut Child, child_wm: &mut Child) -> bool { + let mut inst = DESKTOP_INST.lock().unwrap(); + let mut exited = true; + if let Some(inst) = &mut (*inst) { + if inst.child_exit.load(Ordering::SeqCst) { + exited = true; + } else { + exited = Self::try_wait_x11_child_exit(child_xorg, child_wm); + } + if exited { + println!("=============================MYDEBUG begin to wait x11 children exit"); + Self::wait_x11_children_exit(child_xorg, child_wm); + inst.is_child_running.store(false, Ordering::SeqCst); + inst.child_exit.store(true, Ordering::SeqCst); + } + } + exited + } + + fn wait_stop_x11(mut child_xorg: Child, mut child_wm: Child) { + loop { + if Self::try_wait_stop_x11(&mut child_xorg, &mut child_wm) { + break; + } + std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); + } + } + + fn get_xorg() -> ResultType<&'static str> { + // Fedora 26 or later + let xorg = "/usr/libexec/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // Debian 9 or later + let xorg = "/usr/lib/xorg/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // Ubuntu 16.04 or later + let xorg = "/usr/lib/xorg/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // Arch Linux + let xorg = "/usr/lib/xorg-server/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // Arch Linux + let xorg = "/usr/lib/Xorg"; + if Path::new(xorg).is_file() { + return Ok(xorg); + } + // CentOS 7 /usr/bin/Xorg or param=Xorg + + log::warn!("Failed to find xorg, use default Xorg.\n Please add \"allowed_users=anybody\" to \"/etc/X11/Xwrapper.config\"."); + Ok("Xorg") + } + + fn start_x_server( + xauth: &str, + display: &str, + uid: u32, + gid: u32, + envs: &HashMap<&str, String>, + ) -> ResultType { + let xorg = Self::get_xorg()?; + log::info!("Use xorg: {}", &xorg); + match Command::new(xorg) + .envs(envs) + .uid(uid) + .gid(gid) + .args(vec![ + "-noreset", + "+extension", + "GLX", + "+extension", + "RANDR", + "+extension", + "RENDER", + //"-logfile", + //"/tmp/RustDesk_xorg.log", + "-config", + "rustdesk/xorg.conf", + "-auth", + xauth, + display, + ]) + .spawn() + { + Ok(c) => Ok(c), + Err(e) => { + bail!("Failed to start Xorg with display {}, {}", display, e); + } + } + } + + fn start_x_window_manager( + uid: u32, + gid: u32, + envs: &HashMap<&str, String>, + ) -> ResultType { + match Command::new("/etc/rustdesk/startwm.sh") + .envs(envs) + .uid(uid) + .gid(gid) + .spawn() + { + Ok(c) => Ok(c), + Err(e) => { + bail!("Failed to start window manager, {}", e); + } + } + } + + fn stop_children(&mut self) { + self.child_exit.store(true, Ordering::SeqCst); + for _i in 1..10 { + if !self.is_child_running.load(Ordering::SeqCst) { + break; + } + std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); + } + if self.is_child_running.load(Ordering::SeqCst) { + log::warn!("xdesktop child is still running!"); + } + } +} + +fn pam_get_service_name() -> &'static str { + if Path::new("/etc/pam.d/rustdesk").is_file() { + "rustdesk" + } else { + "gdm" + } +} + +impl ToString for Protocal { + fn to_string(&self) -> String { + match self { + Protocal::X11 => ENV_DESKTOP_PROTOCAL__X11.to_owned(), + Protocal::Wayland => ENV_DESKTOP_PROTOCAL_WAYLAND.to_owned(), + Protocal::Unknown => ENV_DESKTOP_PROTOCAL_UNKNOWN.to_owned(), + } + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 777de3b01..16bcc775a 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -17,6 +17,9 @@ pub mod delegate; #[cfg(target_os = "linux")] pub mod linux; +#[cfg(target_os = "linux")] +pub mod linux_desktop; + #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::{message_proto::CursorData, ResultType}; #[cfg(not(any(target_os = "macos", target_os = "android", target_os = "ios")))] diff --git a/src/port_forward.rs b/src/port_forward.rs index f50f40db8..4e05ad92f 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -199,8 +199,8 @@ async fn connect_and_login_2( }, d = ui_receiver.recv() => { match d { - Some(Data::Login((password, remember))) => { - interface.handle_login_from_ui(password, remember, &mut stream).await; + Some(Data::Login((os_username, os_password, password, remember))) => { + interface.handle_login_from_ui(os_username, os_password, password, remember, &mut stream).await; } _ => {} } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 8b7dae1ba..339a45bf3 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -54,6 +54,8 @@ impl RendezvousMediator { pub async fn start_all() { let mut nat_tested = false; check_zombie(); + #[cfg(target_os = "linux")] + crate::server::check_xdesktop(); let server = new_server(); if Config::get_nat_type() == NatType::UNKNOWN_NAT as i32 { crate::test_nat_type(); @@ -72,7 +74,10 @@ impl RendezvousMediator { allow_err!(super::lan::start_listening()); }); } - loop { + #[cfg(target_os = "linux")] + crate::platform::linux_desktop::start_xdesktop(); + SHOULD_EXIT.store(false, Ordering::SeqCst); + while !SHOULD_EXIT.load(Ordering::SeqCst) { Config::reset_online(); if Config::get_option("stop-service").is_empty() { if !nat_tested { @@ -96,6 +101,8 @@ impl RendezvousMediator { } sleep(1.).await; } + #[cfg(target_os = "linux")] + crate::platform::linux_desktop::stop_xdesktop(); } pub async fn start(server: ServerPtr, host: String) -> ResultType<()> { diff --git a/src/server.rs b/src/server.rs index 681e7bed1..81c967ba1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -356,6 +356,40 @@ pub fn check_zombie() { }); } +#[cfg(target_os = "linux")] +pub fn check_xdesktop() { + std::thread::spawn(|| { + use crate::platform::linux_desktop::{get_desktop_env, DesktopEnv}; + let mut desktop_env = DesktopEnv::new(); + loop { + let new_env = match get_desktop_env() { + Some(env) => env, + None => DesktopEnv::new(), + }; + + if new_env.is_ready() { + if !desktop_env.is_ready() { + desktop_env = new_env.clone(); + } else { + if !desktop_env.is_same_env(&new_env) { + log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env); + break; + } + } + } else { + if desktop_env.is_ready() { + log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env); + break; + } + } + + std::thread::sleep(Duration::from_millis(300)); + } + + crate::RendezvousMediator::restart(); + }); +} + /// Start the host server that allows the remote peer to control the current machine. /// /// # Arguments diff --git a/src/server/connection.rs b/src/server/connection.rs index 23e166fcf..36190485e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -57,6 +57,16 @@ lazy_static::lazy_static! { pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); +pub const LOGIN_MSG_XDESKTOP_NOT_INITED: &str = "xdesktop env is not inited"; +pub const LOGIN_MSG_XSESSION_NOT_READY: &str = "xsession unready"; +pub const LOGIN_MSG_XSESSION_FAILED: &str = "xsession failed"; +pub const LOGIN_MSG_XSESSION_ANOTHER_USER_READTY: &str = "xsession another user login"; +pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY: &str = "xsession unready, password empty"; +pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG: &str = "xsession unready, password wrong"; +pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; +pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; +pub const LOGIN_MSG_OFFLINE: &str = "Offline"; + #[derive(Clone, Default)] pub struct ConnInner { id: i32, @@ -902,7 +912,7 @@ impl Connection { } #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.file_transfer.is_some() { - if crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() { + if !crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() { username = "".to_owned(); } } @@ -1061,6 +1071,47 @@ impl Connection { self.tx_input.send(MessageInput::Key((msg, press))).ok(); } + fn try_start_desktop(_username: &str, _passsword: &str) -> String { + #[cfg(target_os = "linux")] + { + if _username.is_empty() { + match crate::platform::linux_desktop::get_desktop_env() { + Some(desktop_env) => { + if desktop_env.is_ready() { + "" + } else { + LOGIN_MSG_XSESSION_NOT_READY + } + } + None => LOGIN_MSG_XDESKTOP_NOT_INITED, + } + .to_owned() + } else { + match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) { + Ok(desktop_env) => { + if desktop_env.is_ready() { + if _username != desktop_env.username { + LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() + } else { + "".to_owned() + } + } else { + LOGIN_MSG_XSESSION_NOT_READY.to_owned() + } + } + Err(e) => { + log::error!("Failed to start xsession {}", e); + LOGIN_MSG_XSESSION_FAILED.to_owned() + } + } + } + } + #[cfg(not(target_os = "linux"))] + { + "".to_owned() + } + } + fn validate_one_password(&self, password: String) -> bool { if password.len() == 0 { return false; @@ -1225,16 +1276,31 @@ impl Connection { } _ => {} } + + let desktop_err = match lr.os_login.as_ref() { + Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), + None => Self::try_start_desktop("", ""), + }; + if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_XSESSION_NOT_READY { + self.send_login_error(desktop_err).await; + return true; + } + if !hbb_common::is_ipv4_str(&lr.username) && lr.username != Config::get_id() { - self.send_login_error("Offline").await; + self.send_login_error(LOGIN_MSG_OFFLINE).await; } else if password::approve_mode() == ApproveMode::Click || password::approve_mode() == ApproveMode::Both && !password::has_valid_password() { - self.try_start_cm(lr.my_id, lr.my_name, false); - if hbb_common::get_version_number(&lr.version) - >= hbb_common::get_version_number("1.2.0") - { - self.send_login_error("No Password Access").await; + if desktop_err.is_empty() { + self.try_start_cm(lr.my_id, lr.my_name, false); + if hbb_common::get_version_number(&lr.version) + >= hbb_common::get_version_number("1.2.0") + { + self.send_login_error("No Password Access").await; + } + } else { + self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY) + .await; } return true; } else if password::approve_mode() == ApproveMode::Password @@ -1290,16 +1356,25 @@ impl Connection { .lock() .unwrap() .insert(self.ip.clone(), failure); - self.send_login_error("Wrong Password").await; - self.try_start_cm(lr.my_id, lr.my_name, false); + if desktop_err.is_empty() { + self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; + self.try_start_cm(lr.my_id, lr.my_name, false); + } else { + self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG) + .await; + } } else { if failure.0 != 0 { LOGIN_FAILURES.lock().unwrap().remove(&self.ip); } - self.try_start_cm(lr.my_id, lr.my_name, true); - self.send_logon_response().await; - if self.port_forward_socket.is_some() { - return false; + if desktop_err.is_empty() { + self.send_logon_response().await; + self.try_start_cm(lr.my_id, lr.my_name, true); + if self.port_forward_socket.is_some() { + return false; + } + } else { + self.send_login_error(desktop_err).await; } } } @@ -2072,7 +2147,7 @@ async fn start_ipc( tx_from_cm: mpsc::UnboundedSender, ) -> ResultType<()> { loop { - if !crate::platform::is_prelogin() { + if crate::platform::is_prelogin() { break; } sleep(1.).await; diff --git a/src/server/service.rs b/src/server/service.rs index 9cc1e860c..fe038f3c0 100644 --- a/src/server/service.rs +++ b/src/server/service.rs @@ -221,6 +221,7 @@ impl> ServiceTmpl { thread::sleep(interval - elapsed); } } + log::info!("Service {} exit", sp.name()); }); self.0.write().unwrap().handle = Some(thread); } @@ -256,6 +257,7 @@ impl> ServiceTmpl { } thread::sleep(time::Duration::from_millis(HIBERNATE_TIMEOUT)); } + log::info!("Service {} exit", sp.name()); }); self.0.write().unwrap().handle = Some(thread); } diff --git a/src/ui/common.css b/src/ui/common.css index 0fb9afcb1..9845ff104 100644 --- a/src/ui/common.css +++ b/src/ui/common.css @@ -142,6 +142,10 @@ div.password input { font-size: 1.2em; } +div.username input { + font-size: 1.2em; +} + svg { background: none; } diff --git a/src/ui/common.tis b/src/ui/common.tis index b6723b131..ef6d215aa 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -252,7 +252,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= view.close(); return; } - handler.login(res.password, res.remember); + handler.login("", "", res.password, res.remember); if (!is_port_forward) { // Specially handling file transfer for no permission hanging issue (including 60ms // timer in setPermission. @@ -262,6 +262,30 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= else msgbox("connecting", "Connecting...", "Logging in..."); } }; + } else if (type == "xsession-login" || type == "xsession-re-login") { + callback = function (res) { + if (!res) { + view.close(); + return; + } + handler.login(res.osusername, res.ospassword, "", false); + if (!is_port_forward) { + if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in..."); + else msgbox("connecting", "Connecting...", "Logging in..."); + } + }; + } else if (type.indexOf("xsession-login") >= 0) { + callback = function (res) { + if (!res) { + view.close(); + return; + } + handler.login(res.osusername, res.ospassword, res.password, res.remember); + if (!is_port_forward) { + if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in..."); + else msgbox("connecting", "Connecting...", "Logging in..."); + } + }; } else if (type.indexOf("custom") < 0 && !is_port_forward && !callback) { callback = function() { view.close(); } } else if (type == 'wait-remote-accept-nook') { diff --git a/src/ui/msgbox.tis b/src/ui/msgbox.tis index d5c60d95c..d9a311452 100644 --- a/src/ui/msgbox.tis +++ b/src/ui/msgbox.tis @@ -32,7 +32,7 @@ class MsgboxComponent: Reactor.Component { } function getIcon(color) { - if (this.type == "input-password") { + if (this.type == "input-password" || this.type == "xsession-login" || this.type == "xsession-login-password") { return ; } if (this.type == "connecting") { @@ -41,7 +41,7 @@ class MsgboxComponent: Reactor.Component { if (this.type == "success") { return ; } - if (this.type.indexOf("error") >= 0 || this.type == "re-input-password") { + if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") { return ; } return null; @@ -56,11 +56,36 @@ class MsgboxComponent: Reactor.Component {
    ; } + function getInputUserPasswordContent() { + return
    +
    {translate("OS Username")}
    +
    +
    {translate("OS Password")}
    + +
    +
    ; + } + + function getXsessionPasswordContent() { + return
    +
    {translate("OS Username")}
    +
    +
    {translate("OS Password")}
    + +
    {translate('Please enter your password')}
    + +
    {translate('Remember password')}
    +
    ; + } + function getContent() { if (this.type == "input-password") { return this.getInputPasswordContent(); - } - if (this.type == "custom-os-password") { + } else if (this.type == "xsession-login") { + return this.getInputUserPasswordContent(); + } else if (this.type == "xsession-login-password") { + return this.getXsessionPasswordContent(); + } else if (this.type == "custom-os-password") { var ts = this.auto_login ? { checked: true } : {}; return
    @@ -71,13 +96,13 @@ class MsgboxComponent: Reactor.Component { } function getColor() { - if (this.type == "input-password" || this.type == "custom-os-password") { + if (this.type == "input-password" || this.type == "custom-os-password" || this.type == "xsession-login" || this.type == "xsession-login-password") { return "#AD448E"; } if (this.type == "success") { return "#32bea6"; } - if (this.type.indexOf("error") >= 0 || this.type == "re-input-password") { + if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") { return "#e04f5f"; } return "#2C8CFF"; @@ -177,6 +202,16 @@ class MsgboxComponent: Reactor.Component { this.update(); return; } + if (this.type == "xsession-re-login") { + this.type = "xsession-login"; + this.update(); + return; + } + if (this.type == "xsession-login-re-password") { + this.type = "xsession-login-password"; + this.update(); + return; + } var values = this.getValues(); if (this.callback) { var self = this; @@ -238,6 +273,21 @@ class MsgboxComponent: Reactor.Component { return; } } + if (this.type == "xsession-login") { + values.osusername = (values.osusername || "").trim(); + values.ospassword = (values.ospassword || "").trim(); + if (!values.osusername || !values.ospassword) { + return; + } + } + if (this.type == "xsession-login-password") { + values.password = (values.password || "").trim(); + values.osusername = (values.osusername || "").trim(); + values.ospassword = (values.ospassword || "").trim(); + if (!values.osusername || !values.ospassword || !values.password) { + return; + } + } return values; } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 68decf955..966315a23 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -398,7 +398,7 @@ impl sciter::EventHandler for SciterSession { fn is_file_transfer(); fn is_port_forward(); fn is_rdp(); - fn login(String, bool); + fn login(String, String, String, bool); fn new_rdp(); fn send_mouse(i32, i32, i32, bool, bool, bool, bool); fn enter(); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f89be4879..2a0776668 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -711,8 +711,14 @@ impl Session { fs::get_string(&path) } - pub fn login(&self, password: String, remember: bool) { - self.send(Data::Login((password, remember))); + pub fn login( + &mut self, + os_username: String, + os_password: String, + password: String, + remember: bool, + ) { + self.send(Data::Login((os_username, os_password, password, remember))); } pub fn new_rdp(&self) { @@ -1005,8 +1011,23 @@ impl Interface for Session { handle_hash(self.lc.clone(), pass, hash, self, peer).await; } - async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) { - handle_login_from_ui(self.lc.clone(), password, remember, peer).await; + async fn handle_login_from_ui( + &mut self, + os_username: String, + os_password: String, + password: String, + remember: bool, + peer: &mut Stream, + ) { + handle_login_from_ui( + self.lc.clone(), + os_username, + os_password, + password, + remember, + peer, + ) + .await; } async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) { From a94052a24a15ac112d7a91ff0e6cbffc385c8080 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 23 Mar 2023 18:01:27 +0800 Subject: [PATCH 159/366] ignore seat0 on gdm Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 2 +- src/platform/linux_desktop.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 1d826ea97..cf1cf6da5 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -135,7 +135,7 @@ pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("seat0") { + if !line.contains("gdm") && line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { if ignore_gdm_wayland { diff --git a/src/platform/linux_desktop.rs b/src/platform/linux_desktop.rs index bf9f465d0..0d25b3743 100644 --- a/src/platform/linux_desktop.rs +++ b/src/platform/linux_desktop.rs @@ -237,7 +237,7 @@ impl DesktopEnv { fn get_env_seat0(&mut self) -> ResultType { let output = Command::new("loginctl").output()?; for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("seat0") { + if !line.contains("gdm") && line.contains("seat0") { if let Some(sid) = line.split_whitespace().nth(0) { if Self::is_active(sid)? { if let Some(uid) = line.split_whitespace().nth(1) { From c65a3b2489c76b22fb70897bad3e8632d312b660 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 23 Mar 2023 18:40:25 +0800 Subject: [PATCH 160/366] virtual display, update build script Signed-off-by: fufesou --- build.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/build.py b/build.py index 7acf97a7d..3a06c4161 100755 --- a/build.py +++ b/build.py @@ -287,6 +287,8 @@ def build_flutter_deb(version, features): system2('flutter build linux --release') system2('mkdir -p tmpdeb/usr/bin/') system2('mkdir -p tmpdeb/usr/lib/rustdesk') + system2('mkdir -p tmpdeb/etc/rustdesk/') + system2('mkdir -p tmpdeb/etc/pam.d/') system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/') system2('mkdir -p tmpdeb/usr/share/applications/') system2('mkdir -p tmpdeb/usr/share/polkit-1/actions') @@ -303,6 +305,12 @@ def build_flutter_deb(version, features): 'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop') system2( 'cp ../res/com.rustdesk.RustDesk.policy tmpdeb/usr/share/polkit-1/actions/') + system2( + 'cp ../res/startwm.sh tmpdeb/etc/rustdesk/') + system2( + 'cp ../res/xorg.conf tmpdeb/etc/rustdesk/') + system2( + 'cp ../res/pam.d/rustdesk.debian tmpdeb/etc/pam.d/rustdesk') system2( "echo \"#!/bin/sh\" >> tmpdeb/usr/share/rustdesk/files/polkit && chmod a+x tmpdeb/usr/share/rustdesk/files/polkit") @@ -553,12 +561,21 @@ def main(): 'cp res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop') system2( 'cp res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop') - system2('cp -a res/DEBIAN/* tmpdeb/DEBIAN/') + os.system('mkdir -p tmpdeb/etc/rustdesk/') + os.system('cp -a res/startwm.sh tmpdeb/etc/rustdesk/') + os.system('mkdir -p tmpdeb/etc/X11/rustdesk/') + os.system('cp res/xorg.conf tmpdeb/etc/X11/rustdesk/') + os.system('cp -a DEBIAN/* tmpdeb/DEBIAN/') + os.system('mkdir -p tmpdeb/etc/pam.d/') + os.system('cp pam.d/rustdesk.debian tmpdeb/etc/pam.d/rustdesk') system2('strip tmpdeb/usr/bin/rustdesk') system2('mkdir -p tmpdeb/usr/lib/rustdesk') system2('mv tmpdeb/usr/bin/rustdesk tmpdeb/usr/lib/rustdesk/') system2('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/') md5_file('usr/share/rustdesk/files/systemd/rustdesk.service') + md5_file('etc/rustdesk/startwm.sh') + md5_file('etc/X11/rustdesk/xorg.conf') + md5_file('etc/pam.d/rustdesk') md5_file('usr/lib/rustdesk/libsciter-gtk.so') system2('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/') os.rename('rustdesk.deb', 'rustdesk-%s.deb' % version) From e24e05ba5c31db9b1051bcbb2d01bafda888b900 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 23 Mar 2023 19:57:58 +0800 Subject: [PATCH 161/366] tmp commit Signed-off-by: fufesou --- flutter/lib/models/model.dart | 10 ++++++++-- src/flutter_ffi.rs | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 06bfeec45..20999f1bb 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1631,8 +1631,14 @@ class FFI { } /// Login with [password], choose if the client should [remember] it. - void login(String id, String password, bool remember) { - bind.sessionLogin(id: id, password: password, remember: remember); + void login(String osUsername, String osPassword, String id, String password, + bool remember) { + bind.sessionLogin( + id: id, + os_username: osUsername, + os_password: osPassword, + password: password, + remember: remember); } /// Close the remote session. diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index aee486b94..c810437fb 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -136,9 +136,15 @@ pub fn session_get_option(id: String, arg: String) -> Option { } } -pub fn session_login(id: String, password: String, remember: bool) { +pub fn session_login( + id: String, + os_username: String, + os_password: String, + password: String, + remember: bool, +) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.login(password, remember); + session.login(os_username, os_password, password, remember); } } From 988d8dc32c093ca5e1d67d29622602b67570bc50 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 24 Mar 2023 11:34:00 +0800 Subject: [PATCH 162/366] test build Signed-off-by: fufesou --- src/ui_session_interface.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 2a0776668..39e614230 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -712,7 +712,7 @@ impl Session { } pub fn login( - &mut self, + &self, os_username: String, os_password: String, password: String, From 461aa622f8861322b55205db8c4d4bf0e8799c3a Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 08:57:19 +0800 Subject: [PATCH 163/366] fix build Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 4 ++-- flutter/lib/models/model.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index cc815c5a3..4562e519c 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -506,8 +506,8 @@ _connectDialog( final peerPassword = peerPasswordController?.text.trim() ?? ''; if (peerPasswordController != null && peerPassword.isEmpty) return; gFFI.login( - // username, - // password, + username, + password, id, peerPassword, remember, diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 20999f1bb..155956c74 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1635,8 +1635,8 @@ class FFI { bool remember) { bind.sessionLogin( id: id, - os_username: osUsername, - os_password: osPassword, + osUsername: osUsername, + osPassword: osPassword, password: password, remember: remember); } From 5e794818601ba0a7bab881b83f19aa4a2250a442 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 20:20:45 +0800 Subject: [PATCH 164/366] linux, refact desktop env Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 2 +- src/platform/linux.rs | 10 +- src/platform/linux_desktop.rs | 358 ++++++++++---------------- src/rendezvous_mediator.rs | 2 - src/server.rs | 34 --- src/server/connection.rs | 50 ++-- 6 files changed, 163 insertions(+), 293 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index cf1cf6da5..1d826ea97 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -135,7 +135,7 @@ pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { - if !line.contains("gdm") && line.contains("seat0") { + if line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { if ignore_gdm_wayland { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index c1da431a4..857372bd7 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,4 +1,4 @@ -use super::linux_desktop::GNOME_SESSION_BINARY; +use super::linux_desktop::{get_desktop_env, Desktop}; use super::{CursorData, ResultType}; use desktop::Desktop; pub use hbb_common::platform::linux::*; @@ -382,13 +382,13 @@ pub fn start_os_service() { #[inline] pub fn get_active_user_id_name() -> (String, String) { - let vec_id_name = get_values_of_seat0(&[1, 2]); - (vec_id_name[0].clone(), vec_id_name[1].clone()) + let desktop = get_desktop_env(); + (desktop.uid.clone(), desktop.username.clone()) } #[inline] pub fn get_active_userid() -> String { - get_values_of_seat0(&[1])[0].clone() + get_desktop_env().uid.clone() } fn get_cm() -> bool { @@ -434,7 +434,7 @@ fn _get_display_manager() -> String { #[inline] pub fn get_active_username() -> String { - get_values_of_seat0(&[2])[0].clone() + get_desktop_env().username.clone() } pub fn get_active_user_home() -> Option { diff --git a/src/platform/linux_desktop.rs b/src/platform/linux_desktop.rs index 0d25b3743..0cd66512a 100644 --- a/src/platform/linux_desktop.rs +++ b/src/platform/linux_desktop.rs @@ -17,7 +17,8 @@ use users::{get_user_by_name, os::unix::UserExt, User}; lazy_static::lazy_static! { static ref DESKTOP_RUNNING: Arc = Arc::new(AtomicBool::new(false)); - static ref DESKTOP_INST: Arc>> = Arc::new(Mutex::new(None)); + static ref DESKTOP_ENV: Arc> = Arc::new(Mutex::new(Desktop::new())); + static ref CHILD_FLAGS: Arc>> = Arc::new(Mutex::new(None)); } pub const VIRTUAL_X11_DESKTOP: &str = "xfce4"; @@ -37,7 +38,8 @@ pub enum Protocal { } #[derive(Debug, Clone)] -pub struct DesktopEnv { +pub struct Desktop { + pub sid: String, pub protocal: Protocal, pub username: String, pub uid: String, @@ -46,24 +48,31 @@ pub struct DesktopEnv { } #[derive(Debug)] -pub struct Desktop { - env: DesktopEnv, +struct ChildFlags { child_exit: Arc, is_child_running: Arc, } fn check_update_env() { - let mut inst = DESKTOP_INST.lock().unwrap(); - if let Some(inst) = &mut (*inst) { - if !inst.is_child_running.load(Ordering::SeqCst) { - inst.child_exit.store(true, Ordering::SeqCst); - let old_env = inst.env.clone(); - allow_err!(inst.env.refresh()); - if !inst.env.is_same_env(&old_env) { - inst.env.update_env(); - log::debug!("desktop env changed, {:?}", &inst.env); - } + let mut child_flags = CHILD_FLAGS.lock().unwrap(); + let mut desktop = DESKTOP_ENV.lock().unwrap(); + + if let Some(child_flags) = &mut (*child_flags) { + if child_flags.is_child_running.load(Ordering::SeqCst) { + return; } + child_flags.child_exit.store(true, Ordering::SeqCst); + } + + if !desktop.sid.is_empty() && is_active(&desktop.sid) { + return; + } + + let old_desktop = desktop.clone(); + desktop.refresh(); + if !desktop.is_same_env(&old_desktop) { + desktop.update_env(); + log::debug!("desktop env changed, {:?}", &desktop); } } @@ -74,7 +83,7 @@ pub fn start_xdesktop() { } else { log::info!("Wait desktop: none"); } - *DESKTOP_INST.lock().unwrap() = Some(Desktop::new()); + *CHILD_FLAGS.lock().unwrap() = Some(ChildFlags::new()); let interval = time::Duration::from_millis(super::SERVICE_INTERVAL); DESKTOP_RUNNING.store(true, Ordering::SeqCst); @@ -90,19 +99,24 @@ pub fn stop_xdesktop() { DESKTOP_RUNNING.store(false, Ordering::SeqCst); } -pub fn get_desktop_env() -> Option { - match &*DESKTOP_INST.lock().unwrap() { - Some(inst) => Some(inst.env.clone()), - None => None, - } +#[inline] +pub fn get_desktop_env() -> Desktop { + DESKTOP_ENV.lock().unwrap().clone() } -pub fn try_start_x_session(username: &str, password: &str) -> ResultType { - let mut inst = DESKTOP_INST.lock().unwrap(); - if let Some(inst) = &mut (*inst) { - let _ = inst.try_start_x_session(username, password)?; - log::debug!("try_start_x_session, username: {}, {:?}", &username, &inst); - Ok(inst.env.clone()) +pub fn try_start_x_session(username: &str, password: &str) -> ResultType { + let mut child_flags = CHILD_FLAGS.lock().unwrap(); + let mut desktop_lock = DESKTOP_ENV.lock().unwrap(); + let mut desktop = Desktop::new(); + if let Some(child_flags) = &mut (*child_flags) { + let _ = child_flags.try_start_x_session(&mut desktop, username, password)?; + log::debug!( + "try_start_x_session, username: {}, {:?}", + &username, + &child_flags + ); + *desktop_lock = desktop.clone(); + Ok(desktop) } else { bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED); } @@ -111,8 +125,8 @@ pub fn try_start_x_session(username: &str, password: &str) -> ResultType bool { let wait_begin = Instant::now(); while wait_begin.elapsed().as_secs() < timeout_secs { - let seat0 = get_values_of_seat0(&[0]); - if !seat0[0].is_empty() { + let uid = &get_values_of_seat0(&[1])[0]; + if !uid.is_empty() { return true; } @@ -132,11 +146,12 @@ fn wait_xdesktop(timeout_secs: u64) -> bool { false } -impl DesktopEnv { +impl Desktop { pub fn new() -> Self { let xauth = get_env_var("XAUTHORITY"); Self { + sid: "".to_owned(), protocal: Protocal::Unknown, username: "".to_owned(), uid: "".to_owned(), @@ -150,29 +165,43 @@ impl DesktopEnv { } fn update_env(&self) { - if self.is_ready() { + if self.is_x11() { std::env::set_var("DISPLAY", &self.display); std::env::set_var("XAUTHORITY", &self.xauth); std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); } else { std::env::set_var("DISPLAY", ""); std::env::set_var("XAUTHORITY", ""); - std::env::set_var(ENV_DESKTOP_PROTOCAL, &Protocal::Unknown.to_string()); + std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); } } pub fn is_same_env(&self, other: &Self) -> bool { - self.protocal == other.protocal + self.sid == other.sid + && self.protocal == other.protocal && self.uid == other.uid && self.display == other.display && self.xauth == other.xauth } - #[inline(always)] - pub fn is_ready(&self) -> bool { + #[inline] + pub fn is_x11(&self) -> bool { self.protocal == Protocal::X11 } + #[inline] + pub fn is_wayland(&self) -> bool { + self.protocal == Protocal::Wayland + } + + #[inline] + pub fn is_ready(&self) -> bool { + match self.protocal { + Protocal::X11 | Protocal::Wayland => true, + _ => false, + } + } + // The logic mainly fron https://github.com/neutrinolabs/xrdp/blob/34fe9b60ebaea59e8814bbc3ca5383cabaa1b869/sesman/session.c#L334. fn get_avail_display() -> ResultType { let display_range = 0..51; @@ -185,6 +214,7 @@ impl DesktopEnv { bail!("No avaliable display found in range {:?}", display_range) } + #[inline] fn is_x_server_running(display: u32) -> bool { Path::new(&format!("/tmp/.X11-unix/X{}", display)).exists() || Path::new(&format!("/tmp/.X{}-lock", display)).exists() @@ -233,148 +263,6 @@ impl DesktopEnv { } } - // fixme: reduce loginctl - fn get_env_seat0(&mut self) -> ResultType { - let output = Command::new("loginctl").output()?; - for line in String::from_utf8_lossy(&output.stdout).lines() { - if !line.contains("gdm") && line.contains("seat0") { - if let Some(sid) = line.split_whitespace().nth(0) { - if Self::is_active(sid)? { - if let Some(uid) = line.split_whitespace().nth(1) { - self.uid = uid.to_owned(); - } - if let Some(u) = line.split_whitespace().nth(2) { - self.username = u.to_owned(); - } - - self.protocal = Protocal::Unknown; - let type_output = Command::new("loginctl") - .args(vec!["show-session", "-p", "Type", sid]) - .output()?; - let type_stdout = String::from_utf8_lossy(&type_output.stdout); - - if type_stdout.contains("x11") { - self.protocal = Protocal::X11; - break; - } else if type_stdout.contains("wayland") { - self.protocal = Protocal::Wayland; - } - } - } - } - } - Ok(self.is_ready()) - } - - // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 - fn get_env_active(&mut self) -> ResultType { - let output = Command::new("loginctl").output()?; - - // set active Xorg session - for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("sessions listed.") { - continue; - } - if let Some(sid) = line.split_whitespace().nth(0) { - if Self::is_active(sid)? { - if Self::get_display_server_of_session(sid) == ENV_DESKTOP_PROTOCAL__X11 { - if let Some(uid) = line.split_whitespace().nth(1) { - self.uid = uid.to_owned(); - } - if let Some(u) = line.split_whitespace().nth(2) { - self.username = u.to_owned(); - } - - self.protocal = Protocal::X11; - } - } - } - } - // // set active xfce4 session - // for line in String::from_utf8_lossy(&output.stdout).lines() { - // if let Some(sid) = line.split_whitespace().nth(0) { - // if Self::is_active(sid)? { - // let tty_output = Command::new("loginctl") - // .args(vec!["show-session", "-p", "TTY", sid]) - // .output()?; - // let tty: String = String::from_utf8_lossy(&tty_output.stdout) - // .replace("TTY=", "") - // .trim_end() - // .into(); - - // let xfce_panel_info = - // run_cmds(format!("ps -e | grep \"{}.\\\\+{}\"", tty, XFCE4_PANEL))?; - // if xfce_panel_info.trim_end().to_string() != "" { - // if let Some(uid) = line.split_whitespace().nth(1) { - // self.uid = uid.to_owned(); - // } - // if let Some(u) = line.split_whitespace().nth(2) { - // self.username = u.to_owned(); - // } - // } - // } - // } - // } - Ok(self.is_ready()) - } - - // fixme: dup - fn get_display_server_of_session(session: &str) -> String { - if let Ok(output) = Command::new("loginctl") - .args(vec!["show-session", "-p", "Type", session]) - .output() - // Check session type of the session - { - let display_server = String::from_utf8_lossy(&output.stdout) - .replace("Type=", "") - .trim_end() - .into(); - if display_server == "tty" { - // If the type is tty... - if let Ok(output) = Command::new("loginctl") - .args(vec!["show-session", "-p", "TTY", session]) - .output() - // Get the tty number - { - let tty: String = String::from_utf8_lossy(&output.stdout) - .replace("TTY=", "") - .trim_end() - .into(); - if let Ok(xorg_results) = - run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty)) - // And check if Xorg is running on that tty - { - if xorg_results.trim_end().to_string() != "" { - // If it is, manually return "x11", otherwise return tty - ENV_DESKTOP_PROTOCAL__X11.to_owned() - } else { - display_server - } - } else { - // If any of these commands fail just fall back to the display server - display_server - } - } else { - display_server - } - } else { - // If the session is not a tty, then just return the type as usual - display_server - } - } else { - "".to_owned() - } - } - - // fixme: remove - fn is_active(sid: &str) -> ResultType { - let output = Command::new("loginctl") - .args(vec!["show-session", "-p", "State", sid]) - .output()?; - - Ok(String::from_utf8_lossy(&output.stdout).contains("active")) - } - fn get_display_by_user(user: &str) -> String { // log::debug!("w {}", &user); if let Ok(output) = std::process::Command::new("w").arg(&user).output() { @@ -455,38 +343,47 @@ impl DesktopEnv { } } - fn refresh(&mut self) -> ResultType { + fn refresh(&mut self) { *self = Self::new(); - if self.get_env_seat0()? || self.get_env_active()? { - self.get_display(); - self.get_xauth(); - Ok(true) - } else { - Ok(false) + + let seat0_values = get_values_of_seat0(&[0, 1, 2]); + if seat0_values[0].is_empty() { + return; } + + self.sid = seat0_values[0].clone(); + self.uid = seat0_values[1].clone(); + self.username = seat0_values[2].clone(); + self.protocal = get_display_server_of_session(&self.sid).into(); + self.get_display(); + self.get_xauth(); } } -impl Drop for Desktop { +impl Drop for ChildFlags { fn drop(&mut self) { self.stop_children(); } } -impl Desktop { +impl ChildFlags { fn fatal_exit() { std::process::exit(0); } pub fn new() -> Self { Self { - env: DesktopEnv::new(), child_exit: Arc::new(AtomicBool::new(true)), is_child_running: Arc::new(AtomicBool::new(false)), } } - fn try_start_x_session(&mut self, username: &str, password: &str) -> ResultType<()> { + fn try_start_x_session( + &mut self, + desktop: &mut Desktop, + username: &str, + password: &str, + ) -> ResultType<()> { match get_user_by_name(username) { Some(userinfo) => { let mut client = pam::Client::with_password(pam_get_service_name())?; @@ -495,22 +392,24 @@ impl Desktop { .set_credentials(username, password); match client.authenticate() { Ok(_) => { - if self.env.is_ready() && self.env.username == username { + if desktop.is_x11() && desktop.username == username { return Ok(()); } - self.env.username = username.to_string(); - self.env.uid = userinfo.uid().to_string(); - self.env.protocal = Protocal::Unknown; - match self.start_x_session(&userinfo, password) { + // to-do: sid is empty if xorg is managed by this process + desktop.sid = "".to_owned(); + desktop.username = username.to_string(); + desktop.uid = userinfo.uid().to_string(); + desktop.protocal = Protocal::Unknown; + match self.start_x_session(desktop, &userinfo, password) { Ok(_) => { - log::info!("Succeeded to start x11, update env {:?}", &self.env); - self.env.update_env(); + log::info!("Succeeded to start x11, update env {:?}", &desktop); + desktop.update_env(); Ok(()) } Err(e) => { - self.env = DesktopEnv::new(); - self.env.update_env(); + *desktop = Desktop::new(); + desktop.update_env(); bail!("failed to start x session, {}", e); } } @@ -526,19 +425,24 @@ impl Desktop { } } - fn start_x_session(&mut self, userinfo: &User, password: &str) -> ResultType<()> { + fn start_x_session( + &mut self, + desktop: &mut Desktop, + userinfo: &User, + password: &str, + ) -> ResultType<()> { self.stop_children(); - let display_num = DesktopEnv::get_avail_display()?; + let display_num = Desktop::get_avail_display()?; // "xServer_ip:display_num.screen_num" - self.env.display = format!(":{}", display_num); + desktop.display = format!(":{}", display_num); let uid = userinfo.uid(); let gid = userinfo.primary_group_id(); let envs = HashMap::from([ ("SHELL", userinfo.shell().to_string_lossy().to_string()), ("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()), - ("USER", self.env.username.clone()), + ("USER", desktop.username.clone()), ("UID", userinfo.uid().to_string()), ("HOME", userinfo.home_dir().to_string_lossy().to_string()), ( @@ -549,7 +453,7 @@ impl Desktop { // ("XAUTHORITY", self.xauth.clone()), // (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()), ]); - let env = self.env.clone(); + let desktop_clone = desktop.clone(); self.child_exit.store(false, Ordering::SeqCst); let is_child_running = self.is_child_running.clone(); @@ -560,7 +464,7 @@ impl Desktop { match Self::start_x_session_thread( tx_res.clone(), is_child_running, - env, + desktop_clone, uid, gid, display_num, @@ -579,7 +483,7 @@ impl Desktop { match rx_res.recv_timeout(Duration::from_millis(10_000)) { Ok(res) => { if res == "" { - self.env.protocal = Protocal::X11; + desktop.protocal = Protocal::X11; Ok(()) } else { bail!(res) @@ -594,7 +498,7 @@ impl Desktop { fn start_x_session_thread( tx_res: SyncSender, is_child_running: Arc, - env: DesktopEnv, + desktop: Desktop, uid: u32, gid: u32, display_num: u32, @@ -604,16 +508,16 @@ impl Desktop { let mut client = pam::Client::with_password(pam_get_service_name())?; client .conversation_mut() - .set_credentials(&env.username, &password); + .set_credentials(&desktop.username, &password); client.authenticate()?; - client.set_item(pam::PamItemType::TTY, &env.display)?; + client.set_item(pam::PamItemType::TTY, &desktop.display)?; client.open_session()?; // fixme: FreeBSD kernel needs to login here. // see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556 - let (child_xorg, child_wm) = Self::start_x11(&env, uid, gid, display_num, &envs)?; + let (child_xorg, child_wm) = Self::start_x11(&desktop, uid, gid, display_num, &envs)?; is_child_running.store(true, Ordering::SeqCst); log::info!("Start xorg and wm done, notify and wait xtop x11"); @@ -646,25 +550,25 @@ impl Desktop { } fn start_x11( - env: &DesktopEnv, + desktop: &Desktop, uid: u32, gid: u32, display_num: u32, envs: &HashMap<&str, String>, ) -> ResultType<(Child, Child)> { - log::debug!("envs of user {}: {:?}", &env.username, &envs); + log::debug!("envs of user {}: {:?}", &desktop.username, &envs); - DesktopEnv::add_xauth_cookie(&env.xauth, &env.display, uid, gid, &envs)?; + Desktop::add_xauth_cookie(&desktop.xauth, &desktop.display, uid, gid, &envs)?; // Start Xorg - let mut child_xorg = Self::start_x_server(&env.xauth, &env.display, uid, gid, &envs)?; + let mut child_xorg = + Self::start_x_server(&desktop.xauth, &desktop.display, uid, gid, &envs)?; log::info!("xorg started, wait 10 secs to ensuer x server is running"); let max_wait_secs = 10; // wait x server running - if let Err(e) = - DesktopEnv::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) + if let Err(e) = Desktop::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) { match Self::wait_xorg_exit(&mut child_xorg) { Ok(msg) => log::info!("{}", msg), @@ -678,12 +582,12 @@ impl Desktop { log::info!( "xorg is running, start x window manager with DISPLAY: {}, XAUTHORITY: {}", - &env.display, - &env.xauth + &desktop.display, + &desktop.xauth ); - std::env::set_var("DISPLAY", &env.display); - std::env::set_var("XAUTHORITY", &env.xauth); + std::env::set_var("DISPLAY", &desktop.display); + std::env::set_var("XAUTHORITY", &desktop.xauth); // start window manager (startwm.sh) let child_wm = match Self::start_x_window_manager(uid, gid, &envs) { Ok(c) => c, @@ -803,10 +707,10 @@ impl Desktop { } fn try_wait_stop_x11(child_xorg: &mut Child, child_wm: &mut Child) -> bool { - let mut inst = DESKTOP_INST.lock().unwrap(); + let mut child_flags = CHILD_FLAGS.lock().unwrap(); let mut exited = true; - if let Some(inst) = &mut (*inst) { - if inst.child_exit.load(Ordering::SeqCst) { + if let Some(child_flags) = &mut (*child_flags) { + if child_flags.child_exit.load(Ordering::SeqCst) { exited = true; } else { exited = Self::try_wait_x11_child_exit(child_xorg, child_wm); @@ -814,8 +718,8 @@ impl Desktop { if exited { println!("=============================MYDEBUG begin to wait x11 children exit"); Self::wait_x11_children_exit(child_xorg, child_wm); - inst.is_child_running.store(false, Ordering::SeqCst); - inst.child_exit.store(true, Ordering::SeqCst); + child_flags.is_child_running.store(false, Ordering::SeqCst); + child_flags.child_exit.store(true, Ordering::SeqCst); } } exited @@ -949,3 +853,13 @@ impl ToString for Protocal { } } } + +impl From for Protocal { + fn from(value: String) -> Self { + match &value as &str { + ENV_DESKTOP_PROTOCAL__X11 => Protocal::X11, + ENV_DESKTOP_PROTOCAL_WAYLAND => Protocal::Wayland, + _ => Protocal::Unknown, + } + } +} diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 339a45bf3..5a68a4a03 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -54,8 +54,6 @@ impl RendezvousMediator { pub async fn start_all() { let mut nat_tested = false; check_zombie(); - #[cfg(target_os = "linux")] - crate::server::check_xdesktop(); let server = new_server(); if Config::get_nat_type() == NatType::UNKNOWN_NAT as i32 { crate::test_nat_type(); diff --git a/src/server.rs b/src/server.rs index 81c967ba1..681e7bed1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -356,40 +356,6 @@ pub fn check_zombie() { }); } -#[cfg(target_os = "linux")] -pub fn check_xdesktop() { - std::thread::spawn(|| { - use crate::platform::linux_desktop::{get_desktop_env, DesktopEnv}; - let mut desktop_env = DesktopEnv::new(); - loop { - let new_env = match get_desktop_env() { - Some(env) => env, - None => DesktopEnv::new(), - }; - - if new_env.is_ready() { - if !desktop_env.is_ready() { - desktop_env = new_env.clone(); - } else { - if !desktop_env.is_same_env(&new_env) { - log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env); - break; - } - } - } else { - if desktop_env.is_ready() { - log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env); - break; - } - } - - std::thread::sleep(Duration::from_millis(300)); - } - - crate::RendezvousMediator::restart(); - }); -} - /// Start the host server that allows the remote peer to control the current machine. /// /// # Arguments diff --git a/src/server/connection.rs b/src/server/connection.rs index 36190485e..a5c63a80e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1073,43 +1073,35 @@ impl Connection { fn try_start_desktop(_username: &str, _passsword: &str) -> String { #[cfg(target_os = "linux")] - { - if _username.is_empty() { - match crate::platform::linux_desktop::get_desktop_env() { - Some(desktop_env) => { - if desktop_env.is_ready() { - "" - } else { - LOGIN_MSG_XSESSION_NOT_READY - } - } - None => LOGIN_MSG_XDESKTOP_NOT_INITED, - } - .to_owned() + if _username.is_empty() { + let desktop = crate::platform::linux_desktop::get_desktop_env(); + if desktop.is_ready() { + "" } else { - match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) { - Ok(desktop_env) => { - if desktop_env.is_ready() { - if _username != desktop_env.username { - LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() - } else { - "".to_owned() - } + LOGIN_MSG_XSESSION_NOT_READY + } + .to_owned() + } else { + match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) { + Ok(desktop) => { + if desktop.is_ready() { + if _username != desktop.username { + LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() } else { - LOGIN_MSG_XSESSION_NOT_READY.to_owned() + "".to_owned() } + } else { + LOGIN_MSG_XSESSION_NOT_READY.to_owned() } - Err(e) => { - log::error!("Failed to start xsession {}", e); - LOGIN_MSG_XSESSION_FAILED.to_owned() - } + } + Err(e) => { + log::error!("Failed to start xsession {}", e); + LOGIN_MSG_XSESSION_FAILED.to_owned() } } } #[cfg(not(target_os = "linux"))] - { - "".to_owned() - } + "".to_owned() } fn validate_one_password(&self, password: String) -> bool { From 30375a98536a6b3306a605d22692587ded5f3fb2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 22:20:15 +0800 Subject: [PATCH 165/366] refact linux desktop env Signed-off-by: fufesou --- src/platform/linux.rs | 4 +- ...ux_desktop.rs => linux_desktop_manager.rs} | 672 ++++++------------ src/platform/mod.rs | 2 +- src/rendezvous_mediator.rs | 4 +- src/server/connection.rs | 17 +- 5 files changed, 250 insertions(+), 449 deletions(-) rename src/platform/{linux_desktop.rs => linux_desktop_manager.rs} (63%) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 857372bd7..85f763ec6 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,4 +1,3 @@ -use super::linux_desktop::{get_desktop_env, Desktop}; use super::{CursorData, ResultType}; use desktop::Desktop; pub use hbb_common::platform::linux::*; @@ -297,6 +296,7 @@ fn force_stop_server() { pub fn start_os_service() { stop_rustdesk_servers(); start_uinput_service(); + start_check_desktop_env(); let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -371,6 +371,8 @@ pub fn start_os_service() { } } + stop_check_desktop_env(); + if let Some(ps) = user_server.take().as_mut() { allow_err!(ps.kill()); } diff --git a/src/platform/linux_desktop.rs b/src/platform/linux_desktop_manager.rs similarity index 63% rename from src/platform/linux_desktop.rs rename to src/platform/linux_desktop_manager.rs index 0cd66512a..8b9e20640 100644 --- a/src/platform/linux_desktop.rs +++ b/src/platform/linux_desktop_manager.rs @@ -17,188 +17,161 @@ use users::{get_user_by_name, os::unix::UserExt, User}; lazy_static::lazy_static! { static ref DESKTOP_RUNNING: Arc = Arc::new(AtomicBool::new(false)); - static ref DESKTOP_ENV: Arc> = Arc::new(Mutex::new(Desktop::new())); - static ref CHILD_FLAGS: Arc>> = Arc::new(Mutex::new(None)); -} - -pub const VIRTUAL_X11_DESKTOP: &str = "xfce4"; -pub const VIRTUAL_X11_DESKTOP_START: &str = "startxfce4"; -pub const XFCE4_PANEL: &str = "xfce4-panel"; -pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary"; -pub const ENV_DESKTOP_PROTOCAL: &str = "RUSTDESK_PROTOCAL"; -pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland"; -pub const ENV_DESKTOP_PROTOCAL__X11: &str = "x11"; -pub const ENV_DESKTOP_PROTOCAL_UNKNOWN: &str = "unknown"; - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum Protocal { - Wayland, - X11, // Xorg - Unknown, -} - -#[derive(Debug, Clone)] -pub struct Desktop { - pub sid: String, - pub protocal: Protocal, - pub username: String, - pub uid: String, - pub display: String, - pub xauth: String, + static ref DESKTOP_MANAGER: Arc>> = Arc::new(Mutex::new(None)); } #[derive(Debug)] -struct ChildFlags { +struct DesktopManager { + x11_username: String, + child_username: String, child_exit: Arc, is_child_running: Arc, } -fn check_update_env() { - let mut child_flags = CHILD_FLAGS.lock().unwrap(); - let mut desktop = DESKTOP_ENV.lock().unwrap(); - - if let Some(child_flags) = &mut (*child_flags) { - if child_flags.is_child_running.load(Ordering::SeqCst) { +fn check_desktop_manager() { + let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); + if let Some(desktop_manager) = &mut (*desktop_manager) { + if desktop_manager.is_child_running.load(Ordering::SeqCst) { return; } - child_flags.child_exit.store(true, Ordering::SeqCst); - } - - if !desktop.sid.is_empty() && is_active(&desktop.sid) { - return; - } - - let old_desktop = desktop.clone(); - desktop.refresh(); - if !desktop.is_same_env(&old_desktop) { - desktop.update_env(); - log::debug!("desktop env changed, {:?}", &desktop); + desktop_manager.child_exit.store(true, Ordering::SeqCst); } } +// --server process pub fn start_xdesktop() { std::thread::spawn(|| { - if wait_xdesktop(20) { - log::info!("Wait desktop: default"); - } else { - log::info!("Wait desktop: none"); - } - *CHILD_FLAGS.lock().unwrap() = Some(ChildFlags::new()); + *DESKTOP_MANAGER.lock().unwrap() = Some(DesktopManager::new()); let interval = time::Duration::from_millis(super::SERVICE_INTERVAL); DESKTOP_RUNNING.store(true, Ordering::SeqCst); while DESKTOP_RUNNING.load(Ordering::SeqCst) { - check_update_env(); + check_desktop_manager(); std::thread::sleep(interval); } - log::info!("xdesktop update thread exit"); + log::info!("xdesktop child thread exit"); }); } pub fn stop_xdesktop() { DESKTOP_RUNNING.store(false, Ordering::SeqCst); + *DESKTOP_MANAGER.lock().unwrap() = None; } -#[inline] -pub fn get_desktop_env() -> Desktop { - DESKTOP_ENV.lock().unwrap().clone() -} +pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { + let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); + if let Some(desktop_manager) = &mut (*desktop_manager) { + if !desktop_manager.x11_username.is_empty() { + return Ok((desktop_manager.x11_username.clone(), true)); + } -pub fn try_start_x_session(username: &str, password: &str) -> ResultType { - let mut child_flags = CHILD_FLAGS.lock().unwrap(); - let mut desktop_lock = DESKTOP_ENV.lock().unwrap(); - let mut desktop = Desktop::new(); - if let Some(child_flags) = &mut (*child_flags) { - let _ = child_flags.try_start_x_session(&mut desktop, username, password)?; + let _ = desktop_manager.try_start_x_session(username, password)?; log::debug!( "try_start_x_session, username: {}, {:?}", &username, - &child_flags + &desktop_manager ); - *desktop_lock = desktop.clone(); - Ok(desktop) + Ok(( + desktop_manager.child_username.clone(), + desktop_manager.is_running(), + )) } else { bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED); } } -fn wait_xdesktop(timeout_secs: u64) -> bool { - let wait_begin = Instant::now(); - while wait_begin.elapsed().as_secs() < timeout_secs { - let uid = &get_values_of_seat0(&[1])[0]; - if !uid.is_empty() { - return true; +pub fn get_username() -> String { + match &*DESKTOP_MANAGER.lock().unwrap() { + Some(manager) => { + if !manager.x11_username.is_empty() { + manager.x11_username.clone() + } else { + if manager.is_running() && !manager.child_username.is_empty() { + manager.child_username.clone() + } else { + "".to_owned() + } + } } + None => "".to_owned(), + } +} - if let Ok(output) = run_cmds(format!( - "ps -ef | grep -v 'grep' | grep -E 'gnome-session-binary|{}'", - XFCE4_PANEL - )) { - if !output.is_empty() { - log::info!("wait xdesktop: find xclient {}", &output); - return true; +impl Drop for DesktopManager { + fn drop(&mut self) { + self.stop_children(); + } +} + +impl DesktopManager { + fn fatal_exit() { + std::process::exit(0); + } + + pub fn new() -> Self { + let mut x11_username = "".to_owned(); + let seat0_values = get_values_of_seat0(&[0, 1, 2]); + if !seat0_values[0].is_empty() { + if "x11" == get_display_server_of_session(&seat0_values[1]) { + x11_username = seat0_values[2].clone(); } } - std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL)); - } - - false -} - -impl Desktop { - pub fn new() -> Self { - let xauth = get_env_var("XAUTHORITY"); - Self { - sid: "".to_owned(), - protocal: Protocal::Unknown, - username: "".to_owned(), - uid: "".to_owned(), - display: "".to_owned(), - xauth: if xauth.is_empty() { - "/tmp/.Xauthority".to_owned() - } else { - xauth - }, + x11_username, + child_username: "".to_owned(), + child_exit: Arc::new(AtomicBool::new(true)), + is_child_running: Arc::new(AtomicBool::new(false)), } } - fn update_env(&self) { - if self.is_x11() { - std::env::set_var("DISPLAY", &self.display); - std::env::set_var("XAUTHORITY", &self.xauth); - std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); + #[inline] + fn get_xauth() -> String { + let xauth = get_env_var("XAUTHORITY"); + if xauth.is_empty() { + "/tmp/.Xauthority".to_owned() } else { - std::env::set_var("DISPLAY", ""); - std::env::set_var("XAUTHORITY", ""); - std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string()); + xauth } } - pub fn is_same_env(&self, other: &Self) -> bool { - self.sid == other.sid - && self.protocal == other.protocal - && self.uid == other.uid - && self.display == other.display - && self.xauth == other.xauth + #[inline] + fn is_running(&self) -> bool { + self.is_child_running.load(Ordering::SeqCst) } - #[inline] - pub fn is_x11(&self) -> bool { - self.protocal == Protocal::X11 - } + fn try_start_x_session(&mut self, username: &str, password: &str) -> ResultType<()> { + match get_user_by_name(username) { + Some(userinfo) => { + let mut client = pam::Client::with_password(pam_get_service_name())?; + client + .conversation_mut() + .set_credentials(username, password); + match client.authenticate() { + Ok(_) => { + if self.is_running() { + return Ok(()); + } - #[inline] - pub fn is_wayland(&self) -> bool { - self.protocal == Protocal::Wayland - } - - #[inline] - pub fn is_ready(&self) -> bool { - match self.protocal { - Protocal::X11 | Protocal::Wayland => true, - _ => false, + match self.start_x_session(&userinfo, username, password) { + Ok(_) => { + log::info!("Succeeded to start x11"); + self.child_username = username.to_string(); + Ok(()) + } + Err(e) => { + bail!("failed to start x session, {}", e); + } + } + } + Err(e) => { + bail!("failed to check user pass for {}, {}", username, e); + } + } + } + None => { + bail!("failed to get userinfo of {}", username); + } } } @@ -220,83 +193,131 @@ impl Desktop { || Path::new(&format!("/tmp/.X{}-lock", display)).exists() } - fn get_display(&mut self) { - self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10); - if self.display.is_empty() { - self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10); - } - if self.display.is_empty() { - self.display = Self::get_display_by_user(&self.username); - } - if self.display.is_empty() { - self.display = ":0".to_owned(); - } - self.display = self - .display - .replace(&whoami::hostname(), "") - .replace("localhost", ""); - } + fn start_x_session( + &mut self, + userinfo: &User, + username: &str, + password: &str, + ) -> ResultType<()> { + self.stop_children(); - fn get_xauth(&mut self) { - self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10); - if self.xauth.is_empty() { - get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10); - } + let display_num = Self::get_avail_display()?; + // "xServer_ip:display_num.screen_num" - let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid); - if self.xauth.is_empty() { - self.xauth = if std::path::Path::new(&gdm).exists() { - gdm - } else { - let username = &self.username; - if username == "root" { - format!("/{}/.Xauthority", username) + let uid = userinfo.uid(); + let gid = userinfo.primary_group_id(); + let envs = HashMap::from([ + ("SHELL", userinfo.shell().to_string_lossy().to_string()), + ("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()), + ("USER", username.to_string()), + ("UID", userinfo.uid().to_string()), + ("HOME", userinfo.home_dir().to_string_lossy().to_string()), + ( + "XDG_RUNTIME_DIR", + format!("/run/user/{}", userinfo.uid().to_string()), + ), + // ("DISPLAY", self.display.clone()), + // ("XAUTHORITY", self.xauth.clone()), + // (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()), + ]); + self.child_exit.store(false, Ordering::SeqCst); + let is_child_running = self.is_child_running.clone(); + + let (tx_res, rx_res) = sync_channel(1); + let password = password.to_string(); + let username = username.to_string(); + // start x11 + std::thread::spawn(move || { + match Self::start_x_session_thread( + tx_res.clone(), + is_child_running, + uid, + gid, + display_num, + username, + password, + envs, + ) { + Ok(_) => {} + Err(e) => { + log::error!("Failed to start x session thread"); + allow_err!(tx_res.send(format!("Failed to start x session thread, {}", e))); + } + } + }); + + // wait x11 + match rx_res.recv_timeout(Duration::from_millis(10_000)) { + Ok(res) => { + if res == "" { + Ok(()) } else { - let tmp = format!("/home/{}/.Xauthority", username); - if std::path::Path::new(&tmp).exists() { - tmp - } else { - format!("/var/lib/{}/.Xauthority", username) - } + bail!(res) } - }; + } + Err(e) => { + bail!("Failed to recv x11 result {}", e) + } } } - fn get_display_by_user(user: &str) -> String { - // log::debug!("w {}", &user); - if let Ok(output) = std::process::Command::new("w").arg(&user).output() { - for line in String::from_utf8_lossy(&output.stdout).lines() { - let mut iter = line.split_whitespace(); - let b = iter.nth(2); - if let Some(b) = b { - if b.starts_with(":") { - return b.to_owned(); + #[inline] + fn display_from_num(num: u32) -> String { + format!(":{num}") + } + + fn start_x_session_thread( + tx_res: SyncSender, + is_child_running: Arc, + uid: u32, + gid: u32, + display_num: u32, + username: String, + password: String, + envs: HashMap<&str, String>, + ) -> ResultType<()> { + let mut client = pam::Client::with_password(pam_get_service_name())?; + client + .conversation_mut() + .set_credentials(&username, &password); + client.authenticate()?; + + client.set_item(pam::PamItemType::TTY, &Self::display_from_num(display_num))?; + client.open_session()?; + + // fixme: FreeBSD kernel needs to login here. + // see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556 + + let (child_xorg, child_wm) = Self::start_x11(uid, gid, username, display_num, &envs)?; + is_child_running.store(true, Ordering::SeqCst); + + log::info!("Start xorg and wm done, notify and wait xtop x11"); + allow_err!(tx_res.send("".to_owned())); + + Self::wait_stop_x11(child_xorg, child_wm); + log::info!("Wait x11 stop done"); + Ok(()) + } + + fn wait_xorg_exit(child_xorg: &mut Child) -> ResultType { + if let Ok(_) = child_xorg.kill() { + for _ in 0..3 { + match child_xorg.try_wait() { + Ok(Some(status)) => return Ok(format!("Xorg exit with {}", status)), + Ok(None) => {} + Err(e) => { + // fatal error + log::error!("Failed to wait xorg process, {}", e); + bail!("Failed to wait xorg process, {}", e) } } + std::thread::sleep(std::time::Duration::from_millis(1_000)); } + log::error!("Failed to wait xorg process, not exit"); + bail!("Failed to wait xorg process, not exit") + } else { + Ok("Xorg is already exited".to_owned()) } - // above not work for gdm user - //log::debug!("ls -l /tmp/.X11-unix/"); - let mut last = "".to_owned(); - if let Ok(output) = std::process::Command::new("ls") - .args(vec!["-l", "/tmp/.X11-unix/"]) - .output() - { - for line in String::from_utf8_lossy(&output.stdout).lines() { - let mut iter = line.split_whitespace(); - let user_field = iter.nth(2); - if let Some(x) = iter.last() { - if x.starts_with("X") { - last = x.replace("X", ":").to_owned(); - if user_field == Some(&user) { - return last; - } - } - } - } - } - last } fn add_xauth_cookie( @@ -343,233 +364,28 @@ impl Desktop { } } - fn refresh(&mut self) { - *self = Self::new(); - - let seat0_values = get_values_of_seat0(&[0, 1, 2]); - if seat0_values[0].is_empty() { - return; - } - - self.sid = seat0_values[0].clone(); - self.uid = seat0_values[1].clone(); - self.username = seat0_values[2].clone(); - self.protocal = get_display_server_of_session(&self.sid).into(); - self.get_display(); - self.get_xauth(); - } -} - -impl Drop for ChildFlags { - fn drop(&mut self) { - self.stop_children(); - } -} - -impl ChildFlags { - fn fatal_exit() { - std::process::exit(0); - } - - pub fn new() -> Self { - Self { - child_exit: Arc::new(AtomicBool::new(true)), - is_child_running: Arc::new(AtomicBool::new(false)), - } - } - - fn try_start_x_session( - &mut self, - desktop: &mut Desktop, - username: &str, - password: &str, - ) -> ResultType<()> { - match get_user_by_name(username) { - Some(userinfo) => { - let mut client = pam::Client::with_password(pam_get_service_name())?; - client - .conversation_mut() - .set_credentials(username, password); - match client.authenticate() { - Ok(_) => { - if desktop.is_x11() && desktop.username == username { - return Ok(()); - } - - // to-do: sid is empty if xorg is managed by this process - desktop.sid = "".to_owned(); - desktop.username = username.to_string(); - desktop.uid = userinfo.uid().to_string(); - desktop.protocal = Protocal::Unknown; - match self.start_x_session(desktop, &userinfo, password) { - Ok(_) => { - log::info!("Succeeded to start x11, update env {:?}", &desktop); - desktop.update_env(); - Ok(()) - } - Err(e) => { - *desktop = Desktop::new(); - desktop.update_env(); - bail!("failed to start x session, {}", e); - } - } - } - Err(e) => { - bail!("failed to check user pass for {}, {}", username, e); - } - } - } - None => { - bail!("failed to get userinfo of {}", username); - } - } - } - - fn start_x_session( - &mut self, - desktop: &mut Desktop, - userinfo: &User, - password: &str, - ) -> ResultType<()> { - self.stop_children(); - - let display_num = Desktop::get_avail_display()?; - // "xServer_ip:display_num.screen_num" - desktop.display = format!(":{}", display_num); - - let uid = userinfo.uid(); - let gid = userinfo.primary_group_id(); - let envs = HashMap::from([ - ("SHELL", userinfo.shell().to_string_lossy().to_string()), - ("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()), - ("USER", desktop.username.clone()), - ("UID", userinfo.uid().to_string()), - ("HOME", userinfo.home_dir().to_string_lossy().to_string()), - ( - "XDG_RUNTIME_DIR", - format!("/run/user/{}", userinfo.uid().to_string()), - ), - // ("DISPLAY", self.display.clone()), - // ("XAUTHORITY", self.xauth.clone()), - // (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()), - ]); - let desktop_clone = desktop.clone(); - self.child_exit.store(false, Ordering::SeqCst); - let is_child_running = self.is_child_running.clone(); - - let (tx_res, rx_res) = sync_channel(1); - let password = password.to_string(); - // start x11 - std::thread::spawn(move || { - match Self::start_x_session_thread( - tx_res.clone(), - is_child_running, - desktop_clone, - uid, - gid, - display_num, - password, - envs, - ) { - Ok(_) => {} - Err(e) => { - log::error!("Failed to start x session thread"); - allow_err!(tx_res.send(format!("Failed to start x session thread, {}", e))); - } - } - }); - - // wait x11 - match rx_res.recv_timeout(Duration::from_millis(10_000)) { - Ok(res) => { - if res == "" { - desktop.protocal = Protocal::X11; - Ok(()) - } else { - bail!(res) - } - } - Err(e) => { - bail!("Failed to recv x11 result {}", e) - } - } - } - - fn start_x_session_thread( - tx_res: SyncSender, - is_child_running: Arc, - desktop: Desktop, - uid: u32, - gid: u32, - display_num: u32, - password: String, - envs: HashMap<&str, String>, - ) -> ResultType<()> { - let mut client = pam::Client::with_password(pam_get_service_name())?; - client - .conversation_mut() - .set_credentials(&desktop.username, &password); - client.authenticate()?; - - client.set_item(pam::PamItemType::TTY, &desktop.display)?; - client.open_session()?; - - // fixme: FreeBSD kernel needs to login here. - // see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556 - - let (child_xorg, child_wm) = Self::start_x11(&desktop, uid, gid, display_num, &envs)?; - is_child_running.store(true, Ordering::SeqCst); - - log::info!("Start xorg and wm done, notify and wait xtop x11"); - allow_err!(tx_res.send("".to_owned())); - - Self::wait_stop_x11(child_xorg, child_wm); - log::info!("Wait x11 stop done"); - Ok(()) - } - - fn wait_xorg_exit(child_xorg: &mut Child) -> ResultType { - if let Ok(_) = child_xorg.kill() { - for _ in 0..3 { - match child_xorg.try_wait() { - Ok(Some(status)) => return Ok(format!("Xorg exit with {}", status)), - Ok(None) => {} - Err(e) => { - // fatal error - log::error!("Failed to wait xorg process, {}", e); - bail!("Failed to wait xorg process, {}", e) - } - } - std::thread::sleep(std::time::Duration::from_millis(1_000)); - } - log::error!("Failed to wait xorg process, not exit"); - bail!("Failed to wait xorg process, not exit") - } else { - Ok("Xorg is already exited".to_owned()) - } - } - fn start_x11( - desktop: &Desktop, uid: u32, gid: u32, + username: String, display_num: u32, envs: &HashMap<&str, String>, ) -> ResultType<(Child, Child)> { - log::debug!("envs of user {}: {:?}", &desktop.username, &envs); + log::debug!("envs of user {}: {:?}", &username, &envs); - Desktop::add_xauth_cookie(&desktop.xauth, &desktop.display, uid, gid, &envs)?; + let xauth = Self::get_xauth(); + let display = Self::display_from_num(display_num); + + Self::add_xauth_cookie(&xauth, &display, uid, gid, &envs)?; // Start Xorg - let mut child_xorg = - Self::start_x_server(&desktop.xauth, &desktop.display, uid, gid, &envs)?; + let mut child_xorg = Self::start_x_server(&xauth, &display, uid, gid, &envs)?; log::info!("xorg started, wait 10 secs to ensuer x server is running"); let max_wait_secs = 10; // wait x server running - if let Err(e) = Desktop::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) - { + if let Err(e) = Self::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs) { match Self::wait_xorg_exit(&mut child_xorg) { Ok(msg) => log::info!("{}", msg), Err(e) => { @@ -582,12 +398,12 @@ impl ChildFlags { log::info!( "xorg is running, start x window manager with DISPLAY: {}, XAUTHORITY: {}", - &desktop.display, - &desktop.xauth + &display, + &xauth ); - std::env::set_var("DISPLAY", &desktop.display); - std::env::set_var("XAUTHORITY", &desktop.xauth); + std::env::set_var("DISPLAY", &display); + std::env::set_var("XAUTHORITY", &xauth); // start window manager (startwm.sh) let child_wm = match Self::start_x_window_manager(uid, gid, &envs) { Ok(c) => c, @@ -707,10 +523,10 @@ impl ChildFlags { } fn try_wait_stop_x11(child_xorg: &mut Child, child_wm: &mut Child) -> bool { - let mut child_flags = CHILD_FLAGS.lock().unwrap(); + let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); let mut exited = true; - if let Some(child_flags) = &mut (*child_flags) { - if child_flags.child_exit.load(Ordering::SeqCst) { + if let Some(desktop_manager) = &mut (*desktop_manager) { + if desktop_manager.child_exit.load(Ordering::SeqCst) { exited = true; } else { exited = Self::try_wait_x11_child_exit(child_xorg, child_wm); @@ -718,8 +534,10 @@ impl ChildFlags { if exited { println!("=============================MYDEBUG begin to wait x11 children exit"); Self::wait_x11_children_exit(child_xorg, child_wm); - child_flags.is_child_running.store(false, Ordering::SeqCst); - child_flags.child_exit.store(true, Ordering::SeqCst); + desktop_manager + .is_child_running + .store(false, Ordering::SeqCst); + desktop_manager.child_exit.store(true, Ordering::SeqCst); } } exited @@ -843,23 +661,3 @@ fn pam_get_service_name() -> &'static str { "gdm" } } - -impl ToString for Protocal { - fn to_string(&self) -> String { - match self { - Protocal::X11 => ENV_DESKTOP_PROTOCAL__X11.to_owned(), - Protocal::Wayland => ENV_DESKTOP_PROTOCAL_WAYLAND.to_owned(), - Protocal::Unknown => ENV_DESKTOP_PROTOCAL_UNKNOWN.to_owned(), - } - } -} - -impl From for Protocal { - fn from(value: String) -> Self { - match &value as &str { - ENV_DESKTOP_PROTOCAL__X11 => Protocal::X11, - ENV_DESKTOP_PROTOCAL_WAYLAND => Protocal::Wayland, - _ => Protocal::Unknown, - } - } -} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 16bcc775a..7c9b2f0b0 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -18,7 +18,7 @@ pub mod delegate; pub mod linux; #[cfg(target_os = "linux")] -pub mod linux_desktop; +pub mod linux_desktop_manager; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::{message_proto::CursorData, ResultType}; diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 5a68a4a03..3fef9747b 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -73,7 +73,7 @@ impl RendezvousMediator { }); } #[cfg(target_os = "linux")] - crate::platform::linux_desktop::start_xdesktop(); + crate::platform::linux_desktop_manager::start_xdesktop(); SHOULD_EXIT.store(false, Ordering::SeqCst); while !SHOULD_EXIT.load(Ordering::SeqCst) { Config::reset_online(); @@ -100,7 +100,7 @@ impl RendezvousMediator { sleep(1.).await; } #[cfg(target_os = "linux")] - crate::platform::linux_desktop::stop_xdesktop(); + crate::platform::linux_desktop_manager::stop_xdesktop(); } pub async fn start(server: ServerPtr, host: String) -> ResultType<()> { diff --git a/src/server/connection.rs b/src/server/connection.rs index a5c63a80e..2d9e60f04 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1074,18 +1074,19 @@ impl Connection { fn try_start_desktop(_username: &str, _passsword: &str) -> String { #[cfg(target_os = "linux")] if _username.is_empty() { - let desktop = crate::platform::linux_desktop::get_desktop_env(); - if desktop.is_ready() { - "" - } else { + let username = crate::platform::linux_desktop_manager::get_username(); + if username.is_empty() { LOGIN_MSG_XSESSION_NOT_READY + } else { + "" } .to_owned() } else { - match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) { - Ok(desktop) => { - if desktop.is_ready() { - if _username != desktop.username { + match crate::platform::linux_desktop_manager::try_start_x_session(_username, _passsword) + { + Ok((username, x11_ready)) => { + if x11_ready { + if _username != username { LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() } else { "".to_owned() From e4c2e9af00f4dda2ae18e898a196b848687ab744 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 28 Mar 2023 22:47:29 +0800 Subject: [PATCH 166/366] refact linux desktop Signed-off-by: fufesou --- src/platform/linux.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 85f763ec6..dda4b115b 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -296,7 +296,6 @@ fn force_stop_server() { pub fn start_os_service() { stop_rustdesk_servers(); start_uinput_service(); - start_check_desktop_env(); let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -371,8 +370,6 @@ pub fn start_os_service() { } } - stop_check_desktop_env(); - if let Some(ps) = user_server.take().as_mut() { allow_err!(ps.kill()); } @@ -384,13 +381,13 @@ pub fn start_os_service() { #[inline] pub fn get_active_user_id_name() -> (String, String) { - let desktop = get_desktop_env(); - (desktop.uid.clone(), desktop.username.clone()) + let vec_id_name = get_values_of_seat0(&[1, 2]); + (vec_id_name[0].clone(), vec_id_name[1].clone()) } #[inline] pub fn get_active_userid() -> String { - get_desktop_env().uid.clone() + get_values_of_seat0(&[1])[0].clone() } fn get_cm() -> bool { @@ -436,7 +433,7 @@ fn _get_display_manager() -> String { #[inline] pub fn get_active_username() -> String { - get_desktop_env().username.clone() + get_values_of_seat0(&[2])[0].clone() } pub fn get_active_user_home() -> Option { From 6149c7f4777fe1f5a83188fd38a41487b5a90354 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Mar 2023 12:08:24 +0800 Subject: [PATCH 167/366] refact linux desktop env Signed-off-by: fufesou --- src/platform/linux.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index dda4b115b..28c4d9252 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -769,6 +769,7 @@ mod desktop { pub protocal: String, pub display: String, pub xauth: String, + pub is_rustdesk_subprocess: bool, } impl Desktop { @@ -787,6 +788,11 @@ mod desktop { self.sid.is_empty() } + #[inline] + pub fn is_headless(&self) -> bool { + self.sid.is_empty() || self.is_rustdesk_subprocess + } + fn get_display(&mut self) { self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10); if self.display.is_empty() { @@ -901,6 +907,16 @@ mod desktop { last } + fn set_is_subprocess(&mut self) { + self.is_rustdesk_subprocess = false; + let cmd = "ps -ef | grep 'rustdesk/xorg.conf' | grep -v grep | wc -l"; + if let Ok(res) = run_cmds(cmd) { + if res.trim() != "0" { + self.is_rustdesk_subprocess = true; + } + } + } + pub fn refresh(&mut self) { if !self.sid.is_empty() && is_active(&self.sid) { return; @@ -909,6 +925,7 @@ mod desktop { let seat0_values = get_values_of_seat0(&[0, 1, 2]); if seat0_values[0].is_empty() { *self = Self::default(); + self.is_rustdesk_subprocess = false; return; } @@ -924,6 +941,9 @@ mod desktop { self.get_display(); self.get_xauth(); + self.set_is_subprocess(); + + println!("REMOVE ME ======================================= desktop: {:?}", self); } } } From b82207f20bb13428c781df8edb0dee4c65a40a65 Mon Sep 17 00:00:00 2001 From: qcloud Date: Wed, 29 Mar 2023 17:39:16 +0800 Subject: [PATCH 168/366] virtual display, linux, debug Signed-off-by: qcloud --- libs/hbb_common/src/platform/mod.rs | 2 +- src/platform/linux.rs | 12 +++- src/platform/linux_desktop_manager.rs | 3 +- src/server/connection.rs | 83 ++++++--------------------- 4 files changed, 31 insertions(+), 69 deletions(-) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index c37c8aaf5..f4a1122e5 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -57,6 +57,6 @@ where { unsafe { GLOBAL_CALLBACK = Some(Box::new(callback)); - libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); + // libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 28c4d9252..b166f1531 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -225,6 +225,13 @@ fn stop_rustdesk_servers() { )); } +#[inline] +fn stop_xorg_subprocess() { + let _ = run_cmds(format!( + r##"ps -ef | grep '/etc/rustdesk/xorg.conf' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, + )); +} + fn should_start_server( try_x11: bool, uid: &mut String, @@ -295,6 +302,7 @@ fn force_stop_server() { pub fn start_os_service() { stop_rustdesk_servers(); + stop_xorg_subprocess(); start_uinput_service(); let running = Arc::new(AtomicBool::new(true)); @@ -329,6 +337,7 @@ pub fn start_os_service() { &mut last_restart, &mut server, ) { + stop_xorg_subprocess(); force_stop_server(); start_server(None, &mut server); } @@ -921,7 +930,6 @@ mod desktop { if !self.sid.is_empty() && is_active(&self.sid) { return; } - let seat0_values = get_values_of_seat0(&[0, 1, 2]); if seat0_values[0].is_empty() { *self = Self::default(); @@ -942,8 +950,6 @@ mod desktop { self.get_display(); self.get_xauth(); self.set_is_subprocess(); - - println!("REMOVE ME ======================================= desktop: {:?}", self); } } } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 8b9e20640..ea7189b82 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -111,6 +111,7 @@ impl DesktopManager { pub fn new() -> Self { let mut x11_username = "".to_owned(); let seat0_values = get_values_of_seat0(&[0, 1, 2]); + println!("REMOVE ME ======================== DesktopManager seato values {:?}", &seat0_values); if !seat0_values[0].is_empty() { if "x11" == get_display_server_of_session(&seat0_values[1]) { x11_username = seat0_values[2].clone(); @@ -608,7 +609,7 @@ impl DesktopManager { //"-logfile", //"/tmp/RustDesk_xorg.log", "-config", - "rustdesk/xorg.conf", + "/etc/rustdesk/xorg.conf", "-auth", xauth, display, diff --git a/src/server/connection.rs b/src/server/connection.rs index 2d9e60f04..beefaac1a 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1075,6 +1075,7 @@ impl Connection { #[cfg(target_os = "linux")] if _username.is_empty() { let username = crate::platform::linux_desktop_manager::get_username(); + println!("REMOVE ME ===================================== try_start_desktop username '{}'", &username); if username.is_empty() { LOGIN_MSG_XSESSION_NOT_READY } else { @@ -1274,6 +1275,7 @@ impl Connection { Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), None => Self::try_start_desktop("", ""), }; + // If err is LOGIN_MSG_XSESSION_NOT_READY, just keep this msg and go on checking password. if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_XSESSION_NOT_READY { self.send_login_error(desktop_err).await; return true; @@ -1302,73 +1304,26 @@ impl Connection { self.send_login_error("Connection not allowed").await; return false; } else if self.is_recent_session() { - self.try_start_cm(lr.my_id, lr.my_name, true); - self.send_logon_response().await; - if self.port_forward_socket.is_some() { - return false; - } - } else if lr.password.is_empty() { - self.try_start_cm(lr.my_id, lr.my_name, false); - } else { - let mut failure = LOGIN_FAILURES - .lock() - .unwrap() - .get(&self.ip) - .map(|x| x.clone()) - .unwrap_or((0, 0, 0)); - let time = (get_time() / 60_000) as i32; - if failure.2 > 30 { - self.send_login_error("Too many wrong password attempts") - .await; - Self::post_alarm_audit( - AlarmAuditType::ManyWrongPassword, - true, - json!({ - "ip":self.ip, - }), - ); - } else if time == failure.0 && failure.1 > 6 { - self.send_login_error("Please try 1 minute later").await; - Self::post_alarm_audit( - AlarmAuditType::FrequentAttempt, - true, - json!({ - "ip":self.ip, - }), - ); - } else if !self.validate_password() { - if failure.0 == time { - failure.1 += 1; - failure.2 += 1; - } else { - failure.0 = time; - failure.1 = 1; - failure.2 += 1; - } - LOGIN_FAILURES - .lock() - .unwrap() - .insert(self.ip.clone(), failure); - if desktop_err.is_empty() { - self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; - self.try_start_cm(lr.my_id, lr.my_name, false); - } else { - self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG) - .await; + if desktop_err.is_empty() { + self.try_start_cm(lr.my_id, lr.my_name, true); + self.send_logon_response().await; + if self.port_forward_socket.is_some() { + return false; } } else { - if failure.0 != 0 { - LOGIN_FAILURES.lock().unwrap().remove(&self.ip); - } - if desktop_err.is_empty() { - self.send_logon_response().await; - self.try_start_cm(lr.my_id, lr.my_name, true); - if self.port_forward_socket.is_some() { - return false; - } - } else { - self.send_login_error(desktop_err).await; + self.send_login_error(desktop_err).await; + } + } else { + if desktop_err.is_empty() { + println!("REMOVE ME =============================== send logon response"); + self.send_logon_response().await; + self.try_start_cm(lr.my_id, lr.my_name, true); + if self.port_forward_socket.is_some() { + return false; } + } else { + println!("REMOVE ME =============================== send login error {}", &desktop_err); + self.send_login_error(desktop_err).await; } } } else if let Some(message::Union::TestDelay(t)) = msg.union { From bcf08ba26d7adadb8a1f7777d3c173a334bbf357 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Mar 2023 18:31:42 +0800 Subject: [PATCH 169/366] virtual display, linux, debug Signed-off-by: fufesou --- src/platform/linux.rs | 2 +- src/platform/linux_desktop_manager.rs | 35 +++------------------------ src/server/connection.rs | 3 --- 3 files changed, 4 insertions(+), 36 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index b166f1531..8c4a68848 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -227,7 +227,7 @@ fn stop_rustdesk_servers() { #[inline] fn stop_xorg_subprocess() { - let _ = run_cmds(format!( + let _ = run_cmds(&format!( r##"ps -ef | grep '/etc/rustdesk/xorg.conf' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index ea7189b82..b21b2d5b1 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -111,7 +111,6 @@ impl DesktopManager { pub fn new() -> Self { let mut x11_username = "".to_owned(); let seat0_values = get_values_of_seat0(&[0, 1, 2]); - println!("REMOVE ME ======================== DesktopManager seato values {:?}", &seat0_values); if !seat0_values[0].is_empty() { if "x11" == get_display_server_of_session(&seat0_values[1]) { x11_username = seat0_values[2].clone(); @@ -427,10 +426,6 @@ impl DesktopManager { fn try_wait_x11_child_exit(child_xorg: &mut Child, child_wm: &mut Child) -> bool { match child_xorg.try_wait() { Ok(Some(status)) => { - println!( - "=============================MYDEBUG Xorg exit with {}", - status - ); log::info!("Xorg exit with {}", status); return true; } @@ -440,10 +435,7 @@ impl DesktopManager { match child_wm.try_wait() { Ok(Some(status)) => { - println!( - "=============================MYDEBUG: wm exit with {}", - status - ); + // Logout may result "wm exit with signal: 11 (SIGSEGV) (core dumped)" log::info!("wm exit with {}", status); return true; } @@ -460,20 +452,12 @@ impl DesktopManager { for _ in 0..2 { match child_xorg.try_wait() { Ok(Some(status)) => { - println!( - "=============================MYDEBUG Xorg exit with {}", - status - ); log::info!("Xorg exit with {}", status); exited = true; break; } Ok(None) => {} Err(e) => { - println!( - "=============================MYDEBUG Failed to wait xorg process, {}", - &e - ); log::error!("Failed to wait xorg process, {}", e); Self::fatal_exit(); } @@ -481,9 +465,6 @@ impl DesktopManager { std::thread::sleep(std::time::Duration::from_millis(1_000)); } if !exited { - println!( - "=============================MYDEBUG Failed to wait child xorg, after kill()" - ); log::error!("Failed to wait child xorg, after kill()"); // try kill -9? } @@ -494,19 +475,12 @@ impl DesktopManager { for _ in 0..2 { match child_wm.try_wait() { Ok(Some(status)) => { - println!( - "=============================MYDEBUG wm exit with {}", - status - ); + // Logout may result "wm exit with signal: 11 (SIGSEGV) (core dumped)" log::info!("wm exit with {}", status); exited = true; } Ok(None) => {} Err(e) => { - println!( - "=============================MYDEBUG Failed to wait wm process, {}", - &e - ); log::error!("Failed to wait wm process, {}", e); Self::fatal_exit(); } @@ -514,9 +488,6 @@ impl DesktopManager { std::thread::sleep(std::time::Duration::from_millis(1_000)); } if !exited { - println!( - "=============================MYDEBUG Failed to wait child xorg, after kill()" - ); log::error!("Failed to wait child xorg, after kill()"); // try kill -9? } @@ -533,7 +504,7 @@ impl DesktopManager { exited = Self::try_wait_x11_child_exit(child_xorg, child_wm); } if exited { - println!("=============================MYDEBUG begin to wait x11 children exit"); + log::debug!("Wait x11 children exiting"); Self::wait_x11_children_exit(child_xorg, child_wm); desktop_manager .is_child_running diff --git a/src/server/connection.rs b/src/server/connection.rs index beefaac1a..6bbbffefc 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1075,7 +1075,6 @@ impl Connection { #[cfg(target_os = "linux")] if _username.is_empty() { let username = crate::platform::linux_desktop_manager::get_username(); - println!("REMOVE ME ===================================== try_start_desktop username '{}'", &username); if username.is_empty() { LOGIN_MSG_XSESSION_NOT_READY } else { @@ -1315,14 +1314,12 @@ impl Connection { } } else { if desktop_err.is_empty() { - println!("REMOVE ME =============================== send logon response"); self.send_logon_response().await; self.try_start_cm(lr.my_id, lr.my_name, true); if self.port_forward_socket.is_some() { return false; } } else { - println!("REMOVE ME =============================== send login error {}", &desktop_err); self.send_login_error(desktop_err).await; } } From c944d6093d0487bd6a2a995a6e3f031c8dab86a8 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Mar 2023 20:25:41 +0800 Subject: [PATCH 170/366] virtual display, linux, debug Signed-off-by: fufesou --- flutter/lib/models/model.dart | 4 +- libs/hbb_common/src/platform/mod.rs | 2 +- src/server/connection.rs | 85 +++++++++++++++++++++++------ 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 155956c74..470bb47c3 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -294,10 +294,10 @@ class FfiModel with ChangeNotifier { } else if (type == 'input-password') { enterPasswordDialog(id, dialogManager); } else if (type == 'xsession-login' || type == 'xsession-re-login') { - // to-do + enterUserLoginDialog(id, dialogManager); } else if (type == 'xsession-login-password' || type == 'xsession-login-password') { - // to-do + enterUserLoginAndPasswordDialog(id, dialogManager); } else if (type == 'restarting') { showMsgBox(id, type, title, text, link, false, dialogManager, hasCancel: false); diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index f4a1122e5..c37c8aaf5 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -57,6 +57,6 @@ where { unsafe { GLOBAL_CALLBACK = Some(Box::new(callback)); - // libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); + libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } diff --git a/src/server/connection.rs b/src/server/connection.rs index 6bbbffefc..2f86ca5f0 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1285,16 +1285,11 @@ impl Connection { } else if password::approve_mode() == ApproveMode::Click || password::approve_mode() == ApproveMode::Both && !password::has_valid_password() { - if desktop_err.is_empty() { - self.try_start_cm(lr.my_id, lr.my_name, false); - if hbb_common::get_version_number(&lr.version) - >= hbb_common::get_version_number("1.2.0") - { - self.send_login_error("No Password Access").await; - } - } else { - self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY) - .await; + self.try_start_cm(lr.my_id, lr.my_name, false); + if hbb_common::get_version_number(&lr.version) + >= hbb_common::get_version_number("1.2.0") + { + self.send_login_error("No Password Access").await; } return true; } else if password::approve_mode() == ApproveMode::Password @@ -1312,15 +1307,73 @@ impl Connection { } else { self.send_login_error(desktop_err).await; } - } else { + } else if lr.password.is_empty() { if desktop_err.is_empty() { - self.send_logon_response().await; - self.try_start_cm(lr.my_id, lr.my_name, true); - if self.port_forward_socket.is_some() { - return false; + self.try_start_cm(lr.my_id, lr.my_name, false); + } else { + self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY) + .await; + } + } else { + let mut failure = LOGIN_FAILURES + .lock() + .unwrap() + .get(&self.ip) + .map(|x| x.clone()) + .unwrap_or((0, 0, 0)); + let time = (get_time() / 60_000) as i32; + if failure.2 > 30 { + self.send_login_error("Too many wrong password attempts") + .await; + Self::post_alarm_audit( + AlarmAuditType::ManyWrongPassword, + true, + json!({ + "ip":self.ip, + }), + ); + } else if time == failure.0 && failure.1 > 6 { + self.send_login_error("Please try 1 minute later").await; + Self::post_alarm_audit( + AlarmAuditType::FrequentAttempt, + true, + json!({ + "ip":self.ip, + }), + ); + } else if !self.validate_password() { + if failure.0 == time { + failure.1 += 1; + failure.2 += 1; + } else { + failure.0 = time; + failure.1 = 1; + failure.2 += 1; + } + LOGIN_FAILURES + .lock() + .unwrap() + .insert(self.ip.clone(), failure); + if desktop_err.is_empty() { + self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; + self.try_start_cm(lr.my_id, lr.my_name, false); + } else { + self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG) + .await; } } else { - self.send_login_error(desktop_err).await; + if failure.0 != 0 { + LOGIN_FAILURES.lock().unwrap().remove(&self.ip); + } + if desktop_err.is_empty() { + self.send_logon_response().await; + self.try_start_cm(lr.my_id, lr.my_name, true); + if self.port_forward_socket.is_some() { + return false; + } + } else { + self.send_login_error(desktop_err).await; + } } } } else if let Some(message::Union::TestDelay(t)) = msg.union { From 9448e35b461691d57d418c3c0d381eecff902ae2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 29 Mar 2023 23:18:27 +0800 Subject: [PATCH 171/366] update dialog style Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 177 ++-- .../lib/desktop/pages/connection_page.dart | 7 +- src/lang/ca.rs | 6 +- src/lang/cn.rs | 7 +- src/lang/cs.rs | 6 +- src/lang/da.rs | 6 +- src/lang/de.rs | 5 +- src/lang/el.rs | 5 +- src/lang/en.rs | 5 +- src/lang/eo.rs | 6 +- src/lang/es.rs | 6 +- src/lang/fa.rs | 6 +- src/lang/fr.rs | 2 +- src/lang/hu.rs | 6 +- src/lang/id.rs | 6 +- src/lang/it.rs | 6 +- src/lang/ja.rs | 6 +- src/lang/ko.rs | 6 +- src/lang/kz.rs | 6 +- src/lang/nl.rs | 6 +- src/lang/pl.rs | 2 +- src/lang/pt_PT.rs | 6 +- src/lang/ptbr.rs | 6 +- src/lang/ro.rs | 6 +- src/lang/ru.rs | 5 +- src/lang/sk.rs | 6 +- src/lang/sl.rs | 6 +- src/lang/sq.rs | 6 +- src/lang/sr.rs | 6 +- src/lang/sv.rs | 6 +- src/lang/template.rs | 4 +- src/lang/th.rs | 6 +- src/lang/tr.rs | 6 +- src/lang/tw.rs | 979 +++++++++--------- src/lang/ua.rs | 6 +- src/lang/vn.rs | 6 +- 36 files changed, 764 insertions(+), 584 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 4562e519c..569d64428 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -456,7 +456,7 @@ void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async { await _connectDialog( id, dialogManager, - peerPasswordController: TextEditingController(), + passwordController: TextEditingController(), ); } @@ -464,8 +464,8 @@ void enterUserLoginDialog(String id, OverlayDialogManager dialogManager) async { await _connectDialog( id, dialogManager, - usernameController: TextEditingController(), - passwordController: TextEditingController(), + osUsernameController: TextEditingController(), + osPasswordController: TextEditingController(), ); } @@ -474,20 +474,27 @@ void enterUserLoginAndPasswordDialog( await _connectDialog( id, dialogManager, - usernameController: TextEditingController(), + osUsernameController: TextEditingController(), + osPasswordController: TextEditingController(), passwordController: TextEditingController(), - peerPasswordController: TextEditingController(), ); } _connectDialog( String id, OverlayDialogManager dialogManager, { - TextEditingController? usernameController, + TextEditingController? osUsernameController, + TextEditingController? osPasswordController, TextEditingController? passwordController, - TextEditingController? peerPasswordController, }) async { - var remember = await bind.sessionGetRemember(id: id) ?? false; + var rememberPassword = false; + if (passwordController != null) { + rememberPassword = await bind.sessionGetRemember(id: id) ?? false; + } + var rememberAccount = false; + if (osUsernameController != null) { + rememberAccount = await bind.sessionGetRemember(id: id) ?? false; + } dialogManager.dismissAll(); dialogManager.show((setState, close) { cancel() { @@ -501,77 +508,125 @@ _connectDialog( // If the remote side is headless. // The client side should login to remote OS account, to enable X desktop session. // `username` and `password` will be used in the near future. - final username = usernameController?.text.trim() ?? ''; + final osUsername = osUsernameController?.text.trim() ?? ''; + final osPassword = osPasswordController?.text.trim() ?? ''; final password = passwordController?.text.trim() ?? ''; - final peerPassword = peerPasswordController?.text.trim() ?? ''; - if (peerPasswordController != null && peerPassword.isEmpty) return; + if (passwordController != null && password.isEmpty) return; gFFI.login( - username, - password, + osUsername, + osPassword, id, - peerPassword, - remember, + password, + rememberPassword, ); close(); dialogManager.showLoading(translate('Logging in...'), onCancel: closeConnection); } + descWidget(String text) { + return Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + text, + maxLines: 3, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 16), + ), + ), + Container( + height: 8, + ), + ], + ); + } + + rememberWidget( + String desc, + bool remember, + ValueChanged? onChanged, + ) { + return CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text(desc), + value: remember, + onChanged: onChanged, + ); + } + + osAccountWidget() { + if (osUsernameController == null || osPasswordController == null) { + return Offstage(); + } + return Column( + children: [ + descWidget(translate('login_linux_tooltip_tip')), + DialogTextField( + title: translate(DialogTextField.kUsernameTitle), + controller: osUsernameController, + prefixIcon: DialogTextField.kUsernameIcon, + errorText: null, + ), + PasswordWidget( + controller: osPasswordController, + autoFocus: false, + ), + rememberWidget( + translate('remember_account_tip'), + rememberAccount, + (v) { + if (v != null) { + setState(() => rememberAccount = v); + } + }, + ), + ], + ); + } + + passwdWidget() { + if (passwordController == null) { + return Offstage(); + } + return Column( + children: [ + descWidget(translate('verify_rustdesk_password_tip')), + PasswordWidget( + controller: passwordController, + autoFocus: osUsernameController == null, + ), + rememberWidget( + translate('remember_password_tip'), + rememberPassword, + (v) { + if (v != null) { + setState(() => rememberPassword = v); + } + }, + ), + ], + ); + } + return CustomAlertDialog( title: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.password_rounded, color: MyTheme.accent), - (usernameController == null - ? Text(translate('Password Required')) - : Tooltip( - message: translate('login_linux_tooltip_tip'), - child: Text(translate('login_linux_tip')), - )) - .paddingOnly(left: 10), + Text(translate('Password Required')).paddingOnly(left: 10), ], ), content: Column(mainAxisSize: MainAxisSize.min, children: [ - usernameController == null + osAccountWidget(), + osUsernameController == null || passwordController == null ? Offstage() - : DialogTextField( - title: translate(DialogTextField.kUsernameTitle), - controller: usernameController, - prefixIcon: DialogTextField.kUsernameIcon, - errorText: null, - ), - passwordController == null - ? Offstage() - : PasswordWidget( - controller: passwordController, - autoFocus: false, - ), - usernameController == null || peerPasswordController == null - ? Offstage() - : const Divider(), - peerPasswordController == null - ? Offstage() - : PasswordWidget( - controller: peerPasswordController, - autoFocus: usernameController == null, - hintText: 'enter_rustdesk_passwd_tip', - ), - peerPasswordController == null - ? Offstage() - : CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate('remember_rustdesk_passwd_tip'), - ), - value: remember, - onChanged: (v) { - if (v != null) { - setState(() => remember = v); - } - }, - ), + : Container(height: 10), + passwdWidget(), ]), actions: [ dialogButton( diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index a593810fd..63d6b9c7b 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -233,7 +233,12 @@ class _ConnectionPageState extends State const SizedBox( width: 17, ), - Button(onTap: onConnect, text: "Connect"), + Button( + onTap: () => enterUserLoginAndPasswordDialog( + 'fdsfd', + gFFI.dialogManager, + ), + text: "Connect"), ], ), ) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 162a48883..af744d153 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tancar"), ("Retry", "Reintentar"), ("OK", ""), - ("Password Required", "Es necessita la contrasenya"), + ("remember_password_tip", "Es necessita la contrasenya"), ("Please enter your password", "Si us plau, introdueixi la seva contrasenya"), ("Remember password", "Recordar contrasenya"), ("Wrong Password", "Contrasenya incorrecta"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 250f9a5b6..584be4333 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "关闭"), ("Retry", "再试"), ("OK", "确认"), - ("Password Required", "需要密码"), + ("remember_password_tip", "需要密码"), ("Please enter your password", "请输入密码"), ("Remember password", "记住密码"), ("Wrong Password", "密码错误"), @@ -483,6 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "请输入 RustDesk 密码"), ("remember_rustdesk_passwd_tip", "记住 RustDesk 密码"), ("login_linux_tip", "登录被控端的 Linux 账户"), - ("login_linux_tooltip_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), + ("login_linux_tooltip_tip", "登录被控端的 Linux 账户,才能启用 X 桌面。"), + ("verify_rustdesk_password_tip", "验证 RustDesk 密码"), + ("remember_account_tip", "记住此账户"), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 2f9e52f6c..cb882277e 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zavřít"), ("Retry", "Zkusit znovu"), ("OK", "OK"), - ("Password Required", "Vyžadováno heslo"), + ("remember_password_tip", "Vyžadováno heslo"), ("Please enter your password", "Zadejte své heslo"), ("Remember password", "Zapamatovat heslo"), ("Wrong Password", "Nesprávné heslo"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index a9ca8e8a6..ad6ddfc48 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Luk"), ("Retry", "Prøv igen"), ("OK", "OK"), - ("Password Required", "Adgangskode påkrævet"), + ("remember_password_tip", "Adgangskode påkrævet"), ("Please enter your password", "Indtast venligst dit kodeord"), ("Remember password", "Husk kodeord"), ("Wrong Password", "Forkert kodeord"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index b70e9f872..263664d4c 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Schließen"), ("Retry", "Erneut versuchen"), ("OK", "OK"), - ("Password Required", "Passwort erforderlich"), + ("remember_password_tip", "Passwort erforderlich"), ("Please enter your password", "Bitte geben Sie Ihr Passwort ein"), ("Remember password", "Passwort merken"), ("Wrong Password", "Falsches Passwort"), @@ -484,5 +484,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_rustdesk_passwd_tip", "RustDesk-Passwort merken."), ("login_linux_tip", "Anmeldung am entfernten Linux-Konto"), ("login_linux_tooltip_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 85c269d29..9597ae158 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Κλείσιμο"), ("Retry", "Δοκίμασε ξανά"), ("OK", "ΟΚ"), - ("Password Required", "Απαιτείται κωδικός πρόσβασης"), + ("remember_password_tip", "Απαιτείται κωδικός πρόσβασης"), ("Please enter your password", "Παρακαλώ εισάγετε τον κωδικό πρόσβασης"), ("Remember password", "Απομνημόνευση κωδικού πρόσβασης"), ("Wrong Password", "Λάθος κωδικός πρόσβασης"), @@ -484,5 +484,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_rustdesk_passwd_tip", "Να θυμάσαι τον κωδικό του RustDesk."), ("login_linux_tip", "Είσοδος σε απομακρυσμένο λογαριασμό Linux"), ("login_linux_tooltip_tip", "Απαιτείται είσοδος σε απομακρυσμένο λογαριασμό Linux για την ενεργοποίηση του περιβάλλον εργασίας Χ."), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 8a981564b..95186325f 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -56,7 +56,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Show monitors in toolbar."), ("enter_rustdesk_passwd_tip", "Enter RustDesk password."), ("remember_rustdesk_passwd_tip", "Remember RustDesk password."), - ("login_linux_tip", "Login to remote Linux account"), + ("login_linux_tip", "Login to remote Linux account."), ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session."), + ("verify_rustdesk_password_tip", "Veryfy RustDesk password."), + ("remember_account_tip", "Remember this account."), + ("remember_password_tip", "Remember password."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index e430f8f67..aeee842f7 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermi"), ("Retry", "Reprovi"), ("OK", "Konfermi"), - ("Password Required", "Pasvorto deviga"), + ("remember_password_tip", "Pasvorto deviga"), ("Please enter your password", "Bonvolu tajpi vian pasvorton"), ("Remember password", "Memori pasvorton"), ("Wrong Password", "Erara pasvorto"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index a85a5e491..3b998ae7d 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Cerrar"), ("Retry", "Reintentar"), ("OK", ""), - ("Password Required", "Se requiere contraseña"), + ("remember_password_tip", "Se requiere contraseña"), ("Please enter your password", "Por favor, introduzca su contraseña"), ("Remember password", "Recordar contraseña"), ("Wrong Password", "Contraseña incorrecta"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "Introduzca la contraseña de RustDesk"), ("remember_rustdesk_passwd_tip", "Recordar la contraseña de RustDesk"), ("login_linux_tip", "Iniciar sesión para la cuenta remota de Linux"), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index e4d026586..e2137c3e2 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "بستن"), ("Retry", "تلاش مجدد"), ("OK", "قبول"), - ("Password Required", "رمز عبور لازم است"), + ("remember_password_tip", "رمز عبور لازم است"), ("Please enter your password", "رمز عبور خود را وارد کنید"), ("Remember password", "رمز عبور را به خاطر بسپار"), ("Wrong Password", "رمز عبور اشتباه است"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 3279b9175..71c0f6728 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermer"), ("Retry", "Réessayer"), ("OK", "Valider"), - ("Password Required", "Mot de passe requis"), + ("remember_password_tip", "Mot de passe requis"), ("Please enter your password", "Veuillez saisir votre mot de passe"), ("Remember password", "Mémoriser le mot de passe"), ("Wrong Password", "Mauvais mot de passe"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 2e6a20b56..7c7fb2468 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Bezárás"), ("Retry", "Újra"), ("OK", "OK"), - ("Password Required", "Jelszó megadása kötelező"), + ("remember_password_tip", "Jelszó megadása kötelező"), ("Please enter your password", "Kérem írja be a jelszavát"), ("Remember password", "Jelszó megjegyzése"), ("Wrong Password", "Hibás jelszó"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index cca4ba6c1..174d970b6 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tutup"), ("Retry", "Ulangi"), ("OK", "Oke"), - ("Password Required", "Kata sandi dibutuhkan"), + ("remember_password_tip", "Kata sandi dibutuhkan"), ("Please enter your password", "Silahkan masukkan kata sandi anda"), ("Remember password", "Ingat Password"), ("Wrong Password", "Kata sandi Salah"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 3c5973053..94b7d10a9 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Chiudi"), ("Retry", "Riprova"), ("OK", "OK"), - ("Password Required", "Password Richiesta"), + ("remember_password_tip", "Password Richiesta"), ("Please enter your password", "Inserisci la tua password"), ("Remember password", "Ricorda password"), ("Wrong Password", "Password Errata"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "Inserisci la password di RustDesk."), ("remember_rustdesk_passwd_tip", "Ricorda la passowrd di RustDesk."), ("login_linux_tip", "Effettua l'accesso sul tuo account Linux"), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 29bf3c346..95395c516 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "閉じる"), ("Retry", "再試行"), ("OK", "OK"), - ("Password Required", "パスワードが必要"), + ("remember_password_tip", "パスワードが必要"), ("Please enter your password", "パスワードを入力してください"), ("Remember password", "パスワードを記憶する"), ("Wrong Password", "パスワードが間違っています"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 929934016..4e3b52ea2 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "닫기"), ("Retry", "재시도"), ("OK", "확인"), - ("Password Required", "비밀번호 입력"), + ("remember_password_tip", "비밀번호 입력"), ("Please enter your password", "비밀번호를 입력해주세요"), ("Remember password", "이 비밀번호 기억하기"), ("Wrong Password", "틀린 비밀번호"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index a2c7c5983..bfe312e02 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Жабу"), ("Retry", "Қайтадан көру"), ("OK", "OK"), - ("Password Required", "Құпия сөз Қажет"), + ("remember_password_tip", "Құпия сөз Қажет"), ("Please enter your password", "Құпия сөзіңізді еңгізуді өтінеміз"), ("Remember password", "Құпия сөзді есте сақтау"), ("Wrong Password", "Бұрыс Құпия сөз"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index f31135c6c..4a3c9c062 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Sluit"), ("Retry", "Probeer opnieuw"), ("OK", "OK"), - ("Password Required", "Wachtwoord vereist"), + ("remember_password_tip", "Wachtwoord vereist"), ("Please enter your password", "Geef uw wachtwoord in"), ("Remember password", "Wachtwoord onthouden"), ("Wrong Password", "Verkeerd wachtwoord"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "Geef het RustDesk-wachtwoord op."), ("remember_rustdesk_passwd_tip", "RustDesk Wachtwoord onthouden."), ("login_linux_tip", "Je moet inloggen op een Linux Account op afstand om een X desktop sessie te openen."), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 852aa520d..f68d37866 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zamknij"), ("Retry", "Ponów"), ("OK", "OK"), - ("Password Required", "Wymagane jest hasło"), + ("remember_password_tip", "Wymagane jest hasło"), ("Please enter your password", "Wpisz proszę twoje hasło"), ("Remember password", "Zapamiętaj hasło"), ("Wrong Password", "Błędne hasło"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 45226dec6..04689f713 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "Confirmar"), - ("Password Required", "Palavra-chave Necessária"), + ("remember_password_tip", "Palavra-chave Necessária"), ("Please enter your password", "Por favor introduza a sua palavra-chave"), ("Remember password", "Memorizar palavra-chave"), ("Wrong Password", "Palavra-chave inválida"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 8cf8ca6ef..6f6302a59 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "OK"), - ("Password Required", "Senha necessária"), + ("remember_password_tip", "Senha necessária"), ("Please enter your password", "Por favor informe sua senha"), ("Remember password", "Lembrar senha"), ("Wrong Password", "Senha incorreta"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index e81fa0cfd..ade2e3c36 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Închide"), ("Retry", "Reîncearcă"), ("OK", "OK"), - ("Password Required", "Parolă necesară"), + ("remember_password_tip", "Parolă necesară"), ("Please enter your password", "Introdu parola"), ("Remember password", "Memorează parola"), ("Wrong Password", "Parolă incorectă"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 12c3dfb34..c85582e86 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрыть"), ("Retry", "Повторить"), ("OK", "ОК"), - ("Password Required", "Требуется пароль"), + ("remember_password_tip", "Требуется пароль"), ("Please enter your password", "Введите пароль"), ("Remember password", "Запомнить пароль"), ("Wrong Password", "Неправильный пароль"), @@ -484,5 +484,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_rustdesk_passwd_tip", "Запомнить пароль RustDesk"), ("login_linux_tip", "Вход в удалённый аккаунт Linux"), ("login_linux_tooltip_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 7d28cfb72..e7c108a8c 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvoriť"), ("Retry", "Zopakovať"), ("OK", "OK"), - ("Password Required", "Vyžaduje sa heslo"), + ("remember_password_tip", "Vyžaduje sa heslo"), ("Please enter your password", "Zadajte vaše heslo"), ("Remember password", "Zapamätať heslo"), ("Wrong Password", "Chybné heslo"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 544e544f7..0e5265b87 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zapri"), ("Retry", "Ponovi"), ("OK", "V redu"), - ("Password Required", "Potrebno je geslo"), + ("remember_password_tip", "Potrebno je geslo"), ("Please enter your password", "Vnesite vaše geslo"), ("Remember password", "Zapomni si geslo"), ("Wrong Password", "Napačno geslo"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index afd5c2c2d..68ef47be9 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Mbyll"), ("Retry", "Riprovo"), ("OK", "OK"), - ("Password Required", "Fjalëkalimi i detyrueshëm"), + ("remember_password_tip", "Fjalëkalimi i detyrueshëm"), ("Please enter your password", "Ju lutem vendosni fjalëkalimin tuaj"), ("Remember password", "Mbani mend fjalëkalimin"), ("Wrong Password", "Fjalëkalim i gabuar"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 05204f728..ccb328e3e 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvori"), ("Retry", "Ponovi"), ("OK", "Ok"), - ("Password Required", "Potrebna lozinka"), + ("remember_password_tip", "Potrebna lozinka"), ("Please enter your password", "Molimo unesite svoju lozinku"), ("Remember password", "Zapamti lozinku"), ("Wrong Password", "Pogrešna lozinka"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 7941c2f58..496c882bc 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Stäng"), ("Retry", "Försök igen"), ("OK", "OK"), - ("Password Required", "Lösenord krävs"), + ("remember_password_tip", "Lösenord krävs"), ("Please enter your password", "Skriv in ditt lösenord"), ("Remember password", "Kom ihåg lösenord"), ("Wrong Password", "Fel lösenord"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 56508d80a..6ec149289 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", ""), ("Retry", ""), ("OK", ""), - ("Password Required", ""), + ("remember_password_tip", ""), ("Please enter your password", ""), ("Remember password", ""), ("Wrong Password", ""), @@ -484,5 +484,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 4f178bf1d..92ae279c9 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "ปิด"), ("Retry", "ลองใหม่อีกครั้ง"), ("OK", "ตกลง"), - ("Password Required", "ต้องใช้รหัสผ่าน"), + ("remember_password_tip", "ต้องใช้รหัสผ่าน"), ("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"), ("Remember password", "จดจำรหัสผ่าน"), ("Wrong Password", "รหัสผ่านไม่ถูกต้อง"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index f5209129b..b423a021b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Kapat"), ("Retry", "Tekrar Dene"), ("OK", "Tamam"), - ("Password Required", "Şifre Gerekli"), + ("remember_password_tip", "Şifre Gerekli"), ("Please enter your password", "Lütfen şifrenizi giriniz"), ("Remember password", "Şifreyi hatırla"), ("Wrong Password", "Hatalı şifre"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 870105b09..e8685747e 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -1,488 +1,491 @@ -lazy_static::lazy_static! { -pub static ref T: std::collections::HashMap<&'static str, &'static str> = - [ - ("Status", "狀態"), - ("Your Desktop", "您的桌面"), - ("desk_tip", "您可以透過此 ID 及密碼存取您的桌面"), - ("Password", "密碼"), - ("Ready", "就緒"), - ("Established", "已建立"), - ("connecting_status", "正在連線到 RustDesk 網路 ..."), - ("Enable Service", "啟用服務"), - ("Start Service", "啟動服務"), - ("Service is running", "服務正在執行"), - ("Service is not running", "服務尚未執行"), - ("not_ready_status", "尚未就緒,請檢查您的網路連線。"), - ("Control Remote Desktop", "控制遠端桌面"), - ("Transfer File", "傳輸檔案"), - ("Connect", "連線"), - ("Recent Sessions", "近期的工作階段"), - ("Address Book", "通訊錄"), - ("Confirmation", "確認"), - ("TCP Tunneling", "TCP 通道"), - ("Remove", "移除"), - ("Refresh random password", "重新產生隨機密碼"), - ("Set your own password", "自行設定密碼"), - ("Enable Keyboard/Mouse", "啟用鍵盤和滑鼠"), - ("Enable Clipboard", "啟用剪貼簿"), - ("Enable File Transfer", "啟用檔案傳輸"), - ("Enable TCP Tunneling", "啟用 TCP 通道"), - ("IP Whitelisting", "IP 白名單"), - ("ID/Relay Server", "ID / 轉送伺服器"), - ("Import Server Config", "匯入伺服器設定"), - ("Export Server Config", "匯出伺服器設定"), - ("Import server configuration successfully", "匯入伺服器設定成功"), - ("Export server configuration successfully", "匯出伺服器設定成功"), - ("Invalid server configuration", "無效的伺服器設定"), - ("Clipboard is empty", "剪貼簿是空的"), - ("Stop service", "停止服務"), - ("Change ID", "更改 ID"), - ("Your new ID", "您的新 ID"), - ("length %min% to %max%", "長度在 %min% 與 %max% 之間"), - ("starts with a letter", "以字母開頭"), - ("allowed characters", "使用允許的字元"), - ("id_change_tip", "僅能使用以下字元:a-z、A-Z、0-9、_ (底線)。首字元必須為 a-z 或 A-Z。長度介於 6 到 16 之間。"), - ("Website", "網站"), - ("About", "關於"), - ("Slogan_tip", ""), - ("Privacy Statement", "隱私權聲明"), - ("Mute", "靜音"), - ("Build Date", "構建日期"), - ("Version", "版本"), - ("Home", "首頁"), - ("Audio Input", "音訊輸入"), - ("Enhancements", "增強功能"), - ("Hardware Codec", "硬體編解碼器"), - ("Adaptive Bitrate", "自適應位元速率"), - ("ID Server", "ID 伺服器"), - ("Relay Server", "轉送伺服器"), - ("API Server", "API 伺服器"), - ("invalid_http", "開頭必須為 http:// 或 https://"), - ("Invalid IP", "IP 無效"), - ("Invalid format", "格式無效"), - ("server_not_support", "伺服器暫不支持"), - ("Not available", "無法使用"), - ("Too frequent", "修改過於頻繁,請稍後再試。"), - ("Cancel", "取消"), - ("Skip", "跳過"), - ("Close", "關閉"), - ("Retry", "重試"), - ("OK", "確定"), - ("Password Required", "需要密碼"), - ("Please enter your password", "請輸入您的密碼"), - ("Remember password", "記住密碼"), - ("Wrong Password", "密碼錯誤"), - ("Do you want to enter again?", "您要重新輸入嗎?"), - ("Connection Error", "連線錯誤"), - ("Error", "錯誤"), - ("Reset by the peer", "對方重設了連線"), - ("Connecting...", "正在連線 ..."), - ("Connection in progress. Please wait.", "正在連線,請稍候。"), - ("Please try 1 minute later", "請於 1 分鐘後再試"), - ("Login Error", "登入錯誤"), - ("Successful", "成功"), - ("Connected, waiting for image...", "已連線,等待畫面傳輸 ..."), - ("Name", "名稱"), - ("Type", "類型"), - ("Modified", "修改時間"), - ("Size", "大小"), - ("Show Hidden Files", "顯示隱藏檔案"), - ("Receive", "接收"), - ("Send", "傳送"), - ("Refresh File", "重新整理檔案"), - ("Local", "本地"), - ("Remote", "遠端"), - ("Remote Computer", "遠端電腦"), - ("Local Computer", "本地電腦"), - ("Confirm Delete", "確認刪除"), - ("Delete", "刪除"), - ("Properties", "屬性"), - ("Multi Select", "多選"), - ("Select All", "全選"), - ("Unselect All", "取消全選"), - ("Empty Directory", "空資料夾"), - ("Not an empty directory", "不是一個空資料夾"), - ("Are you sure you want to delete this file?", "您確定要刪除此檔案嗎?"), - ("Are you sure you want to delete this empty directory?", "您確定要刪除此空資料夾嗎?"), - ("Are you sure you want to delete the file of this directory?", "您確定要刪除此資料夾中的檔案嗎?"), - ("Do this for all conflicts", "套用到其他衝突"), - ("This is irreversible!", "此操作不可逆!"), - ("Deleting", "正在刪除 ..."), - ("files", "檔案"), - ("Waiting", "正在等候 ..."), - ("Finished", "已完成"), - ("Speed", "速度"), - ("Custom Image Quality", "自訂畫面品質"), - ("Privacy mode", "隱私模式"), - ("Block user input", "封鎖使用者輸入"), - ("Unblock user input", "取消封鎖使用者輸入"), - ("Adjust Window", "調整視窗"), - ("Original", "原始"), - ("Shrink", "縮減"), - ("Stretch", "延展"), - ("Scrollbar", "滾動條"), - ("ScrollAuto", "自動滾動"), - ("Good image quality", "最佳化畫面品質"), - ("Balanced", "平衡"), - ("Optimize reaction time", "最佳化反應時間"), - ("Custom", "自訂"), - ("Show remote cursor", "顯示遠端游標"), - ("Show quality monitor", "顯示質量監測"), - ("Disable clipboard", "停用剪貼簿"), - ("Lock after session end", "工作階段結束後鎖定電腦"), - ("Insert", "插入"), - ("Insert Lock", "鎖定遠端電腦"), - ("Refresh", "重新載入"), - ("ID does not exist", "ID 不存在"), - ("Failed to connect to rendezvous server", "無法連線到 rendezvous 伺服器"), - ("Please try later", "請稍候再試"), - ("Remote desktop is offline", "遠端桌面已離線"), - ("Key mismatch", "金鑰不符"), - ("Timeout", "逾時"), - ("Failed to connect to relay server", "無法連線到轉送伺服器"), - ("Failed to connect via rendezvous server", "無法透過 rendezvous 伺服器連線"), - ("Failed to connect via relay server", "無法透過轉送伺服器連線"), - ("Failed to make direct connection to remote desktop", "無法直接連線到遠端桌面"), - ("Set Password", "設定密碼"), - ("OS Password", "作業系統密碼"), - ("install_tip", "UAC 會導致 RustDesk 在某些情況下無法正常以遠端電腦執行。若要避開 UAC,請點擊下方按鈕將 RustDesk 安裝到系統中。"), - ("Click to upgrade", "點擊以升級"), - ("Click to download", "點擊以下載"), - ("Click to update", "點擊以更新"), - ("Configure", "設定"), - ("config_acc", "您需要授予 RustDesk「協助工具」權限才能存取遠端電腦。"), - ("config_screen", "您需要授予 RustDesk「畫面錄製」權限才能存取遠端電腦。"), - ("Installing ...", "正在安裝 ..."), - ("Install", "安裝"), - ("Installation", "安裝"), - ("Installation Path", "安裝路徑"), - ("Create start menu shortcuts", "新增開始功能表捷徑"), - ("Create desktop icon", "新增桌面捷徑"), - ("agreement_tip", "開始安裝即表示接受許可協議"), - ("Accept and Install", "接受並安裝"), - ("End-user license agreement", "使用者授權合約"), - ("Generating ...", "正在產生 ..."), - ("Your installation is lower version.", "您安裝的版本過舊。"), - ("not_close_tcp_tip", "使用通道時請不要關閉此視窗"), - ("Listening ...", "正在等待通道連線 ..."), - ("Remote Host", "遠端主機"), - ("Remote Port", "遠端連線端口"), - ("Action", "操作"), - ("Add", "新增"), - ("Local Port", "本機連線端口"), - ("Local Address", "本機地址"), - ("Change Local Port", "修改本機連線端口"), - ("setup_server_tip", "若您需要更快的連線速度,可以選擇自行建立伺服器"), - ("Too short, at least 6 characters.", "過短,至少需要 6 個字元。"), - ("The confirmation is not identical.", "兩次輸入不相符"), - ("Permissions", "權限"), - ("Accept", "接受"), - ("Dismiss", "關閉"), - ("Disconnect", "中斷連線"), - ("Allow using keyboard and mouse", "允許使用鍵盤和滑鼠"), - ("Allow using clipboard", "允許使用剪貼簿"), - ("Allow hearing sound", "允許分享音訊"), - ("Allow file copy and paste", "允許檔案複製和貼上"), - ("Connected", "已連線"), - ("Direct and encrypted connection", "加密直接連線"), - ("Relayed and encrypted connection", "加密轉送連線"), - ("Direct and unencrypted connection", "未加密直接連線"), - ("Relayed and unencrypted connection", "未加密轉送連線"), - ("Enter Remote ID", "輸入遠端 ID"), - ("Enter your password", "輸入您的密碼"), - ("Logging in...", "正在登入 ..."), - ("Enable RDP session sharing", "啟用 RDP 工作階段共享"), - ("Auto Login", "自動登入 (鎖定將在設定關閉後套用)"), - ("Enable Direct IP Access", "允許 IP 直接存取"), - ("Rename", "重新命名"), - ("Space", "空白"), - ("Create Desktop Shortcut", "新增桌面捷徑"), - ("Change Path", "更改路徑"), - ("Create Folder", "新增資料夾"), - ("Please enter the folder name", "請輸入資料夾名稱"), - ("Fix it", "修復"), - ("Warning", "警告"), - ("Login screen using Wayland is not supported", "不支援使用 Wayland 的登入畫面"), - ("Reboot required", "需要重新啟動"), - ("Unsupported display server", "不支援顯示伺服器"), - ("x11 expected", "預期 x11"), - ("Port", "端口"), - ("Settings", "設定"), - ("Username", "使用者名稱"), - ("Invalid port", "連線端口無效"), - ("Closed manually by the peer", "遠端使用者關閉了工作階段"), - ("Enable remote configuration modification", "允許遠端使用者更改設定"), - ("Run without install", "跳過安裝直接執行"), - ("Connect via relay", "中繼連線"), - ("Always connect via relay", "一律透過轉送連線"), - ("whitelist_tip", "只有白名單中的 IP 可以存取"), - ("Login", "登入"), - ("Verify", "驗證"), - ("Remember me", "記住我"), - ("Trust this device", "信任此裝置"), - ("Verification code", "驗證碼"), - ("verification_tip", "檢測到新裝置登入,已向註冊電子信箱發送了登入驗證碼,請輸入驗證碼以繼續登入"), - ("Logout", "登出"), - ("Tags", "標籤"), - ("Search ID", "搜尋 ID"), - ("whitelist_sep", "使用逗號、分號、空白,或是換行來分隔"), - ("Add ID", "新增 ID"), - ("Add Tag", "新增標籤"), - ("Unselect all tags", "取消選取所有標籤"), - ("Network error", "網路錯誤"), - ("Username missed", "缺少使用者名稱"), - ("Password missed", "缺少密碼"), - ("Wrong credentials", "提供的登入資訊有誤"), - ("Edit Tag", "編輯標籤"), - ("Unremember Password", "忘掉密碼"), - ("Favorites", "我的最愛"), - ("Add to Favorites", "新增到我的最愛"), - ("Remove from Favorites", "從我的最愛中刪除"), - ("Empty", "空空如也"), - ("Invalid folder name", "資料夾名稱無效"), - ("Socks5 Proxy", "Socks5 代理"), - ("Hostname", "主機名稱"), - ("Discovered", "已探索"), - ("install_daemon_tip", "為了能夠開機時自動啟動,請先安裝系統服務。"), - ("Remote ID", "遠端 ID"), - ("Paste", "貼上"), - ("Paste here?", "貼上到這裡?"), - ("Are you sure to close the connection?", "您確定要關閉連線嗎?"), - ("Download new version", "下載新版本"), - ("Touch mode", "觸控模式"), - ("Mouse mode", "滑鼠模式"), - ("One-Finger Tap", "單指輕觸"), - ("Left Mouse", "滑鼠左鍵"), - ("One-Long Tap", "單指長按"), - ("Two-Finger Tap", "雙指輕觸"), - ("Right Mouse", "滑鼠右鍵"), - ("One-Finger Move", "單指移動"), - ("Double Tap & Move", "雙擊並移動"), - ("Mouse Drag", "滑鼠選中拖動"), - ("Three-Finger vertically", "三指垂直滑動"), - ("Mouse Wheel", "滑鼠滾輪"), - ("Two-Finger Move", "雙指移動"), - ("Canvas Move", "移動畫布"), - ("Pinch to Zoom", "雙指縮放"), - ("Canvas Zoom", "縮放畫布"), - ("Reset canvas", "重設畫布"), - ("No permission of file transfer", "沒有檔案傳輸權限"), - ("Note", "備註"), - ("Connection", "連線"), - ("Share Screen", "共享螢幕畫面"), - ("Chat", "聊天訊息"), - ("Total", "總計"), - ("items", "個項目"), - ("Selected", "已選擇"), - ("Screen Capture", "畫面錄製"), - ("Input Control", "輸入控制"), - ("Audio Capture", "音訊錄製"), - ("File Connection", "檔案連線"), - ("Screen Connection", "畫面連線"), - ("Do you accept?", "是否接受?"), - ("Open System Setting", "開啟系統設定"), - ("How to get Android input permission?", "如何獲取 Android 的輸入權限?"), - ("android_input_permission_tip1", "取得輸入權限後可以讓遠端裝置透過滑鼠控制此 Android 裝置"), - ("android_input_permission_tip2", "請在接下來的系統設定頁面中,找到並進入「已安裝的服務」頁面,並將「RustDesk Input」服務開啟"), - ("android_new_connection_tip", "收到新的連線控制請求,對方想要控制您目前的裝置"), - ("android_service_will_start_tip", "開啟畫面錄製權限將自動開啟服務,允許其他裝置向此裝置請求建立連線。"), - ("android_stop_service_tip", "關閉服務將自動關閉所有已建立的連線。"), - ("android_version_audio_tip", "目前的 Android 版本不支援音訊錄製,請升級到 Android 10 或以上版本。"), - ("android_start_service_tip", "點擊「啟動服務」或啟用「螢幕錄製」權限,以啟動螢幕共享服務。"), - ("android_permission_may_not_change_tip", "對於已經建立的連線,權限可能不會立即發生改變,除非重新建立連線。"), - ("Account", "帳號"), - ("Overwrite", "取代"), - ("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要略過或是取代此檔案嗎?"), - ("Quit", "退出"), - ("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"), - ("Help", "幫助"), - ("Failed", "失敗"), - ("Succeeded", "成功"), - ("Someone turns on privacy mode, exit", "其他使用者開啟隱私模式,退出"), - ("Unsupported", "不支援"), - ("Peer denied", "被控端拒絕"), - ("Please install plugins", "請安裝插件"), - ("Peer exit", "被控端退出"), - ("Failed to turn off", "退出失敗"), - ("Turned off", "退出"), - ("In privacy mode", "開啟隱私模式"), - ("Out privacy mode", "退出隱私模式"), - ("Language", "語言"), - ("Keep RustDesk background service", "保持 RustDesk 後台服務"), - ("Ignore Battery Optimizations", "忽略電池最佳化"), - ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的 RustDesk 應用設定頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), - ("Start on Boot", "開機自動啟動"), - ("Start the screen sharing service on boot, requires special permissions", "開機自動啟動螢幕共享服務,此功能需要一些特殊權限。"), - ("Connection not allowed", "對方不允許連線"), - ("Legacy mode", "傳統模式"), - ("Map mode", "1:1 傳輸模式"), - ("Translate mode", "翻譯模式"), - ("Use permanent password", "使用固定密碼"), - ("Use both passwords", "同時使用兩種密碼"), - ("Set permanent password", "設定固定密碼"), - ("Enable Remote Restart", "啟用遠端重新啟動"), - ("Allow remote restart", "允許遠端重新啟動"), - ("Restart Remote Device", "重新啟動遠端電腦"), - ("Are you sure you want to restart", "確定要重新啟動"), - ("Restarting Remote Device", "正在重新啟動遠端裝置"), - ("remote_restarting_tip", "遠端裝置正在重新啟動,請關閉當前提示框,並在一段時間後使用永久密碼重新連線"), - ("Copied", "已複製"), - ("Exit Fullscreen", "退出全螢幕"), - ("Fullscreen", "全螢幕"), - ("Mobile Actions", "手機操作"), - ("Select Monitor", "選擇顯示器"), - ("Control Actions", "控制操作"), - ("Display Settings", "顯示設定"), - ("Ratio", "比例"), - ("Image Quality", "畫質"), - ("Scroll Style", "滾動樣式"), - ("Show Menubar", "顯示選單欄"), - ("Hide Menubar", "隱藏選單欄"), - ("Direct Connection", "直接連線"), - ("Relay Connection", "中繼連線"), - ("Secure Connection", "安全連線"), - ("Insecure Connection", "非安全連線"), - ("Scale original", "原始尺寸"), - ("Scale adaptive", "適應視窗"), - ("General", "通用"), - ("Security", "安全"), - ("Theme", "主題"), - ("Dark Theme", "黑暗主題"), - ("Light Theme", "明亮主題"), - ("Dark", "黑暗"), - ("Light", "明亮"), - ("Follow System", "跟隨系統"), - ("Enable hardware codec", "使用硬體編解碼器"), - ("Unlock Security Settings", "解鎖安全設定"), - ("Enable Audio", "允許傳輸音訊"), - ("Unlock Network Settings", "解鎖網路設定"), - ("Server", "伺服器"), - ("Direct IP Access", "IP 直接連線"), - ("Proxy", "代理"), - ("Apply", "應用"), - ("Disconnect all devices?", "中斷所有遠端連線?"), - ("Clear", "清空"), - ("Audio Input Device", "音訊輸入裝置"), - ("Deny remote access", "拒絕遠端存取"), - ("Use IP Whitelisting", "只允許白名單上的 IP 進行連線"), - ("Network", "網路"), - ("Enable RDP", "允許 RDP 訪問"), - ("Pin menubar", "固定選單欄"), - ("Unpin menubar", "取消固定選單欄"), - ("Recording", "錄製"), - ("Directory", "路徑"), - ("Automatically record incoming sessions", "自動錄製連入的工作階段"), - ("Change", "變更"), - ("Start session recording", "開始錄影"), - ("Stop session recording", "停止錄影"), - ("Enable Recording Session", "啟用錄製工作階段"), - ("Allow recording session", "允許錄製工作階段"), - ("Enable LAN Discovery", "允許區域網路探索"), - ("Deny LAN Discovery", "拒絕區域網路探索"), - ("Write a message", "輸入聊天訊息"), - ("Prompt", "提示"), - ("Please wait for confirmation of UAC...", "請等待對方確認 UAC ..."), - ("elevated_foreground_window_tip", "目前的遠端桌面視窗需要更高的權限才能繼續操作,暫時無法使用滑鼠、鍵盤,可以請求對方最小化目前視窗,或者在連線管理視窗點擊提升權限。為了避免這個問題,建議在遠端裝置上安裝本軟體。"), - ("Disconnected", "斷開連線"), - ("Other", "其他"), - ("Confirm before closing multiple tabs", "關閉多個分頁前詢問我"), - ("Keyboard Settings", "鍵盤設定"), - ("Full Access", "完全訪問"), - ("Screen Share", "僅分享螢幕畫面"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland 需要 Ubuntu 21.04 或更高版本。"), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland 需要更高版本的 Linux 發行版。請嘗試使用 X11 桌面或更改您的作業系統。"), - ("JumpLink", "查看"), - ("Please Select the screen to be shared(Operate on the peer side).", "請選擇要分享的螢幕畫面(在對端操作)。"), - ("Show RustDesk", "顯示 RustDesk"), - ("This PC", "此電腦"), - ("or", "或"), - ("Continue with", "繼續"), - ("Elevate", "提升權限"), - ("Zoom cursor", "縮放游標"), - ("Accept sessions via password", "只允許透過輸入密碼進行連線"), - ("Accept sessions via click", "只允許透過點擊接受進行連線"), - ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), - ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), - ("One-time Password", "一次性密碼"), - ("Use one-time password", "使用一次性密碼"), - ("One-time password length", "一次性密碼長度"), - ("Request access to your device", "請求訪問您的裝置"), - ("Hide connection management window", "隱藏連線管理視窗"), - ("hide_cm_tip", "在只允許密碼連線並且只用固定密碼的情況下才允許隱藏"), - ("wayland_experiment_tip", "Wayland 支援處於實驗階段,如果您需要使用無人值守訪問,請使用 X11。"), - ("Right click to select tabs", "右鍵選擇分頁"), - ("Skipped", "已略過"), - ("Add to Address Book", "新增到通訊錄"), - ("Group", "群組"), - ("Search", "搜尋"), - ("Closed manually by web console", "被 Web 控制台手動關閉"), - ("Local keyboard type", "本地鍵盤類型"), - ("Select local keyboard type", "請選擇本地鍵盤類型"), - ("software_render_tip", "如果您使用 NVIDIA 顯示卡,並且遠端視窗在建立連線後會立刻關閉,那麼請安裝 nouveau 顯示卡驅動程式並且選擇使用軟體渲染可能會有幫助。重新啟動軟體後生效。"), - ("Always use software rendering", "使用軟體渲染"), - ("config_input", "為了能夠透過鍵盤控制遠端桌面,請給予 RustDesk \"輸入監控\" 權限。"), - ("config_microphone", "為了支援透過麥克風進行音訊傳輸,請給予 RustDesk \"錄音\"權限。"), - ("request_elevation_tip", "如果遠端使用者可以操作電腦,也可以請求提升權限。"), - ("Wait", "等待"), - ("Elevation Error", "權限提升失敗"), - ("Ask the remote user for authentication", "請求遠端使用者進行身分驗證"), - ("Choose this if the remote account is administrator", "當遠端使用者帳戶是管理員時,請選擇此選項"), - ("Transmit the username and password of administrator", "發送管理員的使用者名稱和密碼"), - ("still_click_uac_tip", "依然需要遠端使用者在 UAC 視窗點擊確認。"), - ("Request Elevation", "請求權限提升"), - ("wait_accept_uac_tip", "請等待遠端使用者確認 UAC 對話框。"), - ("Elevate successfully", "權限提升成功"), - ("uppercase", "大寫字母"), - ("lowercase", "小寫字母"), - ("digit", "數字"), - ("special character", "特殊字元"), - ("length>=8", "長度不能小於 8"), - ("Weak", "弱"), - ("Medium", "中"), - ("Strong", "強"), - ("Switch Sides", "反轉存取方向"), - ("Please confirm if you want to share your desktop?", "請確認是否要讓對方存取您的桌面?"), - ("Display", "顯示"), - ("Default View Style", "預設顯示方式"), - ("Default Scroll Style", "預設滾動方式"), - ("Default Image Quality", "預設圖像質量"), - ("Default Codec", "預設編解碼器"), - ("Bitrate", "位元速率"), - ("FPS", "幀率"), - ("Auto", "自動"), - ("Other Default Options", "其他預設選項"), - ("Voice call", "語音通話"), - ("Text chat", "文字聊天"), - ("Stop voice call", "停止語音通話"), - ("relay_hint_tip", "可能無法直接連線,可以嘗試中繼連線。\n另外,如果想要直接使用中繼連線,可以在 ID 後面新增/r,或者在卡片選項裡選擇強制走中繼連線。"), - ("Reconnect", "重新連線"), - ("Codec", "編解碼器"), - ("Resolution", "解析度"), - ("No transfers in progress", "沒有正在進行的傳輸"), - ("Set one-time password length", "設定一次性密碼長度"), - ("idd_driver_tip", "安裝虛擬顯示器驅動程式,以便在沒有連接顯示器的情況下啟動虛擬顯示器進行控制。"), - ("confirm_idd_driver_tip", "安裝虛擬顯示器驅動程式的選項已勾選。請注意,測試證書將被安裝以信任虛擬顯示器驅動。測試證書僅會用於信任 RustDesk 的驅動程式。"), - ("RDP Settings", "RDP 設定"), - ("Sort by", "排序方式"), - ("New Connection", "新連線"), - ("Restore", "還原"), - ("Minimize", "最小化"), - ("Maximize", "最大化"), - ("Your Device", "您的裝置"), - ("empty_recent_tip", "空空如也"), - ("empty_favorite_tip", "空空如也"), - ("empty_lan_tip", "空空如也"), - ("empty_address_book_tip", "空空如也"), - ("eg: admin", "例如:admin"), - ("Empty Username", "空使用者帳號"), - ("Empty Password", "空密碼"), - ("Me", "我"), - ("identical_file_tip", "此檔案與對方的檔案一致"), - ("show_monitors_tip", "在工具列中顯示顯示器"), - ("View Mode", "瀏覽模式"), - ("enter_rustdesk_passwd_tip", "輸入 RustDesk 密碼"), - ("remember_rustdesk_passwd_tip", "記住 RustDesk 密碼"), - ("login_linux_tip", "登入到遠端 Linux 使用者帳戶"), - ("login_linux_tooltip_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), - ].iter().cloned().collect(); -} +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "狀態"), + ("Your Desktop", "您的桌面"), + ("desk_tip", "您可以透過此 ID 及密碼存取您的桌面"), + ("Password", "密碼"), + ("Ready", "就緒"), + ("Established", "已建立"), + ("connecting_status", "正在連線到 RustDesk 網路 ..."), + ("Enable Service", "啟用服務"), + ("Start Service", "啟動服務"), + ("Service is running", "服務正在執行"), + ("Service is not running", "服務尚未執行"), + ("not_ready_status", "尚未就緒,請檢查您的網路連線。"), + ("Control Remote Desktop", "控制遠端桌面"), + ("Transfer File", "傳輸檔案"), + ("Connect", "連線"), + ("Recent Sessions", "近期的工作階段"), + ("Address Book", "通訊錄"), + ("Confirmation", "確認"), + ("TCP Tunneling", "TCP 通道"), + ("Remove", "移除"), + ("Refresh random password", "重新產生隨機密碼"), + ("Set your own password", "自行設定密碼"), + ("Enable Keyboard/Mouse", "啟用鍵盤和滑鼠"), + ("Enable Clipboard", "啟用剪貼簿"), + ("Enable File Transfer", "啟用檔案傳輸"), + ("Enable TCP Tunneling", "啟用 TCP 通道"), + ("IP Whitelisting", "IP 白名單"), + ("ID/Relay Server", "ID / 轉送伺服器"), + ("Import Server Config", "匯入伺服器設定"), + ("Export Server Config", "匯出伺服器設定"), + ("Import server configuration successfully", "匯入伺服器設定成功"), + ("Export server configuration successfully", "匯出伺服器設定成功"), + ("Invalid server configuration", "無效的伺服器設定"), + ("Clipboard is empty", "剪貼簿是空的"), + ("Stop service", "停止服務"), + ("Change ID", "更改 ID"), + ("Your new ID", "您的新 ID"), + ("length %min% to %max%", "長度在 %min% 與 %max% 之間"), + ("starts with a letter", "以字母開頭"), + ("allowed characters", "使用允許的字元"), + ("id_change_tip", "僅能使用以下字元:a-z、A-Z、0-9、_ (底線)。首字元必須為 a-z 或 A-Z。長度介於 6 到 16 之間。"), + ("Website", "網站"), + ("About", "關於"), + ("Slogan_tip", ""), + ("Privacy Statement", "隱私權聲明"), + ("Mute", "靜音"), + ("Build Date", "構建日期"), + ("Version", "版本"), + ("Home", "首頁"), + ("Audio Input", "音訊輸入"), + ("Enhancements", "增強功能"), + ("Hardware Codec", "硬體編解碼器"), + ("Adaptive Bitrate", "自適應位元速率"), + ("ID Server", "ID 伺服器"), + ("Relay Server", "轉送伺服器"), + ("API Server", "API 伺服器"), + ("invalid_http", "開頭必須為 http:// 或 https://"), + ("Invalid IP", "IP 無效"), + ("Invalid format", "格式無效"), + ("server_not_support", "伺服器暫不支持"), + ("Not available", "無法使用"), + ("Too frequent", "修改過於頻繁,請稍後再試。"), + ("Cancel", "取消"), + ("Skip", "跳過"), + ("Close", "關閉"), + ("Retry", "重試"), + ("OK", "確定"), + ("remember_password_tip", "需要密碼"), + ("Please enter your password", "請輸入您的密碼"), + ("Remember password", "記住密碼"), + ("Wrong Password", "密碼錯誤"), + ("Do you want to enter again?", "您要重新輸入嗎?"), + ("Connection Error", "連線錯誤"), + ("Error", "錯誤"), + ("Reset by the peer", "對方重設了連線"), + ("Connecting...", "正在連線 ..."), + ("Connection in progress. Please wait.", "正在連線,請稍候。"), + ("Please try 1 minute later", "請於 1 分鐘後再試"), + ("Login Error", "登入錯誤"), + ("Successful", "成功"), + ("Connected, waiting for image...", "已連線,等待畫面傳輸 ..."), + ("Name", "名稱"), + ("Type", "類型"), + ("Modified", "修改時間"), + ("Size", "大小"), + ("Show Hidden Files", "顯示隱藏檔案"), + ("Receive", "接收"), + ("Send", "傳送"), + ("Refresh File", "重新整理檔案"), + ("Local", "本地"), + ("Remote", "遠端"), + ("Remote Computer", "遠端電腦"), + ("Local Computer", "本地電腦"), + ("Confirm Delete", "確認刪除"), + ("Delete", "刪除"), + ("Properties", "屬性"), + ("Multi Select", "多選"), + ("Select All", "全選"), + ("Unselect All", "取消全選"), + ("Empty Directory", "空資料夾"), + ("Not an empty directory", "不是一個空資料夾"), + ("Are you sure you want to delete this file?", "您確定要刪除此檔案嗎?"), + ("Are you sure you want to delete this empty directory?", "您確定要刪除此空資料夾嗎?"), + ("Are you sure you want to delete the file of this directory?", "您確定要刪除此資料夾中的檔案嗎?"), + ("Do this for all conflicts", "套用到其他衝突"), + ("This is irreversible!", "此操作不可逆!"), + ("Deleting", "正在刪除 ..."), + ("files", "檔案"), + ("Waiting", "正在等候 ..."), + ("Finished", "已完成"), + ("Speed", "速度"), + ("Custom Image Quality", "自訂畫面品質"), + ("Privacy mode", "隱私模式"), + ("Block user input", "封鎖使用者輸入"), + ("Unblock user input", "取消封鎖使用者輸入"), + ("Adjust Window", "調整視窗"), + ("Original", "原始"), + ("Shrink", "縮減"), + ("Stretch", "延展"), + ("Scrollbar", "滾動條"), + ("ScrollAuto", "自動滾動"), + ("Good image quality", "最佳化畫面品質"), + ("Balanced", "平衡"), + ("Optimize reaction time", "最佳化反應時間"), + ("Custom", "自訂"), + ("Show remote cursor", "顯示遠端游標"), + ("Show quality monitor", "顯示質量監測"), + ("Disable clipboard", "停用剪貼簿"), + ("Lock after session end", "工作階段結束後鎖定電腦"), + ("Insert", "插入"), + ("Insert Lock", "鎖定遠端電腦"), + ("Refresh", "重新載入"), + ("ID does not exist", "ID 不存在"), + ("Failed to connect to rendezvous server", "無法連線到 rendezvous 伺服器"), + ("Please try later", "請稍候再試"), + ("Remote desktop is offline", "遠端桌面已離線"), + ("Key mismatch", "金鑰不符"), + ("Timeout", "逾時"), + ("Failed to connect to relay server", "無法連線到轉送伺服器"), + ("Failed to connect via rendezvous server", "無法透過 rendezvous 伺服器連線"), + ("Failed to connect via relay server", "無法透過轉送伺服器連線"), + ("Failed to make direct connection to remote desktop", "無法直接連線到遠端桌面"), + ("Set Password", "設定密碼"), + ("OS Password", "作業系統密碼"), + ("install_tip", "UAC 會導致 RustDesk 在某些情況下無法正常以遠端電腦執行。若要避開 UAC,請點擊下方按鈕將 RustDesk 安裝到系統中。"), + ("Click to upgrade", "點擊以升級"), + ("Click to download", "點擊以下載"), + ("Click to update", "點擊以更新"), + ("Configure", "設定"), + ("config_acc", "您需要授予 RustDesk「協助工具」權限才能存取遠端電腦。"), + ("config_screen", "您需要授予 RustDesk「畫面錄製」權限才能存取遠端電腦。"), + ("Installing ...", "正在安裝 ..."), + ("Install", "安裝"), + ("Installation", "安裝"), + ("Installation Path", "安裝路徑"), + ("Create start menu shortcuts", "新增開始功能表捷徑"), + ("Create desktop icon", "新增桌面捷徑"), + ("agreement_tip", "開始安裝即表示接受許可協議"), + ("Accept and Install", "接受並安裝"), + ("End-user license agreement", "使用者授權合約"), + ("Generating ...", "正在產生 ..."), + ("Your installation is lower version.", "您安裝的版本過舊。"), + ("not_close_tcp_tip", "使用通道時請不要關閉此視窗"), + ("Listening ...", "正在等待通道連線 ..."), + ("Remote Host", "遠端主機"), + ("Remote Port", "遠端連線端口"), + ("Action", "操作"), + ("Add", "新增"), + ("Local Port", "本機連線端口"), + ("Local Address", "本機地址"), + ("Change Local Port", "修改本機連線端口"), + ("setup_server_tip", "若您需要更快的連線速度,可以選擇自行建立伺服器"), + ("Too short, at least 6 characters.", "過短,至少需要 6 個字元。"), + ("The confirmation is not identical.", "兩次輸入不相符"), + ("Permissions", "權限"), + ("Accept", "接受"), + ("Dismiss", "關閉"), + ("Disconnect", "中斷連線"), + ("Allow using keyboard and mouse", "允許使用鍵盤和滑鼠"), + ("Allow using clipboard", "允許使用剪貼簿"), + ("Allow hearing sound", "允許分享音訊"), + ("Allow file copy and paste", "允許檔案複製和貼上"), + ("Connected", "已連線"), + ("Direct and encrypted connection", "加密直接連線"), + ("Relayed and encrypted connection", "加密轉送連線"), + ("Direct and unencrypted connection", "未加密直接連線"), + ("Relayed and unencrypted connection", "未加密轉送連線"), + ("Enter Remote ID", "輸入遠端 ID"), + ("Enter your password", "輸入您的密碼"), + ("Logging in...", "正在登入 ..."), + ("Enable RDP session sharing", "啟用 RDP 工作階段共享"), + ("Auto Login", "自動登入 (鎖定將在設定關閉後套用)"), + ("Enable Direct IP Access", "允許 IP 直接存取"), + ("Rename", "重新命名"), + ("Space", "空白"), + ("Create Desktop Shortcut", "新增桌面捷徑"), + ("Change Path", "更改路徑"), + ("Create Folder", "新增資料夾"), + ("Please enter the folder name", "請輸入資料夾名稱"), + ("Fix it", "修復"), + ("Warning", "警告"), + ("Login screen using Wayland is not supported", "不支援使用 Wayland 的登入畫面"), + ("Reboot required", "需要重新啟動"), + ("Unsupported display server", "不支援顯示伺服器"), + ("x11 expected", "預期 x11"), + ("Port", "端口"), + ("Settings", "設定"), + ("Username", "使用者名稱"), + ("Invalid port", "連線端口無效"), + ("Closed manually by the peer", "遠端使用者關閉了工作階段"), + ("Enable remote configuration modification", "允許遠端使用者更改設定"), + ("Run without install", "跳過安裝直接執行"), + ("Connect via relay", "中繼連線"), + ("Always connect via relay", "一律透過轉送連線"), + ("whitelist_tip", "只有白名單中的 IP 可以存取"), + ("Login", "登入"), + ("Verify", "驗證"), + ("Remember me", "記住我"), + ("Trust this device", "信任此裝置"), + ("Verification code", "驗證碼"), + ("verification_tip", "檢測到新裝置登入,已向註冊電子信箱發送了登入驗證碼,請輸入驗證碼以繼續登入"), + ("Logout", "登出"), + ("Tags", "標籤"), + ("Search ID", "搜尋 ID"), + ("whitelist_sep", "使用逗號、分號、空白,或是換行來分隔"), + ("Add ID", "新增 ID"), + ("Add Tag", "新增標籤"), + ("Unselect all tags", "取消選取所有標籤"), + ("Network error", "網路錯誤"), + ("Username missed", "缺少使用者名稱"), + ("Password missed", "缺少密碼"), + ("Wrong credentials", "提供的登入資訊有誤"), + ("Edit Tag", "編輯標籤"), + ("Unremember Password", "忘掉密碼"), + ("Favorites", "我的最愛"), + ("Add to Favorites", "新增到我的最愛"), + ("Remove from Favorites", "從我的最愛中刪除"), + ("Empty", "空空如也"), + ("Invalid folder name", "資料夾名稱無效"), + ("Socks5 Proxy", "Socks5 代理"), + ("Hostname", "主機名稱"), + ("Discovered", "已探索"), + ("install_daemon_tip", "為了能夠開機時自動啟動,請先安裝系統服務。"), + ("Remote ID", "遠端 ID"), + ("Paste", "貼上"), + ("Paste here?", "貼上到這裡?"), + ("Are you sure to close the connection?", "您確定要關閉連線嗎?"), + ("Download new version", "下載新版本"), + ("Touch mode", "觸控模式"), + ("Mouse mode", "滑鼠模式"), + ("One-Finger Tap", "單指輕觸"), + ("Left Mouse", "滑鼠左鍵"), + ("One-Long Tap", "單指長按"), + ("Two-Finger Tap", "雙指輕觸"), + ("Right Mouse", "滑鼠右鍵"), + ("One-Finger Move", "單指移動"), + ("Double Tap & Move", "雙擊並移動"), + ("Mouse Drag", "滑鼠選中拖動"), + ("Three-Finger vertically", "三指垂直滑動"), + ("Mouse Wheel", "滑鼠滾輪"), + ("Two-Finger Move", "雙指移動"), + ("Canvas Move", "移動畫布"), + ("Pinch to Zoom", "雙指縮放"), + ("Canvas Zoom", "縮放畫布"), + ("Reset canvas", "重設畫布"), + ("No permission of file transfer", "沒有檔案傳輸權限"), + ("Note", "備註"), + ("Connection", "連線"), + ("Share Screen", "共享螢幕畫面"), + ("Chat", "聊天訊息"), + ("Total", "總計"), + ("items", "個項目"), + ("Selected", "已選擇"), + ("Screen Capture", "畫面錄製"), + ("Input Control", "輸入控制"), + ("Audio Capture", "音訊錄製"), + ("File Connection", "檔案連線"), + ("Screen Connection", "畫面連線"), + ("Do you accept?", "是否接受?"), + ("Open System Setting", "開啟系統設定"), + ("How to get Android input permission?", "如何獲取 Android 的輸入權限?"), + ("android_input_permission_tip1", "取得輸入權限後可以讓遠端裝置透過滑鼠控制此 Android 裝置"), + ("android_input_permission_tip2", "請在接下來的系統設定頁面中,找到並進入「已安裝的服務」頁面,並將「RustDesk Input」服務開啟"), + ("android_new_connection_tip", "收到新的連線控制請求,對方想要控制您目前的裝置"), + ("android_service_will_start_tip", "開啟畫面錄製權限將自動開啟服務,允許其他裝置向此裝置請求建立連線。"), + ("android_stop_service_tip", "關閉服務將自動關閉所有已建立的連線。"), + ("android_version_audio_tip", "目前的 Android 版本不支援音訊錄製,請升級到 Android 10 或以上版本。"), + ("android_start_service_tip", "點擊「啟動服務」或啟用「螢幕錄製」權限,以啟動螢幕共享服務。"), + ("android_permission_may_not_change_tip", "對於已經建立的連線,權限可能不會立即發生改變,除非重新建立連線。"), + ("Account", "帳號"), + ("Overwrite", "取代"), + ("This file exists, skip or overwrite this file?", "此檔案/資料夾已存在,要略過或是取代此檔案嗎?"), + ("Quit", "退出"), + ("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"), + ("Help", "幫助"), + ("Failed", "失敗"), + ("Succeeded", "成功"), + ("Someone turns on privacy mode, exit", "其他使用者開啟隱私模式,退出"), + ("Unsupported", "不支援"), + ("Peer denied", "被控端拒絕"), + ("Please install plugins", "請安裝插件"), + ("Peer exit", "被控端退出"), + ("Failed to turn off", "退出失敗"), + ("Turned off", "退出"), + ("In privacy mode", "開啟隱私模式"), + ("Out privacy mode", "退出隱私模式"), + ("Language", "語言"), + ("Keep RustDesk background service", "保持 RustDesk 後台服務"), + ("Ignore Battery Optimizations", "忽略電池最佳化"), + ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的 RustDesk 應用設定頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), + ("Start on Boot", "開機自動啟動"), + ("Start the screen sharing service on boot, requires special permissions", "開機自動啟動螢幕共享服務,此功能需要一些特殊權限。"), + ("Connection not allowed", "對方不允許連線"), + ("Legacy mode", "傳統模式"), + ("Map mode", "1:1 傳輸模式"), + ("Translate mode", "翻譯模式"), + ("Use permanent password", "使用固定密碼"), + ("Use both passwords", "同時使用兩種密碼"), + ("Set permanent password", "設定固定密碼"), + ("Enable Remote Restart", "啟用遠端重新啟動"), + ("Allow remote restart", "允許遠端重新啟動"), + ("Restart Remote Device", "重新啟動遠端電腦"), + ("Are you sure you want to restart", "確定要重新啟動"), + ("Restarting Remote Device", "正在重新啟動遠端裝置"), + ("remote_restarting_tip", "遠端裝置正在重新啟動,請關閉當前提示框,並在一段時間後使用永久密碼重新連線"), + ("Copied", "已複製"), + ("Exit Fullscreen", "退出全螢幕"), + ("Fullscreen", "全螢幕"), + ("Mobile Actions", "手機操作"), + ("Select Monitor", "選擇顯示器"), + ("Control Actions", "控制操作"), + ("Display Settings", "顯示設定"), + ("Ratio", "比例"), + ("Image Quality", "畫質"), + ("Scroll Style", "滾動樣式"), + ("Show Menubar", "顯示選單欄"), + ("Hide Menubar", "隱藏選單欄"), + ("Direct Connection", "直接連線"), + ("Relay Connection", "中繼連線"), + ("Secure Connection", "安全連線"), + ("Insecure Connection", "非安全連線"), + ("Scale original", "原始尺寸"), + ("Scale adaptive", "適應視窗"), + ("General", "通用"), + ("Security", "安全"), + ("Theme", "主題"), + ("Dark Theme", "黑暗主題"), + ("Light Theme", "明亮主題"), + ("Dark", "黑暗"), + ("Light", "明亮"), + ("Follow System", "跟隨系統"), + ("Enable hardware codec", "使用硬體編解碼器"), + ("Unlock Security Settings", "解鎖安全設定"), + ("Enable Audio", "允許傳輸音訊"), + ("Unlock Network Settings", "解鎖網路設定"), + ("Server", "伺服器"), + ("Direct IP Access", "IP 直接連線"), + ("Proxy", "代理"), + ("Apply", "應用"), + ("Disconnect all devices?", "中斷所有遠端連線?"), + ("Clear", "清空"), + ("Audio Input Device", "音訊輸入裝置"), + ("Deny remote access", "拒絕遠端存取"), + ("Use IP Whitelisting", "只允許白名單上的 IP 進行連線"), + ("Network", "網路"), + ("Enable RDP", "允許 RDP 訪問"), + ("Pin menubar", "固定選單欄"), + ("Unpin menubar", "取消固定選單欄"), + ("Recording", "錄製"), + ("Directory", "路徑"), + ("Automatically record incoming sessions", "自動錄製連入的工作階段"), + ("Change", "變更"), + ("Start session recording", "開始錄影"), + ("Stop session recording", "停止錄影"), + ("Enable Recording Session", "啟用錄製工作階段"), + ("Allow recording session", "允許錄製工作階段"), + ("Enable LAN Discovery", "允許區域網路探索"), + ("Deny LAN Discovery", "拒絕區域網路探索"), + ("Write a message", "輸入聊天訊息"), + ("Prompt", "提示"), + ("Please wait for confirmation of UAC...", "請等待對方確認 UAC ..."), + ("elevated_foreground_window_tip", "目前的遠端桌面視窗需要更高的權限才能繼續操作,暫時無法使用滑鼠、鍵盤,可以請求對方最小化目前視窗,或者在連線管理視窗點擊提升權限。為了避免這個問題,建議在遠端裝置上安裝本軟體。"), + ("Disconnected", "斷開連線"), + ("Other", "其他"), + ("Confirm before closing multiple tabs", "關閉多個分頁前詢問我"), + ("Keyboard Settings", "鍵盤設定"), + ("Full Access", "完全訪問"), + ("Screen Share", "僅分享螢幕畫面"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland 需要 Ubuntu 21.04 或更高版本。"), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland 需要更高版本的 Linux 發行版。請嘗試使用 X11 桌面或更改您的作業系統。"), + ("JumpLink", "查看"), + ("Please Select the screen to be shared(Operate on the peer side).", "請選擇要分享的螢幕畫面(在對端操作)。"), + ("Show RustDesk", "顯示 RustDesk"), + ("This PC", "此電腦"), + ("or", "或"), + ("Continue with", "繼續"), + ("Elevate", "提升權限"), + ("Zoom cursor", "縮放游標"), + ("Accept sessions via password", "只允許透過輸入密碼進行連線"), + ("Accept sessions via click", "只允許透過點擊接受進行連線"), + ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), + ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), + ("One-time Password", "一次性密碼"), + ("Use one-time password", "使用一次性密碼"), + ("One-time password length", "一次性密碼長度"), + ("Request access to your device", "請求訪問您的裝置"), + ("Hide connection management window", "隱藏連線管理視窗"), + ("hide_cm_tip", "在只允許密碼連線並且只用固定密碼的情況下才允許隱藏"), + ("wayland_experiment_tip", "Wayland 支援處於實驗階段,如果您需要使用無人值守訪問,請使用 X11。"), + ("Right click to select tabs", "右鍵選擇分頁"), + ("Skipped", "已略過"), + ("Add to Address Book", "新增到通訊錄"), + ("Group", "群組"), + ("Search", "搜尋"), + ("Closed manually by web console", "被 Web 控制台手動關閉"), + ("Local keyboard type", "本地鍵盤類型"), + ("Select local keyboard type", "請選擇本地鍵盤類型"), + ("software_render_tip", "如果您使用 NVIDIA 顯示卡,並且遠端視窗在建立連線後會立刻關閉,那麼請安裝 nouveau 顯示卡驅動程式並且選擇使用軟體渲染可能會有幫助。重新啟動軟體後生效。"), + ("Always use software rendering", "使用軟體渲染"), + ("config_input", "為了能夠透過鍵盤控制遠端桌面,請給予 RustDesk \"輸入監控\" 權限。"), + ("config_microphone", "為了支援透過麥克風進行音訊傳輸,請給予 RustDesk \"錄音\"權限。"), + ("request_elevation_tip", "如果遠端使用者可以操作電腦,也可以請求提升權限。"), + ("Wait", "等待"), + ("Elevation Error", "權限提升失敗"), + ("Ask the remote user for authentication", "請求遠端使用者進行身分驗證"), + ("Choose this if the remote account is administrator", "當遠端使用者帳戶是管理員時,請選擇此選項"), + ("Transmit the username and password of administrator", "發送管理員的使用者名稱和密碼"), + ("still_click_uac_tip", "依然需要遠端使用者在 UAC 視窗點擊確認。"), + ("Request Elevation", "請求權限提升"), + ("wait_accept_uac_tip", "請等待遠端使用者確認 UAC 對話框。"), + ("Elevate successfully", "權限提升成功"), + ("uppercase", "大寫字母"), + ("lowercase", "小寫字母"), + ("digit", "數字"), + ("special character", "特殊字元"), + ("length>=8", "長度不能小於 8"), + ("Weak", "弱"), + ("Medium", "中"), + ("Strong", "強"), + ("Switch Sides", "反轉存取方向"), + ("Please confirm if you want to share your desktop?", "請確認是否要讓對方存取您的桌面?"), + ("Display", "顯示"), + ("Default View Style", "預設顯示方式"), + ("Default Scroll Style", "預設滾動方式"), + ("Default Image Quality", "預設圖像質量"), + ("Default Codec", "預設編解碼器"), + ("Bitrate", "位元速率"), + ("FPS", "幀率"), + ("Auto", "自動"), + ("Other Default Options", "其他預設選項"), + ("Voice call", "語音通話"), + ("Text chat", "文字聊天"), + ("Stop voice call", "停止語音通話"), + ("relay_hint_tip", "可能無法直接連線,可以嘗試中繼連線。\n另外,如果想要直接使用中繼連線,可以在 ID 後面新增/r,或者在卡片選項裡選擇強制走中繼連線。"), + ("Reconnect", "重新連線"), + ("Codec", "編解碼器"), + ("Resolution", "解析度"), + ("No transfers in progress", "沒有正在進行的傳輸"), + ("Set one-time password length", "設定一次性密碼長度"), + ("idd_driver_tip", "安裝虛擬顯示器驅動程式,以便在沒有連接顯示器的情況下啟動虛擬顯示器進行控制。"), + ("confirm_idd_driver_tip", "安裝虛擬顯示器驅動程式的選項已勾選。請注意,測試證書將被安裝以信任虛擬顯示器驅動。測試證書僅會用於信任 RustDesk 的驅動程式。"), + ("RDP Settings", "RDP 設定"), + ("Sort by", "排序方式"), + ("New Connection", "新連線"), + ("Restore", "還原"), + ("Minimize", "最小化"), + ("Maximize", "最大化"), + ("Your Device", "您的裝置"), + ("empty_recent_tip", "空空如也"), + ("empty_favorite_tip", "空空如也"), + ("empty_lan_tip", "空空如也"), + ("empty_address_book_tip", "空空如也"), + ("eg: admin", "例如:admin"), + ("Empty Username", "空使用者帳號"), + ("Empty Password", "空密碼"), + ("Me", "我"), + ("identical_file_tip", "此檔案與對方的檔案一致"), + ("show_monitors_tip", "在工具列中顯示顯示器"), + ("View Mode", "瀏覽模式"), + ("enter_rustdesk_passwd_tip", "輸入 RustDesk 密碼"), + ("remember_rustdesk_passwd_tip", "記住 RustDesk 密碼"), + ("login_linux_tip", "登入到遠端 Linux 使用者帳戶"), + ("login_linux_tooltip_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), + ].iter().cloned().collect(); +} diff --git a/src/lang/ua.rs b/src/lang/ua.rs index e21cd88df..952c36b13 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрити"), ("Retry", "Спробувати знову"), ("OK", "ОК"), - ("Password Required", "Потрібен пароль"), + ("remember_password_tip", "Потрібен пароль"), ("Please enter your password", "Будь ласка, введіть ваш пароль"), ("Remember password", "Запам'ятати пароль"), ("Wrong Password", "Невірний пароль"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index f716bd536..ab1aa2820 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Đóng"), ("Retry", "Thử lại"), ("OK", "OK"), - ("Password Required", "Yêu cầu mật khẩu"), + ("remember_password_tip", "Yêu cầu mật khẩu"), ("Please enter your password", "Mời nhập mật khẩu"), ("Remember password", "Nhớ mật khẩu"), ("Wrong Password", "Sai mật khẩu"), @@ -483,5 +483,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", ""), ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), + ("login_linux_tooltip_tip", ""), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("remember_password_tip", ""), ].iter().cloned().collect(); } From 3fd1da05f4d9fc1bd6f8c866998888eae0d58e4c Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 10:48:18 +0800 Subject: [PATCH 172/366] tmp commit Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 2 +- .../lib/desktop/widgets/remote_toolbar.dart | 68 ++++++++++++++++++- flutter/lib/models/model.dart | 1 + src/lang/en.rs | 14 ++-- src/platform/linux_desktop_manager.rs | 9 +++ src/server/connection.rs | 10 ++- 6 files changed, 92 insertions(+), 12 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 569d64428..0fdd17a4b 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -625,7 +625,7 @@ _connectDialog( osAccountWidget(), osUsernameController == null || passwordController == null ? Offstage() - : Container(height: 10), + : Container(height: 12), passwdWidget(), ]), actions: [ diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 703ed6db9..467cd69b0 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -639,7 +639,7 @@ class _ControlMenu extends StatelessWidget { ffi: ffi, menuChildren: [ requestElevation(), - osPassword(), + ffi.ffiModel.pi.is_headless ? osAccount() : osPassword(), transferFile(context), tcpTunneling(context), note(), @@ -662,6 +662,72 @@ class _ControlMenu extends StatelessWidget { onPressed: () => showRequestElevationDialog(id, ffi.dialogManager)); } + osAccount() { + return _MenuItemButton( + child: Text(translate('OS Account')), + trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), + ffi: ffi, + onPressed: () => _showSetOSAccount(id, false, ffi.dialogManager)); + } + + _showSetOSAccount( + String id, bool login, OverlayDialogManager dialogManager) async { + final usernameController = TextEditingController(); + final passwdController = TextEditingController(); + var username = + await bind.sessionGetOption(id: id, arg: 'os-username') ?? ''; + var password = + await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; + usernameController.text = username; + passwdController.text = password; + dialogManager.show((setState, close) { + submit() { + final username = usernameController.text.trim(); + final password = usernameController.text.trim(); + bind.sessionPeerOption(id: id, name: 'os-username', value: username); + bind.sessionPeerOption(id: id, name: 'os-password', value: password); + close(); + } + + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.password_rounded, color: MyTheme.accent), + Text(translate('OS Password')).paddingOnly(left: 10), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + DialogTextField( + title: translate(DialogTextField.kUsernameTitle), + controller: usernameController, + prefixIcon: DialogTextField.kUsernameIcon, + errorText: null, + ), + PasswordWidget(controller: passwdController), + ], + ), + 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, + ); + }); + } + osPassword() { return _MenuItemButton( child: Text(translate('OS Password')), diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 470bb47c3..7d1249906 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -1725,6 +1725,7 @@ class PeerInfo { Map platform_additions = {}; bool get is_wayland => platform_additions['is_wayland'] == true; + bool get is_headless => platform_additions['headless'] == true; } const canvasKey = 'canvas'; diff --git a/src/lang/en.rs b/src/lang/en.rs index 95186325f..79f18ebc5 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -54,12 +54,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("empty_address_book_tip", "Oh dear, it appears that there are currently no peers listed in your address book."), ("identical_file_tip", "This file is identical with the peer's one."), ("show_monitors_tip", "Show monitors in toolbar."), - ("enter_rustdesk_passwd_tip", "Enter RustDesk password."), - ("remember_rustdesk_passwd_tip", "Remember RustDesk password."), - ("login_linux_tip", "Login to remote Linux account."), - ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session."), - ("verify_rustdesk_password_tip", "Veryfy RustDesk password."), - ("remember_account_tip", "Remember this account."), - ("remember_password_tip", "Remember password."), + ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), + ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), + ("login_linux_tip", "Login to remote Linux account"), + ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session"), + ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), + ("remember_account_tip", "Remember this account"), + ("remember_password_tip", "Remember password"), ].iter().cloned().collect(); } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index b21b2d5b1..a957e045e 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -80,6 +80,15 @@ pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String } } +#[inline] +pub fn is_headless() -> bool { + DESKTOP_MANAGER + .lock() + .unwrap() + .as_ref() + .map_or(false, |manager| manager.x11_username.is_empty()) +} + pub fn get_username() -> String { match &*DESKTOP_MANAGER.lock().unwrap() { Some(manager) => { diff --git a/src/server/connection.rs b/src/server/connection.rs index 2f86ca5f0..ee4bdda35 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -3,6 +3,8 @@ use super::{input_service::*, *}; use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::update_clipboard; +#[cfg(target_os = "linux")] +use crate::platform::linux_desktop_manager; #[cfg(windows)] use crate::portable_service::client as portable_client; use crate::{ @@ -866,6 +868,9 @@ impl Connection { if crate::platform::current_is_wayland() { platform_additions.insert("is_wayland".into(), json!(true)); } + if linux_desktop_manager::is_headless() { + platform_additions.insert("headless".into(), json!(true)); + } if !platform_additions.is_empty() { pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into()); @@ -1074,7 +1079,7 @@ impl Connection { fn try_start_desktop(_username: &str, _passsword: &str) -> String { #[cfg(target_os = "linux")] if _username.is_empty() { - let username = crate::platform::linux_desktop_manager::get_username(); + let username = linux_desktop_manager::get_username(); if username.is_empty() { LOGIN_MSG_XSESSION_NOT_READY } else { @@ -1082,8 +1087,7 @@ impl Connection { } .to_owned() } else { - match crate::platform::linux_desktop_manager::try_start_x_session(_username, _passsword) - { + match linux_desktop_manager::try_start_x_session(_username, _passsword) { Ok((username, x11_ready)) => { if x11_ready { if _username != username { From 888c8511671bac9e649b6bc3a28de912e63155ac Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 11:17:24 +0800 Subject: [PATCH 173/366] desktop, remote toolbar, os account Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 7 +------ .../lib/desktop/pages/connection_page.dart | 14 +++++-------- .../lib/desktop/widgets/remote_toolbar.dart | 21 +++++++++++++++++++ libs/hbb_common/src/config.rs | 4 ++-- src/lang/ca.rs | 8 +++---- src/lang/cn.rs | 10 ++++----- src/lang/cs.rs | 8 +++---- src/lang/da.rs | 8 +++---- src/lang/de.rs | 10 ++++----- src/lang/el.rs | 10 ++++----- src/lang/en.rs | 4 ++-- src/lang/eo.rs | 8 +++---- src/lang/es.rs | 10 ++++----- src/lang/fa.rs | 8 +++---- src/lang/fr.rs | 2 +- src/lang/hu.rs | 8 +++---- src/lang/id.rs | 8 +++---- src/lang/it.rs | 10 ++++----- src/lang/ja.rs | 8 +++---- src/lang/ko.rs | 8 +++---- src/lang/kz.rs | 8 +++---- src/lang/nl.rs | 10 ++++----- src/lang/pl.rs | 2 +- src/lang/pt_PT.rs | 8 +++---- src/lang/ptbr.rs | 8 +++---- src/lang/ro.rs | 8 +++---- src/lang/ru.rs | 10 ++++----- src/lang/sk.rs | 8 +++---- src/lang/sl.rs | 8 +++---- src/lang/sq.rs | 8 +++---- src/lang/sr.rs | 8 +++---- src/lang/sv.rs | 8 +++---- src/lang/template.rs | 5 ++--- src/lang/th.rs | 8 +++---- src/lang/tr.rs | 8 +++---- src/lang/tw.rs | 10 ++++----- src/lang/ua.rs | 8 +++---- src/lang/vn.rs | 8 +++---- 38 files changed, 133 insertions(+), 182 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 0fdd17a4b..86611cd8a 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -503,11 +503,6 @@ _connectDialog( } submit() { - // to-do: - // username and password are about remote OS account. - // If the remote side is headless. - // The client side should login to remote OS account, to enable X desktop session. - // `username` and `password` will be used in the near future. final osUsername = osUsernameController?.text.trim() ?? ''; final osPassword = osPasswordController?.text.trim() ?? ''; final password = passwordController?.text.trim() ?? ''; @@ -565,7 +560,7 @@ _connectDialog( } return Column( children: [ - descWidget(translate('login_linux_tooltip_tip')), + descWidget(translate('login_linux_tip')), DialogTextField( title: translate(DialogTextField.kUsernameTitle), controller: osUsernameController, diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 63d6b9c7b..480ea75ff 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -225,20 +225,16 @@ class _ConnectionPageState extends State children: [ Button( isOutline: true, - onTap: () { - onConnect(isFileTransfer: true); - }, + onTap: () => enterUserLoginAndPasswordDialog( + 'fdsfd', + gFFI.dialogManager, + ), text: "Transfer File", ), const SizedBox( width: 17, ), - Button( - onTap: () => enterUserLoginAndPasswordDialog( - 'fdsfd', - gFFI.dialogManager, - ), - text: "Connect"), + Button(onTap: onConnect, text: "Connect"), ], ), ) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 467cd69b0..ec4d32ee0 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -689,6 +689,26 @@ class _ControlMenu extends StatelessWidget { close(); } + descWidget(String text) { + return Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + text, + maxLines: 3, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 16), + ), + ), + Container( + height: 8, + ), + ], + ); + } + return CustomAlertDialog( title: Row( mainAxisAlignment: MainAxisAlignment.center, @@ -700,6 +720,7 @@ class _ControlMenu extends StatelessWidget { content: Column( mainAxisSize: MainAxisSize.min, children: [ + descWidget(translate("os_account_desk_tip")), DialogTextField( title: translate(DialogTextField.kUsernameTitle), controller: usernameController, diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 6a823c7b7..3841bf976 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -915,7 +915,7 @@ impl PeerConfig { decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); config.password = password; store = store || store2; - for opt in ["rdp_password", "os-password"] { + for opt in ["rdp_password", "os-username", "os-password"] { if let Some(v) = config.options.get_mut(opt) { let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; @@ -938,7 +938,7 @@ impl PeerConfig { let _lock = CONFIG.read().unwrap(); let mut config = self.clone(); config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); - for opt in ["rdp_password", "os-password"] { + for opt in ["rdp_password", "os-username", "os-password"] { if let Some(v) = config.options.get_mut(opt) { *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION) } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index af744d153..6f4ce9543 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tancar"), ("Retry", "Reintentar"), ("OK", ""), - ("remember_password_tip", "Es necessita la contrasenya"), + ("remember_password_tip", ""), ("Please enter your password", "Si us plau, introdueixi la seva contrasenya"), ("Remember password", "Recordar contrasenya"), ("Wrong Password", "Contrasenya incorrecta"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 584be4333..5262db44c 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "关闭"), ("Retry", "再试"), ("OK", "确认"), - ("remember_password_tip", "需要密码"), + ("remember_password_tip", ""), ("Please enter your password", "请输入密码"), ("Remember password", "记住密码"), ("Wrong Password", "密码错误"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此文件与对方的一致"), ("show_monitors_tip", ""), ("View Mode", "浏览模式"), - ("enter_rustdesk_passwd_tip", "请输入 RustDesk 密码"), - ("remember_rustdesk_passwd_tip", "记住 RustDesk 密码"), - ("login_linux_tip", "登录被控端的 Linux 账户"), - ("login_linux_tooltip_tip", "登录被控端的 Linux 账户,才能启用 X 桌面。"), + ("login_linux_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), ("verify_rustdesk_password_tip", "验证 RustDesk 密码"), ("remember_account_tip", "记住此账户"), - ("remember_password_tip", ""), + ("os_account_desk_tip", "在无显示器的环境下,此账户用于登录被控系统,并启用桌面"), + ("OS Account", "系统账户"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index cb882277e..58d10b5ee 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zavřít"), ("Retry", "Zkusit znovu"), ("OK", "OK"), - ("remember_password_tip", "Vyžadováno heslo"), + ("remember_password_tip", ""), ("Please enter your password", "Zadejte své heslo"), ("Remember password", "Zapamatovat heslo"), ("Wrong Password", "Nesprávné heslo"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index ad6ddfc48..7e6888239 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Luk"), ("Retry", "Prøv igen"), ("OK", "OK"), - ("remember_password_tip", "Adgangskode påkrævet"), + ("remember_password_tip", ""), ("Please enter your password", "Indtast venligst dit kodeord"), ("Remember password", "Husk kodeord"), ("Wrong Password", "Forkert kodeord"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 263664d4c..ad9196785 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Schließen"), ("Retry", "Erneut versuchen"), ("OK", "OK"), - ("remember_password_tip", "Passwort erforderlich"), + ("remember_password_tip", ""), ("Please enter your password", "Bitte geben Sie Ihr Passwort ein"), ("Remember password", "Passwort merken"), ("Wrong Password", "Falsches Passwort"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Diese Datei ist identisch mit der Datei der Gegenstelle."), ("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"), ("View Mode", "Ansichtsmodus"), - ("enter_rustdesk_passwd_tip", "RustDesk-Passwort eingeben."), - ("remember_rustdesk_passwd_tip", "RustDesk-Passwort merken."), - ("login_linux_tip", "Anmeldung am entfernten Linux-Konto"), - ("login_linux_tooltip_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), + ("login_linux_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 9597ae158..50778f4c4 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Κλείσιμο"), ("Retry", "Δοκίμασε ξανά"), ("OK", "ΟΚ"), - ("remember_password_tip", "Απαιτείται κωδικός πρόσβασης"), + ("remember_password_tip", ""), ("Please enter your password", "Παρακαλώ εισάγετε τον κωδικό πρόσβασης"), ("Remember password", "Απομνημόνευση κωδικού πρόσβασης"), ("Wrong Password", "Λάθος κωδικός πρόσβασης"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Το αρχείο είναι πανομοιότυπο με αυτό του άλλου υπολογιστή."), ("show_monitors_tip", "Εμφάνιση οθονών στη γραμμή εργαλείων"), ("View Mode", "Λειτουργία προβολής"), - ("enter_rustdesk_passwd_tip", "Εισαγωγή του κωδικού RustDesk."), - ("remember_rustdesk_passwd_tip", "Να θυμάσαι τον κωδικό του RustDesk."), - ("login_linux_tip", "Είσοδος σε απομακρυσμένο λογαριασμό Linux"), - ("login_linux_tooltip_tip", "Απαιτείται είσοδος σε απομακρυσμένο λογαριασμό Linux για την ενεργοποίηση του περιβάλλον εργασίας Χ."), + ("login_linux_tip", "Απαιτείται είσοδος σε απομακρυσμένο λογαριασμό Linux για την ενεργοποίηση του περιβάλλον εργασίας Χ."), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 79f18ebc5..2ea65b05f 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -56,10 +56,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Show monitors in toolbar."), ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), - ("login_linux_tip", "Login to remote Linux account"), - ("login_linux_tooltip_tip", "You need to login to remote Linux account to enable a X desktop session"), + ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), ("remember_password_tip", "Remember password"), + ("os_account_desk_tip", "This account is used to login the remote OS and enable the desktop session in headless"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index aeee842f7..9fcc2b451 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermi"), ("Retry", "Reprovi"), ("OK", "Konfermi"), - ("remember_password_tip", "Pasvorto deviga"), + ("remember_password_tip", ""), ("Please enter your password", "Bonvolu tajpi vian pasvorton"), ("Remember password", "Memori pasvorton"), ("Wrong Password", "Erara pasvorto"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 3b998ae7d..c8120de49 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Cerrar"), ("Retry", "Reintentar"), ("OK", ""), - ("remember_password_tip", "Se requiere contraseña"), + ("remember_password_tip", ""), ("Please enter your password", "Por favor, introduzca su contraseña"), ("Remember password", "Recordar contraseña"), ("Wrong Password", "Contraseña incorrecta"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Este archivo es idéntico al del par."), ("show_monitors_tip", "Mostrar monitores en la barra de herramientas"), ("View Mode", "Modo Vista"), - ("enter_rustdesk_passwd_tip", "Introduzca la contraseña de RustDesk"), - ("remember_rustdesk_passwd_tip", "Recordar la contraseña de RustDesk"), - ("login_linux_tip", "Iniciar sesión para la cuenta remota de Linux"), - ("login_linux_tooltip_tip", ""), + ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index e2137c3e2..ccf2a670a 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "بستن"), ("Retry", "تلاش مجدد"), ("OK", "قبول"), - ("remember_password_tip", "رمز عبور لازم است"), + ("remember_password_tip", ""), ("Please enter your password", "رمز عبور خود را وارد کنید"), ("Remember password", "رمز عبور را به خاطر بسپار"), ("Wrong Password", "رمز عبور اشتباه است"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "این فایل با فایل همتا یکسان است."), ("show_monitors_tip", "نمایش مانیتورها در نوار ابزار"), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 71c0f6728..2ff02d8f9 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermer"), ("Retry", "Réessayer"), ("OK", "Valider"), - ("remember_password_tip", "Mot de passe requis"), + ("remember_password_tip", ""), ("Please enter your password", "Veuillez saisir votre mot de passe"), ("Remember password", "Mémoriser le mot de passe"), ("Wrong Password", "Mauvais mot de passe"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 7c7fb2468..49e767b84 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Bezárás"), ("Retry", "Újra"), ("OK", "OK"), - ("remember_password_tip", "Jelszó megadása kötelező"), + ("remember_password_tip", ""), ("Please enter your password", "Kérem írja be a jelszavát"), ("Remember password", "Jelszó megjegyzése"), ("Wrong Password", "Hibás jelszó"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 174d970b6..2c410c0ec 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tutup"), ("Retry", "Ulangi"), ("OK", "Oke"), - ("remember_password_tip", "Kata sandi dibutuhkan"), + ("remember_password_tip", ""), ("Please enter your password", "Silahkan masukkan kata sandi anda"), ("Remember password", "Ingat Password"), ("Wrong Password", "Kata sandi Salah"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 94b7d10a9..4427743f1 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Chiudi"), ("Retry", "Riprova"), ("OK", "OK"), - ("remember_password_tip", "Password Richiesta"), + ("remember_password_tip", ""), ("Please enter your password", "Inserisci la tua password"), ("Remember password", "Ricorda password"), ("Wrong Password", "Password Errata"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Questo file è identico a quello del peer."), ("show_monitors_tip", "Mostra schermi nella barra degli strumenti"), ("View Mode", "Modalità di visualizzazione"), - ("enter_rustdesk_passwd_tip", "Inserisci la password di RustDesk."), - ("remember_rustdesk_passwd_tip", "Ricorda la passowrd di RustDesk."), - ("login_linux_tip", "Effettua l'accesso sul tuo account Linux"), - ("login_linux_tooltip_tip", ""), + ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 95395c516..dba70c1e1 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "閉じる"), ("Retry", "再試行"), ("OK", "OK"), - ("remember_password_tip", "パスワードが必要"), + ("remember_password_tip", ""), ("Please enter your password", "パスワードを入力してください"), ("Remember password", "パスワードを記憶する"), ("Wrong Password", "パスワードが間違っています"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 4e3b52ea2..ab7714190 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "닫기"), ("Retry", "재시도"), ("OK", "확인"), - ("remember_password_tip", "비밀번호 입력"), + ("remember_password_tip", ""), ("Please enter your password", "비밀번호를 입력해주세요"), ("Remember password", "이 비밀번호 기억하기"), ("Wrong Password", "틀린 비밀번호"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index bfe312e02..7fe2719cc 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Жабу"), ("Retry", "Қайтадан көру"), ("OK", "OK"), - ("remember_password_tip", "Құпия сөз Қажет"), + ("remember_password_tip", ""), ("Please enter your password", "Құпия сөзіңізді еңгізуді өтінеміз"), ("Remember password", "Құпия сөзді есте сақтау"), ("Wrong Password", "Бұрыс Құпия сөз"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 4a3c9c062..360166e50 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Sluit"), ("Retry", "Probeer opnieuw"), ("OK", "OK"), - ("remember_password_tip", "Wachtwoord vereist"), + ("remember_password_tip", ""), ("Please enter your password", "Geef uw wachtwoord in"), ("Remember password", "Wachtwoord onthouden"), ("Wrong Password", "Verkeerd wachtwoord"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Dit bestand is identiek aan het bestand van het externe station."), ("show_monitors_tip", "Monitoren weergeven in de werkbalk"), ("View Mode", "Weergave Mode"), - ("enter_rustdesk_passwd_tip", "Geef het RustDesk-wachtwoord op."), - ("remember_rustdesk_passwd_tip", "RustDesk Wachtwoord onthouden."), - ("login_linux_tip", "Je moet inloggen op een Linux Account op afstand om een X desktop sessie te openen."), - ("login_linux_tooltip_tip", ""), + ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index f68d37866..d89b6f763 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zamknij"), ("Retry", "Ponów"), ("OK", "OK"), - ("remember_password_tip", "Wymagane jest hasło"), + ("remember_password_tip", ""), ("Please enter your password", "Wpisz proszę twoje hasło"), ("Remember password", "Zapamiętaj hasło"), ("Wrong Password", "Błędne hasło"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 04689f713..c23aa1e46 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "Confirmar"), - ("remember_password_tip", "Palavra-chave Necessária"), + ("remember_password_tip", ""), ("Please enter your password", "Por favor introduza a sua palavra-chave"), ("Remember password", "Memorizar palavra-chave"), ("Wrong Password", "Palavra-chave inválida"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 6f6302a59..c741944a1 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "OK"), - ("remember_password_tip", "Senha necessária"), + ("remember_password_tip", ""), ("Please enter your password", "Por favor informe sua senha"), ("Remember password", "Lembrar senha"), ("Wrong Password", "Senha incorreta"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index ade2e3c36..d77345356 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Închide"), ("Retry", "Reîncearcă"), ("OK", "OK"), - ("remember_password_tip", "Parolă necesară"), + ("remember_password_tip", ""), ("Please enter your password", "Introdu parola"), ("Remember password", "Memorează parola"), ("Wrong Password", "Parolă incorectă"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index c85582e86..fbd1c4c62 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрыть"), ("Retry", "Повторить"), ("OK", "ОК"), - ("remember_password_tip", "Требуется пароль"), + ("remember_password_tip", ""), ("Please enter your password", "Введите пароль"), ("Remember password", "Запомнить пароль"), ("Wrong Password", "Неправильный пароль"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Файл идентичен файлу на удалённом узле."), ("show_monitors_tip", "Показывать мониторы на панели инструментов"), ("View Mode", "Режим просмотра"), - ("enter_rustdesk_passwd_tip", "Введите пароль RustDesk"), - ("remember_rustdesk_passwd_tip", "Запомнить пароль RustDesk"), - ("login_linux_tip", "Вход в удалённый аккаунт Linux"), - ("login_linux_tooltip_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), + ("login_linux_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index e7c108a8c..efa66f7ae 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvoriť"), ("Retry", "Zopakovať"), ("OK", "OK"), - ("remember_password_tip", "Vyžaduje sa heslo"), + ("remember_password_tip", ""), ("Please enter your password", "Zadajte vaše heslo"), ("Remember password", "Zapamätať heslo"), ("Wrong Password", "Chybné heslo"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 0e5265b87..d897e4755 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zapri"), ("Retry", "Ponovi"), ("OK", "V redu"), - ("remember_password_tip", "Potrebno je geslo"), + ("remember_password_tip", ""), ("Please enter your password", "Vnesite vaše geslo"), ("Remember password", "Zapomni si geslo"), ("Wrong Password", "Napačno geslo"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 68ef47be9..88d678ed3 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Mbyll"), ("Retry", "Riprovo"), ("OK", "OK"), - ("remember_password_tip", "Fjalëkalimi i detyrueshëm"), + ("remember_password_tip", ""), ("Please enter your password", "Ju lutem vendosni fjalëkalimin tuaj"), ("Remember password", "Mbani mend fjalëkalimin"), ("Wrong Password", "Fjalëkalim i gabuar"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index ccb328e3e..078d9ba29 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvori"), ("Retry", "Ponovi"), ("OK", "Ok"), - ("remember_password_tip", "Potrebna lozinka"), + ("remember_password_tip", ""), ("Please enter your password", "Molimo unesite svoju lozinku"), ("Remember password", "Zapamti lozinku"), ("Wrong Password", "Pogrešna lozinka"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 496c882bc..df5caee28 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Stäng"), ("Retry", "Försök igen"), ("OK", "OK"), - ("remember_password_tip", "Lösenord krävs"), + ("remember_password_tip", ""), ("Please enter your password", "Skriv in ditt lösenord"), ("Remember password", "Kom ihåg lösenord"), ("Wrong Password", "Fel lösenord"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 6ec149289..1b8bf69c2 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -480,11 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 92ae279c9..a152eb7b1 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "ปิด"), ("Retry", "ลองใหม่อีกครั้ง"), ("OK", "ตกลง"), - ("remember_password_tip", "ต้องใช้รหัสผ่าน"), + ("remember_password_tip", ""), ("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"), ("Remember password", "จดจำรหัสผ่าน"), ("Wrong Password", "รหัสผ่านไม่ถูกต้อง"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index b423a021b..c5d21460a 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Kapat"), ("Retry", "Tekrar Dene"), ("OK", "Tamam"), - ("remember_password_tip", "Şifre Gerekli"), + ("remember_password_tip", ""), ("Please enter your password", "Lütfen şifrenizi giriniz"), ("Remember password", "Şifreyi hatırla"), ("Wrong Password", "Hatalı şifre"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index e8685747e..d2156f4b7 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "關閉"), ("Retry", "重試"), ("OK", "確定"), - ("remember_password_tip", "需要密碼"), + ("remember_password_tip", ""), ("Please enter your password", "請輸入您的密碼"), ("Remember password", "記住密碼"), ("Wrong Password", "密碼錯誤"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此檔案與對方的檔案一致"), ("show_monitors_tip", "在工具列中顯示顯示器"), ("View Mode", "瀏覽模式"), - ("enter_rustdesk_passwd_tip", "輸入 RustDesk 密碼"), - ("remember_rustdesk_passwd_tip", "記住 RustDesk 密碼"), - ("login_linux_tip", "登入到遠端 Linux 使用者帳戶"), - ("login_linux_tooltip_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), + ("login_linux_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 952c36b13..1a91bb879 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрити"), ("Retry", "Спробувати знову"), ("OK", "ОК"), - ("remember_password_tip", "Потрібен пароль"), + ("remember_password_tip", ""), ("Please enter your password", "Будь ласка, введіть ваш пароль"), ("Remember password", "Запам'ятати пароль"), ("Wrong Password", "Невірний пароль"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index ab1aa2820..eeafcfa54 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Đóng"), ("Retry", "Thử lại"), ("OK", "OK"), - ("remember_password_tip", "Yêu cầu mật khẩu"), + ("remember_password_tip", ""), ("Please enter your password", "Mời nhập mật khẩu"), ("Remember password", "Nhớ mật khẩu"), ("Wrong Password", "Sai mật khẩu"), @@ -480,12 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", ""), ("show_monitors_tip", ""), ("View Mode", ""), - ("enter_rustdesk_passwd_tip", ""), - ("remember_rustdesk_passwd_tip", ""), ("login_linux_tip", ""), - ("login_linux_tooltip_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), - ("remember_password_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } From 8aa5f3a2a7382f1050195612e89140247058d416 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 11:47:32 +0800 Subject: [PATCH 174/366] mobile, edit os account Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 146 +++++++++++++++++ .../lib/desktop/pages/connection_page.dart | 5 +- .../lib/desktop/widgets/remote_toolbar.dart | 149 +----------------- flutter/lib/mobile/pages/remote_page.dart | 92 ++++------- 4 files changed, 184 insertions(+), 208 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 86611cd8a..0c61abb0d 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -897,3 +897,149 @@ void showRestartRemoteDevice( )); if (res == true) bind.sessionRestartRemoteDevice(id: id); } + +showSetOSPassword( + String id, + bool login, + OverlayDialogManager dialogManager, +) async { + final controller = TextEditingController(); + var password = await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; + var autoLogin = await bind.sessionGetOption(id: id, arg: 'auto-login') != ''; + controller.text = password; + dialogManager.show((setState, close) { + submit() { + var text = controller.text.trim(); + bind.sessionPeerOption(id: id, name: 'os-password', value: text); + bind.sessionPeerOption( + id: id, name: 'auto-login', value: autoLogin ? 'Y' : ''); + if (text != '' && login) { + bind.sessionInputOsPassword(id: id, value: text); + } + close(); + } + + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.password_rounded, color: MyTheme.accent), + Text(translate('OS Password')).paddingOnly(left: 10), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + PasswordWidget(controller: controller), + CheckboxListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text( + translate('Auto Login'), + ), + value: autoLogin, + onChanged: (v) { + if (v == null) return; + setState(() => autoLogin = v); + }, + ), + ], + ), + 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, + ); + }); +} + +showSetOSAccount( + String id, + OverlayDialogManager dialogManager, +) async { + final usernameController = TextEditingController(); + final passwdController = TextEditingController(); + var username = await bind.sessionGetOption(id: id, arg: 'os-username') ?? ''; + var password = await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; + usernameController.text = username; + passwdController.text = password; + dialogManager.show((setState, close) { + submit() { + final username = usernameController.text.trim(); + final password = usernameController.text.trim(); + bind.sessionPeerOption(id: id, name: 'os-username', value: username); + bind.sessionPeerOption(id: id, name: 'os-password', value: password); + close(); + } + + descWidget(String text) { + return Column( + children: [ + Align( + alignment: Alignment.centerLeft, + child: Text( + text, + maxLines: 3, + softWrap: true, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 16), + ), + ), + Container( + height: 8, + ), + ], + ); + } + + return CustomAlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.password_rounded, color: MyTheme.accent), + Text(translate('OS Account')).paddingOnly(left: 10), + ], + ), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + descWidget(translate("os_account_desk_tip")), + DialogTextField( + title: translate(DialogTextField.kUsernameTitle), + controller: usernameController, + prefixIcon: DialogTextField.kUsernameIcon, + errorText: null, + ), + PasswordWidget(controller: passwdController), + ], + ), + 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, + ); + }); +} diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 480ea75ff..3f6bb7d16 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -225,10 +225,7 @@ class _ConnectionPageState extends State children: [ Button( isOutline: true, - onTap: () => enterUserLoginAndPasswordDialog( - 'fdsfd', - gFFI.dialogManager, - ), + onTap: () => onConnect(isFileTransfer: true), text: "Transfer File", ), const SizedBox( diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index ec4d32ee0..b6f97790e 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -667,86 +667,7 @@ class _ControlMenu extends StatelessWidget { child: Text(translate('OS Account')), trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), ffi: ffi, - onPressed: () => _showSetOSAccount(id, false, ffi.dialogManager)); - } - - _showSetOSAccount( - String id, bool login, OverlayDialogManager dialogManager) async { - final usernameController = TextEditingController(); - final passwdController = TextEditingController(); - var username = - await bind.sessionGetOption(id: id, arg: 'os-username') ?? ''; - var password = - await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; - usernameController.text = username; - passwdController.text = password; - dialogManager.show((setState, close) { - submit() { - final username = usernameController.text.trim(); - final password = usernameController.text.trim(); - bind.sessionPeerOption(id: id, name: 'os-username', value: username); - bind.sessionPeerOption(id: id, name: 'os-password', value: password); - close(); - } - - descWidget(String text) { - return Column( - children: [ - Align( - alignment: Alignment.centerLeft, - child: Text( - text, - maxLines: 3, - softWrap: true, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 16), - ), - ), - Container( - height: 8, - ), - ], - ); - } - - return CustomAlertDialog( - title: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.password_rounded, color: MyTheme.accent), - Text(translate('OS Password')).paddingOnly(left: 10), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - descWidget(translate("os_account_desk_tip")), - DialogTextField( - title: translate(DialogTextField.kUsernameTitle), - controller: usernameController, - prefixIcon: DialogTextField.kUsernameIcon, - errorText: null, - ), - PasswordWidget(controller: passwdController), - ], - ), - 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, - ); - }); + onPressed: () => showSetOSAccount(id, ffi.dialogManager)); } osPassword() { @@ -754,73 +675,7 @@ class _ControlMenu extends StatelessWidget { child: Text(translate('OS Password')), trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), ffi: ffi, - onPressed: () => _showSetOSPassword(id, false, ffi.dialogManager)); - } - - _showSetOSPassword( - String id, bool login, OverlayDialogManager dialogManager) async { - final controller = TextEditingController(); - var password = - await bind.sessionGetOption(id: id, arg: 'os-password') ?? ''; - var autoLogin = - await bind.sessionGetOption(id: id, arg: 'auto-login') != ''; - controller.text = password; - dialogManager.show((setState, close) { - submit() { - var text = controller.text.trim(); - bind.sessionPeerOption(id: id, name: 'os-password', value: text); - bind.sessionPeerOption( - id: id, name: 'auto-login', value: autoLogin ? 'Y' : ''); - if (text != '' && login) { - bind.sessionInputOsPassword(id: id, value: text); - } - close(); - } - - return CustomAlertDialog( - title: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(Icons.password_rounded, color: MyTheme.accent), - Text(translate('OS Password')).paddingOnly(left: 10), - ], - ), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - PasswordWidget(controller: controller), - CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate('Auto Login'), - ), - value: autoLogin, - onChanged: (v) { - if (v == null) return; - setState(() => autoLogin = v); - }, - ), - ], - ), - 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, - ); - }); + onPressed: () => showSetOSPassword(id, false, ffi.dialogManager)); } transferFile(BuildContext context) { diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 083cdcd1c..1a7452dd7 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -548,19 +548,39 @@ class _RemotePageState extends State { more.add(PopupMenuItem( child: Text(translate('Refresh')), value: 'refresh')); } - more.add(PopupMenuItem( - child: Row( - children: ([ - Text(translate('OS Password')), - TextButton( - style: flatButtonStyle, - onPressed: () { - showSetOSPassword(id, false, gFFI.dialogManager); - }, - child: Icon(Icons.edit, color: MyTheme.accent), - ) - ])), - value: 'enter_os_password')); + if (gFFI.ffiModel.pi.is_headless) { + more.add( + PopupMenuItem( + child: Row( + children: ([ + Text(translate('OS Account')), + TextButton( + style: flatButtonStyle, + onPressed: () { + showSetOSAccount(id, gFFI.dialogManager); + }, + child: Icon(Icons.edit, color: MyTheme.accent), + ) + ])), + value: 'enter_os_account'), + ); + } else { + more.add( + PopupMenuItem( + child: Row( + children: ([ + Text(translate('OS Password')), + TextButton( + style: flatButtonStyle, + onPressed: () { + showSetOSPassword(id, false, gFFI.dialogManager); + }, + child: Icon(Icons.edit, color: MyTheme.accent), + ) + ])), + value: 'enter_os_password'), + ); + } if (!isWebDesktop) { if (perms['keyboard'] != false && perms['clipboard'] != false) { more.add(PopupMenuItem( @@ -657,6 +677,8 @@ class _RemotePageState extends State { } else { showSetOSPassword(id, true, gFFI.dialogManager); } + } else if (value == 'enter_os_account') { + showSetOSAccount(id, gFFI.dialogManager); } else if (value == 'reset_canvas') { gFFI.cursorModel.reset(); } else if (value == 'restart') { @@ -1071,50 +1093,6 @@ void showOptions( }, clickMaskDismiss: true, backDismiss: true); } -void showSetOSPassword( - String id, bool login, OverlayDialogManager dialogManager) async { - final controller = TextEditingController(); - var password = await bind.sessionGetOption(id: id, arg: "os-password") ?? ""; - var autoLogin = await bind.sessionGetOption(id: id, arg: "auto-login") != ""; - controller.text = password; - dialogManager.show((setState, close) { - return CustomAlertDialog( - title: Text(translate('OS Password')), - content: Column(mainAxisSize: MainAxisSize.min, children: [ - PasswordWidget(controller: controller), - CheckboxListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - translate('Auto Login'), - ), - value: autoLogin, - onChanged: (v) { - if (v == null) return; - setState(() => autoLogin = v); - }, - ), - ]), - actions: [ - dialogButton('Cancel', onPressed: close, isOutline: true), - dialogButton( - 'OK', - onPressed: () { - var text = controller.text.trim(); - bind.sessionPeerOption(id: id, name: "os-password", value: text); - bind.sessionPeerOption( - id: id, name: "auto-login", value: autoLogin ? 'Y' : ''); - if (text != "" && login) { - bind.sessionInputOsPassword(id: id, value: text); - } - close(); - }, - ), - ]); - }); -} - void sendPrompt(bool isMac, String key) { final old = isMac ? gFFI.inputModel.command : gFFI.inputModel.ctrl; if (isMac) { From 127ab57d62539949be82ed330f41a9a53cbc79b5 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 14:11:56 +0800 Subject: [PATCH 175/366] ignore 'gdm' on loginctl Signed-off-by: fufesou --- flutter/lib/models/model.dart | 6 ++--- libs/hbb_common/src/platform/linux.rs | 2 +- src/client.rs | 22 ++++++++-------- src/lang/en.rs | 2 +- src/platform/linux.rs | 10 ++++++++ src/platform/linux_desktop_manager.rs | 29 +++++++++++++-------- src/server/connection.rs | 36 ++++++++++++++++----------- src/ui/common.tis | 4 +-- src/ui/msgbox.tis | 24 +++++++++--------- 9 files changed, 80 insertions(+), 55 deletions(-) diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 7d1249906..77e1b8fbf 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -293,10 +293,10 @@ class FfiModel with ChangeNotifier { wrongPasswordDialog(id, dialogManager, type, title, text); } else if (type == 'input-password') { enterPasswordDialog(id, dialogManager); - } else if (type == 'xsession-login' || type == 'xsession-re-login') { + } else if (type == 'session-login' || type == 'session-re-login') { enterUserLoginDialog(id, dialogManager); - } else if (type == 'xsession-login-password' || - type == 'xsession-login-password') { + } else if (type == 'session-login-password' || + type == 'session-login-password') { enterUserLoginAndPasswordDialog(id, dialogManager); } else if (type == 'restarting') { showMsgBox(id, type, title, text, link, false, dialogManager, diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 1d826ea97..cf1cf6da5 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -135,7 +135,7 @@ pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("seat0") { + if !line.contains("gdm") && line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { if ignore_gdm_wayland { diff --git a/src/client.rs b/src/client.rs index f03cb0e55..3819ed382 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1927,34 +1927,34 @@ pub fn handle_login_error( lc.write().unwrap().password = Default::default(); interface.msgbox("re-input-password", err, "Do you want to enter again?", ""); true - } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY { + } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY { interface.msgbox( - "xsession-login", - "xsession is unready", + "session-login", + "session is unready", "Input linux user/password", "", ); true - } else if err == crate::server::LOGIN_MSG_XSESSION_FAILED { + } else if err == crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED { interface.msgbox( - "xsession-re-login", + "session-re-login", "xsession username/password is wrong", "Do you want to enter again?", "", ); true - } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY { + } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY { interface.msgbox( - "xsession-login-password", - "xsession is unready", + "session-login-password", + "session is unready", "Input connection password and linux user/password", "", ); true - } else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG { + } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG { interface.msgbox( - "xsession-login-re-password", - "xsession is unready and password is wrong", + "session-login-re-password", + "session is unready and password is wrong", "Do you want to enter again?", "", ); diff --git a/src/lang/en.rs b/src/lang/en.rs index 2ea65b05f..11ad719ab 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -56,7 +56,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Show monitors in toolbar."), ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), - ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), + ("login_linux_tip", "Remote desktop is unready. Please \n 1. Login on remote side and then try again\n 2. Or input remote account to login and start a X desktop session"), ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), ("remember_password_tip", "Remember password"), diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 8c4a68848..fa83fdc1f 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -21,6 +21,9 @@ use std::{ type Xdo = *const c_void; +pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland"; +pub const ENV_DESKTOP_PROTOCAL_X11: &str = "x11"; + pub const PA_SAMPLE_RATE: u32 = 48000; static mut UNMODIFIED: bool = true; @@ -930,6 +933,8 @@ mod desktop { if !self.sid.is_empty() && is_active(&self.sid) { return; } + + println!("REMOVE ME ================================== desktop: refresh"); let seat0_values = get_values_of_seat0(&[0, 1, 2]); if seat0_values[0].is_empty() { *self = Self::default(); @@ -950,6 +955,11 @@ mod desktop { self.get_display(); self.get_xauth(); self.set_is_subprocess(); + + println!( + "REMOVE ME ================================== desktop: {:?}", + self + ); } } } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index a957e045e..f1ddec58e 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -22,7 +22,7 @@ lazy_static::lazy_static! { #[derive(Debug)] struct DesktopManager { - x11_username: String, + seat0_username: String, child_username: String, child_exit: Arc, is_child_running: Arc, @@ -61,8 +61,8 @@ pub fn stop_xdesktop() { pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); if let Some(desktop_manager) = &mut (*desktop_manager) { - if !desktop_manager.x11_username.is_empty() { - return Ok((desktop_manager.x11_username.clone(), true)); + if !desktop_manager.seat0_username.is_empty() { + return Ok((desktop_manager.seat0_username.clone(), true)); } let _ = desktop_manager.try_start_x_session(username, password)?; @@ -76,7 +76,7 @@ pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String desktop_manager.is_running(), )) } else { - bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED); + bail!(crate::server::LOGIN_MSG_DESKTOP_NOT_INITED); } } @@ -86,14 +86,14 @@ pub fn is_headless() -> bool { .lock() .unwrap() .as_ref() - .map_or(false, |manager| manager.x11_username.is_empty()) + .map_or(false, |manager| manager.seat0_username.is_empty()) } pub fn get_username() -> String { match &*DESKTOP_MANAGER.lock().unwrap() { Some(manager) => { - if !manager.x11_username.is_empty() { - manager.x11_username.clone() + if !manager.seat0_username.is_empty() { + manager.seat0_username.clone() } else { if manager.is_running() && !manager.child_username.is_empty() { manager.child_username.clone() @@ -118,16 +118,23 @@ impl DesktopManager { } pub fn new() -> Self { - let mut x11_username = "".to_owned(); + let mut seat0_username = "".to_owned(); let seat0_values = get_values_of_seat0(&[0, 1, 2]); + println!( + "REMOVE ME ================================== DesktopManager: {:?}", + &seat0_values + ); if !seat0_values[0].is_empty() { - if "x11" == get_display_server_of_session(&seat0_values[1]) { - x11_username = seat0_values[2].clone(); + let display_server = get_display_server_of_session(&seat0_values[1]); + if display_server == ENV_DESKTOP_PROTOCAL_X11 + || display_server == ENV_DESKTOP_PROTOCAL_WAYLAND + { + seat0_username = seat0_values[2].clone(); } } Self { - x11_username, + seat0_username, child_username: "".to_owned(), child_exit: Arc::new(AtomicBool::new(true)), is_child_running: Arc::new(AtomicBool::new(false)), diff --git a/src/server/connection.rs b/src/server/connection.rs index ee4bdda35..f9c64378e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -59,12 +59,14 @@ lazy_static::lazy_static! { pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); -pub const LOGIN_MSG_XDESKTOP_NOT_INITED: &str = "xdesktop env is not inited"; -pub const LOGIN_MSG_XSESSION_NOT_READY: &str = "xsession unready"; -pub const LOGIN_MSG_XSESSION_FAILED: &str = "xsession failed"; -pub const LOGIN_MSG_XSESSION_ANOTHER_USER_READTY: &str = "xsession another user login"; -pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY: &str = "xsession unready, password empty"; -pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG: &str = "xsession unready, password wrong"; +pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session unready"; +pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; +pub const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER: &str = "Desktop session another user login"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY: &str = + "Desktop session unready, password empty"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG: &str = + "Desktop session unready, password wrong"; pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; pub const LOGIN_MSG_OFFLINE: &str = "Offline"; @@ -1081,27 +1083,33 @@ impl Connection { if _username.is_empty() { let username = linux_desktop_manager::get_username(); if username.is_empty() { - LOGIN_MSG_XSESSION_NOT_READY + LOGIN_MSG_DESKTOP_SESSION_NOT_READY } else { "" } .to_owned() } else { + let username = linux_desktop_manager::get_username(); + if username == _username { + // No need to verify password here. + return "".to_owned(); + } + match linux_desktop_manager::try_start_x_session(_username, _passsword) { Ok((username, x11_ready)) => { if x11_ready { if _username != username { - LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned() + LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER.to_owned() } else { "".to_owned() } } else { - LOGIN_MSG_XSESSION_NOT_READY.to_owned() + LOGIN_MSG_DESKTOP_SESSION_NOT_READY.to_owned() } } Err(e) => { log::error!("Failed to start xsession {}", e); - LOGIN_MSG_XSESSION_FAILED.to_owned() + LOGIN_MSG_DESKTOP_XSESSION_FAILED.to_owned() } } } @@ -1278,8 +1286,8 @@ impl Connection { Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), None => Self::try_start_desktop("", ""), }; - // If err is LOGIN_MSG_XSESSION_NOT_READY, just keep this msg and go on checking password. - if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_XSESSION_NOT_READY { + // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. + if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; return true; } @@ -1315,7 +1323,7 @@ impl Connection { if desktop_err.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { - self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY) + self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY) .await; } } else { @@ -1362,7 +1370,7 @@ impl Connection { self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; self.try_start_cm(lr.my_id, lr.my_name, false); } else { - self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG) + self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG) .await; } } else { diff --git a/src/ui/common.tis b/src/ui/common.tis index ef6d215aa..92e704052 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -262,7 +262,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= else msgbox("connecting", "Connecting...", "Logging in..."); } }; - } else if (type == "xsession-login" || type == "xsession-re-login") { + } else if (type == "session-login" || type == "session-re-login") { callback = function (res) { if (!res) { view.close(); @@ -274,7 +274,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= else msgbox("connecting", "Connecting...", "Logging in..."); } }; - } else if (type.indexOf("xsession-login") >= 0) { + } else if (type.indexOf("session-login") >= 0) { callback = function (res) { if (!res) { view.close(); diff --git a/src/ui/msgbox.tis b/src/ui/msgbox.tis index d9a311452..2099a8e7b 100644 --- a/src/ui/msgbox.tis +++ b/src/ui/msgbox.tis @@ -32,7 +32,7 @@ class MsgboxComponent: Reactor.Component { } function getIcon(color) { - if (this.type == "input-password" || this.type == "xsession-login" || this.type == "xsession-login-password") { + if (this.type == "input-password" || this.type == "session-login" || this.type == "session-login-password") { return ; } if (this.type == "connecting") { @@ -41,7 +41,7 @@ class MsgboxComponent: Reactor.Component { if (this.type == "success") { return ; } - if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") { + if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "session-re-login" || this.type == "session-login-re-password") { return ; } return null; @@ -81,9 +81,9 @@ class MsgboxComponent: Reactor.Component { function getContent() { if (this.type == "input-password") { return this.getInputPasswordContent(); - } else if (this.type == "xsession-login") { + } else if (this.type == "session-login") { return this.getInputUserPasswordContent(); - } else if (this.type == "xsession-login-password") { + } else if (this.type == "session-login-password") { return this.getXsessionPasswordContent(); } else if (this.type == "custom-os-password") { var ts = this.auto_login ? { checked: true } : {}; @@ -96,13 +96,13 @@ class MsgboxComponent: Reactor.Component { } function getColor() { - if (this.type == "input-password" || this.type == "custom-os-password" || this.type == "xsession-login" || this.type == "xsession-login-password") { + if (this.type == "input-password" || this.type == "custom-os-password" || this.type == "session-login" || this.type == "session-login-password") { return "#AD448E"; } if (this.type == "success") { return "#32bea6"; } - if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") { + if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "session-re-login" || this.type == "session-login-re-password") { return "#e04f5f"; } return "#2C8CFF"; @@ -202,13 +202,13 @@ class MsgboxComponent: Reactor.Component { this.update(); return; } - if (this.type == "xsession-re-login") { - this.type = "xsession-login"; + if (this.type == "session-re-login") { + this.type = "session-login"; this.update(); return; } - if (this.type == "xsession-login-re-password") { - this.type = "xsession-login-password"; + if (this.type == "session-login-re-password") { + this.type = "session-login-password"; this.update(); return; } @@ -273,14 +273,14 @@ class MsgboxComponent: Reactor.Component { return; } } - if (this.type == "xsession-login") { + if (this.type == "session-login") { values.osusername = (values.osusername || "").trim(); values.ospassword = (values.ospassword || "").trim(); if (!values.osusername || !values.ospassword) { return; } } - if (this.type == "xsession-login-password") { + if (this.type == "session-login-password") { values.password = (values.password || "").trim(); values.osusername = (values.osusername || "").trim(); values.ospassword = (values.ospassword || "").trim(); From 9ef4f4c1dea28b2c32b5697f0e394008f46a8405 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 15:36:03 +0800 Subject: [PATCH 176/366] handle_hash empty password Signed-off-by: fufesou --- src/client.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3819ed382..741c45416 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1991,7 +1991,7 @@ pub async fn handle_hash( lc: Arc>, password_preset: &str, hash: Hash, - _interface: &impl Interface, + interface: &impl Interface, peer: &mut Stream, ) { lc.write().unwrap().hash = hash.clone(); @@ -2015,22 +2015,21 @@ pub async fn handle_hash( if password.is_empty() { password = lc.read().unwrap().config.password.clone(); } - if password.is_empty() { + let password = if password.is_empty() { // login without password, the remote side can click accept - send_login(lc.clone(), "".to_owned(), "".to_owned(), Vec::new(), peer).await; + interface.msgbox("input-password", "Password Required", "", ""); + Vec::new() } else { let mut hasher = Sha256::new(); hasher.update(&password); hasher.update(&hash.challenge); - send_login( - lc.clone(), - "".to_owned(), - "".to_owned(), - hasher.finalize()[..].into(), - peer, - ) - .await; - } + hasher.finalize()[..].into() + }; + + let os_username = lc.read().unwrap().get_option("os-username"); + let os_password = lc.read().unwrap().get_option("os-password"); + + send_login(lc.clone(), os_username, os_password, password, peer).await; lc.write().unwrap().hash = hash; } From 34c36153201aa4103b20c488784d24c356d5b7d3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 16:17:24 +0800 Subject: [PATCH 177/366] get values of seat0, do not filter gdm Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 2 +- src/lang/en.rs | 2 +- src/platform/linux.rs | 12 +++++++++ src/platform/linux_desktop_manager.rs | 37 +++++++++++++++++++-------- 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index cf1cf6da5..1d826ea97 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -135,7 +135,7 @@ pub fn get_values_of_seat0_with_gdm_wayland(indices: &[usize]) -> Vec { fn _get_values_of_seat0(indices: &[usize], ignore_gdm_wayland: bool) -> Vec { if let Ok(output) = run_loginctl(None) { for line in String::from_utf8_lossy(&output.stdout).lines() { - if !line.contains("gdm") && line.contains("seat0") { + if line.contains("seat0") { if let Some(sid) = line.split_whitespace().next() { if is_active(sid) { if ignore_gdm_wayland { diff --git a/src/lang/en.rs b/src/lang/en.rs index 11ad719ab..2ea65b05f 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -56,7 +56,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Show monitors in toolbar."), ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), - ("login_linux_tip", "Remote desktop is unready. Please \n 1. Login on remote side and then try again\n 2. Or input remote account to login and start a X desktop session"), + ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), ("remember_password_tip", "Remember password"), diff --git a/src/platform/linux.rs b/src/platform/linux.rs index fa83fdc1f..800875a61 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -357,6 +357,7 @@ pub fn start_os_service() { &mut last_restart, &mut user_server, ) { + stop_xorg_subprocess(); force_stop_server(); start_server( Some((desktop.uid.clone(), desktop.username.clone())), @@ -402,6 +403,12 @@ pub fn get_active_userid() -> String { get_values_of_seat0(&[1])[0].clone() } +#[inline] +pub fn is_gdm_user(username: &str) -> bool { + username == "gdm" + // || username == "lightgdm" +} + fn get_cm() -> bool { if let Ok(output) = Command::new("ps").args(vec!["aux"]).output() { for line in String::from_utf8_lossy(&output.stdout).lines() { @@ -800,6 +807,11 @@ mod desktop { self.sid.is_empty() } + #[inline] + pub fn is_login_wayland(&self) -> bool { + super::is_gdm_user(&self.username) && self.protocal == ENV_DESKTOP_PROTOCAL_WAYLAND + } + #[inline] pub fn is_headless(&self) -> bool { self.sid.is_empty() || self.is_rustdesk_subprocess diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index f1ddec58e..f1f098c69 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -23,6 +23,7 @@ lazy_static::lazy_static! { #[derive(Debug)] struct DesktopManager { seat0_username: String, + seat0_display_server: String, child_username: String, child_exit: Arc, is_child_running: Arc, @@ -61,8 +62,8 @@ pub fn stop_xdesktop() { pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); if let Some(desktop_manager) = &mut (*desktop_manager) { - if !desktop_manager.seat0_username.is_empty() { - return Ok((desktop_manager.seat0_username.clone(), true)); + if let Some(seat0_username) = desktop_manager.get_supported_display_seat0_username() { + return Ok((seat0_username, true)); } let _ = desktop_manager.try_start_x_session(username, password)?; @@ -86,14 +87,16 @@ pub fn is_headless() -> bool { .lock() .unwrap() .as_ref() - .map_or(false, |manager| manager.seat0_username.is_empty()) + .map_or(false, |manager| { + manager.get_supported_display_seat0_username().is_none() + }) } pub fn get_username() -> String { match &*DESKTOP_MANAGER.lock().unwrap() { Some(manager) => { - if !manager.seat0_username.is_empty() { - manager.seat0_username.clone() + if let Some(seat0_username) = manager.get_supported_display_seat0_username() { + seat0_username } else { if manager.is_running() && !manager.child_username.is_empty() { manager.child_username.clone() @@ -119,28 +122,40 @@ impl DesktopManager { pub fn new() -> Self { let mut seat0_username = "".to_owned(); + let mut seat0_display_server = "".to_owned(); let seat0_values = get_values_of_seat0(&[0, 1, 2]); println!( "REMOVE ME ================================== DesktopManager: {:?}", &seat0_values ); if !seat0_values[0].is_empty() { - let display_server = get_display_server_of_session(&seat0_values[1]); - if display_server == ENV_DESKTOP_PROTOCAL_X11 - || display_server == ENV_DESKTOP_PROTOCAL_WAYLAND - { - seat0_username = seat0_values[2].clone(); - } + seat0_username = seat0_values[2].clone(); + seat0_display_server = get_display_server_of_session(&seat0_values[1]); } Self { seat0_username, + seat0_display_server, child_username: "".to_owned(), child_exit: Arc::new(AtomicBool::new(true)), is_child_running: Arc::new(AtomicBool::new(false)), } } + fn get_supported_display_seat0_username(&self) -> Option { + if is_gdm_user(&self.seat0_username) + && self.seat0_display_server == ENV_DESKTOP_PROTOCAL_WAYLAND + { + None + } else { + if self.seat0_username.is_empty() { + None + } else { + Some(self.seat0_username.clone()) + } + } + } + #[inline] fn get_xauth() -> String { let xauth = get_env_var("XAUTHORITY"); From d82d2471d752633bc170e2d1c250a202d2c42384 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 18:16:48 +0800 Subject: [PATCH 178/366] temp commit Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 22 ++++++++++++++++++++-- src/core_main.rs | 5 +++++ src/flutter.rs | 17 ++++++++++++++++- src/platform/linux.rs | 12 ++---------- src/platform/linux_desktop_manager.rs | 16 ++++++++-------- src/server/connection.rs | 17 +++++++++++++++-- src/ui_interface.rs | 8 +++++--- 7 files changed, 71 insertions(+), 26 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 1d826ea97..30ceaa760 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -35,6 +35,7 @@ pub fn is_gdm_user(username: &str) -> bool { // || username == "lightgdm" } +<<<<<<< HEAD #[inline] pub fn is_desktop_wayland() -> bool { get_display_server() == DISPLAY_SERVER_WAYLAND @@ -43,12 +44,23 @@ pub fn is_desktop_wayland() -> bool { #[inline] pub fn is_x11_or_headless() -> bool { !is_desktop_wayland() +======= +pub fn is_x11_or_headless() -> bool { + let (username, display_server) = get_user_and_display_server(); + display_server == DISPLAY_SERVER_WAYLAND && is_gdm_user(&username) + || display_server != DISPLAY_SERVER_WAYLAND +} + +pub fn is_desktop_wayland() -> bool { + let (username, display_server) = get_user_and_display_server(); + display_server == DISPLAY_SERVER_WAYLAND && !is_gdm_user(&username) +>>>>>>> temp commit } // -1 const INVALID_SESSION: &str = "4294967295"; -pub fn get_display_server() -> String { +pub fn get_user_and_display_server() -> (String, String) { let mut session = get_values_of_seat0(&[0])[0].clone(); if session.is_empty() { // loginctl has not given the expected output. try something else. @@ -63,11 +75,17 @@ pub fn get_display_server() -> String { } } } +<<<<<<< HEAD if session.is_empty() { +======= + + let display_server = if session.is_empty() { +>>>>>>> temp commit "".to_owned() } else { get_display_server_of_session(&session) - } + }; + (session, display_server) } pub fn get_display_server_of_session(session: &str) -> String { diff --git a/src/core_main.rs b/src/core_main.rs index e5eb64136..05a5ad769 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -224,6 +224,11 @@ pub fn core_main() -> Option> { // call connection manager to establish connections // meanwhile, return true to call flutter window to show control panel crate::ui_interface::start_option_status_sync(); + } else if args[0] == "--cm-no-ui" { + #[cfg(feature = "flutter")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + crate::flutter::connection_manager::start_cm_no_ui(); + return None; } } //_async_logger_holder.map(|x| x.flush()); diff --git a/src/flutter.rs b/src/flutter.rs index b293059d0..3d49dcf03 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -815,8 +815,19 @@ pub mod connection_manager { } } + #[inline] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + pub fn start_cm_no_ui() { + start_listen_ipc(false); + } + + #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn start_listen_ipc_thread() { + start_listen_ipc(true); + } + + fn start_listen_ipc(new_thread: bool) { use crate::ui_cm_interface::{start_ipc, ConnectionManager}; #[cfg(target_os = "linux")] @@ -825,7 +836,11 @@ pub mod connection_manager { let cm = ConnectionManager { ui_handler: FlutterHandler {}, }; - std::thread::spawn(move || start_ipc(cm)); + if new_thread { + std::thread::spawn(move || start_ipc(cm)); + } else { + start_ipc(cm); + } } #[cfg(target_os = "android")] diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 800875a61..745464a58 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -21,9 +21,6 @@ use std::{ type Xdo = *const c_void; -pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland"; -pub const ENV_DESKTOP_PROTOCAL_X11: &str = "x11"; - pub const PA_SAMPLE_RATE: u32 = 48000; static mut UNMODIFIED: bool = true; @@ -403,12 +400,6 @@ pub fn get_active_userid() -> String { get_values_of_seat0(&[1])[0].clone() } -#[inline] -pub fn is_gdm_user(username: &str) -> bool { - username == "gdm" - // || username == "lightgdm" -} - fn get_cm() -> bool { if let Ok(output) = Command::new("ps").args(vec!["aux"]).output() { for line in String::from_utf8_lossy(&output.stdout).lines() { @@ -809,7 +800,7 @@ mod desktop { #[inline] pub fn is_login_wayland(&self) -> bool { - super::is_gdm_user(&self.username) && self.protocal == ENV_DESKTOP_PROTOCAL_WAYLAND + super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND } #[inline] @@ -961,6 +952,7 @@ mod desktop { if self.is_login_wayland() { self.display = "".to_owned(); self.xauth = "".to_owned(); + self.is_rustdesk_subprocess = false; return; } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index f1f098c69..1896060fe 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -123,15 +123,15 @@ impl DesktopManager { pub fn new() -> Self { let mut seat0_username = "".to_owned(); let mut seat0_display_server = "".to_owned(); - let seat0_values = get_values_of_seat0(&[0, 1, 2]); - println!( - "REMOVE ME ================================== DesktopManager: {:?}", - &seat0_values - ); + let seat0_values = get_values_of_seat0(&[0, 2]); if !seat0_values[0].is_empty() { - seat0_username = seat0_values[2].clone(); - seat0_display_server = get_display_server_of_session(&seat0_values[1]); + seat0_username = seat0_values[1].clone(); + seat0_display_server = get_display_server_of_session(&seat0_values[0]); } + println!( + "REMOVE ME ================================== DesktopManager: {:?}, display server: {}", + &seat0_values, &seat0_display_server + ); Self { seat0_username, @@ -144,7 +144,7 @@ impl DesktopManager { fn get_supported_display_seat0_username(&self) -> Option { if is_gdm_user(&self.seat0_username) - && self.seat0_display_server == ENV_DESKTOP_PROTOCAL_WAYLAND + && self.seat0_display_server == DISPLAY_SERVER_WAYLAND { None } else { diff --git a/src/server/connection.rs b/src/server/connection.rs index f9c64378e..2777824df 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -899,8 +899,10 @@ impl Connection { } #[cfg(target_os = "linux")] if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() { - let dtype = crate::platform::linux::get_display_server(); - if dtype != "x11" && dtype != "wayland" { + let (_, dtype) = crate::platform::linux::get_user_and_display_server(); + if dtype != crate::platform::linux::DISPLAY_SERVER_X11 + && dtype != crate::platform::linux::DISPLAY_SERVER_WAYLAND + { res.set_error(format!( "Unsupported display server type \"{}\", x11 or wayland expected", dtype @@ -1131,6 +1133,7 @@ impl Connection { } fn validate_password(&mut self) -> bool { + return true; if password::temporary_enabled() { let password = password::temporary_password(); if self.validate_one_password(password.clone()) { @@ -1286,6 +1289,12 @@ impl Connection { Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), None => Self::try_start_desktop("", ""), }; + + println!( + "REMOVE ME =================================== try_start_desktop '{}'", + &desktop_err + ); + // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; @@ -2170,6 +2179,10 @@ async fn start_ipc( if password::hide_cm() { args.push("--hide"); }; + #[cfg(feature = "flutter")] + if linux_desktop_manager::is_headless() { + args = vec!["--cm-no-ui"]; + } let run_done; if crate::platform::is_root() { let mut res = Ok(None); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 11357be4a..1181598a6 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -511,11 +511,13 @@ pub fn get_error() -> String { #[cfg(not(any(feature = "cli")))] #[cfg(target_os = "linux")] { - let dtype = crate::platform::linux::get_display_server(); - if "wayland" == dtype { + let (username, dtype) = crate::platform::linux::get_user_and_display_server(); + if crate::platform::linux::DISPLAY_SERVER_WAYLAND == dtype + && !crate::platform::linux::is_gdm_user(&username) + { return crate::server::wayland::common_get_error(); } - if dtype != "x11" { + if dtype != crate::platform::linux::DISPLAY_SERVER_X11 { return format!( "{} {}, {}", crate::client::translate("Unsupported display server".to_owned()), From d86ef4a86ebeafdf3e20b438aebb6413257ec141 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 18:39:21 +0800 Subject: [PATCH 179/366] headless, linux, debug Signed-off-by: fufesou --- libs/hbb_common/src/platform/linux.rs | 6 ++++-- src/platform/linux.rs | 1 + src/server/video_service.rs | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index 30ceaa760..ce0782a61 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -61,7 +61,9 @@ pub fn is_desktop_wayland() -> bool { const INVALID_SESSION: &str = "4294967295"; pub fn get_user_and_display_server() -> (String, String) { - let mut session = get_values_of_seat0(&[0])[0].clone(); + let seat0_values = get_values_of_seat0(&[0, 2]); + let mut session = seat0_values[0].clone(); + let username = seat0_values[1].clone(); if session.is_empty() { // loginctl has not given the expected output. try something else. if let Ok(sid) = std::env::var("XDG_SESSION_ID") { @@ -85,7 +87,7 @@ pub fn get_user_and_display_server() -> (String, String) { } else { get_display_server_of_session(&session) }; - (session, display_server) + (username, display_server) } pub fn get_display_server_of_session(session: &str) -> String { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 745464a58..5bfa6a9df 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -464,6 +464,7 @@ pub fn get_env_var(k: &str) -> String { } } +// Headless is enabled, no need to wait prelogin. pub fn is_prelogin() -> bool { let n = get_active_userid().len(); n < 4 && n > 1 diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 205d0584c..483c29cfa 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -450,6 +450,8 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] ensure_close_virtual_device()?; + println!("REMOVE ME ================================= run 111"); + // ensure_inited() is needed because release_resource() may be called. #[cfg(target_os = "linux")] super::wayland::ensure_inited()?; From 4ed6681bfdd554aba284bcca2d32f0d629ac84ed Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 18:52:47 +0800 Subject: [PATCH 180/366] run cm as user Signed-off-by: fufesou --- src/server/connection.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 2777824df..1a83cfed6 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2165,6 +2165,8 @@ async fn start_ipc( mut rx_to_cm: mpsc::UnboundedReceiver, tx_from_cm: mpsc::UnboundedSender, ) -> ResultType<()> { + use hbb_common::platform::linux::run_cmds; + loop { if crate::platform::is_prelogin() { break; @@ -2179,8 +2181,21 @@ async fn start_ipc( if password::hide_cm() { args.push("--hide"); }; + #[cfg(not(feature = "flutter"))] + let user = None; + let mut user = None; #[cfg(feature = "flutter")] if linux_desktop_manager::is_headless() { + let username = linux_desktop_manager::get_username(); + let uid = { + let output = run_cmds(format!("id -u {}", &username))?; + let output = output.trim(); + if output.is_empty() || !output.parse::().is_ok() { + bail!("Invalid username {}", &username); + } + output.to_string() + }; + user = Some((username, uid)); args = vec!["--cm-no-ui"]; } let run_done; @@ -2195,7 +2210,7 @@ async fn start_ipc( #[cfg(target_os = "linux")] { log::debug!("Start cm"); - res = crate::platform::run_as_user(args.clone(), None); + res = crate::platform::run_as_user(args.clone(), user); } if res.is_ok() { break; From b33ae6ec64085d5690622bbf073c099b123f2ac0 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 18:55:03 +0800 Subject: [PATCH 181/366] fix build Signed-off-by: fufesou --- src/server/connection.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 1a83cfed6..24e3af5b1 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2210,7 +2210,7 @@ async fn start_ipc( #[cfg(target_os = "linux")] { log::debug!("Start cm"); - res = crate::platform::run_as_user(args.clone(), user); + res = crate::platform::run_as_user(args.clone(), user.clone()); } if res.is_ok() { break; From 6238098cd0defe4529f58360d3383047e5924172 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 21:17:33 +0800 Subject: [PATCH 182/366] start cm, tmp commit Signed-off-by: fufesou --- src/server/connection.rs | 63 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 24e3af5b1..28c661537 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -18,6 +18,8 @@ use crate::{ use crate::{common::DEVICE_NAME, flutter::connection_manager::start_channel}; use crate::{ipc, VERSION}; use cidr_utils::cidr::IpCidr; +#[cfg(all(target_os = "linux", feature = "flutter"))] +use hbb_common::platform::linux::run_cmds; use hbb_common::{ config::Config, fs, @@ -148,6 +150,8 @@ pub struct Connection { audio_input_device_before_voice_call: Option, options_in_login: Option, pressed_modifiers: HashSet, + rx_cm_stream_ready: mpsc::Receiver<()>, + tx_desktop_ready: mpsc::Sender<()>, } impl ConnInner { @@ -208,6 +212,8 @@ impl Connection { let (tx_video, mut rx_video) = mpsc::unbounded_channel::<(Instant, Arc)>(); let (tx_input, _rx_input) = std_mpsc::channel(); let mut hbbs_rx = crate::hbbs_http::sync::signal_receiver(); + let (tx_cm_stream_ready, rx_cm_stream_ready) = mpsc::channel(1); + let (tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1); #[cfg(not(any(target_os = "android", target_os = "ios")))] let tx_cloned = tx.clone(); @@ -260,10 +266,14 @@ impl Connection { audio_input_device_before_voice_call: None, options_in_login: None, pressed_modifiers: Default::default(), + rx_cm_stream_ready, + tx_desktop_ready, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { - if let Err(err) = start_ipc(rx_to_cm, tx_from_cm).await { + if let Err(err) = + start_ipc(rx_to_cm, tx_from_cm, rx_desktop_ready, tx_cm_stream_ready).await + { log::error!("ipc to connection manager exit: {}", err); } }); @@ -1289,6 +1299,9 @@ impl Connection { Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), None => Self::try_start_desktop("", ""), }; + #[cfg(target_os = "linux")] + let is_headless = linux_desktop_manager::is_headless(); + let wait_ipc_timeout = 10_000; println!( "REMOVE ME =================================== try_start_desktop '{}'", @@ -1320,6 +1333,11 @@ impl Connection { return false; } else if self.is_recent_session() { if desktop_err.is_empty() { + #[cfg(target_os = "linux")] + if is_headless { + self.tx_desktop_ready.send(()).await.ok(); + let _res = timeout(wait_ipc_timeout, self.rx_cm_stream_ready.recv()).await; + } self.try_start_cm(lr.my_id, lr.my_name, true); self.send_logon_response().await; if self.port_forward_socket.is_some() { @@ -1387,6 +1405,12 @@ impl Connection { LOGIN_FAILURES.lock().unwrap().remove(&self.ip); } if desktop_err.is_empty() { + #[cfg(target_os = "linux")] + if is_headless { + self.tx_desktop_ready.send(()).await.ok(); + let _res = + timeout(wait_ipc_timeout, self.rx_cm_stream_ready.recv()).await; + } self.send_logon_response().await; self.try_start_cm(lr.my_id, lr.my_name, true); if self.port_forward_socket.is_some() { @@ -2164,9 +2188,9 @@ pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) { async fn start_ipc( mut rx_to_cm: mpsc::UnboundedReceiver, tx_from_cm: mpsc::UnboundedSender, + mut _rx_desktop_ready: mpsc::Receiver<()>, + tx_stream_ready: mpsc::Sender<()>, ) -> ResultType<()> { - use hbb_common::platform::linux::run_cmds; - loop { if crate::platform::is_prelogin() { break; @@ -2181,12 +2205,25 @@ async fn start_ipc( if password::hide_cm() { args.push("--hide"); }; - #[cfg(not(feature = "flutter"))] + + #[cfg(all(target_os = "linux", not(feature = "flutter")))] let user = None; + #[cfg(all(target_os = "linux", feature = "flutter"))] let mut user = None; - #[cfg(feature = "flutter")] + #[cfg(all(target_os = "linux", feature = "flutter"))] if linux_desktop_manager::is_headless() { - let username = linux_desktop_manager::get_username(); + let mut username = linux_desktop_manager::get_username(); + loop { + if !username.is_empty() { + break; + } + let _res = timeout(500, _rx_desktop_ready.recv()).await; + username = linux_desktop_manager::get_username(); + } + println!( + "REMOVE ME =================================== headless username '{}' ", + &username + ); let uid = { let output = run_cmds(format!("id -u {}", &username))?; let output = output.trim(); @@ -2196,6 +2233,10 @@ async fn start_ipc( output.to_string() }; user = Some((username, uid)); + println!( + "REMOVE ME =================================== headless user '{:?}' ", + &user + ); args = vec!["--cm-no-ui"]; } let run_done; @@ -2210,6 +2251,10 @@ async fn start_ipc( #[cfg(target_os = "linux")] { log::debug!("Start cm"); + println!( + "REMOVE ME =================================== start_ipc 222 '{}' ", + linux_desktop_manager::is_headless() + ); res = crate::platform::run_as_user(args.clone(), user.clone()); } if res.is_ok() { @@ -2224,6 +2269,10 @@ async fn start_ipc( } else { run_done = false; } + println!( + "REMOVE ME =================================== start_ipc 333 '{}' ", + run_done + ); if !run_done { log::debug!("Start cm"); super::CHILD_PROCESS @@ -2242,6 +2291,8 @@ async fn start_ipc( bail!("Failed to connect to connection manager"); } } + + let _res = tx_stream_ready.send(()).await; let mut stream = stream.unwrap(); loop { tokio::select! { From f91514e164f73f494b103db01dcffb7886d76d8f Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 21:35:47 +0800 Subject: [PATCH 183/366] cm-no-ui, debug Signed-off-by: fufesou --- src/platform/linux_desktop_manager.rs | 11 ++++------- src/server/connection.rs | 20 ++------------------ src/server/video_service.rs | 2 -- 3 files changed, 6 insertions(+), 27 deletions(-) diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 1896060fe..7b87b9b02 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -143,16 +143,13 @@ impl DesktopManager { } fn get_supported_display_seat0_username(&self) -> Option { - if is_gdm_user(&self.seat0_username) - && self.seat0_display_server == DISPLAY_SERVER_WAYLAND + if is_gdm_user(&self.seat0_username) && self.seat0_display_server == DISPLAY_SERVER_WAYLAND { None + } else if self.seat0_username.is_empty() { + None } else { - if self.seat0_username.is_empty() { - None - } else { - Some(self.seat0_username.clone()) - } + Some(self.seat0_username.clone()) } } diff --git a/src/server/connection.rs b/src/server/connection.rs index 28c661537..e629fa93a 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2217,13 +2217,9 @@ async fn start_ipc( if !username.is_empty() { break; } - let _res = timeout(500, _rx_desktop_ready.recv()).await; + let _res = timeout(1_000, _rx_desktop_ready.recv()).await; username = linux_desktop_manager::get_username(); } - println!( - "REMOVE ME =================================== headless username '{}' ", - &username - ); let uid = { let output = run_cmds(format!("id -u {}", &username))?; let output = output.trim(); @@ -2232,11 +2228,7 @@ async fn start_ipc( } output.to_string() }; - user = Some((username, uid)); - println!( - "REMOVE ME =================================== headless user '{:?}' ", - &user - ); + user = Some((uid, username)); args = vec!["--cm-no-ui"]; } let run_done; @@ -2251,10 +2243,6 @@ async fn start_ipc( #[cfg(target_os = "linux")] { log::debug!("Start cm"); - println!( - "REMOVE ME =================================== start_ipc 222 '{}' ", - linux_desktop_manager::is_headless() - ); res = crate::platform::run_as_user(args.clone(), user.clone()); } if res.is_ok() { @@ -2269,10 +2257,6 @@ async fn start_ipc( } else { run_done = false; } - println!( - "REMOVE ME =================================== start_ipc 333 '{}' ", - run_done - ); if !run_done { log::debug!("Start cm"); super::CHILD_PROCESS diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 483c29cfa..205d0584c 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -450,8 +450,6 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] ensure_close_virtual_device()?; - println!("REMOVE ME ================================= run 111"); - // ensure_inited() is needed because release_resource() may be called. #[cfg(target_os = "linux")] super::wayland::ensure_inited()?; From 39917c174a2b6eb592e6ab19311e42b857b0f275 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 21:54:20 +0800 Subject: [PATCH 184/366] kill --cm-no-ui Signed-off-by: fufesou --- src/platform/linux.rs | 15 ++++++--------- src/platform/linux_desktop_manager.rs | 5 ----- src/server/connection.rs | 5 ----- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 5bfa6a9df..27c0fde48 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -230,6 +230,9 @@ fn stop_xorg_subprocess() { let _ = run_cmds(&format!( r##"ps -ef | grep '/etc/rustdesk/xorg.conf' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); + let _ = run_cmds(format!( + r##"ps -ef | grep -E 'rustdesk +--cm-no-ui' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, + )); } fn should_start_server( @@ -302,7 +305,7 @@ fn force_stop_server() { pub fn start_os_service() { stop_rustdesk_servers(); - stop_xorg_subprocess(); + stop_subprocess(); start_uinput_service(); let running = Arc::new(AtomicBool::new(true)); @@ -337,7 +340,7 @@ pub fn start_os_service() { &mut last_restart, &mut server, ) { - stop_xorg_subprocess(); + stop_subprocess(); force_stop_server(); start_server(None, &mut server); } @@ -354,7 +357,7 @@ pub fn start_os_service() { &mut last_restart, &mut user_server, ) { - stop_xorg_subprocess(); + stop_subprocess(); force_stop_server(); start_server( Some((desktop.uid.clone(), desktop.username.clone())), @@ -938,7 +941,6 @@ mod desktop { return; } - println!("REMOVE ME ================================== desktop: refresh"); let seat0_values = get_values_of_seat0(&[0, 1, 2]); if seat0_values[0].is_empty() { *self = Self::default(); @@ -960,11 +962,6 @@ mod desktop { self.get_display(); self.get_xauth(); self.set_is_subprocess(); - - println!( - "REMOVE ME ================================== desktop: {:?}", - self - ); } } } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 7b87b9b02..c70724a5e 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -128,11 +128,6 @@ impl DesktopManager { seat0_username = seat0_values[1].clone(); seat0_display_server = get_display_server_of_session(&seat0_values[0]); } - println!( - "REMOVE ME ================================== DesktopManager: {:?}, display server: {}", - &seat0_values, &seat0_display_server - ); - Self { seat0_username, seat0_display_server, diff --git a/src/server/connection.rs b/src/server/connection.rs index e629fa93a..7a9ab86c2 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1303,11 +1303,6 @@ impl Connection { let is_headless = linux_desktop_manager::is_headless(); let wait_ipc_timeout = 10_000; - println!( - "REMOVE ME =================================== try_start_desktop '{}'", - &desktop_err - ); - // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; From 86c063eecbf4d5d8672c4ff7f32049c0ed71887e Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 22:01:43 +0800 Subject: [PATCH 185/366] remember os account Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 4 ++++ src/server/connection.rs | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 0c61abb0d..6b24c9d28 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -507,6 +507,10 @@ _connectDialog( final osPassword = osPasswordController?.text.trim() ?? ''; final password = passwordController?.text.trim() ?? ''; if (passwordController != null && password.isEmpty) return; + if (rememberAccount) { + bind.sessionPeerOption(id: id, name: 'os-username', value: osUsername); + bind.sessionPeerOption(id: id, name: 'os-password', value: osPassword); + } gFFI.login( osUsername, osPassword, diff --git a/src/server/connection.rs b/src/server/connection.rs index 7a9ab86c2..1a8d807bc 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1143,7 +1143,6 @@ impl Connection { } fn validate_password(&mut self) -> bool { - return true; if password::temporary_enabled() { let password = password::temporary_password(); if self.validate_one_password(password.clone()) { From 8373c5994127f4aba27c3f824090a3a41247fcfa Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 22:44:05 +0800 Subject: [PATCH 186/366] update lang Signed-off-by: fufesou --- src/lang/pl.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index d89b6f763..f8c853026 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -480,8 +480,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Ten plik jest identyczny jak plik na drugim komputerze."), ("show_monitors_tip", "Pokaż monitory w zasobniku."), ("View Mode", "Tryb widoku"), - ("enter_rustdesk_passwd_tip", "Podaj hasło RustDesk."), - ("remember_rustdesk_passwd_tip", "Zapamiętaj hasło RustDesk."), ("login_linux_tip", "Zaloguj do zdalnego konta Linux"), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), ].iter().cloned().collect(); } From 41573a94b4dc90dcfef95e2d2ab900b125afc936 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 23:18:09 +0800 Subject: [PATCH 187/366] win, remove build warnings Signed-off-by: fufesou --- src/server/connection.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 1a8d807bc..125800040 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -150,7 +150,9 @@ pub struct Connection { audio_input_device_before_voice_call: Option, options_in_login: Option, pressed_modifiers: HashSet, + #[cfg(target_os = "linux")] rx_cm_stream_ready: mpsc::Receiver<()>, + #[cfg(target_os = "linux")] tx_desktop_ready: mpsc::Sender<()>, } @@ -212,8 +214,8 @@ impl Connection { let (tx_video, mut rx_video) = mpsc::unbounded_channel::<(Instant, Arc)>(); let (tx_input, _rx_input) = std_mpsc::channel(); let mut hbbs_rx = crate::hbbs_http::sync::signal_receiver(); - let (tx_cm_stream_ready, rx_cm_stream_ready) = mpsc::channel(1); - let (tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1); + let (tx_cm_stream_ready, _rx_cm_stream_ready) = mpsc::channel(1); + let (_tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1); #[cfg(not(any(target_os = "android", target_os = "ios")))] let tx_cloned = tx.clone(); @@ -266,8 +268,10 @@ impl Connection { audio_input_device_before_voice_call: None, options_in_login: None, pressed_modifiers: Default::default(), - rx_cm_stream_ready, - tx_desktop_ready, + #[cfg(target_os = "linux")] + rx_cm_stream_ready: _rx_cm_stream_ready, + #[cfg(target_os = "linux")] + tx_desktop_ready: _tx_desktop_ready, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -1300,6 +1304,7 @@ impl Connection { }; #[cfg(target_os = "linux")] let is_headless = linux_desktop_manager::is_headless(); + #[cfg(target_os = "linux")] let wait_ipc_timeout = 10_000; // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. From 5fcb30d3c736c91436da04ccb03adba9648388d7 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 23:26:03 +0800 Subject: [PATCH 188/366] restore lang Signed-off-by: fufesou --- flutter/lib/common/widgets/dialog.dart | 2 +- src/lang/ca.rs | 2 +- src/lang/cn.rs | 2 +- src/lang/cs.rs | 2 +- src/lang/da.rs | 2 +- src/lang/de.rs | 2 +- src/lang/el.rs | 2 +- src/lang/en.rs | 1 - src/lang/eo.rs | 2 +- src/lang/es.rs | 2 +- src/lang/fa.rs | 2 +- src/lang/fr.rs | 2 +- src/lang/hu.rs | 2 +- src/lang/id.rs | 2 +- src/lang/it.rs | 2 +- src/lang/ja.rs | 2 +- src/lang/ko.rs | 2 +- src/lang/kz.rs | 2 +- src/lang/nl.rs | 2 +- src/lang/pl.rs | 2 +- src/lang/pt_PT.rs | 2 +- src/lang/ptbr.rs | 2 +- src/lang/ro.rs | 2 +- src/lang/ru.rs | 2 +- src/lang/sk.rs | 2 +- src/lang/sl.rs | 2 +- src/lang/sq.rs | 2 +- src/lang/sr.rs | 2 +- src/lang/sv.rs | 2 +- src/lang/template.rs | 2 +- src/lang/th.rs | 2 +- src/lang/tr.rs | 2 +- src/lang/tw.rs | 2 +- src/lang/ua.rs | 2 +- src/lang/vn.rs | 2 +- 35 files changed, 34 insertions(+), 35 deletions(-) diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 6b24c9d28..cf1ced92f 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -600,7 +600,7 @@ _connectDialog( autoFocus: osUsernameController == null, ), rememberWidget( - translate('remember_password_tip'), + translate('Remember password'), rememberPassword, (v) { if (v != null) { diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 6f4ce9543..146160fca 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tancar"), ("Retry", "Reintentar"), ("OK", ""), - ("remember_password_tip", ""), + ("Password Required", "Es necessita la contrasenya"), ("Please enter your password", "Si us plau, introdueixi la seva contrasenya"), ("Remember password", "Recordar contrasenya"), ("Wrong Password", "Contrasenya incorrecta"), diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 5262db44c..3f562ebd4 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "关闭"), ("Retry", "再试"), ("OK", "确认"), - ("remember_password_tip", ""), + ("Password Required", "需要密码"), ("Please enter your password", "请输入密码"), ("Remember password", "记住密码"), ("Wrong Password", "密码错误"), diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 58d10b5ee..aa40f1442 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zavřít"), ("Retry", "Zkusit znovu"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Vyžadováno heslo"), ("Please enter your password", "Zadejte své heslo"), ("Remember password", "Zapamatovat heslo"), ("Wrong Password", "Nesprávné heslo"), diff --git a/src/lang/da.rs b/src/lang/da.rs index 7e6888239..2737336c0 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Luk"), ("Retry", "Prøv igen"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Adgangskode påkrævet"), ("Please enter your password", "Indtast venligst dit kodeord"), ("Remember password", "Husk kodeord"), ("Wrong Password", "Forkert kodeord"), diff --git a/src/lang/de.rs b/src/lang/de.rs index ad9196785..6583c25e5 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Schließen"), ("Retry", "Erneut versuchen"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Passwort erforderlich"), ("Please enter your password", "Bitte geben Sie Ihr Passwort ein"), ("Remember password", "Passwort merken"), ("Wrong Password", "Falsches Passwort"), diff --git a/src/lang/el.rs b/src/lang/el.rs index 50778f4c4..fa0a3f990 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Κλείσιμο"), ("Retry", "Δοκίμασε ξανά"), ("OK", "ΟΚ"), - ("remember_password_tip", ""), + ("Password Required", "Απαιτείται κωδικός πρόσβασης"), ("Please enter your password", "Παρακαλώ εισάγετε τον κωδικό πρόσβασης"), ("Remember password", "Απομνημόνευση κωδικού πρόσβασης"), ("Wrong Password", "Λάθος κωδικός πρόσβασης"), diff --git a/src/lang/en.rs b/src/lang/en.rs index 2ea65b05f..fede80eff 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -59,7 +59,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), - ("remember_password_tip", "Remember password"), ("os_account_desk_tip", "This account is used to login the remote OS and enable the desktop session in headless"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 9fcc2b451..0d8815588 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermi"), ("Retry", "Reprovi"), ("OK", "Konfermi"), - ("remember_password_tip", ""), + ("Password Required", "Pasvorto deviga"), ("Please enter your password", "Bonvolu tajpi vian pasvorton"), ("Remember password", "Memori pasvorton"), ("Wrong Password", "Erara pasvorto"), diff --git a/src/lang/es.rs b/src/lang/es.rs index c8120de49..a9d71dea5 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Cerrar"), ("Retry", "Reintentar"), ("OK", ""), - ("remember_password_tip", ""), + ("Password Required", "Se requiere contraseña"), ("Please enter your password", "Por favor, introduzca su contraseña"), ("Remember password", "Recordar contraseña"), ("Wrong Password", "Contraseña incorrecta"), diff --git a/src/lang/fa.rs b/src/lang/fa.rs index ccf2a670a..f73bf3fec 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "بستن"), ("Retry", "تلاش مجدد"), ("OK", "قبول"), - ("remember_password_tip", ""), + ("Password Required", "رمز عبور لازم است"), ("Please enter your password", "رمز عبور خود را وارد کنید"), ("Remember password", "رمز عبور را به خاطر بسپار"), ("Wrong Password", "رمز عبور اشتباه است"), diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 2ff02d8f9..3279b9175 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fermer"), ("Retry", "Réessayer"), ("OK", "Valider"), - ("remember_password_tip", ""), + ("Password Required", "Mot de passe requis"), ("Please enter your password", "Veuillez saisir votre mot de passe"), ("Remember password", "Mémoriser le mot de passe"), ("Wrong Password", "Mauvais mot de passe"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 49e767b84..cfad983a2 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Bezárás"), ("Retry", "Újra"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Jelszó megadása kötelező"), ("Please enter your password", "Kérem írja be a jelszavát"), ("Remember password", "Jelszó megjegyzése"), ("Wrong Password", "Hibás jelszó"), diff --git a/src/lang/id.rs b/src/lang/id.rs index 2c410c0ec..3c0254f8d 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Tutup"), ("Retry", "Ulangi"), ("OK", "Oke"), - ("remember_password_tip", ""), + ("Password Required", "Kata sandi dibutuhkan"), ("Please enter your password", "Silahkan masukkan kata sandi anda"), ("Remember password", "Ingat Password"), ("Wrong Password", "Kata sandi Salah"), diff --git a/src/lang/it.rs b/src/lang/it.rs index 4427743f1..a09bfa97a 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Chiudi"), ("Retry", "Riprova"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Password Richiesta"), ("Please enter your password", "Inserisci la tua password"), ("Remember password", "Ricorda password"), ("Wrong Password", "Password Errata"), diff --git a/src/lang/ja.rs b/src/lang/ja.rs index dba70c1e1..ae153feac 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "閉じる"), ("Retry", "再試行"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "パスワードが必要"), ("Please enter your password", "パスワードを入力してください"), ("Remember password", "パスワードを記憶する"), ("Wrong Password", "パスワードが間違っています"), diff --git a/src/lang/ko.rs b/src/lang/ko.rs index ab7714190..843211c49 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "닫기"), ("Retry", "재시도"), ("OK", "확인"), - ("remember_password_tip", ""), + ("Password Required", "비밀번호 입력"), ("Please enter your password", "비밀번호를 입력해주세요"), ("Remember password", "이 비밀번호 기억하기"), ("Wrong Password", "틀린 비밀번호"), diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 7fe2719cc..b8d665a60 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Жабу"), ("Retry", "Қайтадан көру"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Құпия сөз Қажет"), ("Please enter your password", "Құпия сөзіңізді еңгізуді өтінеміз"), ("Remember password", "Құпия сөзді есте сақтау"), ("Wrong Password", "Бұрыс Құпия сөз"), diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 360166e50..caaf80077 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Sluit"), ("Retry", "Probeer opnieuw"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Wachtwoord vereist"), ("Please enter your password", "Geef uw wachtwoord in"), ("Remember password", "Wachtwoord onthouden"), ("Wrong Password", "Verkeerd wachtwoord"), diff --git a/src/lang/pl.rs b/src/lang/pl.rs index f8c853026..c1f2ef3dc 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zamknij"), ("Retry", "Ponów"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Wymagane jest hasło"), ("Please enter your password", "Wpisz proszę twoje hasło"), ("Remember password", "Zapamiętaj hasło"), ("Wrong Password", "Błędne hasło"), diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index c23aa1e46..880658ff4 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "Confirmar"), - ("remember_password_tip", ""), + ("Password Required", "Palavra-chave Necessária"), ("Please enter your password", "Por favor introduza a sua palavra-chave"), ("Remember password", "Memorizar palavra-chave"), ("Wrong Password", "Palavra-chave inválida"), diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index c741944a1..fe7f4a36b 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Fechar"), ("Retry", "Tentar novamente"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Senha necessária"), ("Please enter your password", "Por favor informe sua senha"), ("Remember password", "Lembrar senha"), ("Wrong Password", "Senha incorreta"), diff --git a/src/lang/ro.rs b/src/lang/ro.rs index d77345356..046774f32 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Închide"), ("Retry", "Reîncearcă"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Parolă necesară"), ("Please enter your password", "Introdu parola"), ("Remember password", "Memorează parola"), ("Wrong Password", "Parolă incorectă"), diff --git a/src/lang/ru.rs b/src/lang/ru.rs index fbd1c4c62..ac87dcb48 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрыть"), ("Retry", "Повторить"), ("OK", "ОК"), - ("remember_password_tip", ""), + ("Password Required", "Требуется пароль"), ("Please enter your password", "Введите пароль"), ("Remember password", "Запомнить пароль"), ("Wrong Password", "Неправильный пароль"), diff --git a/src/lang/sk.rs b/src/lang/sk.rs index efa66f7ae..1a156d8a6 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvoriť"), ("Retry", "Zopakovať"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Vyžaduje sa heslo"), ("Please enter your password", "Zadajte vaše heslo"), ("Remember password", "Zapamätať heslo"), ("Wrong Password", "Chybné heslo"), diff --git a/src/lang/sl.rs b/src/lang/sl.rs index d897e4755..1f07b027c 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zapri"), ("Retry", "Ponovi"), ("OK", "V redu"), - ("remember_password_tip", ""), + ("Password Required", "Potrebno je geslo"), ("Please enter your password", "Vnesite vaše geslo"), ("Remember password", "Zapomni si geslo"), ("Wrong Password", "Napačno geslo"), diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 88d678ed3..558984a1b 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Mbyll"), ("Retry", "Riprovo"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Fjalëkalimi i detyrueshëm"), ("Please enter your password", "Ju lutem vendosni fjalëkalimin tuaj"), ("Remember password", "Mbani mend fjalëkalimin"), ("Wrong Password", "Fjalëkalim i gabuar"), diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 078d9ba29..3ec1d32d1 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Zatvori"), ("Retry", "Ponovi"), ("OK", "Ok"), - ("remember_password_tip", ""), + ("Password Required", "Potrebna lozinka"), ("Please enter your password", "Molimo unesite svoju lozinku"), ("Remember password", "Zapamti lozinku"), ("Wrong Password", "Pogrešna lozinka"), diff --git a/src/lang/sv.rs b/src/lang/sv.rs index df5caee28..93bbaf74e 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Stäng"), ("Retry", "Försök igen"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Lösenord krävs"), ("Please enter your password", "Skriv in ditt lösenord"), ("Remember password", "Kom ihåg lösenord"), ("Wrong Password", "Fel lösenord"), diff --git a/src/lang/template.rs b/src/lang/template.rs index 1b8bf69c2..db1519e9d 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", ""), ("Retry", ""), ("OK", ""), - ("remember_password_tip", ""), + ("Password Required", ""), ("Please enter your password", ""), ("Remember password", ""), ("Wrong Password", ""), diff --git a/src/lang/th.rs b/src/lang/th.rs index a152eb7b1..1b41ff694 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "ปิด"), ("Retry", "ลองใหม่อีกครั้ง"), ("OK", "ตกลง"), - ("remember_password_tip", ""), + ("Password Required", "ต้องใช้รหัสผ่าน"), ("Please enter your password", "กรุณาใส่รหัสผ่านของคุณ"), ("Remember password", "จดจำรหัสผ่าน"), ("Wrong Password", "รหัสผ่านไม่ถูกต้อง"), diff --git a/src/lang/tr.rs b/src/lang/tr.rs index c5d21460a..0aa49a3e4 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Kapat"), ("Retry", "Tekrar Dene"), ("OK", "Tamam"), - ("remember_password_tip", ""), + ("Password Required", "Şifre Gerekli"), ("Please enter your password", "Lütfen şifrenizi giriniz"), ("Remember password", "Şifreyi hatırla"), ("Wrong Password", "Hatalı şifre"), diff --git a/src/lang/tw.rs b/src/lang/tw.rs index d2156f4b7..0077fdaad 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "關閉"), ("Retry", "重試"), ("OK", "確定"), - ("remember_password_tip", ""), + ("Password Required", "需要密碼"), ("Please enter your password", "請輸入您的密碼"), ("Remember password", "記住密碼"), ("Wrong Password", "密碼錯誤"), diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 1a91bb879..811bcd913 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Закрити"), ("Retry", "Спробувати знову"), ("OK", "ОК"), - ("remember_password_tip", ""), + ("Password Required", "Потрібен пароль"), ("Please enter your password", "Будь ласка, введіть ваш пароль"), ("Remember password", "Запам'ятати пароль"), ("Wrong Password", "Невірний пароль"), diff --git a/src/lang/vn.rs b/src/lang/vn.rs index eeafcfa54..6ad03f0f8 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -68,7 +68,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Đóng"), ("Retry", "Thử lại"), ("OK", "OK"), - ("remember_password_tip", ""), + ("Password Required", "Yêu cầu mật khẩu"), ("Please enter your password", "Mời nhập mật khẩu"), ("Remember password", "Nhớ mật khẩu"), ("Wrong Password", "Sai mật khẩu"), From b18bfa749b9747dd7067d28e3c8284b408638031 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 30 Mar 2023 23:46:06 +0800 Subject: [PATCH 189/366] remove wait_prelogin Signed-off-by: fufesou --- src/platform/linux.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index 27c0fde48..b3fde7808 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -467,7 +467,7 @@ pub fn get_env_var(k: &str) -> String { } } -// Headless is enabled, no need to wait prelogin. +// Headless is enabled, always return true. pub fn is_prelogin() -> bool { let n = get_active_userid().len(); n < 4 && n > 1 From 8ea63af4b510ab9503b2d2aa6761c27fc39bc7ed Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 09:49:00 +0800 Subject: [PATCH 190/366] fix build android Signed-off-by: fufesou --- src/flutter.rs | 1 + src/server/connection.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/flutter.rs b/src/flutter.rs index 3d49dcf03..6c9ff7f37 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -827,6 +827,7 @@ pub mod connection_manager { start_listen_ipc(true); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn start_listen_ipc(new_thread: bool) { use crate::ui_cm_interface::{start_ipc, ConnectionManager}; diff --git a/src/server/connection.rs b/src/server/connection.rs index 125800040..8a8714c6f 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -214,7 +214,9 @@ impl Connection { let (tx_video, mut rx_video) = mpsc::unbounded_channel::<(Instant, Arc)>(); let (tx_input, _rx_input) = std_mpsc::channel(); let mut hbbs_rx = crate::hbbs_http::sync::signal_receiver(); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let (tx_cm_stream_ready, _rx_cm_stream_ready) = mpsc::channel(1); + #[cfg(not(any(target_os = "android", target_os = "ios")))] let (_tx_desktop_ready, rx_desktop_ready) = mpsc::channel(1); #[cfg(not(any(target_os = "android", target_os = "ios")))] From ee3ac310833ddc447e3e0e293d44286088550bba Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 10:25:58 +0800 Subject: [PATCH 191/366] add deps libpam0g-dev Signed-off-by: fufesou --- .github/workflows/flutter-nightly.yml | 2 +- libs/hbb_common/src/platform/linux.rs | 30 +++++---------------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index ab625e431..5aa82b847 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -11,4 +11,4 @@ jobs: uses: ./.github/workflows/flutter-build.yml with: upload-artifact: true - \ No newline at end of file + diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs index ce0782a61..89c96799d 100644 --- a/libs/hbb_common/src/platform/linux.rs +++ b/libs/hbb_common/src/platform/linux.rs @@ -35,7 +35,6 @@ pub fn is_gdm_user(username: &str) -> bool { // || username == "lightgdm" } -<<<<<<< HEAD #[inline] pub fn is_desktop_wayland() -> bool { get_display_server() == DISPLAY_SERVER_WAYLAND @@ -44,26 +43,13 @@ pub fn is_desktop_wayland() -> bool { #[inline] pub fn is_x11_or_headless() -> bool { !is_desktop_wayland() -======= -pub fn is_x11_or_headless() -> bool { - let (username, display_server) = get_user_and_display_server(); - display_server == DISPLAY_SERVER_WAYLAND && is_gdm_user(&username) - || display_server != DISPLAY_SERVER_WAYLAND -} - -pub fn is_desktop_wayland() -> bool { - let (username, display_server) = get_user_and_display_server(); - display_server == DISPLAY_SERVER_WAYLAND && !is_gdm_user(&username) ->>>>>>> temp commit } // -1 const INVALID_SESSION: &str = "4294967295"; -pub fn get_user_and_display_server() -> (String, String) { - let seat0_values = get_values_of_seat0(&[0, 2]); - let mut session = seat0_values[0].clone(); - let username = seat0_values[1].clone(); +pub fn get_display_server() -> String { + let mut session = get_values_of_seat0(&[0])[0].clone(); if session.is_empty() { // loginctl has not given the expected output. try something else. if let Ok(sid) = std::env::var("XDG_SESSION_ID") { @@ -77,17 +63,11 @@ pub fn get_user_and_display_server() -> (String, String) { } } } -<<<<<<< HEAD if session.is_empty() { -======= - - let display_server = if session.is_empty() { ->>>>>>> temp commit "".to_owned() } else { get_display_server_of_session(&session) - }; - (username, display_server) + } } pub fn get_display_server_of_session(session: &str) -> String { @@ -211,7 +191,7 @@ pub fn run_cmds(cmds: &str) -> ResultType { } #[cfg(not(feature = "flatpak"))] -pub(super) fn run_loginctl(args: Option>) -> std::io::Result { +fn run_loginctl(args: Option>) -> std::io::Result { let mut cmd = std::process::Command::new("loginctl"); if let Some(a) = args { return cmd.args(a).output(); @@ -220,7 +200,7 @@ pub(super) fn run_loginctl(args: Option>) -> std::io::Result>) -> std::io::Result { +fn run_loginctl(args: Option>) -> std::io::Result { let mut l_args = String::from("loginctl"); if let Some(a) = args { l_args = format!("{} {}", l_args, a.join(" ")); From 571c1df5c446885dfc388527df30c643478d0257 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 31 Mar 2023 17:49:35 +0800 Subject: [PATCH 192/366] fix build Signed-off-by: fufesou --- src/platform/linux.rs | 14 ++------------ src/platform/linux_desktop_manager.rs | 2 +- src/server/connection.rs | 4 ++-- src/ui_interface.rs | 3 +-- 4 files changed, 6 insertions(+), 17 deletions(-) diff --git a/src/platform/linux.rs b/src/platform/linux.rs index b3fde7808..33ebcc82c 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -226,11 +226,11 @@ fn stop_rustdesk_servers() { } #[inline] -fn stop_xorg_subprocess() { +fn stop_subprocess() { let _ = run_cmds(&format!( r##"ps -ef | grep '/etc/rustdesk/xorg.conf' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); - let _ = run_cmds(format!( + let _ = run_cmds(&format!( r##"ps -ef | grep -E 'rustdesk +--cm-no-ui' | grep -v grep | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##, )); } @@ -797,16 +797,6 @@ mod desktop { super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND } - #[inline] - pub fn is_headless(&self) -> bool { - self.sid.is_empty() - } - - #[inline] - pub fn is_login_wayland(&self) -> bool { - super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND - } - #[inline] pub fn is_headless(&self) -> bool { self.sid.is_empty() || self.is_rustdesk_subprocess diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index c70724a5e..3a72f8976 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -373,7 +373,7 @@ impl DesktopManager { fn wait_x_server_running(pid: u32, display_num: u32, max_wait_secs: u64) -> ResultType<()> { let wait_begin = Instant::now(); loop { - if run_cmds(format!("ls /proc/{}", pid))?.is_empty() { + if run_cmds(&format!("ls /proc/{}", pid))?.is_empty() { bail!("X server exit"); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 8a8714c6f..65a3fd025 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -915,7 +915,7 @@ impl Connection { } #[cfg(target_os = "linux")] if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() { - let (_, dtype) = crate::platform::linux::get_user_and_display_server(); + let dtype = crate::platform::linux::get_display_server(); if dtype != crate::platform::linux::DISPLAY_SERVER_X11 && dtype != crate::platform::linux::DISPLAY_SERVER_WAYLAND { @@ -2222,7 +2222,7 @@ async fn start_ipc( username = linux_desktop_manager::get_username(); } let uid = { - let output = run_cmds(format!("id -u {}", &username))?; + let output = run_cmds(&format!("id -u {}", &username))?; let output = output.trim(); if output.is_empty() || !output.parse::().is_ok() { bail!("Invalid username {}", &username); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 1181598a6..fa73490ad 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -511,9 +511,8 @@ pub fn get_error() -> String { #[cfg(not(any(feature = "cli")))] #[cfg(target_os = "linux")] { - let (username, dtype) = crate::platform::linux::get_user_and_display_server(); + let dtype = crate::platform::linux::get_display_server(); if crate::platform::linux::DISPLAY_SERVER_WAYLAND == dtype - && !crate::platform::linux::is_gdm_user(&username) { return crate::server::wayland::common_get_error(); } From 73358502e93062d3012fbcc223e322d911e1f8c4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 00:28:56 +0800 Subject: [PATCH 193/366] linux headless feature, tmp commit Signed-off-by: fufesou --- Cargo.toml | 5 +- libs/hbb_common/src/config.rs | 13 ++- src/client.rs | 119 +++++++++++++++++--------- src/client/io_loop.rs | 1 + src/lang/ca.rs | 16 +++- src/lang/cn.rs | 16 +++- src/lang/cs.rs | 16 +++- src/lang/da.rs | 16 +++- src/lang/de.rs | 16 +++- src/lang/el.rs | 16 +++- src/lang/en.rs | 15 ++++ src/lang/eo.rs | 16 +++- src/lang/es.rs | 16 +++- src/lang/fa.rs | 16 +++- src/lang/fr.rs | 1 - src/lang/hu.rs | 16 +++- src/lang/id.rs | 16 +++- src/lang/it.rs | 16 +++- src/lang/ja.rs | 16 +++- src/lang/ko.rs | 16 +++- src/lang/kz.rs | 16 +++- src/lang/nl.rs | 16 +++- src/lang/pl.rs | 16 +++- src/lang/pt_PT.rs | 16 +++- src/lang/ptbr.rs | 16 +++- src/lang/ro.rs | 16 +++- src/lang/ru.rs | 16 +++- src/lang/sk.rs | 16 +++- src/lang/sl.rs | 16 +++- src/lang/sq.rs | 16 +++- src/lang/sr.rs | 16 +++- src/lang/sv.rs | 16 +++- src/lang/template.rs | 16 +++- src/lang/th.rs | 16 +++- src/lang/tr.rs | 16 +++- src/lang/tw.rs | 16 +++- src/lang/ua.rs | 16 +++- src/lang/vn.rs | 16 +++- src/platform/linux_desktop_manager.rs | 81 ++++++++++++++++-- src/platform/mod.rs | 2 +- src/rendezvous_mediator.rs | 4 +- src/server/connection.rs | 105 +++++++++++------------ 42 files changed, 713 insertions(+), 145 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 10f90f387..48bd16045 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ flutter = ["flutter_rust_bridge"] default = ["use_dasp"] hwcodec = ["scrap/hwcodec"] mediacodec = ["scrap/mediacodec"] +linux_headless = ["pam", "users"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -122,8 +123,8 @@ mouce = { git="https://github.com/fufesou/mouce.git" } evdev = { git="https://github.com/fufesou/evdev" } dbus = "0.9" dbus-crossroads = "0.5" -pam = { git="https://github.com/fufesou/pam" } -users = "0.11.0" +pam = { git="https://github.com/fufesou/pam", optional = true } +users = { version = "0.11.0", optional = true } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 3841bf976..e6c788f9a 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -64,11 +64,15 @@ lazy_static::lazy_static! { pub static ref APP_HOME_DIR: Arc> = Default::default(); } -// #[cfg(any(target_os = "android", target_os = "ios"))] +pub const LINK_DOCS_HOME: &str = "https://rustdesk.com/docs/en/"; +pub const LINK_DOCS_X11_REQUIRED: &str = "https://rustdesk.com/docs/en/manual/linux/#x11-required"; +pub const LINK_HEADLESS_LINUX_SUPPORT: &str = + "https://github.com/rustdesk/rustdesk/wiki/Headless-Linux-Support"; lazy_static::lazy_static! { pub static ref HELPER_URL: HashMap<&'static str, &'static str> = HashMap::from([ - ("rustdesk docs home", "https://rustdesk.com/docs/en/"), - ("rustdesk docs x11-required", "https://rustdesk.com/docs/en/manual/linux/#x11-required"), + ("rustdesk docs home", LINK_DOCS_HOME), + ("rustdesk docs x11-required", LINK_DOCS_X11_REQUIRED), + ("rustdesk x11 headless", LINK_HEADLESS_LINUX_SUPPORT), ]); } @@ -917,7 +921,8 @@ impl PeerConfig { store = store || store2; for opt in ["rdp_password", "os-username", "os-password"] { if let Some(v) = config.options.get_mut(opt) { - let (encrypted, _, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + let (encrypted, _, store2) = + decrypt_str_or_original(v, PASSWORD_ENC_VERSION); *v = encrypted; store = store || store2; } diff --git a/src/client.rs b/src/client.rs index 741c45416..3f587dfe0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1912,6 +1912,71 @@ fn _input_os_password(p: String, activate: bool, interface: impl Interface) { interface.send(Data::Message(msg_out)); } +#[derive(Copy, Clone)] +struct LoginErrorMsgBox { + msgtype: &'static str, + title: &'static str, + text: &'static str, + link: &'static str, + try_again: bool, +} + +lazy_static::lazy_static! { + static ref LOGIN_ERROR_MAP: Arc> = { + use hbb_common::config::LINK_HEADLESS_LINUX_SUPPORT; + let map = HashMap::from([(crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LoginErrorMsgBox{ + msgtype: "session-login", + title: "session_not_ready_title_tip", + text: "session_not_ready_text_tip", + link: "", + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED, LoginErrorMsgBox{ + msgtype: "session-re-login", + title: "xsession_failed_title_tip", + text: "xsession_failed_text_tip", + link: "", + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LoginErrorMsgBox{ + msgtype: "info", + title: "another_user_login_title_tip", + text: "another_user_login_text_tip", + link: "", + try_again: false, + }), (crate::server::LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LoginErrorMsgBox{ + msgtype: "session-re-login", + title: "xorg_not_found_title_tip", + text: "xorg_not_found_text_tip", + link: LINK_HEADLESS_LINUX_SUPPORT, + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_NO_DESKTOP, LoginErrorMsgBox{ + msgtype: "session-re-login", + title: "no_desktop_title_tip", + text: "no_desktop_text_tip", + link: LINK_HEADLESS_LINUX_SUPPORT, + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, LoginErrorMsgBox{ + msgtype: "session-login-password", + title: "session_unready_no_password_title_tip", + text: "session_unready_no_password_text_tip", + link: "", + try_again: true, + }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, LoginErrorMsgBox{ + msgtype: "session-login-re-password", + title: "session_unready_wrong_password_title_tip", + text: "session_unready_wrong_password_text_tip", + link: "", + try_again: true, + }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ + msgtype: "wait-remote-accept-nook", + title: "Prompt", + text: "no_password_access_text_tip", + link: "", + try_again: true, + })]); + Arc::new(map) + }; +} + /// Handle login error. /// Return true if the password is wrong, return false if there's an actual error. pub fn handle_login_error( @@ -1927,47 +1992,19 @@ pub fn handle_login_error( lc.write().unwrap().password = Default::default(); interface.msgbox("re-input-password", err, "Do you want to enter again?", ""); true - } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY { - interface.msgbox( - "session-login", - "session is unready", - "Input linux user/password", - "", - ); - true - } else if err == crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED { - interface.msgbox( - "session-re-login", - "xsession username/password is wrong", - "Do you want to enter again?", - "", - ); - true - } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY { - interface.msgbox( - "session-login-password", - "session is unready", - "Input connection password and linux user/password", - "", - ); - true - } else if err == crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG { - interface.msgbox( - "session-login-re-password", - "session is unready and password is wrong", - "Do you want to enter again?", - "", - ); - true - } else if err == "No Password Access" { - lc.write().unwrap().password = Default::default(); - interface.msgbox( - "wait-remote-accept-nook", - "Prompt", - "Please wait for the remote side to accept your session request...", - "", - ); - true + } else if LOGIN_ERROR_MAP.contains_key(err) { + if let Some(msgbox_info) = LOGIN_ERROR_MAP.get(err) { + interface.msgbox( + msgbox_info.msgtype, + msgbox_info.title, + msgbox_info.text, + msgbox_info.link, + ); + msgbox_info.try_again + } else { + // unreachable! + false + } } else { if err.contains(SCRAP_X11_REQUIRED) { interface.msgbox("error", "Login Error", err, SCRAP_X11_REF_URL); diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index c1c38af40..ab8b76a7f 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1256,6 +1256,7 @@ impl Remote { }, Some(message::Union::MessageBox(msgbox)) => { let mut link = msgbox.link; + // Links from remote side must be verified. if !link.starts_with("rustdesk://") { if let Some(v) = hbb_common::config::HELPER_URL.get(&link as &str) { link = v.to_string(); diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 146160fca..3dc8cdb12 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 3f562ebd4..b8df31c77 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "只允许密码访问"), ("Accept sessions via click", "只允许点击访问"), ("Accept sessions via both", "允许密码或点击访问"), - ("Please wait for the remote side to accept your session request...", "请等待对方接受你的连接..."), ("One-time Password", "一次性密码"), ("Use one-time password", "使用一次性密码"), ("One-time password length", "一次性密码长度"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", "记住此账户"), ("os_account_desk_tip", "在无显示器的环境下,此账户用于登录被控系统,并启用桌面"), ("OS Account", "系统账户"), + ("session_not_ready_title_tip", "桌面 session 未准备好"), + ("session_not_ready_text_tip", "请输入用户名密码"), + ("xsession_failed_title_tip", "用户名密码错误"), + ("xsession_failed_text_tip", "是否重试"), + ("another_user_login_title_tip", "其他用户已登录"), + ("another_user_login_text_tip", "断开"), + ("xorg_not_found_title_tip", "Xorg 未安装"), + ("xorg_not_found_text_tip", "请安装 Xorg"), + ("no_desktop_title_tip", "desktop 未安装"), + ("no_desktop_text_tip", "请安装 desktop"), + ("session_unready_no_password_title_tip", "桌面 session 未准备好"), + ("session_unready_no_password_text_tip", "请输入用户名密码和连接密码"), + ("session_unready_wrong_password_title_tip", "密码错误"), + ("session_unready_wrong_password_text_tip", "是否重试"), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index aa40f1442..020bea621 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 2737336c0..754b9671c 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptér sessioner via adgangskode"), ("Accept sessions via click", "Acceptér sessioner via klik"), ("Accept sessions via both", "Acceptér sessioner via begge"), - ("Please wait for the remote side to accept your session request...", "Vent venligst på at fjernklienten accepterer din sessionsforespørgsel..."), ("One-time Password", "Engangskode"), ("Use one-time password", "Brug engangskode"), ("One-time password length", "Engangskode længde"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 6583c25e5..3d72a0242 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sitzung mit Passwort bestätigen"), ("Accept sessions via click", "Sitzung mit einem Klick bestätigen"), ("Accept sessions via both", "Sitzung mit Klick und Passwort bestätigen"), - ("Please wait for the remote side to accept your session request...", "Bitte warten Sie, bis die Gegenseite Ihre Sitzungsanfrage akzeptiert hat …"), ("One-time Password", "Einmalpasswort"), ("Use one-time password", "Einmalpasswort verwenden"), ("One-time password length", "Länge des Einmalpassworts"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index fa0a3f990..b10d9f971 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Αποδοχή συνεδριών με κωδικό πρόσβασης"), ("Accept sessions via click", "Αποδοχή συνεδριών με κλικ"), ("Accept sessions via both", "Αποδοχή συνεδριών και με τα δύο"), - ("Please wait for the remote side to accept your session request...", "Παρακαλώ περιμένετε μέχρι η απομακρυσμένη πλευρά να αποδεχτεί το αίτημα συνεδρίας σας..."), ("One-time Password", "Κωδικός μίας χρήσης"), ("Use one-time password", "Χρήση κωδικού πρόσβασης μίας χρήσης"), ("One-time password length", "Μήκος κωδικού πρόσβασης μίας χρήσης"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index fede80eff..6035db7ba 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -60,5 +60,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), ("remember_account_tip", "Remember this account"), ("os_account_desk_tip", "This account is used to login the remote OS and enable the desktop session in headless"), + ("session_not_ready_title_tip", "Session is unready"), + ("session_not_ready_text_tip", "Input linux user/password"), + ("xsession_failed_title_tip", "Xsession username/password is wrong"), + ("xsession_failed_text_tip", "Do you want to enter again"), + ("another_user_login_title_tip", "Another user already login"), + ("another_user_login_text_tip", "Disconnect"), + ("xorg_not_found_title_tip", "Xorg not found"), + ("xorg_not_found_text_tip", "Please install Xorg"), + ("no_desktop_title_tip", "No desktop is avaliable"), + ("no_desktop_text_tip", "Please install GNOME desktop"), + ("session_unready_no_password_title_tip", "Session is unready"), + ("session_unready_no_password_text_tip", "Input connection password and linux user/password"), + ("session_unready_wrong_password_title_tip", "Password is wrong"), + ("session_unready_wrong_password_text_tip", "Do you want to enter again"), + ("no_password_access_text_tip", "Please wait for the remote side to accept your session request..."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 0d8815588..3f2a0d573 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index a9d71dea5..9f06fff59 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Aceptar sesiones a través de contraseña"), ("Accept sessions via click", "Aceptar sesiones a través de clic"), ("Accept sessions via both", "Aceptar sesiones a través de ambos"), - ("Please wait for the remote side to accept your session request...", "Por favor, espere a que el lado remoto acepte su solicitud de sesión"), ("One-time Password", "Constaseña de un solo uso"), ("Use one-time password", "Usar contraseña de un solo uso"), ("One-time password length", "Longitud de la contraseña de un solo uso"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index f73bf3fec..1fad5bba1 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "قبول درخواست با رمز عبور"), ("Accept sessions via click", "قبول درخواست با کلیک موس"), ("Accept sessions via both", "قبول درخواست با هر دو"), - ("Please wait for the remote side to accept your session request...", "...لطفا صبر کنید تا میزبان درخواست شما را قبول کند"), ("One-time Password", "رمز عبور یکبار مصرف"), ("Use one-time password", "استفاده از رمز عبور یکبار مصرف"), ("One-time password length", "طول رمز عبور یکبار مصرف"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 3279b9175..58a65b3f4 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Accepter les sessions via mot de passe"), ("Accept sessions via click", "Accepter les sessions via clique de confirmation"), ("Accept sessions via both", "Accepter les sessions via mot de passe ou clique de confirmation"), - ("Please wait for the remote side to accept your session request...", "Veuillez attendre que votre demande de session distante soit accepter ..."), ("One-time Password", "Mot de passe unique"), ("Use one-time password", "Utiliser un mot de passe unique"), ("One-time password length", "Longueur du mot de passe unique"), diff --git a/src/lang/hu.rs b/src/lang/hu.rs index cfad983a2..5075802f1 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 3c0254f8d..4f87a926c 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Izinkan sesi dengan kata sandi"), ("Accept sessions via click", "Izinkan sesi dengan klik"), ("Accept sessions via both", "Izinkan sesi dengan keduanya"), - ("Please wait for the remote side to accept your session request...", "Harap tunggu sisi jarak jauh untuk menerima permintaan sesi Anda..."), ("One-time Password", "Kata sandi satu kali"), ("Use one-time password", "Gunakan kata sandi satu kali"), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index a09bfa97a..14a50ede9 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Accetta sessioni via password"), ("Accept sessions via click", "Accetta sessioni via click"), ("Accept sessions via both", "Accetta sessioni con entrambi"), - ("Please wait for the remote side to accept your session request...", "Attendere che il lato remoto accetti la richiesta di sessione..."), ("One-time Password", "Password monouso"), ("Use one-time password", "Usa password monouso"), ("One-time password length", "Lunghezza password monouso"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index ae153feac..e91137d8c 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 843211c49..c7c654632 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index b8d665a60..5422083bb 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index caaf80077..b043b8f7f 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sessies accepteren via wachtwoord"), ("Accept sessions via click", "Sessies accepteren via klik"), ("Accept sessions via both", "Accepteer sessies via beide"), - ("Please wait for the remote side to accept your session request...", "Wacht tot de andere kant uw sessieverzoek accepteert..."), ("One-time Password", "Eenmalig Wachtwoord"), ("Use one-time password", "Gebruik een eenmalig Wachtwoord"), ("One-time password length", "Eenmalig Wachtwoord lengre"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index c1f2ef3dc..938a4e881 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Uwierzytelnij sesję używając hasła"), ("Accept sessions via click", "Uwierzytelnij sesję poprzez kliknięcie"), ("Accept sessions via both", "Uwierzytelnij sesję za pomocą obu sposobów"), - ("Please wait for the remote side to accept your session request...", "Oczekiwanie, na zatwierdzenie sesji przez host zdalny..."), ("One-time Password", "Hasło jednorazowe"), ("Use one-time password", "Użyj hasła jednorazowego"), ("One-time password length", "Długość hasła jednorazowego"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 880658ff4..ae4dbccba 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index fe7f4a36b..c9c0b84f7 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Aceitar sessões via senha"), ("Accept sessions via click", "Aceitar sessões via clique"), ("Accept sessions via both", "Aceitar sessões de ambos os modos"), - ("Please wait for the remote side to accept your session request...", "Por favor aguarde enquanto o cliente remoto aceita seu pedido de sessão..."), ("One-time Password", "Senha de uso único"), ("Use one-time password", "Usar senha de uso único"), ("One-time password length", "Comprimento da senha de uso único"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 046774f32..d6632d18a 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptă sesiunile folosind parola"), ("Accept sessions via click", "Acceptă sesiunile cu un clic de confirmare"), ("Accept sessions via both", "Acceptă sesiunile folosind ambele moduri"), - ("Please wait for the remote side to accept your session request...", "Așteaptă ca solicitarea ta de conectare la distanță să fie acceptată..."), ("One-time Password", "Parolă unică"), ("Use one-time password", "Folosește parola unică"), ("One-time password length", "Lungimea parolei unice"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index ac87dcb48..a99523fca 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Принимать сеансы по паролю"), ("Accept sessions via click", "Принимать сеансы по нажатию"), ("Accept sessions via both", "Принимать сеансы по паролю+нажатию"), - ("Please wait for the remote side to accept your session request...", "Подождите, пока удалённая сторона примет ваш запрос на сеанс..."), ("One-time Password", "Одноразовый пароль"), ("Use one-time password", "Использовать одноразовый пароль"), ("One-time password length", "Длина одноразового пароля"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 1a156d8a6..7a8b9a778 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 1f07b027c..581507457 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sprejmi seje z geslom"), ("Accept sessions via click", "Sprejmi seje s potrditvijo"), ("Accept sessions via both", "Sprejmi seje z geslom ali potrditvijo"), - ("Please wait for the remote side to accept your session request...", "Počakajte, da oddaljeni računalnik sprejme povezavo..."), ("One-time Password", "Enkratno geslo"), ("Use one-time password", "Uporabi enkratno geslo"), ("One-time password length", "Dolžina enkratnega gesla"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 558984a1b..eb1b50762 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Prano sesionin nëpërmjet fjalëkalimit"), ("Accept sessions via click", "Prano sesionet nëpërmjet klikimit"), ("Accept sessions via both", "Prano sesionet nëpërmjet të dyjave"), - ("Please wait for the remote side to accept your session request...", "Ju lutem prisni që ana në distancë të pranoj kërkësen tuaj"), ("One-time Password", "Fjalëkalim Një-herë"), ("Use one-time password", "Përdorni fjalëkalim Një-herë"), ("One-time password length", "Gjatësia e fjalëkalimit një herë"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 3ec1d32d1..260eadc24 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Prihvati sesije preko lozinke"), ("Accept sessions via click", "Prihvati sesije preko klika"), ("Accept sessions via both", "Prihvati sesije preko oboje"), - ("Please wait for the remote side to accept your session request...", "Molimo sačekajte da udaljena strana prihvati vaš zahtev za sesijom..."), ("One-time Password", "Jednokratna lozinka"), ("Use one-time password", "Koristi jednokratnu lozinku"), ("One-time password length", "Dužina jednokratne lozinke"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 93bbaf74e..9db56dcc9 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptera sessioner via lösenord"), ("Accept sessions via click", "Acceptera sessioner via klick"), ("Accept sessions via both", "Acceptera sessioner via båda"), - ("Please wait for the remote side to accept your session request...", "Var god vänta på att klienten accepterar din förfrågan..."), ("One-time Password", "En-gångs lösenord"), ("Use one-time password", "Använd en-gångs lösenord"), ("One-time password length", "Längd på en-gångs lösenord"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index db1519e9d..ac4413201 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 1b41ff694..11260f440 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"), ("Accept sessions via click", "ยอมรับการเชื่อมต่อด้วยการคลิก"), ("Accept sessions via both", "ยอมรับการเชื่อมต่อด้วยทั้งสองวิธิ"), - ("Please wait for the remote side to accept your session request...", "กรุณารอให้อีกฝั่งยอมรับการเชื่อมต่อของคุณ..."), ("One-time Password", "รหัสผ่านครั้งเดียว"), ("Use one-time password", "ใช้รหัสผ่านครั้งเดียว"), ("One-time password length", "ความยาวรหัสผ่านครั้งเดียว"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 0aa49a3e4..b76484b33 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Oturumları parola ile kabul etme"), ("Accept sessions via click", "Tıklama yoluyla oturumları kabul edin"), ("Accept sessions via both", "Her ikisi aracılığıyla oturumları kabul edin"), - ("Please wait for the remote side to accept your session request...", "Lütfen uzak tarafın oturum isteğinizi kabul etmesini bekleyin..."), ("One-time Password", "Tek Kullanımlık Şifre"), ("Use one-time password", "Tek seferlik parola kullanın"), ("One-time password length", "Tek seferlik şifre uzunluğu"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 0077fdaad..f77014e8f 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "只允許透過輸入密碼進行連線"), ("Accept sessions via click", "只允許透過點擊接受進行連線"), ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), - ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), ("One-time Password", "一次性密碼"), ("Use one-time password", "使用一次性密碼"), ("One-time password length", "一次性密碼長度"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 811bcd913..873dad281 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Підтверджувати сеанси паролем"), ("Accept sessions via click", "Підтверджувати сеанси натисканням"), ("Accept sessions via both", "Підтверджувати сеанси обома способами"), - ("Please wait for the remote side to accept your session request...", "Буль ласка, зачекайте, поки віддалена сторона підтвердить запит на сеанс..."), ("One-time Password", "Одноразовий пароль"), ("Use one-time password", "Використати одноразовий пароль"), ("One-time password length", "Довжина одноразового пароля"), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 6ad03f0f8..7756b8894 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -402,7 +402,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -485,5 +484,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 3a72f8976..e9dc00ef6 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -1,4 +1,9 @@ use super::{linux::*, ResultType}; +use crate::server::{ + LOGIN_MSG_DESKTOP_NO_DESKTOP, LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, + LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, + LOGIN_MSG_DESKTOP_XSESSION_FAILED, +}; use hbb_common::{allow_err, bail, log, rand::prelude::*, tokio::time}; use pam; use std::{ @@ -59,7 +64,65 @@ pub fn stop_xdesktop() { *DESKTOP_MANAGER.lock().unwrap() = None; } -pub fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { +pub fn try_start_desktop(_username: &str, _passsword: &str) -> String { + if _username.is_empty() { + let username = get_username(); + if username.is_empty() { + LOGIN_MSG_DESKTOP_SESSION_NOT_READY + } else { + "" + } + .to_owned() + } else { + let username = get_username(); + if username == _username { + // No need to verify password here. + return "".to_owned(); + } + + match run_cmds(&format!("which {}", DesktopManager::get_xorg())) { + Ok(output) => { + if output.trim().is_empty() { + return LOGIN_MSG_DESKTOP_XORG_NOT_FOUND.to_owned(); + } + } + _ => { + return LOGIN_MSG_DESKTOP_XORG_NOT_FOUND.to_owned(); + } + } + + match run_cmds("ls /usr/share/xsessions/") { + Ok(output) => { + if output.trim().is_empty() { + return LOGIN_MSG_DESKTOP_NO_DESKTOP.to_owned(); + } + } + _ => { + return LOGIN_MSG_DESKTOP_NO_DESKTOP.to_owned(); + } + } + + match try_start_x_session(_username, _passsword) { + Ok((username, x11_ready)) => { + if x11_ready { + if _username != username { + LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER.to_owned() + } else { + "".to_owned() + } + } else { + LOGIN_MSG_DESKTOP_SESSION_NOT_READY.to_owned() + } + } + Err(e) => { + log::error!("Failed to start xsession {}", e); + LOGIN_MSG_DESKTOP_XSESSION_FAILED.to_owned() + } + } + } +} + +fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bool)> { let mut desktop_manager = DESKTOP_MANAGER.lock().unwrap(); if let Some(desktop_manager) = &mut (*desktop_manager) { if let Some(seat0_username) = desktop_manager.get_supported_display_seat0_username() { @@ -547,36 +610,36 @@ impl DesktopManager { } } - fn get_xorg() -> ResultType<&'static str> { + fn get_xorg() -> &'static str { // Fedora 26 or later let xorg = "/usr/libexec/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // Debian 9 or later let xorg = "/usr/lib/xorg/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // Ubuntu 16.04 or later let xorg = "/usr/lib/xorg/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // Arch Linux let xorg = "/usr/lib/xorg-server/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // Arch Linux let xorg = "/usr/lib/Xorg"; if Path::new(xorg).is_file() { - return Ok(xorg); + return xorg; } // CentOS 7 /usr/bin/Xorg or param=Xorg log::warn!("Failed to find xorg, use default Xorg.\n Please add \"allowed_users=anybody\" to \"/etc/X11/Xwrapper.config\"."); - Ok("Xorg") + "Xorg" } fn start_x_server( @@ -586,7 +649,7 @@ impl DesktopManager { gid: u32, envs: &HashMap<&str, String>, ) -> ResultType { - let xorg = Self::get_xorg()?; + let xorg = Self::get_xorg(); log::info!("Use xorg: {}", &xorg); match Command::new(xorg) .envs(envs) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 7c9b2f0b0..fc27be098 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -17,7 +17,7 @@ pub mod delegate; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", feature = "linux_headless"))] pub mod linux_desktop_manager; #[cfg(not(any(target_os = "android", target_os = "ios")))] diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 3fef9747b..cc93dbb45 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -72,7 +72,7 @@ impl RendezvousMediator { allow_err!(super::lan::start_listening()); }); } - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] crate::platform::linux_desktop_manager::start_xdesktop(); SHOULD_EXIT.store(false, Ordering::SeqCst); while !SHOULD_EXIT.load(Ordering::SeqCst) { @@ -99,7 +99,7 @@ impl RendezvousMediator { } sleep(1.).await; } - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] crate::platform::linux_desktop_manager::stop_xdesktop(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 65a3fd025..b442ff899 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -3,7 +3,7 @@ use super::{input_service::*, *}; use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::update_clipboard; -#[cfg(target_os = "linux")] +#[cfg(all(target_os = "linux", feature = "linux_headless"))] use crate::platform::linux_desktop_manager; #[cfg(windows)] use crate::portable_service::client as portable_client; @@ -18,7 +18,7 @@ use crate::{ use crate::{common::DEVICE_NAME, flutter::connection_manager::start_channel}; use crate::{ipc, VERSION}; use cidr_utils::cidr::IpCidr; -#[cfg(all(target_os = "linux", feature = "flutter"))] +#[cfg(all(target_os = "linux", feature = "linux_headless"))] use hbb_common::platform::linux::run_cmds; use hbb_common::{ config::Config, @@ -65,12 +65,16 @@ pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session unready"; pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; pub const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER: &str = "Desktop session another user login"; +pub const LOGIN_MSG_DESKTOP_XORG_NOT_FOUND: &str = "Desktop xorg not found"; +// ls /usr/share/xsessions/ +pub const LOGIN_MSG_DESKTOP_NO_DESKTOP: &str = "Desktop none"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY: &str = "Desktop session unready, password empty"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG: &str = "Desktop session unready, password wrong"; pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; +pub const LOGIN_MSG_NO_PASSWORD_ACCESS: &str = "No Password Access"; pub const LOGIN_MSG_OFFLINE: &str = "Offline"; #[derive(Clone, Default)] @@ -150,9 +154,9 @@ pub struct Connection { audio_input_device_before_voice_call: Option, options_in_login: Option, pressed_modifiers: HashSet, - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] rx_cm_stream_ready: mpsc::Receiver<()>, - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] tx_desktop_ready: mpsc::Sender<()>, } @@ -270,9 +274,9 @@ impl Connection { audio_input_device_before_voice_call: None, options_in_login: None, pressed_modifiers: Default::default(), - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] rx_cm_stream_ready: _rx_cm_stream_ready, - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] tx_desktop_ready: _tx_desktop_ready, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -886,6 +890,7 @@ impl Connection { if crate::platform::current_is_wayland() { platform_additions.insert("is_wayland".into(), json!(true)); } + #[cfg(feature = "linux_headless")] if linux_desktop_manager::is_headless() { platform_additions.insert("headless".into(), json!(true)); } @@ -1096,45 +1101,6 @@ impl Connection { self.tx_input.send(MessageInput::Key((msg, press))).ok(); } - fn try_start_desktop(_username: &str, _passsword: &str) -> String { - #[cfg(target_os = "linux")] - if _username.is_empty() { - let username = linux_desktop_manager::get_username(); - if username.is_empty() { - LOGIN_MSG_DESKTOP_SESSION_NOT_READY - } else { - "" - } - .to_owned() - } else { - let username = linux_desktop_manager::get_username(); - if username == _username { - // No need to verify password here. - return "".to_owned(); - } - - match linux_desktop_manager::try_start_x_session(_username, _passsword) { - Ok((username, x11_ready)) => { - if x11_ready { - if _username != username { - LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER.to_owned() - } else { - "".to_owned() - } - } else { - LOGIN_MSG_DESKTOP_SESSION_NOT_READY.to_owned() - } - } - Err(e) => { - log::error!("Failed to start xsession {}", e); - LOGIN_MSG_DESKTOP_XSESSION_FAILED.to_owned() - } - } - } - #[cfg(not(target_os = "linux"))] - "".to_owned() - } - fn validate_one_password(&self, password: String) -> bool { if password.len() == 0 { return false; @@ -1300,16 +1266,20 @@ impl Connection { _ => {} } + #[cfg(all(target_os = "linux", feature = "linux_headless"))] let desktop_err = match lr.os_login.as_ref() { - Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password), - None => Self::try_start_desktop("", ""), + Some(os_login) => { + linux_desktop_manager::try_start_desktop(&os_login.username, &os_login.password) + } + None => linux_desktop_manager::try_start_desktop("", ""), }; - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] let is_headless = linux_desktop_manager::is_headless(); - #[cfg(target_os = "linux")] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] let wait_ipc_timeout = 10_000; // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; return true; @@ -1324,7 +1294,7 @@ impl Connection { if hbb_common::get_version_number(&lr.version) >= hbb_common::get_version_number("1.2.0") { - self.send_login_error("No Password Access").await; + self.send_login_error(LOGIN_MSG_NO_PASSWORD_ACCESS).await; } return true; } else if password::approve_mode() == ApproveMode::Password @@ -1333,6 +1303,7 @@ impl Connection { self.send_login_error("Connection not allowed").await; return false; } else if self.is_recent_session() { + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if desktop_err.is_empty() { #[cfg(target_os = "linux")] if is_headless { @@ -1347,13 +1318,24 @@ impl Connection { } else { self.send_login_error(desktop_err).await; } + #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] + { + self.try_start_cm(lr.my_id, lr.my_name, true); + self.send_logon_response().await; + if self.port_forward_socket.is_some() { + return false; + } + } } else if lr.password.is_empty() { + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if desktop_err.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY) .await; } + #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] + self.try_start_cm(lr.my_id, lr.my_name, false); } else { let mut failure = LOGIN_FAILURES .lock() @@ -1394,6 +1376,7 @@ impl Connection { .lock() .unwrap() .insert(self.ip.clone(), failure); + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if desktop_err.is_empty() { self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; self.try_start_cm(lr.my_id, lr.my_name, false); @@ -1401,10 +1384,16 @@ impl Connection { self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG) .await; } + #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] + { + self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; + self.try_start_cm(lr.my_id, lr.my_name, false); + } } else { if failure.0 != 0 { LOGIN_FAILURES.lock().unwrap().remove(&self.ip); } + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if desktop_err.is_empty() { #[cfg(target_os = "linux")] if is_headless { @@ -1420,6 +1409,14 @@ impl Connection { } else { self.send_login_error(desktop_err).await; } + #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] + { + self.send_logon_response().await; + self.try_start_cm(lr.my_id, lr.my_name, true); + if self.port_forward_socket.is_some() { + return false; + } + } } } } else if let Some(message::Union::TestDelay(t)) = msg.union { @@ -2207,11 +2204,13 @@ async fn start_ipc( args.push("--hide"); }; - #[cfg(all(target_os = "linux", not(feature = "flutter")))] + #[cfg(target_os = "linux")] + #[cfg(not(feature = "linux_headless"))] let user = None; - #[cfg(all(target_os = "linux", feature = "flutter"))] + #[cfg(all(target_os = "linux", feature = "linux_headless"))] let mut user = None; - #[cfg(all(target_os = "linux", feature = "flutter"))] + // Cm run as user, wait until desktop session is ready. + #[cfg(all(target_os = "linux", feature = "linux_headless"))] if linux_desktop_manager::is_headless() { let mut username = linux_desktop_manager::get_username(); loop { From 5eedbdb436b719656fcf885e4d74ecd945cc2965 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 22:24:42 +0800 Subject: [PATCH 194/366] tmp commit Signed-off-by: fufesou --- src/lang/fr.rs | 22 +++++++++++++++++++--- src/server/connection.rs | 4 ++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 58a65b3f4..96ac0d7bd 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -479,9 +479,25 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Ce fichier est identique à celui du pair."), ("show_monitors_tip", "Afficher les moniteurs dans la barre d'outils."), ("View Mode", "Mode vue"), - ("enter_rustdesk_passwd_tip", "Saisissez le mot de passe RustDesk."), - ("remember_rustdesk_passwd_tip", "Se rappeler du mot de passe RustDesk."), ("login_linux_tip", "Se connecter au compte Linux distant"), - ("login_linux_tooltip_tip", "Vous devez vous connecter à un compte Linux distant pour activer une session de bureau X."), + ("verify_rustdesk_password_tip", ""), + ("remember_account_tip", ""), + ("os_account_desk_tip", ""), + ("OS Account", ""), + ("session_not_ready_title_tip", ""), + ("session_not_ready_text_tip", ""), + ("xsession_failed_title_tip", ""), + ("xsession_failed_text_tip", ""), + ("another_user_login_title_tip", ""), + ("another_user_login_text_tip", ""), + ("xorg_not_found_title_tip", ""), + ("xorg_not_found_text_tip", ""), + ("no_desktop_title_tip", ""), + ("no_desktop_text_tip", ""), + ("session_unready_no_password_title_tip", ""), + ("session_unready_no_password_text_tip", ""), + ("session_unready_wrong_password_title_tip", ""), + ("session_unready_wrong_password_text_tip", ""), + ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index b442ff899..297ab18c7 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -942,7 +942,7 @@ impl Connection { } #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.file_transfer.is_some() { - if !crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() { + if crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() { username = "".to_owned(); } } @@ -2190,7 +2190,7 @@ async fn start_ipc( tx_stream_ready: mpsc::Sender<()>, ) -> ResultType<()> { loop { - if crate::platform::is_prelogin() { + if !crate::platform::is_prelogin() { break; } sleep(1.).await; From 86313fa32855121b497478b29c8448e4e07d2f8b Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 22:47:57 +0800 Subject: [PATCH 195/366] revert lang Signed-off-by: fufesou --- src/client.rs | 2 +- src/lang/ca.rs | 2 +- src/lang/cn.rs | 2 +- src/lang/cs.rs | 2 +- src/lang/da.rs | 2 +- src/lang/de.rs | 2 +- src/lang/el.rs | 2 +- src/lang/en.rs | 1 - src/lang/eo.rs | 2 +- src/lang/es.rs | 2 +- src/lang/fa.rs | 2 +- src/lang/fr.rs | 2 +- src/lang/hu.rs | 2 +- src/lang/id.rs | 2 +- src/lang/it.rs | 2 +- src/lang/ja.rs | 2 +- src/lang/ko.rs | 2 +- src/lang/kz.rs | 2 +- src/lang/nl.rs | 2 +- src/lang/pl.rs | 2 +- src/lang/pt_PT.rs | 2 +- src/lang/ptbr.rs | 2 +- src/lang/ro.rs | 2 +- src/lang/ru.rs | 2 +- src/lang/sk.rs | 2 +- src/lang/sl.rs | 2 +- src/lang/sq.rs | 2 +- src/lang/sr.rs | 2 +- src/lang/sv.rs | 2 +- src/lang/template.rs | 2 +- src/lang/th.rs | 2 +- src/lang/tr.rs | 2 +- src/lang/tw.rs | 2 +- src/lang/ua.rs | 2 +- src/lang/vn.rs | 2 +- 35 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3f587dfe0..a541a8458 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1969,7 +1969,7 @@ lazy_static::lazy_static! { }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ msgtype: "wait-remote-accept-nook", title: "Prompt", - text: "no_password_access_text_tip", + text: "Please wait for the remote side to accept your session request...", link: "", try_again: true, })]); diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 3dc8cdb12..230a9730b 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index b8df31c77..7f0e40581 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "只允许密码访问"), ("Accept sessions via click", "只允许点击访问"), ("Accept sessions via both", "允许密码或点击访问"), + ("Please wait for the remote side to accept your session request...", "请等待对方接受你的连接..."), ("One-time Password", "一次性密码"), ("Use one-time password", "使用一次性密码"), ("One-time password length", "一次性密码长度"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", "请输入用户名密码和连接密码"), ("session_unready_wrong_password_title_tip", "密码错误"), ("session_unready_wrong_password_text_tip", "是否重试"), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 020bea621..7237b7a79 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 754b9671c..f761832da 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptér sessioner via adgangskode"), ("Accept sessions via click", "Acceptér sessioner via klik"), ("Accept sessions via both", "Acceptér sessioner via begge"), + ("Please wait for the remote side to accept your session request...", "Vent venligst på at fjernklienten accepterer din sessionsforespørgsel..."), ("One-time Password", "Engangskode"), ("Use one-time password", "Brug engangskode"), ("One-time password length", "Engangskode længde"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 3d72a0242..e77ab938c 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sitzung mit Passwort bestätigen"), ("Accept sessions via click", "Sitzung mit einem Klick bestätigen"), ("Accept sessions via both", "Sitzung mit Klick und Passwort bestätigen"), + ("Please wait for the remote side to accept your session request...", "Bitte warten Sie, bis die Gegenseite Ihre Sitzungsanfrage akzeptiert hat …"), ("One-time Password", "Einmalpasswort"), ("Use one-time password", "Einmalpasswort verwenden"), ("One-time password length", "Länge des Einmalpassworts"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index b10d9f971..3703aad27 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Αποδοχή συνεδριών με κωδικό πρόσβασης"), ("Accept sessions via click", "Αποδοχή συνεδριών με κλικ"), ("Accept sessions via both", "Αποδοχή συνεδριών και με τα δύο"), + ("Please wait for the remote side to accept your session request...", "Παρακαλώ περιμένετε μέχρι η απομακρυσμένη πλευρά να αποδεχτεί το αίτημα συνεδρίας σας..."), ("One-time Password", "Κωδικός μίας χρήσης"), ("Use one-time password", "Χρήση κωδικού πρόσβασης μίας χρήσης"), ("One-time password length", "Μήκος κωδικού πρόσβασης μίας χρήσης"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 6035db7ba..e6499ea1c 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -74,6 +74,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", "Input connection password and linux user/password"), ("session_unready_wrong_password_title_tip", "Password is wrong"), ("session_unready_wrong_password_text_tip", "Do you want to enter again"), - ("no_password_access_text_tip", "Please wait for the remote side to accept your session request..."), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 3f2a0d573..645c8be7c 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 9f06fff59..7a672dfec 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Aceptar sesiones a través de contraseña"), ("Accept sessions via click", "Aceptar sesiones a través de clic"), ("Accept sessions via both", "Aceptar sesiones a través de ambos"), + ("Please wait for the remote side to accept your session request...", "Por favor, espere a que el lado remoto acepte su solicitud de sesión"), ("One-time Password", "Constaseña de un solo uso"), ("Use one-time password", "Usar contraseña de un solo uso"), ("One-time password length", "Longitud de la contraseña de un solo uso"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 1fad5bba1..281478bb0 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "قبول درخواست با رمز عبور"), ("Accept sessions via click", "قبول درخواست با کلیک موس"), ("Accept sessions via both", "قبول درخواست با هر دو"), + ("Please wait for the remote side to accept your session request...", "...لطفا صبر کنید تا میزبان درخواست شما را قبول کند"), ("One-time Password", "رمز عبور یکبار مصرف"), ("Use one-time password", "استفاده از رمز عبور یکبار مصرف"), ("One-time password length", "طول رمز عبور یکبار مصرف"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 96ac0d7bd..1c4126377 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Accepter les sessions via mot de passe"), ("Accept sessions via click", "Accepter les sessions via clique de confirmation"), ("Accept sessions via both", "Accepter les sessions via mot de passe ou clique de confirmation"), + ("Please wait for the remote side to accept your session request...", "Veuillez attendre que votre demande de session distante soit accepter ..."), ("One-time Password", "Mot de passe unique"), ("Use one-time password", "Utiliser un mot de passe unique"), ("One-time password length", "Longueur du mot de passe unique"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 5075802f1..547107607 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 4f87a926c..c896db6ba 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Izinkan sesi dengan kata sandi"), ("Accept sessions via click", "Izinkan sesi dengan klik"), ("Accept sessions via both", "Izinkan sesi dengan keduanya"), + ("Please wait for the remote side to accept your session request...", "Harap tunggu sisi jarak jauh untuk menerima permintaan sesi Anda..."), ("One-time Password", "Kata sandi satu kali"), ("Use one-time password", "Gunakan kata sandi satu kali"), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 14a50ede9..0d1b6914e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Accetta sessioni via password"), ("Accept sessions via click", "Accetta sessioni via click"), ("Accept sessions via both", "Accetta sessioni con entrambi"), + ("Please wait for the remote side to accept your session request...", "Attendere che il lato remoto accetti la richiesta di sessione..."), ("One-time Password", "Password monouso"), ("Use one-time password", "Usa password monouso"), ("One-time password length", "Lunghezza password monouso"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index e91137d8c..b7cfaf40f 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index c7c654632..849fa7ffb 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 5422083bb..b7e242eaa 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index b043b8f7f..6cd679953 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sessies accepteren via wachtwoord"), ("Accept sessions via click", "Sessies accepteren via klik"), ("Accept sessions via both", "Accepteer sessies via beide"), + ("Please wait for the remote side to accept your session request...", "Wacht tot de andere kant uw sessieverzoek accepteert..."), ("One-time Password", "Eenmalig Wachtwoord"), ("Use one-time password", "Gebruik een eenmalig Wachtwoord"), ("One-time password length", "Eenmalig Wachtwoord lengre"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 938a4e881..529289751 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Uwierzytelnij sesję używając hasła"), ("Accept sessions via click", "Uwierzytelnij sesję poprzez kliknięcie"), ("Accept sessions via both", "Uwierzytelnij sesję za pomocą obu sposobów"), + ("Please wait for the remote side to accept your session request...", "Oczekiwanie, na zatwierdzenie sesji przez host zdalny..."), ("One-time Password", "Hasło jednorazowe"), ("Use one-time password", "Użyj hasła jednorazowego"), ("One-time password length", "Długość hasła jednorazowego"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index ae4dbccba..0fcbd360c 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index c9c0b84f7..491b48a44 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Aceitar sessões via senha"), ("Accept sessions via click", "Aceitar sessões via clique"), ("Accept sessions via both", "Aceitar sessões de ambos os modos"), + ("Please wait for the remote side to accept your session request...", "Por favor aguarde enquanto o cliente remoto aceita seu pedido de sessão..."), ("One-time Password", "Senha de uso único"), ("Use one-time password", "Usar senha de uso único"), ("One-time password length", "Comprimento da senha de uso único"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index d6632d18a..60d293e36 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptă sesiunile folosind parola"), ("Accept sessions via click", "Acceptă sesiunile cu un clic de confirmare"), ("Accept sessions via both", "Acceptă sesiunile folosind ambele moduri"), + ("Please wait for the remote side to accept your session request...", "Așteaptă ca solicitarea ta de conectare la distanță să fie acceptată..."), ("One-time Password", "Parolă unică"), ("Use one-time password", "Folosește parola unică"), ("One-time password length", "Lungimea parolei unice"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index a99523fca..90be51f01 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Принимать сеансы по паролю"), ("Accept sessions via click", "Принимать сеансы по нажатию"), ("Accept sessions via both", "Принимать сеансы по паролю+нажатию"), + ("Please wait for the remote side to accept your session request...", "Подождите, пока удалённая сторона примет ваш запрос на сеанс..."), ("One-time Password", "Одноразовый пароль"), ("Use one-time password", "Использовать одноразовый пароль"), ("One-time password length", "Длина одноразового пароля"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 7a8b9a778..aec122f70 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 581507457..18bd8f44c 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Sprejmi seje z geslom"), ("Accept sessions via click", "Sprejmi seje s potrditvijo"), ("Accept sessions via both", "Sprejmi seje z geslom ali potrditvijo"), + ("Please wait for the remote side to accept your session request...", "Počakajte, da oddaljeni računalnik sprejme povezavo..."), ("One-time Password", "Enkratno geslo"), ("Use one-time password", "Uporabi enkratno geslo"), ("One-time password length", "Dolžina enkratnega gesla"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index eb1b50762..746eed96d 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Prano sesionin nëpërmjet fjalëkalimit"), ("Accept sessions via click", "Prano sesionet nëpërmjet klikimit"), ("Accept sessions via both", "Prano sesionet nëpërmjet të dyjave"), + ("Please wait for the remote side to accept your session request...", "Ju lutem prisni që ana në distancë të pranoj kërkësen tuaj"), ("One-time Password", "Fjalëkalim Një-herë"), ("Use one-time password", "Përdorni fjalëkalim Një-herë"), ("One-time password length", "Gjatësia e fjalëkalimit një herë"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 260eadc24..0a94fe103 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Prihvati sesije preko lozinke"), ("Accept sessions via click", "Prihvati sesije preko klika"), ("Accept sessions via both", "Prihvati sesije preko oboje"), + ("Please wait for the remote side to accept your session request...", "Molimo sačekajte da udaljena strana prihvati vaš zahtev za sesijom..."), ("One-time Password", "Jednokratna lozinka"), ("Use one-time password", "Koristi jednokratnu lozinku"), ("One-time password length", "Dužina jednokratne lozinke"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 9db56dcc9..25d3144d8 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Acceptera sessioner via lösenord"), ("Accept sessions via click", "Acceptera sessioner via klick"), ("Accept sessions via both", "Acceptera sessioner via båda"), + ("Please wait for the remote side to accept your session request...", "Var god vänta på att klienten accepterar din förfrågan..."), ("One-time Password", "En-gångs lösenord"), ("Use one-time password", "Använd en-gångs lösenord"), ("One-time password length", "Längd på en-gångs lösenord"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index ac4413201..1b4b6ff14 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 11260f440..2fe64b100 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "ยอมรับการเชื่อมต่อด้วยรหัสผ่าน"), ("Accept sessions via click", "ยอมรับการเชื่อมต่อด้วยการคลิก"), ("Accept sessions via both", "ยอมรับการเชื่อมต่อด้วยทั้งสองวิธิ"), + ("Please wait for the remote side to accept your session request...", "กรุณารอให้อีกฝั่งยอมรับการเชื่อมต่อของคุณ..."), ("One-time Password", "รหัสผ่านครั้งเดียว"), ("Use one-time password", "ใช้รหัสผ่านครั้งเดียว"), ("One-time password length", "ความยาวรหัสผ่านครั้งเดียว"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index b76484b33..3a925942b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Oturumları parola ile kabul etme"), ("Accept sessions via click", "Tıklama yoluyla oturumları kabul edin"), ("Accept sessions via both", "Her ikisi aracılığıyla oturumları kabul edin"), + ("Please wait for the remote side to accept your session request...", "Lütfen uzak tarafın oturum isteğinizi kabul etmesini bekleyin..."), ("One-time Password", "Tek Kullanımlık Şifre"), ("Use one-time password", "Tek seferlik parola kullanın"), ("One-time password length", "Tek seferlik şifre uzunluğu"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index f77014e8f..86e0dfb98 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "只允許透過輸入密碼進行連線"), ("Accept sessions via click", "只允許透過點擊接受進行連線"), ("Accept sessions via both", "允許輸入密碼或點擊接受進行連線"), + ("Please wait for the remote side to accept your session request...", "請等待對方接受您的連線請求 ..."), ("One-time Password", "一次性密碼"), ("Use one-time password", "使用一次性密碼"), ("One-time password length", "一次性密碼長度"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 873dad281..36b27089c 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", "Підтверджувати сеанси паролем"), ("Accept sessions via click", "Підтверджувати сеанси натисканням"), ("Accept sessions via both", "Підтверджувати сеанси обома способами"), + ("Please wait for the remote side to accept your session request...", "Буль ласка, зачекайте, поки віддалена сторона підтвердить запит на сеанс..."), ("One-time Password", "Одноразовий пароль"), ("Use one-time password", "Використати одноразовий пароль"), ("One-time password length", "Довжина одноразового пароля"), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 7756b8894..35c40a794 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -402,6 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept sessions via password", ""), ("Accept sessions via click", ""), ("Accept sessions via both", ""), + ("Please wait for the remote side to accept your session request...", ""), ("One-time Password", ""), ("Use one-time password", ""), ("One-time password length", ""), @@ -498,6 +499,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("session_unready_no_password_text_tip", ""), ("session_unready_wrong_password_title_tip", ""), ("session_unready_wrong_password_text_tip", ""), - ("no_password_access_text_tip", ""), ].iter().cloned().collect(); } From b207468795f2c7164e0573045519012ce5473240 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 1 Apr 2023 23:02:37 +0800 Subject: [PATCH 196/366] tmp commit Signed-off-by: fufesou --- src/rendezvous_mediator.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index cc93dbb45..6dd0a1284 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -74,8 +74,7 @@ impl RendezvousMediator { } #[cfg(all(target_os = "linux", feature = "linux_headless"))] crate::platform::linux_desktop_manager::start_xdesktop(); - SHOULD_EXIT.store(false, Ordering::SeqCst); - while !SHOULD_EXIT.load(Ordering::SeqCst) { + loop { Config::reset_online(); if Config::get_option("stop-service").is_empty() { if !nat_tested { @@ -99,8 +98,10 @@ impl RendezvousMediator { } sleep(1.).await; } - #[cfg(all(target_os = "linux", feature = "linux_headless"))] - crate::platform::linux_desktop_manager::stop_xdesktop(); + // It should be better to call stop_xdesktop. + // But for server, it also is Ok without calling this method. + // #[cfg(all(target_os = "linux", feature = "linux_headless"))] + // crate::platform::linux_desktop_manager::stop_xdesktop(); } pub async fn start(server: ServerPtr, host: String) -> ResultType<()> { From 51b77100a7b6157c19ab2d0f8f7cad153270ae39 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 10:10:06 +0800 Subject: [PATCH 197/366] add libpam0g-dev Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 97a4073af..198e6d03e 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -402,7 +402,7 @@ jobs: - name: Install dependencies run: | sudo apt update - sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless + sudo apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libpam0g-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ libc6-dev gcc-multilib g++-multilib openjdk-11-jdk-headless - name: Checkout source code uses: actions/checkout@v3 - name: Install flutter @@ -654,7 +654,7 @@ jobs: install: | apt update -y echo -e "installing deps" - apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null + apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libpam0g-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null # we have libopus compiled by us. apt remove -y libopus-dev || true # output devs @@ -816,7 +816,7 @@ jobs: install: | apt update -y echo -e "installing deps" - apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null + apt-get -qq install -y git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libclang-dev ninja-build libappindicator3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvdpau-dev libva-dev libpam0g-dev libclang-dev llvm-dev libclang-10-dev llvm-10-dev pkg-config tree g++ gcc libvpx-dev tree > /dev/null # we have libopus compiled by us. apt remove -y libopus-dev || true # output devs @@ -947,7 +947,7 @@ jobs: apt update -y apt-get -qq install -y git cmake g++ gcc build-essential nasm yasm curl unzip xz-utils python3 wget pkg-config ninja-build pkg-config libgtk-3-dev liblzma-dev clang libappindicator3-dev rpm libclang-dev apt-get -qq install -y libdbus-1-dev pkg-config nasm yasm libglib2.0-dev libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev - apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev + apt-get -qq install -y libpulse-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libvpx-dev libvdpau-dev libva-dev libpam0g-dev run: | # disable git safe.directory git config --global --add safe.directory "*" From a5158d2bb75866c95375ee003a1e93883de0795b Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 10:13:25 +0800 Subject: [PATCH 198/366] build linux headless Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 198e6d03e..bf73bdaa1 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -687,7 +687,7 @@ jobs: x86_64) # no need mock on x86_64 export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release + cargo build --lib --features hwcodec,flutter,flutter_texture_render,linux_headless,${{ matrix.job.extra-build-features }} --release ;; esac From a0c899bd6749c2e2c5c410d5ff0c21ad10fe4b64 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 13:14:20 +0800 Subject: [PATCH 199/366] debug fedora Signed-off-by: fufesou --- src/client.rs | 4 +- src/platform/linux_desktop_manager.rs | 54 ++++++++++++++++----------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/client.rs b/src/client.rs index a541a8458..72bdac531 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1943,13 +1943,13 @@ lazy_static::lazy_static! { link: "", try_again: false, }), (crate::server::LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LoginErrorMsgBox{ - msgtype: "session-re-login", + msgtype: "info-nocancel-nocancel", title: "xorg_not_found_title_tip", text: "xorg_not_found_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_NO_DESKTOP, LoginErrorMsgBox{ - msgtype: "session-re-login", + msgtype: "info-nocancel-nocancel", title: "no_desktop_title_tip", text: "no_desktop_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index e9dc00ef6..25fada503 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -64,11 +64,41 @@ pub fn stop_xdesktop() { *DESKTOP_MANAGER.lock().unwrap() = None; } +fn detect_headless() -> Option<&'static str> { + match run_cmds(&format!("which {}", DesktopManager::get_xorg())) { + Ok(output) => { + if output.trim().is_empty() { + return Some(LOGIN_MSG_DESKTOP_XORG_NOT_FOUND); + } + } + _ => { + return Some(LOGIN_MSG_DESKTOP_XORG_NOT_FOUND); + } + } + + match run_cmds("ls /usr/share/xsessions/") { + Ok(output) => { + if output.trim().is_empty() { + return Some(LOGIN_MSG_DESKTOP_NO_DESKTOP); + } + } + _ => { + return Some(LOGIN_MSG_DESKTOP_NO_DESKTOP); + } + } + + None +} + pub fn try_start_desktop(_username: &str, _passsword: &str) -> String { if _username.is_empty() { let username = get_username(); if username.is_empty() { - LOGIN_MSG_DESKTOP_SESSION_NOT_READY + if let Some(msg) = detect_headless() { + msg + } else { + LOGIN_MSG_DESKTOP_SESSION_NOT_READY + } } else { "" } @@ -80,26 +110,8 @@ pub fn try_start_desktop(_username: &str, _passsword: &str) -> String { return "".to_owned(); } - match run_cmds(&format!("which {}", DesktopManager::get_xorg())) { - Ok(output) => { - if output.trim().is_empty() { - return LOGIN_MSG_DESKTOP_XORG_NOT_FOUND.to_owned(); - } - } - _ => { - return LOGIN_MSG_DESKTOP_XORG_NOT_FOUND.to_owned(); - } - } - - match run_cmds("ls /usr/share/xsessions/") { - Ok(output) => { - if output.trim().is_empty() { - return LOGIN_MSG_DESKTOP_NO_DESKTOP.to_owned(); - } - } - _ => { - return LOGIN_MSG_DESKTOP_NO_DESKTOP.to_owned(); - } + if let Some(msg) = detect_headless() { + return msg.to_owned(); } match try_start_x_session(_username, _passsword) { From 96a62f188dd9bb4fe1895941e6cec32988c385bd Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 13:36:47 +0800 Subject: [PATCH 200/366] trivial changes Signed-off-by: fufesou --- .github/workflows/flutter-nightly.yml | 1 - src/client/io_loop.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index 5aa82b847..c812f43fa 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -11,4 +11,3 @@ jobs: uses: ./.github/workflows/flutter-build.yml with: upload-artifact: true - diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index ab8b76a7f..509b5bd24 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1256,7 +1256,7 @@ impl Remote { }, Some(message::Union::MessageBox(msgbox)) => { let mut link = msgbox.link; - // Links from remote side must be verified. + // Links from the remote side must be verified. if !link.starts_with("rustdesk://") { if let Some(v) = hbb_common::config::HELPER_URL.get(&link as &str) { link = v.to_string(); From 67ddb49aab6f97feea436df528c2e678f6912819 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 14:24:17 +0800 Subject: [PATCH 201/366] remove unused lang Signed-off-by: fufesou --- src/client.rs | 18 +++++++++--------- src/lang/ca.rs | 8 -------- src/lang/cn.rs | 8 -------- src/lang/cs.rs | 8 -------- src/lang/da.rs | 8 -------- src/lang/de.rs | 8 -------- src/lang/el.rs | 8 -------- src/lang/en.rs | 12 ++---------- src/lang/eo.rs | 8 -------- src/lang/es.rs | 8 -------- src/lang/fa.rs | 8 -------- src/lang/fr.rs | 8 -------- src/lang/hu.rs | 8 -------- src/lang/id.rs | 8 -------- src/lang/it.rs | 8 -------- src/lang/ja.rs | 8 -------- src/lang/ko.rs | 8 -------- src/lang/kz.rs | 8 -------- src/lang/nl.rs | 8 -------- src/lang/pl.rs | 8 -------- src/lang/pt_PT.rs | 8 -------- src/lang/ptbr.rs | 8 -------- src/lang/ro.rs | 8 -------- src/lang/ru.rs | 8 -------- src/lang/sk.rs | 8 -------- src/lang/sl.rs | 8 -------- src/lang/sq.rs | 8 -------- src/lang/sr.rs | 8 -------- src/lang/sv.rs | 8 -------- src/lang/template.rs | 8 -------- src/lang/th.rs | 8 -------- src/lang/tr.rs | 8 -------- src/lang/tw.rs | 8 -------- src/lang/ua.rs | 8 -------- src/lang/vn.rs | 8 -------- src/server/connection.rs | 6 +++--- 36 files changed, 14 insertions(+), 286 deletions(-) diff --git a/src/client.rs b/src/client.rs index 72bdac531..3bd00db47 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1926,8 +1926,8 @@ lazy_static::lazy_static! { use hbb_common::config::LINK_HEADLESS_LINUX_SUPPORT; let map = HashMap::from([(crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LoginErrorMsgBox{ msgtype: "session-login", - title: "session_not_ready_title_tip", - text: "session_not_ready_text_tip", + title: "", + text: "", link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED, LoginErrorMsgBox{ @@ -1937,33 +1937,33 @@ lazy_static::lazy_static! { link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LoginErrorMsgBox{ - msgtype: "info", + msgtype: "info-nocancel", title: "another_user_login_title_tip", text: "another_user_login_text_tip", link: "", try_again: false, }), (crate::server::LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LoginErrorMsgBox{ - msgtype: "info-nocancel-nocancel", + msgtype: "info-nocancel", title: "xorg_not_found_title_tip", text: "xorg_not_found_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_NO_DESKTOP, LoginErrorMsgBox{ - msgtype: "info-nocancel-nocancel", + msgtype: "info-nocancel", title: "no_desktop_title_tip", text: "no_desktop_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, LoginErrorMsgBox{ msgtype: "session-login-password", - title: "session_unready_no_password_title_tip", - text: "session_unready_no_password_text_tip", + title: "session_not_ready_no_password_title_tip", + text: "session_not_ready_no_password_text_tip", link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, LoginErrorMsgBox{ msgtype: "session-login-re-password", - title: "session_unready_wrong_password_title_tip", - text: "session_unready_wrong_password_text_tip", + title: "session_not_ready_wrong_password_title_tip", + text: "session_not_ready_wrong_password_text_tip", link: "", try_again: true, }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 230a9730b..f4a8a1204 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 7f0e40581..11b3144c7 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", "记住此账户"), ("os_account_desk_tip", "在无显示器的环境下,此账户用于登录被控系统,并启用桌面"), ("OS Account", "系统账户"), - ("session_not_ready_title_tip", "桌面 session 未准备好"), - ("session_not_ready_text_tip", "请输入用户名密码"), - ("xsession_failed_title_tip", "用户名密码错误"), - ("xsession_failed_text_tip", "是否重试"), ("another_user_login_title_tip", "其他用户已登录"), ("another_user_login_text_tip", "断开"), ("xorg_not_found_title_tip", "Xorg 未安装"), ("xorg_not_found_text_tip", "请安装 Xorg"), ("no_desktop_title_tip", "desktop 未安装"), ("no_desktop_text_tip", "请安装 desktop"), - ("session_unready_no_password_title_tip", "桌面 session 未准备好"), - ("session_unready_no_password_text_tip", "请输入用户名密码和连接密码"), - ("session_unready_wrong_password_title_tip", "密码错误"), - ("session_unready_wrong_password_text_tip", "是否重试"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 7237b7a79..acfbed636 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index f761832da..b165540e8 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index e77ab938c..64ac4d9d4 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 3703aad27..e74551046 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index e6499ea1c..b20035dc9 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -57,22 +57,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter_rustdesk_passwd_tip", "Enter RustDesk password"), ("remember_rustdesk_passwd_tip", "Remember RustDesk password"), ("login_linux_tip", "You need to login to remote Linux account to enable a X desktop session"), - ("verify_rustdesk_password_tip", "Veryfy RustDesk password"), + ("verify_rustdesk_password_tip", "Verify RustDesk password"), ("remember_account_tip", "Remember this account"), ("os_account_desk_tip", "This account is used to login the remote OS and enable the desktop session in headless"), - ("session_not_ready_title_tip", "Session is unready"), - ("session_not_ready_text_tip", "Input linux user/password"), - ("xsession_failed_title_tip", "Xsession username/password is wrong"), - ("xsession_failed_text_tip", "Do you want to enter again"), - ("another_user_login_title_tip", "Another user already login"), + ("another_user_login_title_tip", "Another user already logged in"), ("another_user_login_text_tip", "Disconnect"), ("xorg_not_found_title_tip", "Xorg not found"), ("xorg_not_found_text_tip", "Please install Xorg"), ("no_desktop_title_tip", "No desktop is avaliable"), ("no_desktop_text_tip", "Please install GNOME desktop"), - ("session_unready_no_password_title_tip", "Session is unready"), - ("session_unready_no_password_text_tip", "Input connection password and linux user/password"), - ("session_unready_wrong_password_title_tip", "Password is wrong"), - ("session_unready_wrong_password_text_tip", "Do you want to enter again"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 645c8be7c..435bc6b0a 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 7a672dfec..6207fac3b 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 281478bb0..d78cc8b37 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 1c4126377..cd2a6c3cc 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 547107607..e8efc6aa3 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index c896db6ba..97cab077c 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 0d1b6914e..1211ceb3c 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index b7cfaf40f..cfe029cc4 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 849fa7ffb..9e1278c6e 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index b7e242eaa..a86d1c81a 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 6cd679953..329c4dd70 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 529289751..afa07046a 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 0fcbd360c..6a44584a0 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 491b48a44..51ef174f5 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 60d293e36..5de632ec4 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 90be51f01..f813c4b2f 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index aec122f70..3b7f62b74 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 18bd8f44c..2f050333c 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 746eed96d..453534a86 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 0a94fe103..e2728caaa 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 25d3144d8..802eff33d 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 1b4b6ff14..8941c00d4 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 2fe64b100..1bdece3b7 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 3a925942b..99977515b 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 86e0dfb98..bff471b37 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 36b27089c..755e7b041 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 35c40a794..b8086f126 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -485,19 +485,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", ""), ("os_account_desk_tip", ""), ("OS Account", ""), - ("session_not_ready_title_tip", ""), - ("session_not_ready_text_tip", ""), - ("xsession_failed_title_tip", ""), - ("xsession_failed_text_tip", ""), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), - ("session_unready_no_password_title_tip", ""), - ("session_unready_no_password_text_tip", ""), - ("session_unready_wrong_password_title_tip", ""), - ("session_unready_wrong_password_text_tip", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 297ab18c7..5eb3bb374 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -62,16 +62,16 @@ pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; -pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session unready"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session not ready"; pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; pub const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER: &str = "Desktop session another user login"; pub const LOGIN_MSG_DESKTOP_XORG_NOT_FOUND: &str = "Desktop xorg not found"; // ls /usr/share/xsessions/ pub const LOGIN_MSG_DESKTOP_NO_DESKTOP: &str = "Desktop none"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY: &str = - "Desktop session unready, password empty"; + "Desktop session not ready, password empty"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG: &str = - "Desktop session unready, password wrong"; + "Desktop session not ready, password wrong"; pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; pub const LOGIN_MSG_NO_PASSWORD_ACCESS: &str = "No Password Access"; From 9def078424d2c9661b037b2c54cd9d970f8fc49f Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 3 Apr 2023 14:46:28 +0800 Subject: [PATCH 202/366] remove unused tips Signed-off-by: fufesou --- src/client.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3bd00db47..0cc20e1e2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1932,8 +1932,8 @@ lazy_static::lazy_static! { try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED, LoginErrorMsgBox{ msgtype: "session-re-login", - title: "xsession_failed_title_tip", - text: "xsession_failed_text_tip", + title: "", + text: "", link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LoginErrorMsgBox{ @@ -1956,14 +1956,14 @@ lazy_static::lazy_static! { try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, LoginErrorMsgBox{ msgtype: "session-login-password", - title: "session_not_ready_no_password_title_tip", - text: "session_not_ready_no_password_text_tip", + title: "", + text: "", link: "", try_again: true, }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, LoginErrorMsgBox{ msgtype: "session-login-re-password", - title: "session_not_ready_wrong_password_title_tip", - text: "session_not_ready_wrong_password_text_tip", + title: "", + text: "", link: "", try_again: true, }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ From 07366a7fd54d9bb7485051e7199b2ff7f87d4776 Mon Sep 17 00:00:00 2001 From: Dmitrii Kirianov Date: Mon, 3 Apr 2023 12:02:26 +0500 Subject: [PATCH 203/366] Update ru.rs --- src/lang/ru.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index f813c4b2f..447e5223b 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -481,15 +481,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Показывать мониторы на панели инструментов"), ("View Mode", "Режим просмотра"), ("login_linux_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("verify_rustdesk_password_tip", "Подтвердить пароль RustDesk"), + ("remember_account_tip", "Запомнить этот аккаунт"), + ("os_account_desk_tip", "Эта учетная запись используется для входа в удаленную ОС и включения сеанса рабочего стола в режиме headless"), + ("OS Account", "Аккаунт ОС"), + ("another_user_login_title_tip", "Другой пользователь уже вошел в систему"), + ("another_user_login_text_tip", "Отключить"), + ("xorg_not_found_title_tip", "Xorg не найден"), + ("xorg_not_found_text_tip", "Установите, пожалуйста, Xorg"), + ("no_desktop_title_tip", "Нет доступных рабочих столов"), + ("no_desktop_text_tip", "Установите, пожалуйста, GNOME Desktop"), ].iter().cloned().collect(); } From 2437b9e851d7136ab8482f68fcd66fc6ac20d192 Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Mon, 3 Apr 2023 19:55:46 +0800 Subject: [PATCH 204/366] fix #3909 --- .github/workflows/flutter-nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml index c812f43fa..c251a2b25 100644 --- a/.github/workflows/flutter-nightly.yml +++ b/.github/workflows/flutter-nightly.yml @@ -9,5 +9,6 @@ on: jobs: run-flutter-nightly-build: uses: ./.github/workflows/flutter-build.yml + secrets: inherit with: upload-artifact: true From eb79ce7a3292e62495d3e15d1e09cb2e697c589c Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 3 Apr 2023 20:22:17 +0800 Subject: [PATCH 205/366] fix: change running host >= 20.04 https://github.com/actions/runner-images/issues/6002 --- .github/workflows/bridge.yml | 2 +- .github/workflows/flutter-build.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index 5d6ebbb57..2a69ca871 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -13,7 +13,7 @@ jobs: job: - { target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, + os: ubuntu-20.04, extra-build-args: "", } steps: diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index bf73bdaa1..f94e2dc9c 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -394,7 +394,7 @@ jobs: - { arch: x86_64, target: armv7-linux-androideabi, - os: ubuntu-18.04, + os: ubuntu-20.04, extra-build-features: "", openssl-arch: android-arm } @@ -709,14 +709,14 @@ jobs: - { arch: aarch64, target: aarch64-unknown-linux-gnu, - os: ubuntu-20.04, + os: ubuntu-20.04, # just for naming package, not running host use-cross: true, extra-build-features: "", } - { arch: aarch64, target: aarch64-unknown-linux-gnu, - os: ubuntu-18.04, # just for naming package, not running host + os: ubuntu-20.04, # just for naming package, not running host use-cross: true, extra-build-features: "appimage", } @@ -1281,7 +1281,7 @@ jobs: - { arch: x86_64, target: x86_64-unknown-linux-gnu, - os: ubuntu-18.04, + os: ubuntu-18.04, extra-build-features: "", } - { From 8e1c5ad64d73131c6c46488241b2a263a3238039 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 3 Apr 2023 20:21:50 +0300 Subject: [PATCH 206/366] Update lang.rs --- src/lang.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lang.rs b/src/lang.rs index 4776eb89e..6de810f23 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -32,6 +32,7 @@ mod tr; mod tw; mod ua; mod vn; +mod lt; pub const LANGS: &[(&str, &str)] = &[ ("en", "English"), @@ -66,6 +67,7 @@ pub const LANGS: &[(&str, &str)] = &[ ("th", "ภาษาไทย"), ("sl", "Slovenščina"), ("ro", "Română"), + ("lt", "Lietuvių"), ]; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -129,6 +131,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "th" => th::T.deref(), "sl" => sl::T.deref(), "ro" => ro::T.deref(), + "lt" => lt::T.deref(), _ => en::T.deref(), }; if let Some(v) = m.get(&name as &str) { From 005c1704011e5a4419bf1abb6d8dc5f59c954fe1 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 3 Apr 2023 21:36:18 +0300 Subject: [PATCH 207/366] Add files via upload --- src/lang/lt.rs | 495 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 src/lang/lt.rs diff --git a/src/lang/lt.rs b/src/lang/lt.rs new file mode 100644 index 000000000..87fbd3752 --- /dev/null +++ b/src/lang/lt.rs @@ -0,0 +1,495 @@ +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Būsena"), + ("Your Desktop", "Jūsų darbalaukis"), + ("desk_tip", "desk_tip"), + ("Password", "Slaptažodis"), + ("Ready", "Pasiruošęs"), + ("Established", "Įsteigta"), + ("connecting_status", "jungimo_būsena"), + ("Enable Service", "Įgalinti paslaugą"), + ("Start Service", "Pradėti paslaugą"), + ("Service is running", "Paslauga veikia"), + ("Service is not running", "Paslauga neveikia"), + ("not_ready_status", "not_ready_status"), + ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), + ("Transfer File", "Perkelti failą"), + ("Connect", "Prisijungti"), + ("Recent Sessions", "Naujausios sesijos"), + ("Address Book", "Adresų knyga"), + ("Confirmation", "Patvirtinimas"), + ("TCP Tunneling", "TCP tuneliavimas"), + ("Remove", "Pašalinti"), + ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), + ("Set your own password", "Nustatykite savo slaptažodį"), + ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), + ("Enable Clipboard", "Įgalinti iškarpinę"), + ("Enable File Transfer", "Įgalinti failų perdavimą"), + ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), + ("IP Whitelisting", "IP baltasis sąrašas"), + ("ID/Relay Server", "ID / perdavimo serveris"), + ("Import Server Config", "Importuoti serverio konfigūraciją"), + ("Export Server Config", "Eksportuoti serverio konfigūraciją"), + ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), + ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), + ("Invalid server configuration", "Netinkama serverio konfigūracija"), + ("Clipboard is empty", "Iškarpinė tuščia"), + ("Stop service", "Sustabdyti paslaugą"), + ("Change ID", "Keisti ID"), + ("Your new ID", "Jūsų naujasis ID"), + ("length %min% to %max%", "ilgis %min% iki %max%"), + ("starts with a letter", "prasideda raide"), + ("allowed characters", "leistini simboliai"), + ("id_change_tip", "id_change_tip"), + ("Website", "Interneto svetainė"), + ("About", "Apie"), + ("Slogan_tip", "Slogan_tip"), + ("Privacy Statement", "Privatumo pareiškimas"), + ("Mute", "Nutildyti"), + ("Build Date", "Sukūrimo data"), + ("Version", "Versija"), + ("Home", "Namai"), + ("Audio Input", "Garso įvestis"), + ("Enhancements", "Patobulinimai"), + ("Hardware Codec", "Aparatūros kodekas"), + ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), + ("ID Server", "ID serveris"), + ("Relay Server", "Perdavimo serveris"), + ("API Server", "API serveris"), + ("invalid_http", "netinkamas_http"), + ("Invalid IP", "Netinkamas IP"), + ("Invalid format", "Neteisingas formatas"), + ("server_not_support", "server_not_support"), + ("Not available", "Nepasiekiamas"), + ("Too frequent", "Per dažnai"), + ("Cancel", "Atšaukti"), + ("Skip", "Praleisti"), + ("Close", "Uždaryti"), + ("Retry", "Bandykite dar kartą"), + ("OK", "GERAI"), + ("Password Required", "Reikalingas slaptažodis"), + ("Please enter your password", "Prašome įvesti savo slaptažodį"), + ("Remember password", "Prisiminti slaptažodį"), + ("Wrong Password", "Neteisingas slaptažodis"), + ("Do you want to enter again?", "Ar norite įeiti dar kartą?"), + ("Connection Error", "Ryšio klaida"), + ("Error", "Klaida"), + ("Reset by the peer", "Iš naujo nustatė bendraamžis"), + ("Connecting...", "Prisijungiama..."), + ("Connection in progress. Please wait.", "Ryšys vyksta. Palaukite."), + ("Please try 1 minute later", "Prašome pabandyti po 1 minutės vėliau"), + ("Login Error", "Prisijungimo klaida"), + ("Successful", "Sėkmingai"), + ("Connected, waiting for image...", "Prisijungta, laukiama vaizdo..."), + ("Name", "Vardas"), + ("Type", "Tipas"), + ("Modified", "Pakeista"), + ("Size", "Dydis"), + ("Show Hidden Files", "Rodyti paslėptus failus"), + ("Receive", "Gauti"), + ("Send", "Siųsti"), + ("Refresh File", "Atnaujinti failą"), + ("Local", "Vietinis"), + ("Remote", "Nuotolinis"), + ("Remote Computer", "Nuotolinis kompiuteris"), + ("Local Computer", "Vietinis kompiuteris"), + ("Confirm Delete", "Patvirtinti ištrynimą"), + ("Delete", "Ištrinti"), + ("Properties", "Ypatybės"), + ("Multi Select", "Keli pasirinkimas"), + ("Select All", "Pasirinkti viską"), + ("Unselect All", "Atšaukti visų pasirinkimą"), + ("Empty Directory", "Tuščias katalogas"), + ("Not an empty directory", "Ne tuščias katalogas"), + ("Are you sure you want to delete this file?", "Ar tikrai norite ištrinti šį failą?"), + ("Are you sure you want to delete this empty directory?", "Ar tikrai norite ištrinti šį tuščią katalogą?"), + ("Are you sure you want to delete the file of this directory?", "Ar tikrai norite ištrinti šio katalogo failą?"), + ("Do this for all conflicts", "Padarykite tai visiems konfliktams"), + ("This is irreversible!", "Tai negrįžtama!"), + ("Deleting", "Ištrinama"), + ("files", "failai"), + ("Waiting", "Laukiu"), + ("Finished", "Baigta"), + ("Speed", "Greitis"), + ("Custom Image Quality", "Tinkinta vaizdo kokybė"), + ("Privacy mode", "Privatumo režimas"), + ("Block user input", "Blokuoti naudotojo įvestį"), + ("Unblock user input", "Atblokuoti naudotojo įvestį"), + ("Adjust Window", "Koreguoti langą"), + ("Original", "Originalas"), + ("Shrink", "Susitraukti"), + ("Stretch", "Ištempti"), + ("Scrollbar", "Slinkties juosta"), + ("ScrollAuto", "ScrollAuto"), + ("Good image quality", "Gera vaizdo kokybė"), + ("Balanced", "Subalansuotas"), + ("Optimize reaction time", "Optimizuoti reakcijos laiką"), + ("Custom", "Tinkintas"), + ("Show remote cursor", "Rodyti nuotolinį žymeklį"), + ("Show quality monitor", "Rodyti kokybės monitorių"), + ("Disable clipboard", "Išjungti mainų sritį"), + ("Lock after session end", "Užrakinti pasibaigus seansui"), + ("Insert", "Įdėti"), + ("Insert Lock", "Įterpti užraktą"), + ("Refresh", "Atnaujinti"), + ("ID does not exist", "ID neegzistuoja"), + ("Failed to connect to rendezvous server", "Nepavyko prisijungti prie susitikimo serverio"), + ("Please try later", "Prašome pabandyti vėliau"), + ("Remote desktop is offline", "Nuotolinis darbalaukis neprisijungęs"), + ("Key mismatch", "Raktų neatitikimas"), + ("Timeout", "Laikas baigėsi"), + ("Failed to connect to relay server", "Nepavyko prisijungti prie perdavimo serverio"), + ("Failed to connect via rendezvous server", "Nepavyko prisijungti per susitikimo serverį"), + ("Failed to connect via relay server", "Nepavyko prisijungti per perdavimo serverį"), + ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), + ("Set Password", "Nustatyti slaptažodį"), + ("OS Password", "OS slaptažodis"), + ("install_tip", "install_tip"), + ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), + ("Click to download", "Spustelėkite norėdami atsisiųsti"), + ("Click to update", "Spustelėkite norėdami atnaujinti"), + ("Configure", "Konfigūruoti"), + ("config_acc", "config_acc"), + ("config_screen", "config_screen"), + ("Installing ...", "Diegiama ..."), + ("Install", "Diegti"), + ("Installation", "Įdiegimas"), + ("Installation Path", "Įdiegimo kelias"), + ("Create start menu shortcuts", "Sukurti pradžios meniu sparčiuosius klavišus"), + ("Create desktop icon", "Sukurti darbalaukio piktogramą"), + ("agreement_tip", "agreement_tip"), + ("Accept and Install", "Priimti ir įdiegti"), + ("End-user license agreement", "Galutinio vartotojo licencijos sutartis"), + ("Generating ...", "Generuojamas..."), + ("Your installation is lower version.", "Jūsų įdiegta senesnė versija"), + ("not_close_tcp_tip", "not_close_tcp_tip"), + ("Listening ...", "Klausau..."), + ("Remote Host", "Nuotolinis pagrindinis kompiuteris"), + ("Remote Port", "Nuotolinis prievadas"), + ("Action", "Veiksmas"), + ("Add", "Papildyti"), + ("Local Port", "Vietinis uostas"), + ("Local Address", "Vietinis adresas"), + ("Change Local Port", "Keisti vietinį prievadą"), + ("setup_server_tip", "setup_server_tip"), + ("Too short, at least 6 characters.", "Per trumpas, mažiausiai 6 simboliai."), + ("The confirmation is not identical.", "Patvirtinimas nėra tapatus"), + ("Permissions", "Leidimai"), + ("Accept", "Priimti"), + ("Dismiss", "Atmesti"), + ("Disconnect", "Atjungti"), + ("Allow using keyboard and mouse", "Leisti naudojant klaviatūrą ir pelę"), + ("Allow using clipboard", "Leisti naudoti mainų sritį"), + ("Allow hearing sound", "Leisti girdėti garsą"), + ("Allow file copy and paste", "Leisti failą kopijuoti ir įklijuoti"), + ("Connected", "Prisijungta"), + ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), + ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), + ("Direct and unencrypted connection", "Tiesioginis ir nešifruotas ryšys"), + ("Relayed and unencrypted connection", "Perduotas ir nešifruotas ryšys"), + ("Enter Remote ID", "Įveskite nuotolinio ID"), + ("Enter your password", "Įveskite savo slaptažodį"), + ("Logging in...", "Prisijungiama..."), + ("Enable RDP session sharing", "Įgalinti KPP seansų bendrinimą"), + ("Auto Login", "Automatinis prisijungimas"), + ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), + ("Rename", "Pervardyti"), + ("Space", "Erdvė"), + ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), + ("Change Path", "Keisti kelią"), + ("Create Folder", "Sukurti aplanką"), + ("Please enter the folder name", "Įveskite aplanko pavadinimą"), + ("Fix it", "Pataisyk tai"), + ("Warning", "Įspėjimas"), + ("Login screen using Wayland is not supported", "Prisijungimo ekranas naudojant „Wayland“ nepalaikomas"), + ("Reboot required", "Reikia paleisti iš naujo"), + ("Unsupported display server", "Nepalaikomas rodymo serveris"), + ("x11 expected", "tikimasi x 11"), + ("Port", "Uostas"), + ("Settings", "Nustatymai"), + ("Username", "Vartotojo vardas"), + ("Invalid port", "Netinkamas prievadas"), + ("Closed manually by the peer", "Peer uždarė rankiniu būdu"), + ("Enable remote configuration modification", "Įgalinti nuotolinio konfigūracijos modifikavimą"), + ("Run without install", "Vykdyti be diegimo"), + ("Connect via relay", "Prisijungti per relę"), + ("Always connect via relay", "Visada prisijunkite per relę"), + ("whitelist_tip", "whitelist_tip"), + ("Login", "Prisijungti"), + ("Verify", "Patvirtinti"), + ("Remember me", "Prisimink mane"), + ("Trust this device", "Pasitikėk šiuo įrenginiu"), + ("Verification code", "Patvirtinimo kodas"), + ("verification_tip", "verification_tip"), + ("Logout", "Atsijungti"), + ("Tags", "Žymos"), + ("Search ID", "Paieškos ID"), + ("whitelist_sep", "whitelist_sep"), + ("Add ID", "Pridėti ID"), + ("Add Tag", "Pridėti žymą"), + ("Unselect all tags", "Atšaukti visų žymų pasirinkimą"), + ("Network error", "Tinklo klaida"), + ("Username missed", "Prarastas vartotojo vardas"), + ("Password missed", "Slaptažodis praleistas"), + ("Wrong credentials", "Klaidingi kredencialai"), + ("Edit Tag", "Redaguoti žymą"), + ("Unremember Password", "Neprisiminti slaptažodžio"), + ("Favorites", "Mėgstamiausi"), + ("Add to Favorites", "Įtraukti į adresyną"), + ("Remove from Favorites", "Pašalinti iš parankinių"), + ("Empty", "Tuščia"), + ("Invalid folder name", "Neteisingas aplanko pavadinimas"), + ("Socks5 Proxy", "Socks5 Proxy"), + ("Hostname", "Pagrindinio kompiuterio pavadinimas"), + ("Discovered", "Atrasta"), + ("install_daemon_tip", "install_daemon_tip"), + ("Remote ID", "Nuotolinis ID"), + ("Paste", "Įklijuoti"), + ("Paste here?", "Įklijuoti čia?"), + ("Are you sure to close the connection?", "Ar tikrai uždarysite ryšį?"), + ("Download new version", "Atsisiųsti naują versiją"), + ("Touch mode", "Palietimo režimas"), + ("Mouse mode", "Pelės režimas"), + ("One-Finger Tap", "Palietimas vienu pirštu"), + ("Left Mouse", "Kairė pelė"), + ("One-Long Tap", "Vienas palietimas"), + ("Two-Finger Tap", "Palietimas dviem pirštais"), + ("Right Mouse", "Dešinysis pelė"), + ("One-Finger Move", "Vieno piršto judesys"), + ("Double Tap & Move", "Dukart palieskite ir perkelkite"), + ("Mouse Drag", "Pelės vilkimas"), + ("Three-Finger vertically", "Trys pirštai vertikaliai"), + ("Mouse Wheel", "Pelės ratas"), + ("Two-Finger Move", "Dviejų pirštų judesys"), + ("Canvas Move", "Drobės perkėlimas"), + ("Pinch to Zoom", "Suimkite, kad padidintumėte"), + ("Canvas Zoom", "Drobės mastelis"), + ("Reset canvas", "Atstatyti drobę"), + ("No permission of file transfer", "Nėra leidimo perkelti failus"), + ("Note", "Pastaba"), + ("Connection", "Ryšys"), + ("Share Screen", "Bendrinti ekraną"), + ("Chat", "Pokalbis"), + ("Total", "Iš viso"), + ("items", "elementai"), + ("Selected", "Pasirinkta"), + ("Screen Capture", "Ekrano nuotrauka"), + ("Input Control", "Įvesties valdymas"), + ("Audio Capture", "Garso fiksavimas"), + ("File Connection", "Failo ryšys"), + ("Screen Connection", "Ekrano jungtis"), + ("Do you accept?", "Ar sutinki?"), + ("Open System Setting", "Atviros sistemos nustatymas"), + ("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"), + ("android_input_permission_tip1", "android_input_permission_tip1"), + ("android_input_permission_tip2", "android_input_permission_tip2"), + ("android_new_connection_tip", "android_new_connection_tip"), + ("android_service_will_start_tip", "android_service_will_start_tip"), + ("android_stop_service_tip", "android_stop_service_tip"), + ("android_version_audio_tip", "android_version_audio_tip"), + ("android_start_service_tip", "android_start_service_tip"), + ("android_permission_may_not_change_tip", "android_permission_may_not_change_tip"), + ("Account", "Paskyra"), + ("Overwrite", "Perrašyti"), + ("This file exists, skip or overwrite this file?", "Šis failas egzistuoja, praleisti arba perrašyti šį failą?"), + ("Quit", "Išeiti"), + ("doc_mac_permission", "doc_mac_permission"), + ("Help", "Pagalba"), + ("Failed", "Nepavyko"), + ("Succeeded", "Pavyko"), + ("Someone turns on privacy mode, exit", "Kažkas įjungia privatumo režimą, išeiti"), + ("Unsupported", "Nepalaikomas"), + ("Peer denied", "Atšaukė"), + ("Please install plugins", "Įdiekite papildinius"), + ("Peer exit", "Peer exit"), + ("Failed to turn off", "Nepavyko išjungti"), + ("Turned off", "Išjungė"), + ("In privacy mode", "Privatumo režimu"), + ("Out privacy mode", "Iš privatumo režimo"), + ("Language", "Kalba"), + ("Keep RustDesk background service", "Keep RustDesk foninė paslauga"), + ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), + ("android_open_battery_optimizations_tip", "android_open_battery_optimizations_tip"), + ("Start on Boot", "Start on Boot"), + ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), + ("Connection not allowed", "Ryšys neleidžiamas"), + ("Legacy mode", "Senasis režimas"), + ("Map mode", "Žemėlapio režimas"), + ("Translate mode", "Vertimo režimas"), + ("Use permanent password", "Naudoti nuolatinį slaptažodį"), + ("Use both passwords", "Naudoti abu slaptažodžius"), + ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), + ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), + ("Allow remote restart", "Leisti nuotolinį paleidimą iš naujo"), + ("Restart Remote Device", "Paleisti nuotolinį įrenginį iš naujo"), + ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo"), + ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), + ("remote_restarting_tip", "remote_restarting_tip"), + ("Copied", "Nukopijuota"), + ("Exit Fullscreen", "Išeiti per visą ekraną"), + ("Fullscreen", "Per visą ekraną"), + ("Mobile Actions", "Veiksmai mobiliesiems"), + ("Select Monitor", "Pasirinkite monitorių"), + ("Control Actions", "Valdymo veiksmai"), + ("Display Settings", "Ekrano nustatymai"), + ("Ratio", "Santykis"), + ("Image Quality", "Vaizdo kokybė"), + ("Scroll Style", "Slinkimo stilius"), + ("Show Menubar", "Rodyti meniu juostą"), + ("Hide Menubar", "Slėpti meniu juostą"), + ("Direct Connection", "Tiesioginis ryšys"), + ("Relay Connection", "Relės jungtis"), + ("Secure Connection", "Saugus ryšys"), + ("Insecure Connection", "Nesaugus ryšys"), + ("Scale original", "Pakeisti originalų mastelį"), + ("Scale adaptive", "Pritaikomas mastelis"), + ("General", "Bendra"), + ("Security", "Sauga"), + ("Theme", "Tema"), + ("Dark Theme", "Tamsioji tema"), + ("Light Theme", "Šviesi tema"), + ("Dark", "Tamsiai"), + ("Light", "Šviesa"), + ("Follow System", "Sekti sistemą"), + ("Enable hardware codec", "Įgalinti aparatūros kodeką"), + ("Unlock Security Settings", "Atrakinti saugos nustatymus"), + ("Enable Audio", "Įgalinti garsą"), + ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), + ("Server", "Serveris"), + ("Direct IP Access", "Tiesioginė IP prieiga"), + ("Proxy", "Įgaliotinis serveris"), + ("Apply", "Taikyti"), + ("Disconnect all devices?", "Atjungti visus įrenginius?"), + ("Clear", "Išvalyti"), + ("Audio Input Device", "Garso įvesties įrenginys"), + ("Deny remote access", "Uždrausti nuotolinę prieigą"), + ("Use IP Whitelisting", "Naudoti IP baltąjį sąrašą"), + ("Network", "Tinklas"), + ("Enable RDP", "Įgalinti KPP"), + ("Pin menubar", "Prisegti meniu juostą"), + ("Unpin menubar", "Atsegti meniu juostą"), + ("Recording", "Įrašymas"), + ("Directory", "Katalogas"), + ("Automatically record incoming sessions", "Automatiškai įrašyti gaunamus seansus"), + ("Change", "Keisti"), + ("Start session recording", "Pradėti seanso įrašymą"), + ("Stop session recording", "Sustabdyti seanso įrašymą"), + ("Enable Recording Session", "Įgalinti įrašymo seansą"), + ("Allow recording session", "Leisti įrašymo sesiją"), + ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), + ("Deny LAN Discovery", "Neleisti LAN atradimo"), + ("Write a message", "Rašyti žinutę"), + ("Prompt", "Prompt"), + ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), + ("elevated_foreground_window_tip", "iškeltas_foreground_window_tip"), + ("Disconnected", "Atjungtas"), + ("Other", "Kita"), + ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), + ("Keyboard Settings", "Klaviatūros nustatymai"), + ("Full Access", "Visa prieiga"), + ("Screen Share", "Ekrano bendrinimas"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland reikalauja Ubuntu 21.04 arba naujesnės versijos."), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland“ reikalinga naujesnė „Linux Distro“ versija. Išbandykite X11 darbalaukį arba pakeiskite OS."), + ("JumpLink", "JumpLink"), + ("Please Select the screen to be shared(Operate on the peer side).", "Prašome pasirinkti ekraną, kurį norite bendrinti (veikti lygiavertėje pusėje)."), + ("Show RustDesk", "Rodyti RustDesk"), + ("This PC", "Šis kompiuteris"), + ("or", "arba"), + ("Continue with", "Tęsti su"), + ("Elevate", "Pakelti"), + ("Zoom cursor", "Mastelio keitimo žymeklis"), + ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), + ("Accept sessions via click", "Priimti seansus spustelėjus"), + ("Accept sessions via both", "Priimti seansus per abu"), + ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), + ("One-time Password", "Vienkartinis slaptažodis"), + ("Use one-time password", "Naudoti vienkartinį slaptažodį"), + ("One-time password length", "Vienkartinio slaptažodžio ilgis"), + ("Request access to your device", "Prašyti prieigos prie įrenginio"), + ("Hide connection management window", "Slėpti ryšio valdymo langą"), + ("hide_cm_tip", "hide_cm_tip"), + ("wayland_experiment_tip", "wayland_experiment_tip"), + ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), + ("Skipped", "Praleisti"), + ("Add to Address Book", "Pridėti prie adresų knygos"), + ("Group", "Grupė"), + ("Search", "Paieška"), + ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), + ("Local keyboard type", "Vietinės klaviatūros tipas"), + ("Select local keyboard type", "Pasirinkite vietinės klaviatūros tipą"), + ("software_render_tip", "software_render_tip"), + ("Always use software rendering", "Visada naudoti programinės įrangos atvaizdavimą"), + ("config_input", "config_input"), + ("config_microphone", "config_microphone"), + ("request_elevation_tip", "request_elevation_tip"), + ("Wait", "Laukti"), + ("Elevation Error", "Aukščio klaida"), + ("Ask the remote user for authentication", "Paklauskite nuotolinio vartotojo autentifikavimo"), + ("Choose this if the remote account is administrator", "Pasirinkite tai, jei nuotolinė paskyra yra administratorius"), + ("Transmit the username and password of administrator", "Persiųsti administratoriaus vartotojo vardą ir slaptažodį"), + ("still_click_uac_tip", "still_click_uac_tip"), + ("Request Elevation", "Prašyti paaukštinimo"), + ("wait_accept_uac_tip", "wait_accept_uac_tip"), + ("Elevate successfully", "Pakelti sėkmingai"), + ("uppercase", "didžiosios raidės"), + ("lowercase", "mažosios raidės"), + ("digit", "skaitmenų"), + ("special character", "specialusis simbolis"), + ("length>=8", "ilgis>=8"), + ("Weak", "Silpnas"), + ("Medium", "Vidutinis"), + ("Strong", "Stiprus"), + ("Switch Sides", "Perjungti puses"), + ("Please confirm if you want to share your desktop?", "Prašome patvirtinti, jei norite bendrinti darbalaukį?"), + ("Display", "Ekranas"), + ("Default View Style", "Numatytasis peržiūros stilius"), + ("Default Scroll Style", "Numatytasis slinkties stilius"), + ("Default Image Quality", "Numatytoji vaizdo kokybė"), + ("Default Codec", "Numatytasis kodekas"), + ("Bitrate", "Bitrate"), + ("FPS", "FPS"), + ("Auto", "Automatinis"), + ("Other Default Options", "Kitos numatytosios parinktys"), + ("Voice call", "Balso skambutis"), + ("Text chat", "Teksto pokalbis"), + ("Stop voice call", "Sustabdyti balso skambutį"), + ("relay_hint_tip", "relay_hint_tip"), + ("Reconnect", "Prisijungti iš naujo"), + ("Codec", "Kodekas"), + ("Resolution", "Rezoliucija"), + ("No transfers in progress", "Nevyksta jokių pervedimų"), + ("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"), + ("idd_driver_tip", "idd_driver_tip"), + ("confirm_idd_driver_tip", "confirm_idd_driver_tip"), + ("RDP Settings", "RDP nustatymai"), + ("Sort by", "Rūšiuoti pagal"), + ("New Connection", "Naujas ryšys"), + ("Restore", "Atkurti"), + ("Minimize", "Sumažinti"), + ("Maximize", "Padidinti"), + ("Your Device", "Jūsų įrenginys"), + ("empty_recent_tip", "tuščias_paskutinis_patarimas"), + ("empty_favorite_tip", "empty_favorite_tip"), + ("empty_lan_tip", "empty_lan_tip"), + ("empty_address_book_tip", "empty_address_book_tip"), + ("eg: admin", "pvz.: administratorius"), + ("Empty Username", "Tuščias naudotojo vardas"), + ("Empty Password", "Tuščias slaptažodis"), + ("Me", "Aš"), + ("identical_file_tip", "identical_file_tip"), + ("show_monitors_tip", "show_monitors_tip"), + ("View Mode", "Peržiūros režimas"), + ("login_linux_tip", "login_linux_tip"), + ("verify_rustdesk_password_tip", "verify_rustdesk_password_tip"), + ("remember_account_tip", "remember_account_tip"), + ("os_account_desk_tip", "os_account_desk_tip"), + ("OS Account", "OS paskyra"), + ("another_user_login_title_tip", "another_user_login_title_tip"), + ("another_user_login_text_tip", "antro_user_login_text_tip"), + ("xorg_not_found_title_tip", "xorg_not_found_title_tip"), + ("xorg_not_found_text_tip", "xorg_not_found_text_tip"), + ("no_desktop_title_tip", "no_desktop_title_tip"), + ("no_desktop_text_tip", "no_desktop_text_tip"), + ].iter().cloned().collect(); +} \ No newline at end of file From a08ba2a1014993114cea5546b1f4978789d8532c Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Mon, 3 Apr 2023 22:18:43 +0200 Subject: [PATCH 208/366] Update de.rs --- src/lang/de.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 64ac4d9d4..6777ad733 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -481,15 +481,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Monitore in der Symbolleiste anzeigen"), ("View Mode", "Ansichtsmodus"), ("login_linux_tip", "Sie müssen sich an einem entfernten Linux-Konto anmelden, um eine X-Desktop-Sitzung zu eröffnen."), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("verify_rustdesk_password_tip", "RustDesk-Passwort bestätigen"), + ("remember_account_tip", "Dieses Konto merken"), + ("os_account_desk_tip", "Dieses Konto wird verwendet, um sich beim entfernten Betriebssystem anzumelden und die Desktop-Sitzung im Headless-Modus zu aktivieren."), + ("OS Account", "Betriebssystem-Konto"), + ("another_user_login_title_tip", "Ein anderer Benutzer ist bereits angemeldet."), + ("another_user_login_text_tip", "Trennen"), + ("xorg_not_found_title_tip", "Xorg nicht gefunden."), + ("xorg_not_found_text_tip", "Bitte installieren Sie Xorg."), + ("no_desktop_title_tip", "Es ist kein Desktop verfügbar."), + ("no_desktop_text_tip", "Bitte installieren Sie den GNOME-Desktop."), ].iter().cloned().collect(); } From f210bf98e2e32f9c741ffe5edfb829ebe268b126 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:22:22 +0300 Subject: [PATCH 209/366] Delete lt.rs --- src/lang/lt.rs | 495 ------------------------------------------------- 1 file changed, 495 deletions(-) delete mode 100644 src/lang/lt.rs diff --git a/src/lang/lt.rs b/src/lang/lt.rs deleted file mode 100644 index 87fbd3752..000000000 --- a/src/lang/lt.rs +++ /dev/null @@ -1,495 +0,0 @@ -lazy_static::lazy_static! { -pub static ref T: std::collections::HashMap<&'static str, &'static str> = - [ - ("Status", "Būsena"), - ("Your Desktop", "Jūsų darbalaukis"), - ("desk_tip", "desk_tip"), - ("Password", "Slaptažodis"), - ("Ready", "Pasiruošęs"), - ("Established", "Įsteigta"), - ("connecting_status", "jungimo_būsena"), - ("Enable Service", "Įgalinti paslaugą"), - ("Start Service", "Pradėti paslaugą"), - ("Service is running", "Paslauga veikia"), - ("Service is not running", "Paslauga neveikia"), - ("not_ready_status", "not_ready_status"), - ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), - ("Transfer File", "Perkelti failą"), - ("Connect", "Prisijungti"), - ("Recent Sessions", "Naujausios sesijos"), - ("Address Book", "Adresų knyga"), - ("Confirmation", "Patvirtinimas"), - ("TCP Tunneling", "TCP tuneliavimas"), - ("Remove", "Pašalinti"), - ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), - ("Set your own password", "Nustatykite savo slaptažodį"), - ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), - ("Enable Clipboard", "Įgalinti iškarpinę"), - ("Enable File Transfer", "Įgalinti failų perdavimą"), - ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), - ("IP Whitelisting", "IP baltasis sąrašas"), - ("ID/Relay Server", "ID / perdavimo serveris"), - ("Import Server Config", "Importuoti serverio konfigūraciją"), - ("Export Server Config", "Eksportuoti serverio konfigūraciją"), - ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), - ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), - ("Invalid server configuration", "Netinkama serverio konfigūracija"), - ("Clipboard is empty", "Iškarpinė tuščia"), - ("Stop service", "Sustabdyti paslaugą"), - ("Change ID", "Keisti ID"), - ("Your new ID", "Jūsų naujasis ID"), - ("length %min% to %max%", "ilgis %min% iki %max%"), - ("starts with a letter", "prasideda raide"), - ("allowed characters", "leistini simboliai"), - ("id_change_tip", "id_change_tip"), - ("Website", "Interneto svetainė"), - ("About", "Apie"), - ("Slogan_tip", "Slogan_tip"), - ("Privacy Statement", "Privatumo pareiškimas"), - ("Mute", "Nutildyti"), - ("Build Date", "Sukūrimo data"), - ("Version", "Versija"), - ("Home", "Namai"), - ("Audio Input", "Garso įvestis"), - ("Enhancements", "Patobulinimai"), - ("Hardware Codec", "Aparatūros kodekas"), - ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), - ("ID Server", "ID serveris"), - ("Relay Server", "Perdavimo serveris"), - ("API Server", "API serveris"), - ("invalid_http", "netinkamas_http"), - ("Invalid IP", "Netinkamas IP"), - ("Invalid format", "Neteisingas formatas"), - ("server_not_support", "server_not_support"), - ("Not available", "Nepasiekiamas"), - ("Too frequent", "Per dažnai"), - ("Cancel", "Atšaukti"), - ("Skip", "Praleisti"), - ("Close", "Uždaryti"), - ("Retry", "Bandykite dar kartą"), - ("OK", "GERAI"), - ("Password Required", "Reikalingas slaptažodis"), - ("Please enter your password", "Prašome įvesti savo slaptažodį"), - ("Remember password", "Prisiminti slaptažodį"), - ("Wrong Password", "Neteisingas slaptažodis"), - ("Do you want to enter again?", "Ar norite įeiti dar kartą?"), - ("Connection Error", "Ryšio klaida"), - ("Error", "Klaida"), - ("Reset by the peer", "Iš naujo nustatė bendraamžis"), - ("Connecting...", "Prisijungiama..."), - ("Connection in progress. Please wait.", "Ryšys vyksta. Palaukite."), - ("Please try 1 minute later", "Prašome pabandyti po 1 minutės vėliau"), - ("Login Error", "Prisijungimo klaida"), - ("Successful", "Sėkmingai"), - ("Connected, waiting for image...", "Prisijungta, laukiama vaizdo..."), - ("Name", "Vardas"), - ("Type", "Tipas"), - ("Modified", "Pakeista"), - ("Size", "Dydis"), - ("Show Hidden Files", "Rodyti paslėptus failus"), - ("Receive", "Gauti"), - ("Send", "Siųsti"), - ("Refresh File", "Atnaujinti failą"), - ("Local", "Vietinis"), - ("Remote", "Nuotolinis"), - ("Remote Computer", "Nuotolinis kompiuteris"), - ("Local Computer", "Vietinis kompiuteris"), - ("Confirm Delete", "Patvirtinti ištrynimą"), - ("Delete", "Ištrinti"), - ("Properties", "Ypatybės"), - ("Multi Select", "Keli pasirinkimas"), - ("Select All", "Pasirinkti viską"), - ("Unselect All", "Atšaukti visų pasirinkimą"), - ("Empty Directory", "Tuščias katalogas"), - ("Not an empty directory", "Ne tuščias katalogas"), - ("Are you sure you want to delete this file?", "Ar tikrai norite ištrinti šį failą?"), - ("Are you sure you want to delete this empty directory?", "Ar tikrai norite ištrinti šį tuščią katalogą?"), - ("Are you sure you want to delete the file of this directory?", "Ar tikrai norite ištrinti šio katalogo failą?"), - ("Do this for all conflicts", "Padarykite tai visiems konfliktams"), - ("This is irreversible!", "Tai negrįžtama!"), - ("Deleting", "Ištrinama"), - ("files", "failai"), - ("Waiting", "Laukiu"), - ("Finished", "Baigta"), - ("Speed", "Greitis"), - ("Custom Image Quality", "Tinkinta vaizdo kokybė"), - ("Privacy mode", "Privatumo režimas"), - ("Block user input", "Blokuoti naudotojo įvestį"), - ("Unblock user input", "Atblokuoti naudotojo įvestį"), - ("Adjust Window", "Koreguoti langą"), - ("Original", "Originalas"), - ("Shrink", "Susitraukti"), - ("Stretch", "Ištempti"), - ("Scrollbar", "Slinkties juosta"), - ("ScrollAuto", "ScrollAuto"), - ("Good image quality", "Gera vaizdo kokybė"), - ("Balanced", "Subalansuotas"), - ("Optimize reaction time", "Optimizuoti reakcijos laiką"), - ("Custom", "Tinkintas"), - ("Show remote cursor", "Rodyti nuotolinį žymeklį"), - ("Show quality monitor", "Rodyti kokybės monitorių"), - ("Disable clipboard", "Išjungti mainų sritį"), - ("Lock after session end", "Užrakinti pasibaigus seansui"), - ("Insert", "Įdėti"), - ("Insert Lock", "Įterpti užraktą"), - ("Refresh", "Atnaujinti"), - ("ID does not exist", "ID neegzistuoja"), - ("Failed to connect to rendezvous server", "Nepavyko prisijungti prie susitikimo serverio"), - ("Please try later", "Prašome pabandyti vėliau"), - ("Remote desktop is offline", "Nuotolinis darbalaukis neprisijungęs"), - ("Key mismatch", "Raktų neatitikimas"), - ("Timeout", "Laikas baigėsi"), - ("Failed to connect to relay server", "Nepavyko prisijungti prie perdavimo serverio"), - ("Failed to connect via rendezvous server", "Nepavyko prisijungti per susitikimo serverį"), - ("Failed to connect via relay server", "Nepavyko prisijungti per perdavimo serverį"), - ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), - ("Set Password", "Nustatyti slaptažodį"), - ("OS Password", "OS slaptažodis"), - ("install_tip", "install_tip"), - ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), - ("Click to download", "Spustelėkite norėdami atsisiųsti"), - ("Click to update", "Spustelėkite norėdami atnaujinti"), - ("Configure", "Konfigūruoti"), - ("config_acc", "config_acc"), - ("config_screen", "config_screen"), - ("Installing ...", "Diegiama ..."), - ("Install", "Diegti"), - ("Installation", "Įdiegimas"), - ("Installation Path", "Įdiegimo kelias"), - ("Create start menu shortcuts", "Sukurti pradžios meniu sparčiuosius klavišus"), - ("Create desktop icon", "Sukurti darbalaukio piktogramą"), - ("agreement_tip", "agreement_tip"), - ("Accept and Install", "Priimti ir įdiegti"), - ("End-user license agreement", "Galutinio vartotojo licencijos sutartis"), - ("Generating ...", "Generuojamas..."), - ("Your installation is lower version.", "Jūsų įdiegta senesnė versija"), - ("not_close_tcp_tip", "not_close_tcp_tip"), - ("Listening ...", "Klausau..."), - ("Remote Host", "Nuotolinis pagrindinis kompiuteris"), - ("Remote Port", "Nuotolinis prievadas"), - ("Action", "Veiksmas"), - ("Add", "Papildyti"), - ("Local Port", "Vietinis uostas"), - ("Local Address", "Vietinis adresas"), - ("Change Local Port", "Keisti vietinį prievadą"), - ("setup_server_tip", "setup_server_tip"), - ("Too short, at least 6 characters.", "Per trumpas, mažiausiai 6 simboliai."), - ("The confirmation is not identical.", "Patvirtinimas nėra tapatus"), - ("Permissions", "Leidimai"), - ("Accept", "Priimti"), - ("Dismiss", "Atmesti"), - ("Disconnect", "Atjungti"), - ("Allow using keyboard and mouse", "Leisti naudojant klaviatūrą ir pelę"), - ("Allow using clipboard", "Leisti naudoti mainų sritį"), - ("Allow hearing sound", "Leisti girdėti garsą"), - ("Allow file copy and paste", "Leisti failą kopijuoti ir įklijuoti"), - ("Connected", "Prisijungta"), - ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), - ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), - ("Direct and unencrypted connection", "Tiesioginis ir nešifruotas ryšys"), - ("Relayed and unencrypted connection", "Perduotas ir nešifruotas ryšys"), - ("Enter Remote ID", "Įveskite nuotolinio ID"), - ("Enter your password", "Įveskite savo slaptažodį"), - ("Logging in...", "Prisijungiama..."), - ("Enable RDP session sharing", "Įgalinti KPP seansų bendrinimą"), - ("Auto Login", "Automatinis prisijungimas"), - ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), - ("Rename", "Pervardyti"), - ("Space", "Erdvė"), - ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), - ("Change Path", "Keisti kelią"), - ("Create Folder", "Sukurti aplanką"), - ("Please enter the folder name", "Įveskite aplanko pavadinimą"), - ("Fix it", "Pataisyk tai"), - ("Warning", "Įspėjimas"), - ("Login screen using Wayland is not supported", "Prisijungimo ekranas naudojant „Wayland“ nepalaikomas"), - ("Reboot required", "Reikia paleisti iš naujo"), - ("Unsupported display server", "Nepalaikomas rodymo serveris"), - ("x11 expected", "tikimasi x 11"), - ("Port", "Uostas"), - ("Settings", "Nustatymai"), - ("Username", "Vartotojo vardas"), - ("Invalid port", "Netinkamas prievadas"), - ("Closed manually by the peer", "Peer uždarė rankiniu būdu"), - ("Enable remote configuration modification", "Įgalinti nuotolinio konfigūracijos modifikavimą"), - ("Run without install", "Vykdyti be diegimo"), - ("Connect via relay", "Prisijungti per relę"), - ("Always connect via relay", "Visada prisijunkite per relę"), - ("whitelist_tip", "whitelist_tip"), - ("Login", "Prisijungti"), - ("Verify", "Patvirtinti"), - ("Remember me", "Prisimink mane"), - ("Trust this device", "Pasitikėk šiuo įrenginiu"), - ("Verification code", "Patvirtinimo kodas"), - ("verification_tip", "verification_tip"), - ("Logout", "Atsijungti"), - ("Tags", "Žymos"), - ("Search ID", "Paieškos ID"), - ("whitelist_sep", "whitelist_sep"), - ("Add ID", "Pridėti ID"), - ("Add Tag", "Pridėti žymą"), - ("Unselect all tags", "Atšaukti visų žymų pasirinkimą"), - ("Network error", "Tinklo klaida"), - ("Username missed", "Prarastas vartotojo vardas"), - ("Password missed", "Slaptažodis praleistas"), - ("Wrong credentials", "Klaidingi kredencialai"), - ("Edit Tag", "Redaguoti žymą"), - ("Unremember Password", "Neprisiminti slaptažodžio"), - ("Favorites", "Mėgstamiausi"), - ("Add to Favorites", "Įtraukti į adresyną"), - ("Remove from Favorites", "Pašalinti iš parankinių"), - ("Empty", "Tuščia"), - ("Invalid folder name", "Neteisingas aplanko pavadinimas"), - ("Socks5 Proxy", "Socks5 Proxy"), - ("Hostname", "Pagrindinio kompiuterio pavadinimas"), - ("Discovered", "Atrasta"), - ("install_daemon_tip", "install_daemon_tip"), - ("Remote ID", "Nuotolinis ID"), - ("Paste", "Įklijuoti"), - ("Paste here?", "Įklijuoti čia?"), - ("Are you sure to close the connection?", "Ar tikrai uždarysite ryšį?"), - ("Download new version", "Atsisiųsti naują versiją"), - ("Touch mode", "Palietimo režimas"), - ("Mouse mode", "Pelės režimas"), - ("One-Finger Tap", "Palietimas vienu pirštu"), - ("Left Mouse", "Kairė pelė"), - ("One-Long Tap", "Vienas palietimas"), - ("Two-Finger Tap", "Palietimas dviem pirštais"), - ("Right Mouse", "Dešinysis pelė"), - ("One-Finger Move", "Vieno piršto judesys"), - ("Double Tap & Move", "Dukart palieskite ir perkelkite"), - ("Mouse Drag", "Pelės vilkimas"), - ("Three-Finger vertically", "Trys pirštai vertikaliai"), - ("Mouse Wheel", "Pelės ratas"), - ("Two-Finger Move", "Dviejų pirštų judesys"), - ("Canvas Move", "Drobės perkėlimas"), - ("Pinch to Zoom", "Suimkite, kad padidintumėte"), - ("Canvas Zoom", "Drobės mastelis"), - ("Reset canvas", "Atstatyti drobę"), - ("No permission of file transfer", "Nėra leidimo perkelti failus"), - ("Note", "Pastaba"), - ("Connection", "Ryšys"), - ("Share Screen", "Bendrinti ekraną"), - ("Chat", "Pokalbis"), - ("Total", "Iš viso"), - ("items", "elementai"), - ("Selected", "Pasirinkta"), - ("Screen Capture", "Ekrano nuotrauka"), - ("Input Control", "Įvesties valdymas"), - ("Audio Capture", "Garso fiksavimas"), - ("File Connection", "Failo ryšys"), - ("Screen Connection", "Ekrano jungtis"), - ("Do you accept?", "Ar sutinki?"), - ("Open System Setting", "Atviros sistemos nustatymas"), - ("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"), - ("android_input_permission_tip1", "android_input_permission_tip1"), - ("android_input_permission_tip2", "android_input_permission_tip2"), - ("android_new_connection_tip", "android_new_connection_tip"), - ("android_service_will_start_tip", "android_service_will_start_tip"), - ("android_stop_service_tip", "android_stop_service_tip"), - ("android_version_audio_tip", "android_version_audio_tip"), - ("android_start_service_tip", "android_start_service_tip"), - ("android_permission_may_not_change_tip", "android_permission_may_not_change_tip"), - ("Account", "Paskyra"), - ("Overwrite", "Perrašyti"), - ("This file exists, skip or overwrite this file?", "Šis failas egzistuoja, praleisti arba perrašyti šį failą?"), - ("Quit", "Išeiti"), - ("doc_mac_permission", "doc_mac_permission"), - ("Help", "Pagalba"), - ("Failed", "Nepavyko"), - ("Succeeded", "Pavyko"), - ("Someone turns on privacy mode, exit", "Kažkas įjungia privatumo režimą, išeiti"), - ("Unsupported", "Nepalaikomas"), - ("Peer denied", "Atšaukė"), - ("Please install plugins", "Įdiekite papildinius"), - ("Peer exit", "Peer exit"), - ("Failed to turn off", "Nepavyko išjungti"), - ("Turned off", "Išjungė"), - ("In privacy mode", "Privatumo režimu"), - ("Out privacy mode", "Iš privatumo režimo"), - ("Language", "Kalba"), - ("Keep RustDesk background service", "Keep RustDesk foninė paslauga"), - ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), - ("android_open_battery_optimizations_tip", "android_open_battery_optimizations_tip"), - ("Start on Boot", "Start on Boot"), - ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), - ("Connection not allowed", "Ryšys neleidžiamas"), - ("Legacy mode", "Senasis režimas"), - ("Map mode", "Žemėlapio režimas"), - ("Translate mode", "Vertimo režimas"), - ("Use permanent password", "Naudoti nuolatinį slaptažodį"), - ("Use both passwords", "Naudoti abu slaptažodžius"), - ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), - ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), - ("Allow remote restart", "Leisti nuotolinį paleidimą iš naujo"), - ("Restart Remote Device", "Paleisti nuotolinį įrenginį iš naujo"), - ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo"), - ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), - ("remote_restarting_tip", "remote_restarting_tip"), - ("Copied", "Nukopijuota"), - ("Exit Fullscreen", "Išeiti per visą ekraną"), - ("Fullscreen", "Per visą ekraną"), - ("Mobile Actions", "Veiksmai mobiliesiems"), - ("Select Monitor", "Pasirinkite monitorių"), - ("Control Actions", "Valdymo veiksmai"), - ("Display Settings", "Ekrano nustatymai"), - ("Ratio", "Santykis"), - ("Image Quality", "Vaizdo kokybė"), - ("Scroll Style", "Slinkimo stilius"), - ("Show Menubar", "Rodyti meniu juostą"), - ("Hide Menubar", "Slėpti meniu juostą"), - ("Direct Connection", "Tiesioginis ryšys"), - ("Relay Connection", "Relės jungtis"), - ("Secure Connection", "Saugus ryšys"), - ("Insecure Connection", "Nesaugus ryšys"), - ("Scale original", "Pakeisti originalų mastelį"), - ("Scale adaptive", "Pritaikomas mastelis"), - ("General", "Bendra"), - ("Security", "Sauga"), - ("Theme", "Tema"), - ("Dark Theme", "Tamsioji tema"), - ("Light Theme", "Šviesi tema"), - ("Dark", "Tamsiai"), - ("Light", "Šviesa"), - ("Follow System", "Sekti sistemą"), - ("Enable hardware codec", "Įgalinti aparatūros kodeką"), - ("Unlock Security Settings", "Atrakinti saugos nustatymus"), - ("Enable Audio", "Įgalinti garsą"), - ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), - ("Server", "Serveris"), - ("Direct IP Access", "Tiesioginė IP prieiga"), - ("Proxy", "Įgaliotinis serveris"), - ("Apply", "Taikyti"), - ("Disconnect all devices?", "Atjungti visus įrenginius?"), - ("Clear", "Išvalyti"), - ("Audio Input Device", "Garso įvesties įrenginys"), - ("Deny remote access", "Uždrausti nuotolinę prieigą"), - ("Use IP Whitelisting", "Naudoti IP baltąjį sąrašą"), - ("Network", "Tinklas"), - ("Enable RDP", "Įgalinti KPP"), - ("Pin menubar", "Prisegti meniu juostą"), - ("Unpin menubar", "Atsegti meniu juostą"), - ("Recording", "Įrašymas"), - ("Directory", "Katalogas"), - ("Automatically record incoming sessions", "Automatiškai įrašyti gaunamus seansus"), - ("Change", "Keisti"), - ("Start session recording", "Pradėti seanso įrašymą"), - ("Stop session recording", "Sustabdyti seanso įrašymą"), - ("Enable Recording Session", "Įgalinti įrašymo seansą"), - ("Allow recording session", "Leisti įrašymo sesiją"), - ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), - ("Deny LAN Discovery", "Neleisti LAN atradimo"), - ("Write a message", "Rašyti žinutę"), - ("Prompt", "Prompt"), - ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), - ("elevated_foreground_window_tip", "iškeltas_foreground_window_tip"), - ("Disconnected", "Atjungtas"), - ("Other", "Kita"), - ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), - ("Keyboard Settings", "Klaviatūros nustatymai"), - ("Full Access", "Visa prieiga"), - ("Screen Share", "Ekrano bendrinimas"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland reikalauja Ubuntu 21.04 arba naujesnės versijos."), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland“ reikalinga naujesnė „Linux Distro“ versija. Išbandykite X11 darbalaukį arba pakeiskite OS."), - ("JumpLink", "JumpLink"), - ("Please Select the screen to be shared(Operate on the peer side).", "Prašome pasirinkti ekraną, kurį norite bendrinti (veikti lygiavertėje pusėje)."), - ("Show RustDesk", "Rodyti RustDesk"), - ("This PC", "Šis kompiuteris"), - ("or", "arba"), - ("Continue with", "Tęsti su"), - ("Elevate", "Pakelti"), - ("Zoom cursor", "Mastelio keitimo žymeklis"), - ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), - ("Accept sessions via click", "Priimti seansus spustelėjus"), - ("Accept sessions via both", "Priimti seansus per abu"), - ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), - ("One-time Password", "Vienkartinis slaptažodis"), - ("Use one-time password", "Naudoti vienkartinį slaptažodį"), - ("One-time password length", "Vienkartinio slaptažodžio ilgis"), - ("Request access to your device", "Prašyti prieigos prie įrenginio"), - ("Hide connection management window", "Slėpti ryšio valdymo langą"), - ("hide_cm_tip", "hide_cm_tip"), - ("wayland_experiment_tip", "wayland_experiment_tip"), - ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), - ("Skipped", "Praleisti"), - ("Add to Address Book", "Pridėti prie adresų knygos"), - ("Group", "Grupė"), - ("Search", "Paieška"), - ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), - ("Local keyboard type", "Vietinės klaviatūros tipas"), - ("Select local keyboard type", "Pasirinkite vietinės klaviatūros tipą"), - ("software_render_tip", "software_render_tip"), - ("Always use software rendering", "Visada naudoti programinės įrangos atvaizdavimą"), - ("config_input", "config_input"), - ("config_microphone", "config_microphone"), - ("request_elevation_tip", "request_elevation_tip"), - ("Wait", "Laukti"), - ("Elevation Error", "Aukščio klaida"), - ("Ask the remote user for authentication", "Paklauskite nuotolinio vartotojo autentifikavimo"), - ("Choose this if the remote account is administrator", "Pasirinkite tai, jei nuotolinė paskyra yra administratorius"), - ("Transmit the username and password of administrator", "Persiųsti administratoriaus vartotojo vardą ir slaptažodį"), - ("still_click_uac_tip", "still_click_uac_tip"), - ("Request Elevation", "Prašyti paaukštinimo"), - ("wait_accept_uac_tip", "wait_accept_uac_tip"), - ("Elevate successfully", "Pakelti sėkmingai"), - ("uppercase", "didžiosios raidės"), - ("lowercase", "mažosios raidės"), - ("digit", "skaitmenų"), - ("special character", "specialusis simbolis"), - ("length>=8", "ilgis>=8"), - ("Weak", "Silpnas"), - ("Medium", "Vidutinis"), - ("Strong", "Stiprus"), - ("Switch Sides", "Perjungti puses"), - ("Please confirm if you want to share your desktop?", "Prašome patvirtinti, jei norite bendrinti darbalaukį?"), - ("Display", "Ekranas"), - ("Default View Style", "Numatytasis peržiūros stilius"), - ("Default Scroll Style", "Numatytasis slinkties stilius"), - ("Default Image Quality", "Numatytoji vaizdo kokybė"), - ("Default Codec", "Numatytasis kodekas"), - ("Bitrate", "Bitrate"), - ("FPS", "FPS"), - ("Auto", "Automatinis"), - ("Other Default Options", "Kitos numatytosios parinktys"), - ("Voice call", "Balso skambutis"), - ("Text chat", "Teksto pokalbis"), - ("Stop voice call", "Sustabdyti balso skambutį"), - ("relay_hint_tip", "relay_hint_tip"), - ("Reconnect", "Prisijungti iš naujo"), - ("Codec", "Kodekas"), - ("Resolution", "Rezoliucija"), - ("No transfers in progress", "Nevyksta jokių pervedimų"), - ("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"), - ("idd_driver_tip", "idd_driver_tip"), - ("confirm_idd_driver_tip", "confirm_idd_driver_tip"), - ("RDP Settings", "RDP nustatymai"), - ("Sort by", "Rūšiuoti pagal"), - ("New Connection", "Naujas ryšys"), - ("Restore", "Atkurti"), - ("Minimize", "Sumažinti"), - ("Maximize", "Padidinti"), - ("Your Device", "Jūsų įrenginys"), - ("empty_recent_tip", "tuščias_paskutinis_patarimas"), - ("empty_favorite_tip", "empty_favorite_tip"), - ("empty_lan_tip", "empty_lan_tip"), - ("empty_address_book_tip", "empty_address_book_tip"), - ("eg: admin", "pvz.: administratorius"), - ("Empty Username", "Tuščias naudotojo vardas"), - ("Empty Password", "Tuščias slaptažodis"), - ("Me", "Aš"), - ("identical_file_tip", "identical_file_tip"), - ("show_monitors_tip", "show_monitors_tip"), - ("View Mode", "Peržiūros režimas"), - ("login_linux_tip", "login_linux_tip"), - ("verify_rustdesk_password_tip", "verify_rustdesk_password_tip"), - ("remember_account_tip", "remember_account_tip"), - ("os_account_desk_tip", "os_account_desk_tip"), - ("OS Account", "OS paskyra"), - ("another_user_login_title_tip", "another_user_login_title_tip"), - ("another_user_login_text_tip", "antro_user_login_text_tip"), - ("xorg_not_found_title_tip", "xorg_not_found_title_tip"), - ("xorg_not_found_text_tip", "xorg_not_found_text_tip"), - ("no_desktop_title_tip", "no_desktop_title_tip"), - ("no_desktop_text_tip", "no_desktop_text_tip"), - ].iter().cloned().collect(); -} \ No newline at end of file From b120f4383c21cb8f6785403db4512e2ac370b366 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:23:39 +0300 Subject: [PATCH 210/366] Add files via upload --- src/lang/lt.rs | 495 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 495 insertions(+) create mode 100644 src/lang/lt.rs diff --git a/src/lang/lt.rs b/src/lang/lt.rs new file mode 100644 index 000000000..e21ff677d --- /dev/null +++ b/src/lang/lt.rs @@ -0,0 +1,495 @@ +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Būsena"), + ("Your Desktop", "Jūsų darbalaukis"), + ("desk_tip", "Jūsų darbalaukis pasiekiamas naudojant šį ID ir slaptažodį"), + ("Password", "Slaptažodis"), + ("Ready", "Pasiruošęs"), + ("Established", "Įsteigta"), + ("connecting_status", "Prisijungiama prie RustDesk tinklo..."), + ("Enable Service", "Įgalinti paslaugą"), + ("Start Service", "Pradėti paslaugą"), + ("Service is running", "Paslauga veikia"), + ("Service is not running", "Paslauga neveikia"), + ("not_ready_status", "Neprisijungęs. Patikrinkite ryšį."), + ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), + ("Transfer File", "Perkelti failą"), + ("Connect", "Prisijungti"), + ("Recent Sessions", "Naujausios sesijos"), + ("Address Book", "Adresų knyga"), + ("Confirmation", "Patvirtinimas"), + ("TCP Tunneling", "TCP tuneliavimas"), + ("Remove", "Pašalinti"), + ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), + ("Set your own password", "Nustatykite savo slaptažodį"), + ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), + ("Enable Clipboard", "Įgalinti iškarpinę"), + ("Enable File Transfer", "Įgalinti failų perdavimą"), + ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), + ("IP Whitelisting", "IP baltasis sąrašas"), + ("ID/Relay Server", "ID / perdavimo serveris"), + ("Import Server Config", "Importuoti serverio konfigūraciją"), + ("Export Server Config", "Eksportuoti serverio konfigūraciją"), + ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), + ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), + ("Invalid server configuration", "Netinkama serverio konfigūracija"), + ("Clipboard is empty", "Iškarpinė tuščia"), + ("Stop service", "Sustabdyti paslaugą"), + ("Change ID", "Keisti ID"), + ("Your new ID", "Jūsų naujasis ID"), + ("length %min% to %max%", "ilgis %min% iki %max%"), + ("starts with a letter", "prasideda raide"), + ("allowed characters", "leistini simboliai"), + ("id_change_tip", "Leidžiami tik simboliai a–z, A–Z, 0–9 ir _ (pabraukimas). Pirmoji raidė turi būti a-z, A-Z. Ilgis nuo 6 iki 16."), + ("Website", "Interneto svetainė"), + ("About", "Apie"), + ("Slogan_tip", "Sukurta su siela šiame beprotiškame pasaulyje!"), + ("Privacy Statement", "Privatumo pareiškimas"), + ("Mute", "Nutildyti"), + ("Build Date", "Sukūrimo data"), + ("Version", "Versija"), + ("Home", "Namai"), + ("Audio Input", "Garso įvestis"), + ("Enhancements", "Patobulinimai"), + ("Hardware Codec", "Aparatūros kodekas"), + ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), + ("ID Server", "ID serveris"), + ("Relay Server", "Perdavimo serveris"), + ("API Server", "API serveris"), + ("invalid_http", "Turi prasidėti http:// arba https://"), + ("Invalid IP", "Netinkamas IP"), + ("Invalid format", "Neteisingas formatas"), + ("server_not_support", "Serveris dar nepalaikomas"), + ("Not available", "Nepasiekiamas"), + ("Too frequent", "Per dažnai"), + ("Cancel", "Atšaukti"), + ("Skip", "Praleisti"), + ("Close", "Uždaryti"), + ("Retry", "Bandykite dar kartą"), + ("OK", "GERAI"), + ("Password Required", "Reikalingas slaptažodis"), + ("Please enter your password", "Prašome įvesti savo slaptažodį"), + ("Remember password", "Prisiminti slaptažodį"), + ("Wrong Password", "Neteisingas slaptažodis"), + ("Do you want to enter again?", "Ar norite įeiti dar kartą?"), + ("Connection Error", "Ryšio klaida"), + ("Error", "Klaida"), + ("Reset by the peer", "Atmetė nuotolinis kompiuteris"), + ("Connecting...", "Jungiamasi..."), + ("Connection in progress. Please wait.", "Jungiamasi. Palaukite."), + ("Please try 1 minute later", "Prašome pabandyti po 1 minutės"), + ("Login Error", "Prisijungimo klaida"), + ("Successful", "Sėkmingai"), + ("Connected, waiting for image...", "Prisijungta, laukiama vaizdo..."), + ("Name", "Vardas"), + ("Type", "Tipas"), + ("Modified", "Pakeista"), + ("Size", "Dydis"), + ("Show Hidden Files", "Rodyti paslėptus failus"), + ("Receive", "Gauti"), + ("Send", "Siųsti"), + ("Refresh File", "Atnaujinti failą"), + ("Local", "Vietinis"), + ("Remote", "Nuotolinis"), + ("Remote Computer", "Nuotolinis kompiuteris"), + ("Local Computer", "Vietinis kompiuteris"), + ("Confirm Delete", "Patvirtinti ištrynimą"), + ("Delete", "Ištrinti"), + ("Properties", "Ypatybės"), + ("Multi Select", "Keli pasirinkimas"), + ("Select All", "Pasirinkti viską"), + ("Unselect All", "Atšaukti visų pasirinkimą"), + ("Empty Directory", "Tuščias katalogas"), + ("Not an empty directory", "Ne tuščias katalogas"), + ("Are you sure you want to delete this file?", "Ar tikrai norite ištrinti šį failą?"), + ("Are you sure you want to delete this empty directory?", "Ar tikrai norite ištrinti šį tuščią katalogą?"), + ("Are you sure you want to delete the file of this directory?", "Ar tikrai norite ištrinti šio katalogo failą?"), + ("Do this for all conflicts", "Taikyti visiems konfliktams"), + ("This is irreversible!", "Tai negrįžtama!"), + ("Deleting", "Ištrinama"), + ("files", "failai"), + ("Waiting", "Laukiu"), + ("Finished", "Baigta"), + ("Speed", "Greitis"), + ("Custom Image Quality", "Tinkinta vaizdo kokybė"), + ("Privacy mode", "Privatumo režimas"), + ("Block user input", "Blokuoti naudotojo įvestį"), + ("Unblock user input", "Atblokuoti naudotojo įvestį"), + ("Adjust Window", "Koreguoti langą"), + ("Original", "Originalas"), + ("Shrink", "Susitraukti"), + ("Stretch", "Ištempti"), + ("Scrollbar", "Slinkties juosta"), + ("ScrollAuto", "Automatinis slinkimas"), + ("Good image quality", "Gera vaizdo kokybė"), + ("Balanced", "Subalansuotas"), + ("Optimize reaction time", "Optimizuoti reakcijos laiką"), + ("Custom", "Tinkintas"), + ("Show remote cursor", "Rodyti nuotolinį žymeklį"), + ("Show quality monitor", "Rodyti kokybės monitorių"), + ("Disable clipboard", "Išjungti mainų sritį"), + ("Lock after session end", "Užrakinti pasibaigus seansui"), + ("Insert", "Įdėti"), + ("Insert Lock", "Įterpti užraktą"), + ("Refresh", "Atnaujinti"), + ("ID does not exist", "ID neegzistuoja"), + ("Failed to connect to rendezvous server", "Nepavyko prisijungti prie susitikimo serverio"), + ("Please try later", "Prašome pabandyti vėliau"), + ("Remote desktop is offline", "Nuotolinis darbalaukis neprisijungęs"), + ("Key mismatch", "Raktų neatitikimas"), + ("Timeout", "Laikas baigėsi"), + ("Failed to connect to relay server", "Nepavyko prisijungti prie perdavimo serverio"), + ("Failed to connect via rendezvous server", "Nepavyko prisijungti per susitikimo serverį"), + ("Failed to connect via relay server", "Nepavyko prisijungti per perdavimo serverį"), + ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), + ("Set Password", "Nustatyti slaptažodį"), + ("OS Password", "OS slaptažodis"), + ("install_tip", "Kai kuriais atvejais UAC gali priversti RustDesk netinkamai veikti nuotoliniame pagrindiniame kompiuteryje. Norėdami išvengti UAC, spustelėkite toliau esantį mygtuką, kad įdiegtumėte RustDesk savo sistemoje."), + ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), + ("Click to download", "Spustelėkite norėdami atsisiųsti"), + ("Click to update", "Spustelėkite norėdami atnaujinti"), + ("Configure", "Konfigūruoti"), + ("config_acc", "Norėdami nuotoliniu būdu valdyti darbalaukį, turite suteikti RustDesk \"prieigos\" leidimus"), + ("config_screen", "Norėdami nuotoliniu būdu pasiekti darbalaukį, turite suteikti RustDesk leidimus \"ekrano kopija\""), + ("Installing ...", "Diegiama ..."), + ("Install", "Diegti"), + ("Installation", "Įdiegimas"), + ("Installation Path", "Įdiegimo kelias"), + ("Create start menu shortcuts", "Sukurti pradžios meniu sparčiuosius klavišus"), + ("Create desktop icon", "Sukurti darbalaukio piktogramą"), + ("agreement_tip", "Pradėdami diegimą sutinkate su licencijos sutarties sąlygomis"), + ("Accept and Install", "Priimti ir įdiegti"), + ("End-user license agreement", "Galutinio vartotojo licencijos sutartis"), + ("Generating ...", "Generuojamas..."), + ("Your installation is lower version.", "Jūsų įdiegta versija senesnė."), + ("not_close_tcp_tip", "Naudodami tunelį neuždarykite šio lango"), + ("Listening ...", "Laukimas..."), + ("Remote Host", "Nuotolinis pagrindinis kompiuteris"), + ("Remote Port", "Nuotolinis prievadas"), + ("Action", "Veiksmas"), + ("Add", "Papildyti"), + ("Local Port", "Vietinis prievadas"), + ("Local Address", "Vietinis adresas"), + ("Change Local Port", "Keisti vietinį prievadą"), + ("setup_server_tip", "Kad ryšys būtų greitesnis, nustatykite savo serverį"), + ("Too short, at least 6 characters.", "Per trumpas, mažiausiai 6 simboliai."), + ("The confirmation is not identical.", "Patvirtinimas nėra tapatus."), + ("Permissions", "Leidimai"), + ("Accept", "Priimti"), + ("Dismiss", "Atmesti"), + ("Disconnect", "Atjungti"), + ("Allow using keyboard and mouse", "Leisti naudojant klaviatūrą ir pelę"), + ("Allow using clipboard", "Leisti naudoti mainų sritį"), + ("Allow hearing sound", "Leisti girdėti garsą"), + ("Allow file copy and paste", "Leisti failą kopijuoti ir įklijuoti"), + ("Connected", "Prisijungta"), + ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), + ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), + ("Direct and unencrypted connection", "Tiesioginis ir nešifruotas ryšys"), + ("Relayed and unencrypted connection", "Perduotas ir nešifruotas ryšys"), + ("Enter Remote ID", "Įveskite nuotolinio ID"), + ("Enter your password", "Įveskite savo slaptažodį"), + ("Logging in...", "Prisijungiama..."), + ("Enable RDP session sharing", "Įgalinti RDP seansų bendrinimą"), + ("Auto Login", "Automatinis prisijungimas"), + ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), + ("Rename", "Pervardyti"), + ("Space", "Erdvė"), + ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), + ("Change Path", "Keisti kelią"), + ("Create Folder", "Sukurti aplanką"), + ("Please enter the folder name", "Įveskite aplanko pavadinimą"), + ("Fix it", "Pataisyk tai"), + ("Warning", "Įspėjimas"), + ("Login screen using Wayland is not supported", "Prisijungimo ekranas naudojant Wayland nepalaikomas"), + ("Reboot required", "Reikia paleisti iš naujo"), + ("Unsupported display server", "Nepalaikomas rodymo serveris"), + ("x11 expected", "reikalingas x11"), + ("Port", "Prievadas"), + ("Settings", "Nustatymai"), + ("Username", "Vartotojo vardas"), + ("Invalid port", "Netinkamas prievadas"), + ("Closed manually by the peer", "Peer uždarė rankiniu būdu"), + ("Enable remote configuration modification", "Įgalinti nuotolinį konfigūracijos modifikavimą"), + ("Run without install", "Vykdyti be diegimo"), + ("Connect via relay", "Prisijungti per relę"), + ("Always connect via relay", "Visada prisijunkite per relę"), + ("whitelist_tip", "Mane gali pasiekti tik baltajame sąraše esantys IP adresai"), + ("Login", "Prisijungti"), + ("Verify", "Patvirtinti"), + ("Remember me", "Prisimink mane"), + ("Trust this device", "Pasitikėk šiuo įrenginiu"), + ("Verification code", "Patvirtinimo kodas"), + ("verification_tip", "Aptiktas naujas įrenginys ir registruotu el. pašto adresu išsiųstas patvirtinimo kodas. Įveskite jį norėdami tęsti prisijungimą."), + ("Logout", "Atsijungti"), + ("Tags", "Žymos"), + ("Search ID", "Paieškos ID"), + ("whitelist_sep", "Atskirti kableliu, kabliataškiu, tarpu arba nauja eilute"), + ("Add ID", "Pridėti ID"), + ("Add Tag", "Pridėti žymą"), + ("Unselect all tags", "Atšaukti visų žymų pasirinkimą"), + ("Network error", "Tinklo klaida"), + ("Username missed", "Prarastas vartotojo vardas"), + ("Password missed", "Slaptažodis praleistas"), + ("Wrong credentials", "Klaidingi kredencialai"), + ("Edit Tag", "Redaguoti žymą"), + ("Unremember Password", "Neprisiminti slaptažodžio"), + ("Favorites", "Mėgstamiausi"), + ("Add to Favorites", "Įtraukti į parankinius"), + ("Remove from Favorites", "Pašalinti iš parankinių"), + ("Empty", "Tuščia"), + ("Invalid folder name", "Neteisingas aplanko pavadinimas"), + ("Socks5 Proxy", "Socks5 Proxy"), + ("Hostname", "Pagrindinio kompiuterio pavadinimas"), + ("Discovered", "Atrasta"), + ("install_daemon_tip", "Norėdami, kad RustDesk startuotų automatiškai, turite ją įdiegti"), + ("Remote ID", "Nuotolinis ID"), + ("Paste", "Įklijuoti"), + ("Paste here?", "Įklijuoti čia?"), + ("Are you sure to close the connection?", "Ar tikrai norite atsijungti?"), + ("Download new version", "Atsisiųsti naują versiją"), + ("Touch mode", "Palietimo režimas"), + ("Mouse mode", "Pelės režimas"), + ("One-Finger Tap", "Palietimas vienu pirštu"), + ("Left Mouse", "Kairysis pelės kl."), + ("One-Long Tap", "Vienas palietimas"), + ("Two-Finger Tap", "Palietimas dviem pirštais"), + ("Right Mouse", "Dešinysis pelės kl."), + ("One-Finger Move", "Vieno piršto judesys"), + ("Double Tap & Move", "Dukart palieskite ir perkelkite"), + ("Mouse Drag", "Pelės vilkimas"), + ("Three-Finger vertically", "Trys pirštai vertikaliai"), + ("Mouse Wheel", "Pelės ratukas"), + ("Two-Finger Move", "Dviejų pirštų judesys"), + ("Canvas Move", "Drobės perkėlimas"), + ("Pinch to Zoom", "Suimkite, kad padidintumėte"), + ("Canvas Zoom", "Drobės mastelis"), + ("Reset canvas", "Atstatyti drobę"), + ("No permission of file transfer", "Nėra leidimo perkelti failus"), + ("Note", "Pastaba"), + ("Connection", "Ryšys"), + ("Share Screen", "Bendrinti ekraną"), + ("Chat", "Pokalbis"), + ("Total", "Iš viso"), + ("items", "elementai"), + ("Selected", "Pasirinkta"), + ("Screen Capture", "Ekrano nuotrauka"), + ("Input Control", "Įvesties valdymas"), + ("Audio Capture", "Garso fiksavimas"), + ("File Connection", "Failo ryšys"), + ("Screen Connection", "Ekrano jungtis"), + ("Do you accept?", "Ar sutinki?"), + ("Open System Setting", "Atviros sistemos nustatymas"), + ("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"), + ("android_input_permission_tip1", "Kad nuotolinis įrenginys galėtų valdyti Android įrenginį pele arba liesti, turite leisti RustDesk naudoti \"Prieinamumo\" paslaugą."), + ("android_input_permission_tip2", "Eikite į kitą sistemos nustatymų puslapį, suraskite \"Įdiegtos paslaugos\" ir įgalinkite \"RustDesk įvestis\" paslaugą."), + ("android_new_connection_tip", "Gauta nauja užklausa tvarkyti dabartinį įrenginį."), + ("android_service_will_start_tip", "Įgalinus ekrano fiksavimo paslaugą, kiti įrenginiai gali pateikti užklausą prisijungti prie to įrenginio."), + ("android_stop_service_tip", "Uždarius paslaugą automatiškai bus uždaryti visi užmegzti ryšiai."), + ("android_version_audio_tip", "Dabartinė Android versija nepalaiko garso įrašymo, atnaujinkite į Android 10 ar naujesnę versiją."), + ("android_start_service_tip", "Spustelėkite [Paleisti paslaugą] arba įjunkite [Fiksuoti ekraną], kad paleistumėte ekrano bendrinimo paslaugą."), + ("android_permission_may_not_change_tip", "Užmegztų ryšių leidimų keisti negalima, reikia prisijungti iš naujo."), + ("Account", "Paskyra"), + ("Overwrite", "Perrašyti"), + ("This file exists, skip or overwrite this file?", "Šis failas egzistuoja, praleisti arba perrašyti šį failą?"), + ("Quit", "Išeiti"), + ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/"), + ("Help", "Pagalba"), + ("Failed", "Nepavyko"), + ("Succeeded", "Pavyko"), + ("Someone turns on privacy mode, exit", "Kažkas įjungė privatumo režimą, išeiti"), + ("Unsupported", "Nepalaikomas"), + ("Peer denied", "Atšaukė"), + ("Please install plugins", "Įdiekite papildinius"), + ("Peer exit", "Nuotolinis mazgas neveikia"), + ("Failed to turn off", "Nepavyko išjungti"), + ("Turned off", "Išjungti"), + ("In privacy mode", "Privatumo režimas"), + ("Out privacy mode", "Išėjimas iš privatumo režimo"), + ("Language", "Kalba"), + ("Keep RustDesk background service", "Palikti RustDesk fonine paslauga"), + ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), + ("android_open_battery_optimizations_tip", "Eikite į kitą nustatymų puslapį"), + ("Start on Boot", "Pradėti paleidžiant"), + ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), + ("Connection not allowed", "Ryšys neleidžiamas"), + ("Legacy mode", "Senasis režimas"), + ("Map mode", "Žemėlapio režimas"), + ("Translate mode", "Vertimo režimas"), + ("Use permanent password", "Naudoti nuolatinį slaptažodį"), + ("Use both passwords", "Naudoti abu slaptažodžius"), + ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), + ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), + ("Allow remote restart", "Leisti nuotolinį paleidimą iš naujo"), + ("Restart Remote Device", "Paleisti nuotolinį įrenginį iš naujo"), + ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo"), + ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), + ("remote_restarting_tip", "Nuotolinis įrenginys paleidžiamas iš naujo. Uždarykite šį pranešimą ir po kurio laiko vėl prisijunkite naudodami nuolatinį slaptažodį."), + ("Copied", "Nukopijuota"), + ("Exit Fullscreen", "Išeiti iš pilno ekrano"), + ("Fullscreen", "Per visą ekraną"), + ("Mobile Actions", "Veiksmai mobiliesiems"), + ("Select Monitor", "Pasirinkite monitorių"), + ("Control Actions", "Valdymo veiksmai"), + ("Display Settings", "Ekrano nustatymai"), + ("Ratio", "Santykis"), + ("Image Quality", "Vaizdo kokybė"), + ("Scroll Style", "Slinkimo stilius"), + ("Show Menubar", "Rodyti meniu juostą"), + ("Hide Menubar", "Slėpti meniu juostą"), + ("Direct Connection", "Tiesioginis ryšys"), + ("Relay Connection", "Tarpinė jungtis"), + ("Secure Connection", "Saugus ryšys"), + ("Insecure Connection", "Nesaugus ryšys"), + ("Scale original", "Pakeisti originalų mastelį"), + ("Scale adaptive", "Pritaikomas mastelis"), + ("General", "Bendra"), + ("Security", "Sauga"), + ("Theme", "Tema"), + ("Dark Theme", "Tamsioji tema"), + ("Light Theme", "Šviesi tema"), + ("Dark", "Tamsiai"), + ("Light", "Šviesiai"), + ("Follow System", "Sekti sistemą"), + ("Enable hardware codec", "Įgalinti aparatūrinį kodavimą"), + ("Unlock Security Settings", "Atrakinti saugos nustatymus"), + ("Enable Audio", "Įgalinti garsą"), + ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), + ("Server", "Serveris"), + ("Direct IP Access", "Tiesioginė IP prieiga"), + ("Proxy", "Įgaliotinis serveris"), + ("Apply", "Taikyti"), + ("Disconnect all devices?", "Atjungti visus įrenginius?"), + ("Clear", "Išvalyti"), + ("Audio Input Device", "Garso įvesties įrenginys"), + ("Deny remote access", "Uždrausti nuotolinę prieigą"), + ("Use IP Whitelisting", "Naudoti IP baltąjį sąrašą"), + ("Network", "Tinklas"), + ("Enable RDP", "Įgalinti RDP"), + ("Pin menubar", "Prisegti meniu juostą"), + ("Unpin menubar", "Atsegti meniu juostą"), + ("Recording", "Įrašymas"), + ("Directory", "Katalogas"), + ("Automatically record incoming sessions", "Automatiškai įrašyti įeinančius seansus"), + ("Change", "Keisti"), + ("Start session recording", "Pradėti seanso įrašymą"), + ("Stop session recording", "Sustabdyti seanso įrašymą"), + ("Enable Recording Session", "Įgalinti įrašymo seansą"), + ("Allow recording session", "Leisti įrašymo sesiją"), + ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), + ("Deny LAN Discovery", "Neleisti LAN aptikimo"), + ("Write a message", "Rašyti žinutę"), + ("Prompt", "Užuomina"), + ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), + ("elevated_foreground_window_tip", "Dabartinis nuotolinio darbalaukio langas reikalauja didesnių privilegijų, todėl laikinai neįmanoma naudoti pelės ir klaviatūros. Galite paprašyti nuotolinio vartotojo sumažinti dabartinį langą arba spustelėti aukščio mygtuką ryšio valdymo lange. Norint išvengti šios problemos ateityje, rekomenduojama programinę įrangą įdiegti nuotoliniame įrenginyje."), + ("Disconnected", "Atjungtas"), + ("Other", "Kita"), + ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), + ("Keyboard Settings", "Klaviatūros nustatymai"), + ("Full Access", "Pilna prieiga"), + ("Screen Share", "Ekrano bendrinimas"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland reikalauja Ubuntu 21.04 arba naujesnės versijos."), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland reikalinga naujesnės Linux Distro versijos. Išbandykite X11 darbalaukį arba pakeiskite OS."), + ("JumpLink", "Peržiūra"), + ("Please Select the screen to be shared(Operate on the peer side).", "Prašome pasirinkti ekraną, kurį norite bendrinti (veikiantį kitoje pusėje)."), + ("Show RustDesk", "Rodyti RustDesk"), + ("This PC", "Šis kompiuteris"), + ("or", "arba"), + ("Continue with", "Tęsti su"), + ("Elevate", "Pakelti"), + ("Zoom cursor", "Mastelio keitimo žymeklis"), + ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), + ("Accept sessions via click", "Priimti seansus spustelėjus"), + ("Accept sessions via both", "Priimti seansus per abu"), + ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), + ("One-time Password", "Vienkartinis slaptažodis"), + ("Use one-time password", "Naudoti vienkartinį slaptažodį"), + ("One-time password length", "Vienkartinio slaptažodžio ilgis"), + ("Request access to your device", "Prašyti prieigos prie įrenginio"), + ("Hide connection management window", "Slėpti ryšio valdymo langą"), + ("hide_cm_tip", "Leisti paslėpti didžiąją ir mažąją raidę, jei priimamos slaptažodžio sesijos arba naudojamas nuolatinis slaptažodis"), + ("wayland_experiment_tip", "Wayland palaikymas yra eksperimentinis, naudokite X11, jei jums reikalingas automatinis prisijungimas."), + ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), + ("Skipped", "Praleisti"), + ("Add to Address Book", "Pridėti prie adresų knygos"), + ("Group", "Grupė"), + ("Search", "Paieška"), + ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), + ("Local keyboard type", "Vietinės klaviatūros tipas"), + ("Select local keyboard type", "Pasirinkite vietinės klaviatūros tipą"), + ("software_render_tip", "Jei turite Nvidia vaizdo plokštę ir nuotolinis langas iškart užsidaro prisijungus, gali padėti „Nouveau“ tvarkyklės įdiegimas ir programinės įrangos atvaizdavimo pasirinkimas. Būtina paleisti iš naujo."), + ("Always use software rendering", "Visada naudoti programinį spartintuvą"), + ("config_input", "Norėdami valdyti nuotolinį darbalaukį naudodami klaviatūrą, turite suteikti RustDesk leidimus \"Įvesties monitoringas\"."), + ("config_microphone", "Norėdami kalbėtis su nuotoline puse, turite suteikti RustDesk leidimą \"Įrašyti garsą\"."), + ("request_elevation_tip", "Taip pat galite prašyti tesių suteikimo, jeigu kas nors yra nuotolinėje pusėje."), + ("Wait", "Laukti"), + ("Elevation Error", "Teisių suteikimo klaida"), + ("Ask the remote user for authentication", "Klauskite nuotolinio vartotojo autentifikavimo"), + ("Choose this if the remote account is administrator", "Pasirinkite tai, jei nuotolinė paskyra yra administratorius"), + ("Transmit the username and password of administrator", "Persiųsti administratoriaus vartotojo vardą ir slaptažodį"), + ("still_click_uac_tip", "Vis tiek reikia, kad nuotolinis vartotojas paleidžiant RustDesk UAC lange paspaustų \"OK\"."), + ("Request Elevation", "Prašyti teisių"), + ("wait_accept_uac_tip", "Palaukite, kol nuotolinis vartotojas patvirtins UAC užklausą."), + ("Elevate successfully", "Teisės suteiktos"), + ("uppercase", "didžiosios raidės"), + ("lowercase", "mažosios raidės"), + ("digit", "skaitmuo"), + ("special character", "specialusis simbolis"), + ("length>=8", "ilgis>=8"), + ("Weak", "Silpnas"), + ("Medium", "Vidutinis"), + ("Strong", "Stiprus"), + ("Switch Sides", "Perjungti puses"), + ("Please confirm if you want to share your desktop?", "Prašome patvirtinti, jeigu norite bendrinti darbalaukį?"), + ("Display", "Ekranas"), + ("Default View Style", "Numatytasis peržiūros stilius"), + ("Default Scroll Style", "Numatytasis slinkties stilius"), + ("Default Image Quality", "Numatytoji vaizdo kokybė"), + ("Default Codec", "Numatytasis kodekas"), + ("Bitrate", "Sparta"), + ("FPS", "FPS"), + ("Auto", "Automatinis"), + ("Other Default Options", "Kitos numatytosios parinktys"), + ("Voice call", "Balso skambutis"), + ("Text chat", "Tekstinis pokalbis"), + ("Stop voice call", "Sustabdyti balso skambutį"), + ("relay_hint_tip", "Tiesioginis ryšys gali būti neįmanomas. Tokiu atveju galite pabandyti prisijungti per perdavimo serverį. \nArba, jei norite iš karto naudoti perdavimo serverį, prie ID galite pridėti priesagą \"/r\" arba nuotolinio pagrindinio kompiuterio nustatymuose įgalinti \"Visada prisijungti per relę\"."), + ("Reconnect", "Prisijungti iš naujo"), + ("Codec", "Kodekas"), + ("Resolution", "Rezoliucija"), + ("No transfers in progress", "Nevyksta jokių perdavimų"), + ("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"), + ("idd_driver_tip", "Įdiekite virtualaus ekrano tvarkyklę (naudojama, kai nėra fizinių ekranų)"), + ("confirm_idd_driver_tip", "Įjungta virtualaus ekrano tvarkyklės diegimo funkcija. Atminkite, kad bus įdiegtas bandomasis sertifikatas, kad būtų galima pasitikėti tvarkykle. Šis sertifikatas bus naudojamas tik pasitikėjimui Rustdesk tvarkyklėmis patikrinti."), + ("RDP Settings", "RDP nustatymai"), + ("Sort by", "Rūšiuoti pagal"), + ("New Connection", "Naujas ryšys"), + ("Restore", "Atkurti"), + ("Minimize", "Sumažinti"), + ("Maximize", "Padidinti"), + ("Your Device", "Jūsų įrenginys"), + ("empty_recent_tip", "Nėra paskutinių seansų!\nLaikas suplanuoti naują."), + ("empty_favorite_tip", "Dar neturite parankinių nuotolinių seansų."), + ("empty_lan_tip", "Nuotolinių mazgų nerasta."), + ("empty_address_book_tip", "Adresų knygelėje nėra nuotolinių kompiuterių."), + ("eg: admin", "pvz.: administratorius"), + ("Empty Username", "Tuščias naudotojo vardas"), + ("Empty Password", "Tuščias slaptažodis"), + ("Me", "Aš"), + ("identical_file_tip", "Failas yra identiškas nuotoliniame kompiuteryje esančiam failui."), + ("show_monitors_tip", "Rodyti monitorius įrankių juostoje"), + ("View Mode", "Peržiūros režimas"), + ("login_linux_tip", "Norėdami įjungti X darbalaukio seansą, turite būti prisijungę prie nuotolinės Linux paskyros."), + ("verify_rustdesk_password_tip", "Patvirtinkite RustDesk slaptažodį"), + ("remember_account_tip", "Prisiminti šią paskyrą"), + ("os_account_desk_tip", "Ši paskyra naudojama norint prisijungti prie nuotolinės OS ir įgalinti darbalaukio seansą režimu headless"), + ("OS Account", "OS paskyra"), + ("another_user_login_title_tip", "Kitas vartotojas jau yra prisijungęs"), + ("another_user_login_text_tip", "Atjungti"), + ("xorg_not_found_title_tip", "Xorg nerastas"), + ("xorg_not_found_text_tip", "Prašom įdiegti Xorg"), + ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), + ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), + ].iter().cloned().collect(); +} \ No newline at end of file From 6c78e7bf7427223abdbd91585704af2f4a7cec8d Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 4 Apr 2023 09:51:41 +0800 Subject: [PATCH 211/366] fix mobile show chat window Signed-off-by: fufesou --- flutter/lib/common/widgets/overlay.dart | 9 +++++++++ flutter/lib/desktop/pages/remote_page.dart | 7 +------ flutter/lib/mobile/pages/remote_page.dart | 4 +++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index c67f0f7fb..8c44e7ee4 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -388,6 +388,15 @@ class BlockableOverlayState extends OverlayKeyState { _middleBlocked.value = blocked; } } + + void applyFfi(FFI ffi) { + ffi.dialogManager.setOverlayState(this); + ffi.chatModel.setOverlayState(this); + // make remote page penetrable automatically, effective for chat over remote + onMiddleBlockedClick = () { + setMiddleBlocked(false); + }; + } } class BlockableOverlay extends StatelessWidget { diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 3bc450345..312f9bbed 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -158,12 +158,7 @@ class _RemotePageState extends State // _isCustomCursorInited = true; // } - _ffi.dialogManager.setOverlayState(_blockableOverlayState); - _ffi.chatModel.setOverlayState(_blockableOverlayState); - // make remote page penetrable automatically, effective for chat over remote - _blockableOverlayState.onMiddleBlockedClick = () { - _blockableOverlayState.setMiddleBlocked(false); - }; + _blockableOverlayState.applyFfi(_ffi); } @override diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 776aa5455..04852c98c 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -20,7 +20,6 @@ import '../../models/input_model.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; import '../../utils/image.dart'; -import '../widgets/dialog.dart'; import '../widgets/gestures.dart'; final initText = '\1' * 1024; @@ -43,6 +42,8 @@ class _RemotePageState extends State { double _mouseScrollIntegral = 0; // mouse scroll speed controller Orientation? _currentOrientation; + final _blockableOverlayState = BlockableOverlayState(); + final keyboardVisibilityController = KeyboardVisibilityController(); late final StreamSubscription keyboardSubscription; final FocusNode _mobileFocusNode = FocusNode(); @@ -67,6 +68,7 @@ class _RemotePageState extends State { gFFI.qualityMonitorModel.checkShowQualityMonitor(widget.id); keyboardSubscription = keyboardVisibilityController.onChange.listen(onSoftKeyboardChanged); + _blockableOverlayState.applyFfi(gFFI); } @override From f083c0f9fe16dc62cfc889a63755648dcd2b54ff Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 3 Apr 2023 20:35:09 +0800 Subject: [PATCH 212/366] opt: simplify release names --- .github/workflows/flutter-build.yml | 66 +++++++++++++---------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index f94e2dc9c..5e230f20f 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -34,7 +34,7 @@ jobs: 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 } + - { target: x86_64-pc-windows-msvc, os: windows-2019, arch: x86_64 } # - { target: aarch64-pc-windows-msvc, os: windows-2019, arch: aarch64 } steps: - name: Checkout source code @@ -107,14 +107,7 @@ jobs: 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-${{ env.VERSION }}-${{ matrix.job.target }}.exe - - # - name: Rename rustdesk - # shell: bash - # run: | - # for name in rustdesk*??-install.exe; do - # mv "$name" ./SignOutput/"${name%%-install.exe}-${{ matrix.job.target }}.exe" - # done + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.exe - name: Sign rustdesk self-extracted file uses: GermanBluefox/code-sign-action@v7 @@ -148,7 +141,7 @@ jobs: job: # - { target: i686-pc-windows-msvc , os: windows-2019 } # - { target: x86_64-pc-windows-gnu , os: windows-2019 } - - { target: i686-pc-windows-msvc, os: windows-2019 } + - { target: i686-pc-windows-msvc, os: windows-2019, arch: x86 } # - { target: aarch64-pc-windows-msvc, os: windows-2019 } steps: - name: Checkout source code @@ -218,7 +211,7 @@ jobs: python3 ./generate.py -f ../../Release/ -o . -e ../../Release/rustdesk.exe popd mkdir -p ./SignOutput - mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-sciter.exe + mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.exe - name: Sign rustdesk self-extracted file uses: GermanBluefox/code-sign-action@v7 @@ -250,6 +243,7 @@ jobs: target: x86_64-apple-darwin, os: macos-latest, extra-build-args: "", + arch: x86_64 } steps: - name: Checkout source code @@ -358,7 +352,7 @@ jobs: - name: Rename rustdesk run: | for name in rustdesk*??.dmg; do - mv "$name" "${name%%.dmg}-${{ matrix.job.target }}.dmg" + mv "$name" "${name%%.dmg}-${{ matrix.job.arch }}.dmg" done - name: Publish DMG package @@ -368,7 +362,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk*-${{ matrix.job.target }}.dmg + rustdesk*-${{ matrix.job.arch }}.dmg build-vcpkg-deps-linux: uses: ./.github/workflows/vcpkg-deps-linux.yml @@ -385,14 +379,14 @@ jobs: matrix: job: - { - arch: x86_64, + arch: aarch64, target: aarch64-linux-android, os: ubuntu-20.04, extra-build-features: "", openssl-arch: android-arm64 } - { - arch: x86_64, + arch: armv7, target: armv7-linux-androideabi, os: ubuntu-20.04, extra-build-features: "", @@ -481,7 +475,7 @@ jobs: # build flutter pushd flutter flutter build apk --release --target-platform android-arm64 --split-per-abi - mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ;; armv7-linux-androideabi) mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a @@ -490,12 +484,12 @@ jobs: # build flutter pushd flutter flutter build apk --release --target-platform android-arm --split-per-abi - mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk ;; esac popd mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk . + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk . - uses: r0adkll/sign-android-release@v1 name: Sign app APK @@ -515,7 +509,7 @@ jobs: if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' uses: actions/upload-artifact@master with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release-signed.apk + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk path: ${{steps.sign-rustdesk.outputs.signedReleaseFile}} - name: Publish signed apk package @@ -534,7 +528,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-release.apk + signed-apk/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk build-rustdesk-lib-linux-amd64: needs: [generate-bridge-linux, build-vcpkg-deps-linux] @@ -986,7 +980,7 @@ jobs: run: | for name in rustdesk*??.deb; do # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb" + cp "$name" "${name%%.deb}--${{ matrix.job.arch }}-sciter.deb" done - name: Publish debian package @@ -996,14 +990,14 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb - name: Upload Artifact uses: actions/upload-artifact@master if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}-sciter.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}-sciter.deb build-rustdesk-linux-arm: needs: [build-rustdesk-lib-linux-arm] @@ -1159,7 +1153,7 @@ jobs: shell: bash run: | for name in rustdesk*??.deb; do - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + cp "$name" "${name%%.deb}-${{ matrix.job.arch }}.deb" done - name: Publish debian package @@ -1169,7 +1163,7 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - name: Build appimage package if: ${{ matrix.job.extra-build-features == 'appimage' }} @@ -1198,8 +1192,8 @@ jobs: uses: actions/upload-artifact@master if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - name: Patch archlinux PKGBUILD if: ${{ matrix.job.extra-build-features == '' }} @@ -1379,7 +1373,7 @@ jobs: run: | for name in rustdesk*??.deb; do # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb" + cp "$name" "${name%%.deb}-${{ matrix.job.arch }}.deb" done - name: Publish debian package @@ -1389,14 +1383,14 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - name: Upload Artifact uses: actions/upload-artifact@master if: ${{ contains(matrix.job.extra-build-features, 'flatpak') }} with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb - path: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb + path: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb - name: Patch archlinux PKGBUILD if: ${{ matrix.job.extra-build-features == '' }} @@ -1559,12 +1553,12 @@ jobs: - name: Download Binary uses: actions/download-artifact@master with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb path: . - name: Rename Binary run: | - mv rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}-${{ matrix.job.os }}.deb rustdesk-${{ env.VERSION }}.deb + mv rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.deb rustdesk-${{ env.VERSION }}.deb - uses: Kingtous/run-on-arch-action@amd64-support name: Build rustdesk flatpak package for ${{ matrix.job.arch }} @@ -1596,7 +1590,7 @@ jobs: pushd flatpak git clone https://github.com/flathub/shared-modules.git --depth=1 flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json - flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak com.rustdesk.RustDesk + flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.flatpak com.rustdesk.RustDesk - name: Publish flatpak package uses: softprops/action-gh-release@v1 @@ -1605,4 +1599,4 @@ jobs: prerelease: true tag_name: ${{ env.TAG_NAME }} files: | - flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak + flatpak/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.flatpak From f56adbb56e9edbe419409ec94a09bc9a0356ff41 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 4 Apr 2023 18:35:01 +0800 Subject: [PATCH 213/366] fix wayland map mode Signed-off-by: fufesou --- flutter/lib/models/input_model.dart | 33 +++++++++++++---------------- src/flutter_ffi.rs | 6 +++--- src/ui_session_interface.rs | 16 +++++++------- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 95f0f2634..f18f63647 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -117,44 +117,41 @@ class InputModel { } void mapKeyboardMode(RawKeyEvent e) { - int scanCode; - int keyCode; + int positionCode = -1; + int platformCode = -1; bool down; if (e.data is RawKeyEventDataMacOs) { RawKeyEventDataMacOs newData = e.data as RawKeyEventDataMacOs; - scanCode = newData.keyCode; - keyCode = newData.keyCode; + positionCode = newData.keyCode; + platformCode = newData.keyCode; } else if (e.data is RawKeyEventDataWindows) { RawKeyEventDataWindows newData = e.data as RawKeyEventDataWindows; - scanCode = newData.scanCode; - keyCode = newData.keyCode; + positionCode = newData.scanCode; + platformCode = newData.keyCode; } else if (e.data is RawKeyEventDataLinux) { RawKeyEventDataLinux newData = e.data as RawKeyEventDataLinux; // scanCode and keyCode of RawKeyEventDataLinux are incorrect. // 1. scanCode means keycode // 2. keyCode means keysym - scanCode = 0; - keyCode = newData.scanCode; + positionCode = newData.scanCode; + platformCode = newData.keyCode; } else if (e.data is RawKeyEventDataAndroid) { RawKeyEventDataAndroid newData = e.data as RawKeyEventDataAndroid; - scanCode = newData.scanCode + 8; - keyCode = newData.keyCode; - } else { - scanCode = -1; - keyCode = -1; - } + positionCode = newData.scanCode + 8; + platformCode = newData.keyCode; + } else {} if (e is RawKeyDownEvent) { down = true; } else { down = false; } - inputRawKey(e.character ?? '', keyCode, scanCode, down); + inputRawKey(e.character ?? '', platformCode, positionCode, down); } /// Send raw Key Event - void inputRawKey(String name, int keyCode, int scanCode, bool down) { + void inputRawKey(String name, int platformCode, int positionCode, bool down) { const capslock = 1; const numlock = 2; const scrolllock = 3; @@ -174,8 +171,8 @@ class InputModel { bind.sessionHandleFlutterKeyEvent( id: id, name: name, - keycode: keyCode, - scancode: scanCode, + platformCode: platformCode, + positionCode: positionCode, lockModes: lockModes, downOrUp: down); } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 6d4e468ab..a8fe66c8a 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -332,13 +332,13 @@ pub fn session_switch_display(id: String, value: i32) { pub fn session_handle_flutter_key_event( id: String, name: String, - keycode: i32, - scancode: i32, + platform_code: i32, + position_code: i32, lock_modes: i32, down_or_up: bool, ) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.handle_flutter_key_event(&name, keycode, scancode, lock_modes, down_or_up); + session.handle_flutter_key_event(&name, platform_code, position_code, lock_modes, down_or_up); } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 796e13246..f7a4a7334 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -529,19 +529,19 @@ impl Session { pub fn handle_flutter_key_event( &self, _name: &str, - keycode: i32, - scancode: i32, + platform_code: i32, + position_code: i32, lock_modes: i32, down_or_up: bool, ) { - if scancode < 0 || keycode < 0 { + if position_code < 0 || platform_code < 0 { return; } - let keycode: KeyCode = keycode as _; - let scancode: u32 = scancode as _; + let platform_code: KeyCode = platform_code as _; + let position_code: u32 = position_code as _; #[cfg(not(target_os = "windows"))] - let key = rdev::key_from_code(keycode) as rdev::Key; + let key = rdev::key_from_code(position_code) as rdev::Key; // Windows requires special handling #[cfg(target_os = "windows")] let key = rdev::get_win_key(keycode, scancode); @@ -554,8 +554,8 @@ impl Session { let event = Event { time: SystemTime::now(), unicode: None, - platform_code: keycode as _, - position_code: scancode as _, + platform_code: platform_code as _, + position_code: position_code as _, event_type: event_type, }; keyboard::client::process_event(&event, Some(lock_modes)); From f2aeff974cbc4e9cc7ce3e53e5064e96b2110c0f Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 4 Apr 2023 20:35:04 +0800 Subject: [PATCH 214/366] disable hwcodec if causing crash Signed-off-by: 21pages --- libs/hbb_common/src/platform/mod.rs | 10 ++++++++++ libs/scrap/src/common/codec.rs | 23 +++++++++++++---------- src/ui_session_interface.rs | 2 +- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index c37c8aaf5..c1c766753 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -29,6 +29,16 @@ extern "C" fn breakdown_signal_handler(sig: i32) { info = "Always use software rendering will be set.".to_string(); log::info!("{}", info); } + if stack.iter().any(|s| { + s.to_lowercase().contains("nvidia") + || s.to_lowercase().contains("amf") + || s.to_lowercase().contains("mfx") + || s.contains("cuProfilerStop") + }) { + Config::set_option("enable-hwcodec".to_string(), "N".to_string()); + info = "Perhaps hwcodec causing the crash, disable it first".to_string(); + log::info!("{}", info); + } log::error!( "Got signal {} and exit. stack:\n{}", sig, diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 3209933b4..a3d8a0696 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -109,6 +109,7 @@ impl Encoder { }), Err(e) => { check_config_process(true); + *CODEC_NAME.lock().unwrap() = CodecName::VP9; Err(e) } }, @@ -144,16 +145,18 @@ impl Encoder { let mut h265_name = None; #[cfg(feature = "hwcodec")] { - let best = HwEncoder::best(); - let h264_useable = - decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); - let h265_useable = - decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); - if h264_useable { - h264_name = best.h264.map_or(None, |c| Some(c.name)); - } - if h265_useable { - h265_name = best.h265.map_or(None, |c| Some(c.name)); + if enable_hwcodec_option() { + let best = HwEncoder::best(); + let h264_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h264 > 0); + let h265_useable = + decodings.len() > 0 && decodings.iter().all(|(_, s)| s.ability_h265 > 0); + if h264_useable { + h264_name = best.h264.map_or(None, |c| Some(c.name)); + } + if h265_useable { + h265_name = best.h265.map_or(None, |c| Some(c.name)); + } } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index f7a4a7334..18ef27cc0 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -544,7 +544,7 @@ impl Session { let key = rdev::key_from_code(position_code) as rdev::Key; // Windows requires special handling #[cfg(target_os = "windows")] - let key = rdev::get_win_key(keycode, scancode); + let key = rdev::get_win_key(platform_code, position_code); let event_type = if down_or_up { KeyPress(key) From be2e26e758a3770c4da4176330ec8a863450f3bc Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 4 Apr 2023 21:21:00 +0800 Subject: [PATCH 215/366] delete RustDesk_hwcodec.toml on every check Signed-off-by: 21pages --- libs/scrap/src/common/codec.rs | 2 +- libs/scrap/src/common/hwcodec.rs | 8 +++----- src/server.rs | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index a3d8a0696..a4b7c27f8 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -108,7 +108,7 @@ impl Encoder { codec: Box::new(hw), }), Err(e) => { - check_config_process(true); + check_config_process(); *CODEC_NAME.lock().unwrap() = CodecName::VP9; Err(e) } diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index f4de4bf84..8daa6551c 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -199,7 +199,7 @@ impl HwDecoder { } } if fail { - check_config_process(true); + check_config_process(); } HwDecoders { h264, h265 } } @@ -323,13 +323,11 @@ pub fn check_config() { log::error!("Failed to serialize codec info"); } -pub fn check_config_process(force_reset: bool) { +pub fn check_config_process() { use hbb_common::sysinfo::{ProcessExt, System, SystemExt}; std::thread::spawn(move || { - if force_reset { - HwCodecConfig::remove(); - } + HwCodecConfig::remove(); if let Ok(exe) = std::env::current_exe() { if let Some(file_name) = exe.file_name().to_owned() { let s = System::new_all(); diff --git a/src/server.rs b/src/server.rs index 681e7bed1..9c7fb2260 100644 --- a/src/server.rs +++ b/src/server.rs @@ -389,7 +389,7 @@ pub async fn start_server(is_server: bool) { use std::sync::Once; static ONCE: Once = Once::new(); ONCE.call_once(|| { - scrap::hwcodec::check_config_process(false); + scrap::hwcodec::check_config_process(); }) } From c4fec2c19ef13c60a50ca33957cff243f9e58524 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 4 Apr 2023 22:52:33 +0800 Subject: [PATCH 216/366] fix build macos Signed-off-by: fufesou --- src/ui_session_interface.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 18ef27cc0..678db0326 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -537,8 +537,8 @@ impl Session { if position_code < 0 || platform_code < 0 { return; } - let platform_code: KeyCode = platform_code as _; - let position_code: u32 = position_code as _; + let platform_code: u32 = platform_code as _; + let position_code: KeyCode = position_code as _; #[cfg(not(target_os = "windows"))] let key = rdev::key_from_code(position_code) as rdev::Key; @@ -554,7 +554,7 @@ impl Session { let event = Event { time: SystemTime::now(), unicode: None, - platform_code: platform_code as _, + platform_code: platform_code, position_code: position_code as _, event_type: event_type, }; From c4d883c0831a899fc2c9dd8a4c4c78f1b8364b0a Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 4 Apr 2023 23:04:09 +0800 Subject: [PATCH 217/366] trivial changes Signed-off-by: fufesou --- src/ui_session_interface.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 678db0326..3a0ae6598 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -554,9 +554,9 @@ impl Session { let event = Event { time: SystemTime::now(), unicode: None, - platform_code: platform_code, + platform_code, position_code: position_code as _, - event_type: event_type, + event_type, }; keyboard::client::process_event(&event, Some(lock_modes)); } From 23732eb8f37d4cc0a533f0afc122b7ea89ecc0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=85=C3=BC?= <53787985+LaiYueTing@users.noreply.github.com> Date: Wed, 5 Apr 2023 01:32:19 +0800 Subject: [PATCH 218/366] Update tw.rs --- src/lang/tw.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index bff471b37..1eb74d7b2 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -480,16 +480,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "此檔案與對方的檔案一致"), ("show_monitors_tip", "在工具列中顯示顯示器"), ("View Mode", "瀏覽模式"), - ("login_linux_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面。"), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("login_linux_tip", "需要登入到遠端 Linux 使用者帳戶才能啟用 X 介面"), + ("verify_rustdesk_password_tip", "驗證 RustDesk 密碼"), + ("remember_account_tip", "記住此使用者帳戶"), + ("os_account_desk_tip", "此使用者帳戶將用於登入遠端作業系統並啟用無頭模式的桌面連線"), + ("OS Account", "作業系統使用者帳戶"), + ("another_user_login_title_tip", "另一個使用者已經登入"), + ("another_user_login_text_tip", "斷開連線"), + ("xorg_not_found_title_tip", "未找到 Xorg"), + ("xorg_not_found_text_tip", "請安裝 Xorg"), + ("no_desktop_title_tip", "沒有可用的桌面"), + ("no_desktop_text_tip", "請安裝 GNOME 桌面"), ].iter().cloned().collect(); } From 2c25be346c59da4fddacfb288df4e74019345711 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 5 Apr 2023 13:51:59 +0800 Subject: [PATCH 219/366] fix press/release single meta key Signed-off-by: fufesou --- src/lib.rs | 1 + src/server/input_service.rs | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5dcd6389c..a702e5f11 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ /// cbindgen:ignore pub mod platform; mod keyboard; +pub use keyboard::keycode_to_rdev_key; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; #[cfg(not(any(target_os = "ios")))] diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 7d3229c12..fb04107ca 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1371,6 +1371,35 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { allow_err!(rdev::simulate_code(Some(keycode), None, down)); } +#[cfg(any(target_os = "android", target_os = "ios"))] +fn is_meta_key(_evt: &KeyEvent) -> bool { + false +} + +#[cfg(not(any(target_os = "android", target_os = "ios")))] +fn is_meta_key(evt: &KeyEvent) -> bool { + match evt.mode.unwrap() { + KeyboardMode::Map | KeyboardMode::Translate => match &evt.union { + Some(key_event::Union::ControlKey(ck)) => { + return *ck == ControlKey::Meta.into(); + } + Some(key_event::Union::Chr(code)) => { + let key = crate::keycode_to_rdev_key(*code); + return key == RdevKey::MetaLeft || key == RdevKey::MetaRight; + } + _ => {} + }, + KeyboardMode::Legacy => match &evt.union { + Some(key_event::Union::ControlKey(ck)) => { + return *ck == ControlKey::Meta.into(); + } + _ => {} + }, + _ => {} + } + false +} + pub fn handle_key_(evt: &KeyEvent) { if EXITING.load(Ordering::SeqCst) { return; @@ -1381,8 +1410,8 @@ pub fn handle_key_(evt: &KeyEvent) { Some(LockModesHandler::new(&evt)) } _ => { - if evt.down { - Some(LockModesHandler::new(&evt)) + if evt.down && !is_meta_key(evt) { + Some(LockModesHandler::new(evt)) } else { None } From acac3054410a1885593273c18f39b41f46c28124 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 5 Apr 2023 14:30:47 +0800 Subject: [PATCH 220/366] win, linux, single meta key Signed-off-by: fufesou --- src/server/input_service.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index fb04107ca..7ea12c965 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1371,13 +1371,13 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { allow_err!(rdev::simulate_code(Some(keycode), None, down)); } -#[cfg(any(target_os = "android", target_os = "ios"))] -fn is_meta_key(_evt: &KeyEvent) -> bool { +#[cfg(not(any(target_os = "windows", target_os = "linux")))] +fn is_win_linux_meta_key(_evt: &KeyEvent) -> bool { false } -#[cfg(not(any(target_os = "android", target_os = "ios")))] -fn is_meta_key(evt: &KeyEvent) -> bool { +#[cfg(any(target_os = "windows", target_os = "linux"))] +fn is_win_linux_meta_key(evt: &KeyEvent) -> bool { match evt.mode.unwrap() { KeyboardMode::Map | KeyboardMode::Translate => match &evt.union { Some(key_event::Union::ControlKey(ck)) => { @@ -1410,7 +1410,7 @@ pub fn handle_key_(evt: &KeyEvent) { Some(LockModesHandler::new(&evt)) } _ => { - if evt.down && !is_meta_key(evt) { + if evt.down && !is_win_linux_meta_key(evt) { Some(LockModesHandler::new(evt)) } else { None From d279588a64589bce9e5a364cf2003f32036f81f6 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 5 Apr 2023 14:54:23 +0800 Subject: [PATCH 221/366] fix build Signed-off-by: fufesou --- src/lib.rs | 1 + src/server/input_service.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a702e5f11..45b4c63f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ /// cbindgen:ignore pub mod platform; mod keyboard; +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub use keyboard::keycode_to_rdev_key; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 7ea12c965..5fb63a597 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1410,6 +1410,9 @@ pub fn handle_key_(evt: &KeyEvent) { Some(LockModesHandler::new(&evt)) } _ => { + // LockModesHandler should not be created when single meta is pressing and releasing. + // Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click. + // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687 if evt.down && !is_win_linux_meta_key(evt) { Some(LockModesHandler::new(evt)) } else { From df73fde6bcbd34b0190e1ddb1b39c97c958d08c3 Mon Sep 17 00:00:00 2001 From: mehdi-song Date: Wed, 5 Apr 2023 07:21:27 +0000 Subject: [PATCH 222/366] Update fa.rs --- src/lang/fa.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lang/fa.rs b/src/lang/fa.rs index d78cc8b37..79f39154a 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -479,17 +479,17 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Me", "من"), ("identical_file_tip", "این فایل با فایل همتا یکسان است."), ("show_monitors_tip", "نمایش مانیتورها در نوار ابزار"), - ("View Mode", ""), - ("login_linux_tip", ""), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("View Mode", "حالت مشاهده"), + ("login_linux_tip", "برای فعال کردن دسکتاپ X، باید به حساب لینوکس راه دور وارد شوید"), + ("verify_rustdesk_password_tip", "رمز عبور RustDesk را تأیید کنید"), + ("remember_account_tip", "این حساب را به خاطر بسپارید"), + ("os_account_desk_tip", "این حساب برای ورود به سیستم عامل راه دور و فعال کردن جلسه دسکتاپ در هدلس استفاده می شود"), + ("OS Account", "حساب کاربری سیستم عامل"), + ("another_user_login_title_tip", "کاربر دیگری قبلاً وارد شده است"), + ("another_user_login_text_tip", "قطع شدن"), + ("xorg_not_found_title_tip", "پیدا نشد Xorg"), + ("xorg_not_found_text_tip", "لطفا Xorg را نصب کنید"), + ("no_desktop_title_tip", "هیچ دسکتاپی در دسترس نیست"), + ("no_desktop_text_tip", "لطفا دسکتاپ گنوم را نصب کنید"), ].iter().cloned().collect(); } From f6189455ac6324e14c3cd010541bf86773af39da Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 6 Apr 2023 09:31:36 +0800 Subject: [PATCH 223/366] fix: arm architecture env --- .github/workflows/flutter-build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 5e230f20f..1bc519f56 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -980,7 +980,7 @@ jobs: run: | for name in rustdesk*??.deb; do # use cp to duplicate deb files to fit other packages. - cp "$name" "${name%%.deb}--${{ matrix.job.arch }}-sciter.deb" + cp "$name" "${name%%.deb}-${{ matrix.job.arch }}-sciter.deb" done - name: Publish debian package @@ -1103,11 +1103,11 @@ jobs: # edit to corresponding arch case ${{ matrix.job.arch }} in aarch64) - sed -i "s/Architecture: amd64/Architecture: arm64/g" ./build.py + export ARCH=arm64 sed -i "s/x64\/release/arm64\/release/g" ./build.py ;; armv7) - sed -i "s/Architecture: amd64/Architecture: arm/g" ./build.py + export ARCH=armhf sed -i "s/x64\/release/arm\/release/g" ./build.py ;; esac From bd1993e034a51ce3fef017df35e0d35e186536c9 Mon Sep 17 00:00:00 2001 From: junbinhe Date: Thu, 6 Apr 2023 11:39:14 +0800 Subject: [PATCH 224/366] Update devcontainer.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 同步安卓ndk到23版本 --- .devcontainer/devcontainer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cd82c75e3..953196eb3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,7 +10,7 @@ "features": { "ghcr.io/devcontainers/features/java:1": {}, "ghcr.io/akhildevelops/devcontainer-features/android-cli:latest": { - "PACKAGES": "platform-tools,ndk;22.1.7171670" + "PACKAGES": "platform-tools,ndk;23.2.8568313" } }, "customizations": { @@ -31,4 +31,4 @@ } } } -} \ No newline at end of file +} From a65611da48a2c1630f6a5f79b92fdbf0c978e5af Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 6 Apr 2023 12:02:01 +0800 Subject: [PATCH 225/366] do not handle signal on debug Signed-off-by: fufesou --- libs/hbb_common/src/platform/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index c1c766753..b109ac683 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -65,6 +65,7 @@ pub fn register_breakdown_handler(callback: T) where T: Fn() + 'static, { + #[cfg(not(debug_assertions))] unsafe { GLOBAL_CALLBACK = Some(Box::new(callback)); libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); From 83cfa24bae0edcfcd0ac85a50bbcc8745c86103e Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 6 Apr 2023 12:02:49 +0800 Subject: [PATCH 226/366] suppress warn Signed-off-by: fufesou --- libs/hbb_common/src/platform/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index b109ac683..fd57476c0 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -61,13 +61,13 @@ extern "C" fn breakdown_signal_handler(sig: i32) { exit(0); } -pub fn register_breakdown_handler(callback: T) +pub fn register_breakdown_handler(_callback: T) where T: Fn() + 'static, { #[cfg(not(debug_assertions))] unsafe { - GLOBAL_CALLBACK = Some(Box::new(callback)); + GLOBAL_CALLBACK = Some(Box::new(_callback)); libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } From 15ea1366d414e8bbdec1b5fd33d370fe96964a04 Mon Sep 17 00:00:00 2001 From: junbinhe Date: Thu, 6 Apr 2023 12:57:42 +0800 Subject: [PATCH 227/366] Update Dockerfile ndk23 --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 32a440b28..08fd65544 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -38,7 +38,7 @@ RUN sudo apt-get install -y libclang-dev RUN sudo apt install -y gcc-multilib WORKDIR $WORKDIR -ENV ANDROID_NDK_HOME=/opt/android/ndk/22.1.7171670 +ENV ANDROID_NDK_HOME=/opt/android/ndk/23.2.8568313 # Somehow try to automate flutter pub get # https://rustdesk.com/docs/en/dev/build/android/ From ddf740ab0dc73dad8d6311aced793ba3f32b0c71 Mon Sep 17 00:00:00 2001 From: youhers <106003909+youhers@users.noreply.github.com> Date: Thu, 6 Apr 2023 13:32:09 +0800 Subject: [PATCH 228/366] =?UTF-8?q?Update=20and=20rename=20src/lang/cn.rs?= =?UTF-8?q?=20to=20=E6=9D=A5=E6=BA=90/=E6=9C=97/cn.rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {src/lang => 来源/朗}/cn.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) rename {src/lang => 来源/朗}/cn.rs (98%) diff --git a/src/lang/cn.rs b/来源/朗/cn.rs similarity index 98% rename from src/lang/cn.rs rename to 来源/朗/cn.rs index 11b3144c7..2c058d2ab 100644 --- a/src/lang/cn.rs +++ b/来源/朗/cn.rs @@ -459,26 +459,26 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Codec", "编解码"), ("Resolution", "分辨率"), ("No transfers in progress", ""), - ("Set one-time password length", ""), + ("Set one-time password length", "设置一次性密码长度"), ("idd_driver_tip", "安装虚拟显示器驱动,以便在没有连接显示器的情况下启动虚拟显示器进行控制。"), ("confirm_idd_driver_tip", "安装虚拟显示器驱动的选项已勾选。请注意,测试证书将被安装以信任虚拟显示器驱动。测试证书仅会用于信任Rustdesk的驱动。"), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), + ("RDP Settings", "RDP 设置"), + ("Sort by", "排序依据"), + ("New Connection", "添加新的连接"), + ("Restore", "恢复"), + ("Minimize", "最小化"), + ("Maximize", "最大化"), + ("Your Device", "您的设备"), ("empty_recent_tip", ""), ("empty_favorite_tip", ""), ("empty_lan_tip", ""), ("empty_address_book_tip", ""), ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), + ("Empty Username", "空用户名"), + ("Empty Password", "空密码"), ("Me", ""), ("identical_file_tip", "此文件与对方的一致"), - ("show_monitors_tip", ""), + ("show_monitors_tip", "显示监视器提示"), ("View Mode", "浏览模式"), ("login_linux_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), ("verify_rustdesk_password_tip", "验证 RustDesk 密码"), From e4fb82b5798615625cfe6c4b5e0f70c42bfd483b Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Thu, 6 Apr 2023 13:50:17 +0800 Subject: [PATCH 229/366] =?UTF-8?q?Revert=20"Update=20and=20rename=20src/l?= =?UTF-8?q?ang/cn.rs=20to=20=E6=9D=A5=E6=BA=90/=E6=9C=97/cn.rs"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {来源/朗 => src/lang}/cn.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) rename {来源/朗 => src/lang}/cn.rs (98%) diff --git a/来源/朗/cn.rs b/src/lang/cn.rs similarity index 98% rename from 来源/朗/cn.rs rename to src/lang/cn.rs index 2c058d2ab..11b3144c7 100644 --- a/来源/朗/cn.rs +++ b/src/lang/cn.rs @@ -459,26 +459,26 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Codec", "编解码"), ("Resolution", "分辨率"), ("No transfers in progress", ""), - ("Set one-time password length", "设置一次性密码长度"), + ("Set one-time password length", ""), ("idd_driver_tip", "安装虚拟显示器驱动,以便在没有连接显示器的情况下启动虚拟显示器进行控制。"), ("confirm_idd_driver_tip", "安装虚拟显示器驱动的选项已勾选。请注意,测试证书将被安装以信任虚拟显示器驱动。测试证书仅会用于信任Rustdesk的驱动。"), - ("RDP Settings", "RDP 设置"), - ("Sort by", "排序依据"), - ("New Connection", "添加新的连接"), - ("Restore", "恢复"), - ("Minimize", "最小化"), - ("Maximize", "最大化"), - ("Your Device", "您的设备"), + ("RDP Settings", ""), + ("Sort by", ""), + ("New Connection", ""), + ("Restore", ""), + ("Minimize", ""), + ("Maximize", ""), + ("Your Device", ""), ("empty_recent_tip", ""), ("empty_favorite_tip", ""), ("empty_lan_tip", ""), ("empty_address_book_tip", ""), ("eg: admin", ""), - ("Empty Username", "空用户名"), - ("Empty Password", "空密码"), + ("Empty Username", ""), + ("Empty Password", ""), ("Me", ""), ("identical_file_tip", "此文件与对方的一致"), - ("show_monitors_tip", "显示监视器提示"), + ("show_monitors_tip", ""), ("View Mode", "浏览模式"), ("login_linux_tip", "登录被控端的 Linux 账户,才能启用 X 桌面"), ("verify_rustdesk_password_tip", "验证 RustDesk 密码"), From ac74ed1914dbdbf0566673bb7aeeacfc6387c8a2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 6 Apr 2023 17:53:07 +0800 Subject: [PATCH 230/366] legacy mode, win, fix layout code simulation Signed-off-by: fufesou --- libs/enigo/src/win/win_impl.rs | 95 +++++++++++++++++++---------- libs/hbb_common/src/platform/mod.rs | 10 ++- src/core_main.rs | 2 + 3 files changed, 73 insertions(+), 34 deletions(-) diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs index 115cb9789..068f3463a 100644 --- a/libs/enigo/src/win/win_impl.rs +++ b/libs/enigo/src/win/win_impl.rs @@ -156,7 +156,7 @@ impl MouseControllable for Enigo { match button { MouseButton::Back => XBUTTON1 as _, MouseButton::Forward => XBUTTON2 as _, - _ => 0, + _ => 0, }, 0, 0, @@ -186,7 +186,7 @@ impl MouseControllable for Enigo { match button { MouseButton::Back => XBUTTON1 as _, MouseButton::Forward => XBUTTON2 as _, - _ => 0, + _ => 0, }, 0, 0, @@ -215,7 +215,7 @@ impl KeyboardControllable for Enigo { fn as_mut_any(&mut self) -> &mut dyn std::any::Any { self } - + fn key_sequence(&mut self, sequence: &str) { let mut buffer = [0; 2]; @@ -247,15 +247,51 @@ impl KeyboardControllable for Enigo { } fn key_down(&mut self, key: Key) -> crate::ResultType { - let code = self.key_to_keycode(key); - if code == 0 || code == 65535 { - return Err("".into()); - } - let res = keybd_event(0, code, 0); - if res == 0 { - let err = get_error(); - if !err.is_empty() { - return Err(err.into()); + match &key { + Key::Layout(c) => { + // to-do: dup code + // https://github.com/rustdesk/rustdesk/blob/1bc0dd791ed8344997024dc46626bd2ca7df73d2/src/server/input_service.rs#L1348 + let code = self.get_layoutdependent_keycode(*c); + if code as u16 != 0xFFFF { + let vk = code & 0x00FF; + let flag = code >> 8; + let modifiers = [Key::Shift, Key::Control, Key::Alt]; + let mod_len = modifiers.len(); + for pos in 0..mod_len { + if flag & (0x0001 << pos) != 0 { + self.key_down(modifiers[pos])?; + } + } + + let res = keybd_event(0, vk, 0); + let err = if res == 0 { get_error() } else { "".to_owned() }; + + for pos in 0..mod_len { + let rpos = mod_len - 1 - pos; + if flag & (0x0001 << rpos) != 0 { + self.key_up(modifiers[pos]); + } + } + + if !err.is_empty() { + return Err(err.into()); + } + } else { + return Err(format!("Failed to get keycode of {}", c).into()); + } + } + _ => { + let code = self.key_to_keycode(key); + if code == 0 || code == 65535 { + return Err("".into()); + } + let res = keybd_event(0, code, 0); + if res == 0 { + let err = get_error(); + if !err.is_empty() { + return Err(err.into()); + } + } } } Ok(()) @@ -411,30 +447,27 @@ impl Enigo { Key::RightAlt => EVK_RMENU, Key::Raw(raw_keycode) => raw_keycode, - Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()), Key::Super | Key::Command | Key::Windows | Key::Meta => EVK_LWIN, + Key::Layout(..) => { + // unreachable + 0 + } } } - fn get_layoutdependent_keycode(&self, string: String) -> u16 { - // get the first char from the string ignore the rest - // ensure its not a multybyte char - if let Some(chr) = string.chars().nth(0) { - // NOTE VkKeyScanW uses the current keyboard LAYOUT - // to specify a LAYOUT use VkKeyScanExW and GetKeyboardLayout - // or load one with LoadKeyboardLayoutW - let current_window_thread_id = - unsafe { GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()) }; - unsafe { LAYOUT = GetKeyboardLayout(current_window_thread_id) }; - let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) }; - if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' { - // a workaround of italian keyboard shift + '.' issue - EVK_PERIOD as _ - } else { - keycode_and_shiftstate as _ - } + fn get_layoutdependent_keycode(&self, chr: char) -> u16 { + // NOTE VkKeyScanW uses the current keyboard LAYOUT + // to specify a LAYOUT use VkKeyScanExW and GetKeyboardLayout + // or load one with LoadKeyboardLayoutW + let current_window_thread_id = + unsafe { GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()) }; + unsafe { LAYOUT = GetKeyboardLayout(current_window_thread_id) }; + let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) }; + if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' { + // a workaround of italian keyboard shift + '.' issue + EVK_PERIOD as _ } else { - 0 + keycode_and_shiftstate as _ } } } diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs index fd57476c0..137868e12 100644 --- a/libs/hbb_common/src/platform/mod.rs +++ b/libs/hbb_common/src/platform/mod.rs @@ -4,11 +4,15 @@ pub mod linux; #[cfg(target_os = "macos")] pub mod macos; +#[cfg(not(debug_assertions))] use crate::{config::Config, log}; +#[cfg(not(debug_assertions))] use std::process::exit; +#[cfg(not(debug_assertions))] static mut GLOBAL_CALLBACK: Option> = None; +#[cfg(not(debug_assertions))] extern "C" fn breakdown_signal_handler(sig: i32) { let mut stack = vec![]; backtrace::trace(|frame| { @@ -61,13 +65,13 @@ extern "C" fn breakdown_signal_handler(sig: i32) { exit(0); } -pub fn register_breakdown_handler(_callback: T) +#[cfg(not(debug_assertions))] +pub fn register_breakdown_handler(callback: T) where T: Fn() + 'static, { - #[cfg(not(debug_assertions))] unsafe { - GLOBAL_CALLBACK = Some(Box::new(_callback)); + GLOBAL_CALLBACK = Some(Box::new(callback)); libc::signal(libc::SIGSEGV, breakdown_signal_handler as _); } } diff --git a/src/core_main.rs b/src/core_main.rs index 05a5ad769..a3e11a49e 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -1,5 +1,6 @@ use crate::platform::breakdown_callback; use hbb_common::log; +#[cfg(not(debug_assertions))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::platform::register_breakdown_handler; @@ -39,6 +40,7 @@ pub fn core_main() -> Option> { } i += 1; } + #[cfg(not(debug_assertions))] #[cfg(not(any(target_os = "android", target_os = "ios")))] register_breakdown_handler(breakdown_callback); #[cfg(target_os = "linux")] From 66dcd80299da3e1afc7fdec26bd16d50951652a9 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Thu, 6 Apr 2023 20:42:17 +0300 Subject: [PATCH 231/366] Update lt.rs --- src/lang/lt.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/lang/lt.rs b/src/lang/lt.rs index e21ff677d..9355a95a2 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -16,7 +16,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), ("Transfer File", "Perkelti failą"), ("Connect", "Prisijungti"), - ("Recent Sessions", "Naujausios sesijos"), + ("Recent Sessions", "Seansų istorija"), ("Address Book", "Adresų knyga"), ("Confirmation", "Patvirtinimas"), ("TCP Tunneling", "TCP tuneliavimas"), @@ -52,7 +52,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Home", "Namai"), ("Audio Input", "Garso įvestis"), ("Enhancements", "Patobulinimai"), - ("Hardware Codec", "Aparatūros kodekas"), + ("Hardware Codec", "Aparatinės įrangos paspartinimas"), ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), ("ID Server", "ID serveris"), ("Relay Server", "Perdavimo serveris"), @@ -93,7 +93,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Local", "Vietinis"), ("Remote", "Nuotolinis"), ("Remote Computer", "Nuotolinis kompiuteris"), - ("Local Computer", "Vietinis kompiuteris"), + ("Local Computer", "Šis kompiuteris"), ("Confirm Delete", "Patvirtinti ištrynimą"), ("Delete", "Ištrinti"), ("Properties", "Ypatybės"), @@ -145,7 +145,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), ("Set Password", "Nustatyti slaptažodį"), ("OS Password", "OS slaptažodis"), - ("install_tip", "Kai kuriais atvejais UAC gali priversti RustDesk netinkamai veikti nuotoliniame pagrindiniame kompiuteryje. Norėdami išvengti UAC, spustelėkite toliau esantį mygtuką, kad įdiegtumėte RustDesk savo sistemoje."), + ("install_tip", "Kai kuriais atvejais UAC gali priversti RustDesk netinkamai veikti nuotoliniame pagrindiniame kompiuteryje. Norėdami apeiti UAC, spustelėkite toliau esantį mygtuką, kad įdiegtumėte RustDesk į savo kompiuterį."), ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), ("Click to download", "Spustelėkite norėdami atsisiųsti"), ("Click to update", "Spustelėkite norėdami atnaujinti"), @@ -210,7 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Settings", "Nustatymai"), ("Username", "Vartotojo vardas"), ("Invalid port", "Netinkamas prievadas"), - ("Closed manually by the peer", "Peer uždarė rankiniu būdu"), + ("Closed manually by the peer", "Partneris atmetė prašymą prisijungti"), ("Enable remote configuration modification", "Įgalinti nuotolinį konfigūracijos modifikavimą"), ("Run without install", "Vykdyti be diegimo"), ("Connect via relay", "Prisijungti per relę"), @@ -234,15 +234,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Password missed", "Slaptažodis praleistas"), ("Wrong credentials", "Klaidingi kredencialai"), ("Edit Tag", "Redaguoti žymą"), - ("Unremember Password", "Neprisiminti slaptažodžio"), - ("Favorites", "Mėgstamiausi"), + ("Unremember Password", "Nebeprisiminti slaptažodžio"), + ("Favorites", "Parankiniai"), ("Add to Favorites", "Įtraukti į parankinius"), ("Remove from Favorites", "Pašalinti iš parankinių"), ("Empty", "Tuščia"), ("Invalid folder name", "Neteisingas aplanko pavadinimas"), ("Socks5 Proxy", "Socks5 Proxy"), ("Hostname", "Pagrindinio kompiuterio pavadinimas"), - ("Discovered", "Atrasta"), + ("Discovered", "Aptikta tinkle"), ("install_daemon_tip", "Norėdami, kad RustDesk startuotų automatiškai, turite ją įdiegti"), ("Remote ID", "Nuotolinis ID"), ("Paste", "Įklijuoti"), @@ -349,22 +349,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Theme", "Tema"), ("Dark Theme", "Tamsioji tema"), ("Light Theme", "Šviesi tema"), - ("Dark", "Tamsiai"), - ("Light", "Šviesiai"), - ("Follow System", "Sekti sistemą"), - ("Enable hardware codec", "Įgalinti aparatūrinį kodavimą"), + ("Dark", "Tamsi"), + ("Light", "Šviesi"), + ("Follow System", "Kaip sistemos"), + ("Enable hardware codec", "Įgalinti"), ("Unlock Security Settings", "Atrakinti saugos nustatymus"), ("Enable Audio", "Įgalinti garsą"), ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), ("Server", "Serveris"), ("Direct IP Access", "Tiesioginė IP prieiga"), - ("Proxy", "Įgaliotinis serveris"), + ("Proxy", "Tarpinis serveris"), ("Apply", "Taikyti"), ("Disconnect all devices?", "Atjungti visus įrenginius?"), ("Clear", "Išvalyti"), - ("Audio Input Device", "Garso įvesties įrenginys"), + ("Audio Input Device", "Garso įvestis"), ("Deny remote access", "Uždrausti nuotolinę prieigą"), - ("Use IP Whitelisting", "Naudoti IP baltąjį sąrašą"), + ("Use IP Whitelisting", "Naudoti patikimą IP sąrašą"), ("Network", "Tinklas"), ("Enable RDP", "Įgalinti RDP"), ("Pin menubar", "Prisegti meniu juostą"), @@ -401,12 +401,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Zoom cursor", "Mastelio keitimo žymeklis"), ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), ("Accept sessions via click", "Priimti seansus spustelėjus"), - ("Accept sessions via both", "Priimti seansus per abu"), + ("Accept sessions via both", "Priimti seansus abiem variantais"), ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), ("One-time Password", "Vienkartinis slaptažodis"), ("Use one-time password", "Naudoti vienkartinį slaptažodį"), ("One-time password length", "Vienkartinio slaptažodžio ilgis"), - ("Request access to your device", "Prašyti prieigos prie įrenginio"), + ("Request access to your device", "Prašo leidimo valdyti jūsų įrenginį"), ("Hide connection management window", "Slėpti ryšio valdymo langą"), ("hide_cm_tip", "Leisti paslėpti didžiąją ir mažąją raidę, jei priimamos slaptažodžio sesijos arba naudojamas nuolatinis slaptažodis"), ("wayland_experiment_tip", "Wayland palaikymas yra eksperimentinis, naudokite X11, jei jums reikalingas automatinis prisijungimas."), @@ -481,7 +481,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Rodyti monitorius įrankių juostoje"), ("View Mode", "Peržiūros režimas"), ("login_linux_tip", "Norėdami įjungti X darbalaukio seansą, turite būti prisijungę prie nuotolinės Linux paskyros."), - ("verify_rustdesk_password_tip", "Patvirtinkite RustDesk slaptažodį"), + ("verify_rustdesk_password_tip", "Įveskite kliento RustDesk slaptažodį"), ("remember_account_tip", "Prisiminti šią paskyrą"), ("os_account_desk_tip", "Ši paskyra naudojama norint prisijungti prie nuotolinės OS ir įgalinti darbalaukio seansą režimu headless"), ("OS Account", "OS paskyra"), @@ -492,4 +492,4 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), ].iter().cloned().collect(); -} \ No newline at end of file +} From bc5c6e9a06128adfd3ab2d8cf3714be1f442e285 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 6 Apr 2023 16:48:29 +0800 Subject: [PATCH 232/366] tmp fix video qos reset Signed-off-by: 21pages --- src/server/video_qos.rs | 4 +++- src/server/video_service.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index 47bf49707..5bb687473 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -221,7 +221,9 @@ impl VideoQoS { } pub fn reset(&mut self) { - *self = Default::default(); + self.fps = FPS; + self.user_fps = FPS; + self.updated = true; } pub fn check_abr_config(&mut self) -> bool { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 691ca4abe..4abeafff5 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -547,7 +547,7 @@ fn run(sp: GenericService) -> ResultType<()> { check_uac_switch(c.privacy_mode_id, c._capturer_privacy_mode_id)?; let mut video_qos = VIDEO_QOS.lock().unwrap(); - if video_qos.check_if_updated() { + if video_qos.check_if_updated() && video_qos.target_bitrate > 0 { log::debug!( "qos is updated, target_bitrate:{}, fps:{}", video_qos.target_bitrate, From d6c8fb2b281bca6b17ec41d0423d56be93e04732 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 7 Apr 2023 14:46:18 +0800 Subject: [PATCH 233/366] refact is_peer_version_ge, and fix version comparation > to >= Signed-off-by: fufesou --- src/common.rs | 18 ++++++++++++++++++ src/keyboard.rs | 20 ++------------------ src/server/connection.rs | 6 +++++- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/common.rs b/src/common.rs index d07c7bbc6..3cf10eaba 100644 --- a/src/common.rs +++ b/src/common.rs @@ -818,3 +818,21 @@ pub async fn get_key(sync: bool) -> String { } key } + +pub fn is_peer_version_ge(v: &str) -> bool { + #[cfg(not(any(feature = "flutter", feature = "cli")))] + if let Some(session) = crate::ui::CUR_SESSION.lock().unwrap().as_ref() { + return session.get_peer_version() >= hbb_common::get_version_number(v); + } + + #[cfg(feature = "flutter")] + if let Some(session) = crate::flutter::SESSIONS + .read() + .unwrap() + .get(&*crate::flutter::CUR_SESSION_ID.read().unwrap()) + { + return session.get_peer_version() >= hbb_common::get_version_number(v); + } + + false +} diff --git a/src/keyboard.rs b/src/keyboard.rs index 7f1f7e5f8..15629bb00 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -339,24 +339,8 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { "translate" => KeyboardMode::Translate, "legacy" => KeyboardMode::Legacy, _ => { - // Set "map" as default mode if version > 1.2.0. - let mut is_peer_version_gt_1_2_0 = false; - - #[cfg(not(any(feature = "flutter", feature = "cli")))] - if let Some(session) = CUR_SESSION.lock().unwrap().as_ref() { - is_peer_version_gt_1_2_0 = - session.get_peer_version() > hbb_common::get_version_number("1.2.0"); - } - #[cfg(feature = "flutter")] - if let Some(session) = SESSIONS - .read() - .unwrap() - .get(&*CUR_SESSION_ID.read().unwrap()) - { - is_peer_version_gt_1_2_0 = - session.get_peer_version() > hbb_common::get_version_number("1.2.0"); - } - if is_peer_version_gt_1_2_0 { + // Set "map" as default mode if version >= 1.2.0. + if crate::is_peer_version_ge("1.2.0") { KeyboardMode::Map } else { KeyboardMode::Legacy diff --git a/src/server/connection.rs b/src/server/connection.rs index 53dccc3ca..cf962d983 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -43,13 +43,15 @@ use sha2::{Digest, Sha256}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use std::sync::atomic::Ordering; use std::{ - collections::HashSet, num::NonZeroI64, sync::{atomic::AtomicI64, mpsc as std_mpsc}, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use system_shutdown; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use std::collections::HashSet; + pub type Sender = mpsc::UnboundedSender<(Instant, Arc)>; lazy_static::lazy_static! { @@ -153,6 +155,7 @@ pub struct Connection { voice_call_request_timestamp: Option, audio_input_device_before_voice_call: Option, options_in_login: Option, + #[cfg(not(any(target_os = "android", target_os = "ios")))] pressed_modifiers: HashSet, #[cfg(all(target_os = "linux", feature = "linux_headless"))] rx_cm_stream_ready: mpsc::Receiver<()>, @@ -273,6 +276,7 @@ impl Connection { voice_call_request_timestamp: None, audio_input_device_before_voice_call: None, options_in_login: None, + #[cfg(not(any(target_os = "android", target_os = "ios")))] pressed_modifiers: Default::default(), #[cfg(all(target_os = "linux", feature = "linux_headless"))] rx_cm_stream_ready: _rx_cm_stream_ready, From 1d4772af1846d02fd9fbc926d0d23ebc43f6c259 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 7 Apr 2023 16:03:18 +0800 Subject: [PATCH 234/366] fix android physical keyboard input Signed-off-by: fufesou --- flutter/lib/models/input_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index f18f63647..ff0faf7a4 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -64,7 +64,7 @@ class InputModel { InputModel(this.parent); KeyEventResult handleRawKeyEvent(FocusNode data, RawKeyEvent e) { - if (!stateGlobal.grabKeyboard) { + if (isDesktop && !stateGlobal.grabKeyboard) { return KeyEventResult.handled; } From 850f48abb8c2486b764954c679fd85e5f060c3fb Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 7 Apr 2023 16:28:34 +0800 Subject: [PATCH 235/366] remove unused code from https://github.com/fufesou/ustdsk/comit/b526bf4a67f7e05b2d3f739864ecc31carembc18a85 Signed-off-by: fufesou --- libs/enigo/src/win/win_impl.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs index 068f3463a..a2839621c 100644 --- a/libs/enigo/src/win/win_impl.rs +++ b/libs/enigo/src/win/win_impl.rs @@ -462,12 +462,6 @@ impl Enigo { let current_window_thread_id = unsafe { GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()) }; unsafe { LAYOUT = GetKeyboardLayout(current_window_thread_id) }; - let keycode_and_shiftstate = unsafe { VkKeyScanExW(chr as _, LAYOUT) }; - if keycode_and_shiftstate == (EVK_DECIMAL as i16) && chr == '.' { - // a workaround of italian keyboard shift + '.' issue - EVK_PERIOD as _ - } else { - keycode_and_shiftstate as _ - } + unsafe { VkKeyScanExW(chr as _, LAYOUT) as _ } } } From b79f14af12507fe183cf0629b1658fa28aa16e16 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 6 Apr 2023 21:36:37 +0800 Subject: [PATCH 236/366] client side fps control for reduce delay Signed-off-by: 21pages --- .../desktop/pages/desktop_setting_page.dart | 4 +- .../lib/desktop/widgets/remote_toolbar.dart | 6 +- libs/hbb_common/src/config.rs | 2 +- src/client.rs | 64 +++++++++++--- src/client/io_loop.rs | 86 ++++++++++++++++++- src/server/video_qos.rs | 2 +- src/ui_session_interface.rs | 3 +- 7 files changed, 143 insertions(+), 24 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 74d51407c..fe5b2fbf4 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1228,9 +1228,9 @@ class _DisplayState extends State<_Display> { children: [ Slider( value: fpsValue.value, - min: 10.0, + min: 5.0, max: 120.0, - divisions: 22, + divisions: 23, onChanged: (double value) async { fpsValue.value = value; await bind.mainSetUserDefaultOption( diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 75bae3d08..9627b107b 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1237,7 +1237,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { final fpsOption = await bind.sessionGetOption(id: widget.id, arg: 'custom-fps'); fpsInitValue = fpsOption == null ? 30 : double.tryParse(fpsOption) ?? 30; - if (fpsInitValue < 10 || fpsInitValue > 120) { + if (fpsInitValue < 5 || fpsInitValue > 120) { fpsInitValue = 30; } final RxDouble fpsSliderValue = RxDouble(fpsInitValue); @@ -1260,9 +1260,9 @@ class _DisplayMenuState extends State<_DisplayMenu> { children: [ Obx((() => Slider( value: fpsSliderValue.value, - min: 10, + min: 5, max: 120, - divisions: 22, + divisions: 23, onChanged: (double value) { fpsSliderValue.value = value; debouncerFps.value = value; diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 4c60e1eb3..369920982 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1363,7 +1363,7 @@ impl UserDefaultConfig { "image_quality" => self.get_string(key, "balanced", vec!["best", "low", "custom"]), "codec-preference" => self.get_string(key, "auto", vec!["vp8", "vp9", "h264", "h265"]), "custom_image_quality" => self.get_double_string(key, 50.0, 10.0, 100.0), - "custom-fps" => self.get_double_string(key, 30.0, 10.0, 120.0), + "custom-fps" => self.get_double_string(key, 30.0, 5.0, 120.0), _ => self .options .get(key) diff --git a/src/client.rs b/src/client.rs index a5e51065f..a24c531c1 100644 --- a/src/client.rs +++ b/src/client.rs @@ -3,7 +3,10 @@ use std::{ net::SocketAddr, ops::Deref, str::FromStr, - sync::{mpsc, Arc, Mutex, RwLock}, + sync::{ + atomic::{AtomicUsize, Ordering}, + mpsc, Arc, Mutex, RwLock, + }, }; pub use async_trait::async_trait; @@ -1291,11 +1294,12 @@ impl LoginConfigHandler { config.custom_image_quality[0] }; msg.custom_image_quality = quality << 8; + #[cfg(feature = "flutter")] + if let Some(custom_fps) = self.options.get("custom-fps") { + msg.custom_fps = custom_fps.parse().unwrap_or(30); + } n += 1; } - if let Some(custom_fps) = self.options.get("custom-fps") { - msg.custom_fps = custom_fps.parse().unwrap_or(30); - } let view_only = self.get_toggle_option("view-only"); if view_only { msg.disable_keyboard = BoolOption::Yes.into(); @@ -1677,7 +1681,12 @@ pub type MediaSender = mpsc::Sender; /// * `video_callback` - The callback for video frame. Being called when a video frame is ready. pub fn start_video_audio_threads( video_callback: F, -) -> (MediaSender, MediaSender, Arc>) +) -> ( + MediaSender, + MediaSender, + Arc>, + Arc, +) where F: 'static + FnMut(&mut Vec) + Send, { @@ -1685,21 +1694,48 @@ where let video_queue = Arc::new(ArrayQueue::::new(VIDEO_QUEUE_SIZE)); let video_queue_cloned = video_queue.clone(); let mut video_callback = video_callback; + let mut duration = std::time::Duration::ZERO; + let mut count = 0; + let fps = Arc::new(AtomicUsize::new(0)); + let decode_fps = fps.clone(); + let mut skip_beginning = 0; std::thread::spawn(move || { let mut video_handler = VideoHandler::new(); loop { if let Ok(data) = video_receiver.recv() { match data { - MediaData::VideoFrame(vf) => { - if let Ok(true) = video_handler.handle_frame(*vf) { + MediaData::VideoFrame(_) | MediaData::VideoQueue => { + let vf = if let MediaData::VideoFrame(vf) = data { + *vf + } else { + if let Some(vf) = video_queue.pop() { + vf + } else { + continue; + } + }; + let start = std::time::Instant::now(); + if let Ok(true) = video_handler.handle_frame(vf) { video_callback(&mut video_handler.rgb); - } - } - MediaData::VideoQueue => { - if let Some(vf) = video_queue.pop() { - if let Ok(true) = video_handler.handle_frame(vf) { - video_callback(&mut video_handler.rgb); + // fps calculation + // The first frame will be very slow + if skip_beginning < 5 { + skip_beginning += 1; + continue; + } + duration += start.elapsed(); + count += 1; + if count % 10 == 0 { + fps.store( + (count * 1000 / duration.as_millis()) as usize, + Ordering::Relaxed, + ); + } + // Clear to get real-time fps + if count > 300 { + count = 0; + duration = Duration::ZERO; } } } @@ -1718,7 +1754,7 @@ where log::info!("Video decoder loop exits"); }); let audio_sender = start_audio_thread(); - return (video_sender, audio_sender, video_queue_cloned); + return (video_sender, audio_sender, video_queue_cloned, decode_fps); } /// Start an audio thread diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index bc6ad8a80..f7530cc37 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -65,6 +65,8 @@ pub struct Remote { frame_count: Arc, video_format: CodecFormat, elevation_requested: bool, + fps_control: FpsControl, + decode_fps: Arc, } impl Remote { @@ -76,6 +78,7 @@ impl Remote { receiver: mpsc::UnboundedReceiver, sender: mpsc::UnboundedSender, frame_count: Arc, + decode_fps: Arc, ) -> Self { Self { handler, @@ -100,6 +103,8 @@ impl Remote { stop_voice_call_sender: None, voice_call_request_timestamp: None, elevation_requested: false, + fps_control: Default::default(), + decode_fps, } } @@ -147,6 +152,7 @@ impl Remote { let mut rx_clip_client = rx_clip_client_lock.lock().await; let mut status_timer = time::interval(Duration::new(1, 0)); + let mut fps_instant = Instant::now(); loop { tokio::select! { @@ -224,9 +230,18 @@ impl Remote { } } _ = status_timer.tick() => { - let speed = self.data_count.swap(0, Ordering::Relaxed); + self.fps_control(); + let elapsed = fps_instant.elapsed().as_millis(); + if elapsed < 1000 { + continue; + } + fps_instant = Instant::now(); + let mut speed = self.data_count.swap(0, Ordering::Relaxed); + speed = speed * 1000 / elapsed as usize; let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); - let fps = self.frame_count.swap(0, Ordering::Relaxed) as _; + let mut fps = self.frame_count.swap(0, Ordering::Relaxed) as _; + // Correcting the inaccuracy of status_timer + fps = fps * 1000 / elapsed as i32; self.handler.update_quality_status(QualityStatus { speed:Some(speed), fps:Some(fps), @@ -826,6 +841,53 @@ impl Remote { None => false, } } + #[inline] + fn fps_control(&mut self) { + let len = self.video_queue.len(); + let ctl = &mut self.fps_control; + // Current full speed decoding fps + let decode_fps = self.decode_fps.load(std::sync::atomic::Ordering::Relaxed); + // 500ms + let debounce = if decode_fps > 10 { decode_fps / 2 } else { 5 }; + if len < debounce || decode_fps == 0 { + return; + } + let mut refresh = false; + // First setting , or the length of the queue still increases after setting, or exceed the size of the last setting again + if ctl.set_times < 10 // enough + && (ctl.set_times == 0 + || (len > ctl.last_queue_size && ctl.last_set_instant.elapsed().as_secs() > 30)) + { + // 80% fps to ensure decoding is faster than encoding + let mut custom_fps = decode_fps as i32 * 4 / 5; + if custom_fps < 1 { + custom_fps = 1; + } + // send custom fps + let mut misc = Misc::new(); + misc.set_option(OptionMessage { + custom_fps, + ..Default::default() + }); + let mut msg = Message::new(); + msg.set_misc(misc); + self.sender.send(Data::Message(msg)).ok(); + ctl.last_queue_size = len; + ctl.set_times += 1; + ctl.last_set_instant = Instant::now(); + refresh = true; + } + // send refresh + if ctl.refresh_times < 10 // enough + && (refresh + || (len > self.video_queue.len() / 2 + && ctl.last_refresh_instant.elapsed().as_secs() > 30)) + { + self.handler.refresh_video(); + ctl.refresh_times += 1; + ctl.last_refresh_instant = Instant::now(); + } + } async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { if let Ok(msg_in) = Message::parse_from_bytes(&data) { @@ -1489,3 +1551,23 @@ impl RemoveJob { } } } + +struct FpsControl { + last_queue_size: usize, + set_times: usize, + refresh_times: usize, + last_set_instant: Instant, + last_refresh_instant: Instant, +} + +impl Default for FpsControl { + fn default() -> Self { + Self { + last_queue_size: Default::default(), + set_times: Default::default(), + refresh_times: Default::default(), + last_set_instant: Instant::now(), + last_refresh_instant: Instant::now(), + } + } +} diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index 5bb687473..d53053691 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -1,7 +1,7 @@ use super::*; use std::time::Duration; pub const FPS: u8 = 30; -pub const MIN_FPS: u8 = 10; +pub const MIN_FPS: u8 = 1; pub const MAX_FPS: u8 = 120; trait Percent { fn as_percent(&self) -> u32; diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 3a0ae6598..504982f50 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1162,7 +1162,7 @@ pub async fn io_loop(handler: Session) { let frame_count = Arc::new(AtomicUsize::new(0)); let frame_count_cl = frame_count.clone(); let ui_handler = handler.ui_handler.clone(); - let (video_sender, audio_sender, video_queue) = + let (video_sender, audio_sender, video_queue, decode_fps) = start_video_audio_threads(move |data: &mut Vec| { frame_count_cl.fetch_add(1, Ordering::Relaxed); ui_handler.on_rgba(data); @@ -1176,6 +1176,7 @@ pub async fn io_loop(handler: Session) { receiver, sender, frame_count, + decode_fps, ); remote.io_loop(&key, &token).await; remote.sync_jobs_status_to_local().await; From cd294b919c691d41da659eedb5b9694e9894c20b Mon Sep 17 00:00:00 2001 From: RustDesk <71636191+rustdesk@users.noreply.github.com> Date: Sat, 8 Apr 2023 09:20:41 +0800 Subject: [PATCH 237/366] Revert "Update Dockerfile" --- .devcontainer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 08fd65544..32a440b28 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -38,7 +38,7 @@ RUN sudo apt-get install -y libclang-dev RUN sudo apt install -y gcc-multilib WORKDIR $WORKDIR -ENV ANDROID_NDK_HOME=/opt/android/ndk/23.2.8568313 +ENV ANDROID_NDK_HOME=/opt/android/ndk/22.1.7171670 # Somehow try to automate flutter pub get # https://rustdesk.com/docs/en/dev/build/android/ From 4a8d61ac099b11c8edf44831d8969e10430666c2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 15:35:10 +0800 Subject: [PATCH 238/366] Do not sync led, when Control, Shift, Alt, Tab, Enter are pressed Signed-off-by: fufesou --- libs/enigo/src/win/keycodes.rs | 1 + src/core_main.rs | 2 ++ src/server/input_service.rs | 54 +++++++++++++++++++++------------- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/libs/enigo/src/win/keycodes.rs b/libs/enigo/src/win/keycodes.rs index ea35685c5..500582bf0 100644 --- a/libs/enigo/src/win/keycodes.rs +++ b/libs/enigo/src/win/keycodes.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731 // // JP/KR mapping https://github.com/TigerVNC/tigervnc/blob/1a008c1380305648ab50f1d99e73439747e9d61d/vncviewer/win32.c#L267 diff --git a/src/core_main.rs b/src/core_main.rs index a3e11a49e..d6c3bbc59 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -1,3 +1,5 @@ +#[cfg(not(debug_assertions))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::platform::breakdown_callback; use hbb_common::log; #[cfg(not(debug_assertions))] diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 5fb63a597..c9a92e432 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1372,29 +1372,41 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } #[cfg(not(any(target_os = "windows", target_os = "linux")))] -fn is_win_linux_meta_key(_evt: &KeyEvent) -> bool { +fn skip_led_sync(_evt: &KeyEvent) -> bool { false } #[cfg(any(target_os = "windows", target_os = "linux"))] -fn is_win_linux_meta_key(evt: &KeyEvent) -> bool { - match evt.mode.unwrap() { - KeyboardMode::Map | KeyboardMode::Translate => match &evt.union { - Some(key_event::Union::ControlKey(ck)) => { - return *ck == ControlKey::Meta.into(); - } - Some(key_event::Union::Chr(code)) => { - let key = crate::keycode_to_rdev_key(*code); - return key == RdevKey::MetaLeft || key == RdevKey::MetaRight; - } - _ => {} - }, - KeyboardMode::Legacy => match &evt.union { - Some(key_event::Union::ControlKey(ck)) => { - return *ck == ControlKey::Meta.into(); - } - _ => {} - }, +fn skip_led_sync(evt: &KeyEvent) -> bool { + match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { + (Some(key_event::Union::ControlKey(ck)), _) => { + let key = ck.enum_value_or(ControlKey::Unknown); + return [ + ControlKey::Control, + ControlKey::Meta, + ControlKey::Shift, + ControlKey::Alt, + ControlKey::Tab, + ControlKey::Return, + ] + .contains(&key); + } + (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { + let key = crate::keycode_to_rdev_key(*code); + return [ + RdevKey::ControlLeft, + RdevKey::ControlRight, + RdevKey::MetaLeft, + RdevKey::MetaRight, + RdevKey::ShiftRight, + RdevKey::ShiftRight, + RdevKey::Alt, + RdevKey::AltGr, + RdevKey::Tab, + RdevKey::Return, + ] + .contains(&key); + } _ => {} } false @@ -1413,7 +1425,9 @@ pub fn handle_key_(evt: &KeyEvent) { // LockModesHandler should not be created when single meta is pressing and releasing. // Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click. // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687 - if evt.down && !is_win_linux_meta_key(evt) { + // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500415822 + // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500773473 + if evt.down && !skip_led_sync(evt) { Some(LockModesHandler::new(evt)) } else { None From 072da85f09ec230bb6705b52dff06d430ea83cd4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 17:56:04 +0800 Subject: [PATCH 239/366] simple refact Signed-off-by: fufesou --- src/server/input_service.rs | 100 +++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index c9a92e432..0be6f0d84 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1372,44 +1372,48 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } #[cfg(not(any(target_os = "windows", target_os = "linux")))] -fn skip_led_sync(_evt: &KeyEvent) -> bool { +fn skip_led_sync_control_key(_evt: &KeyEvent) -> bool { + false +} + +// LockModesHandler should not be created when single meta is pressing and releasing. +// Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click. +// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687 +// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500415822 +// https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500773473 +#[cfg(any(target_os = "windows", target_os = "linux"))] +fn skip_led_sync_control_key(key: &ControlKey) -> bool { + [ + ControlKey::Control, + ControlKey::Meta, + ControlKey::Shift, + ControlKey::Alt, + ControlKey::Tab, + ControlKey::Return, + ] + .contains(key) +} + +#[cfg(not(any(target_os = "windows", target_os = "linux")))] +fn skip_led_sync_rdev_key(_evt: &KeyEvent) -> bool { false } #[cfg(any(target_os = "windows", target_os = "linux"))] -fn skip_led_sync(evt: &KeyEvent) -> bool { - match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { - (Some(key_event::Union::ControlKey(ck)), _) => { - let key = ck.enum_value_or(ControlKey::Unknown); - return [ - ControlKey::Control, - ControlKey::Meta, - ControlKey::Shift, - ControlKey::Alt, - ControlKey::Tab, - ControlKey::Return, - ] - .contains(&key); - } - (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { - let key = crate::keycode_to_rdev_key(*code); - return [ - RdevKey::ControlLeft, - RdevKey::ControlRight, - RdevKey::MetaLeft, - RdevKey::MetaRight, - RdevKey::ShiftRight, - RdevKey::ShiftRight, - RdevKey::Alt, - RdevKey::AltGr, - RdevKey::Tab, - RdevKey::Return, - ] - .contains(&key); - } - _ => {} - } - false +fn skip_led_sync_rdev_key(key: &RdevKey) -> bool { + [ + RdevKey::ControlLeft, + RdevKey::ControlRight, + RdevKey::MetaLeft, + RdevKey::MetaRight, + RdevKey::ShiftRight, + RdevKey::ShiftRight, + RdevKey::Alt, + RdevKey::AltGr, + RdevKey::Tab, + RdevKey::Return, + ] + .contains(key) } pub fn handle_key_(evt: &KeyEvent) { @@ -1417,22 +1421,24 @@ pub fn handle_key_(evt: &KeyEvent) { return; } - let _lock_mode_handler = match &evt.union { - Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)) => { - Some(LockModesHandler::new(&evt)) + let mut _lock_mode_handler = None; + match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { + (Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)), _) => { + _lock_mode_handler = Some(LockModesHandler::new(&evt)); } - _ => { - // LockModesHandler should not be created when single meta is pressing and releasing. - // Because the drop function may insert "CapsLock Click" and "NumLock Click", which breaks single meta click. - // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1496936687 - // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500415822 - // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500773473 - if evt.down && !skip_led_sync(evt) { - Some(LockModesHandler::new(evt)) - } else { - None + (Some(key_event::Union::ControlKey(ck)), _) => { + let key = ck.enum_value_or(ControlKey::Unknown); + if !skip_led_sync_control_key(&key) { + _lock_mode_handler = Some(LockModesHandler::new(&evt)); } } + (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { + let key = crate::keycode_to_rdev_key(*code); + if !skip_led_sync_rdev_key(&key) { + _lock_mode_handler = Some(LockModesHandler::new(evt)); + } + } + _ => {} }; match evt.mode.unwrap() { From f72593c28116dd3c201ebcb8c598778dd3e2e488 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 19:07:24 +0800 Subject: [PATCH 240/366] tmp commit Signed-off-by: fufesou --- libs/enigo/src/win/win_impl.rs | 6 +- src/keyboard.rs | 71 ++++++++++++++++++---- src/lib.rs | 2 +- src/server/input_service.rs | 105 ++++++++++++++++----------------- 4 files changed, 114 insertions(+), 70 deletions(-) diff --git a/libs/enigo/src/win/win_impl.rs b/libs/enigo/src/win/win_impl.rs index a2839621c..f183e94ae 100644 --- a/libs/enigo/src/win/win_impl.rs +++ b/libs/enigo/src/win/win_impl.rs @@ -359,9 +359,6 @@ impl Enigo { } fn key_to_keycode(&self, key: Key) -> u16 { - unsafe { - LAYOUT = std::ptr::null_mut(); - } // do not use the codes from crate winapi they're // wrongly typed with i32 instead of i16 use the // ones provided by win/keycodes.rs that are prefixed @@ -456,6 +453,9 @@ impl Enigo { } fn get_layoutdependent_keycode(&self, chr: char) -> u16 { + unsafe { + LAYOUT = std::ptr::null_mut(); + } // NOTE VkKeyScanW uses the current keyboard LAYOUT // to specify a LAYOUT use VkKeyScanExW and GetKeyboardLayout // or load one with LoadKeyboardLayoutW diff --git a/src/keyboard.rs b/src/keyboard.rs index 15629bb00..7fa00da80 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -364,26 +364,73 @@ pub fn is_modifier(key: &rdev::Key) -> bool { ) } +#[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn is_numpad_rdev_key(key: &rdev::Key) -> bool { + matches!( + key, + Key::Kp0 + | Key::Kp1 + | Key::Kp2 + | Key::Kp3 + | Key::Kp4 + | Key::Kp5 + | Key::Kp6 + | Key::Kp7 + | Key::Kp8 + | Key::Kp9 + | Key::KpMinus + | Key::KpMultiply + | Key::KpDivide + | Key::KpPlus + | Key::KpDecimal + ) +} + +#[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn is_letter_rdev_key(key: &rdev::Key) -> bool { + matches!( + key, + Key::KeyA + | Key::KeyB + | Key::KeyC + | Key::KeyD + | Key::KeyE + | Key::KeyF + | Key::KeyG + | Key::KeyH + | Key::KeyI + | Key::KeyJ + | Key::KeyK + | Key::KeyL + | Key::KeyM + | Key::KeyN + | Key::KeyO + | Key::KeyP + | Key::KeyQ + | Key::KeyR + | Key::KeyS + | Key::KeyT + | Key::KeyU + | Key::KeyV + | Key::KeyW + | Key::KeyX + | Key::KeyY + | Key::KeyZ + ) +} + #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] fn is_numpad_key(event: &Event) -> bool { - matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { - Key::Kp0 | Key::Kp1 | Key::Kp2 | Key::Kp3 | Key::Kp4 | Key::Kp5 | Key::Kp6 | Key::Kp7 | Key::Kp8 | - Key::Kp9 | Key::KpMinus | Key::KpMultiply | Key::KpDivide | Key::KpPlus | Key::KpDecimal => true, - _ => false - }) + matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if is_numpad_rdev_key(&key)) } #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] fn is_letter_key(event: &Event) -> bool { - matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if match key { - Key::KeyA | Key::KeyB | Key::KeyC | Key::KeyD | Key::KeyE | Key::KeyF | Key::KeyG | Key::KeyH | - Key::KeyI | Key::KeyJ | Key::KeyK | Key::KeyL | Key::KeyM | Key::KeyN | Key::KeyO | Key::KeyP | - Key::KeyQ | Key::KeyR | Key::KeyS | Key::KeyT | Key::KeyU | Key::KeyV | Key::KeyW | Key::KeyX | - Key::KeyY | Key::KeyZ => true, - _ => false - }) + matches!(event.event_type, EventType::KeyPress(key) | EventType::KeyRelease(key) if is_letter_rdev_key(&key)) } #[cfg(not(any(target_os = "android", target_os = "ios")))] diff --git a/src/lib.rs b/src/lib.rs index 45b4c63f3..f8ee744a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ pub mod platform; mod keyboard; #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub use keyboard::keycode_to_rdev_key; +pub use keyboard::{keycode_to_rdev_key, is_numpad_rdev_key, is_letter_rdev_key}; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; #[cfg(not(any(target_os = "ios")))] diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 0be6f0d84..12b625f9d 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -8,13 +8,14 @@ use hbb_common::{config::COMPRESS_LEVEL, get_time, protobuf::EnumOrUnknown}; use rdev::{self, EventType, Key as RdevKey, KeyCode, RawKey}; #[cfg(target_os = "macos")] use rdev::{CGEventSourceStateID, CGEventTapLocation, VirtualInput}; -use std::time::Duration; +#[cfg(any(target_os = "windows", target_os = "linux"))] +use std::collections::HashSet; use std::{ convert::TryFrom, ops::Sub, sync::atomic::{AtomicBool, Ordering}, thread, - time::{self, Instant}, + time::{self, Duration, Instant}, }; #[cfg(target_os = "windows")] use winapi::um::winuser::{ @@ -153,7 +154,15 @@ impl LockModesHandler { #[cfg(not(target_os = "windows"))] let disable_numlock = false; #[cfg(target_os = "windows")] - let disable_numlock = is_numlock_disabled(key_event); + let disable_numlock = if key_event.mode == KeyboardMode::Legacy.into() { + // disable numlock if press home etc when numlock is on, + // because we will get numpad value (7,8,9 etc) if not + has_numpad_key(key_event) + } else { + false + }; + let disable_numlock = false; + println!("REMOVE ME ======================= event_num_enabled {}, local_num_enabled {}", event_num_enabled, local_num_enabled); let num_lock_changed = event_num_enabled != local_num_enabled && !disable_numlock; if num_lock_changed { en.key_click(enigo::Key::NumLock); @@ -1020,35 +1029,6 @@ fn has_numpad_key(key_event: &KeyEvent) -> bool { != 0 } -#[cfg(target_os = "windows")] -fn is_rdev_numpad_key(key_event: &KeyEvent) -> bool { - let code = key_event.chr(); - let key = rdev::get_win_key(code, 0); - match key { - RdevKey::Home - | RdevKey::UpArrow - | RdevKey::PageUp - | RdevKey::LeftArrow - | RdevKey::RightArrow - | RdevKey::End - | RdevKey::DownArrow - | RdevKey::PageDown - | RdevKey::Insert - | RdevKey::Delete => true, - _ => false, - } -} - -#[cfg(target_os = "windows")] -fn is_numlock_disabled(key_event: &KeyEvent) -> bool { - // disable numlock if press home etc when numlock is on, - // because we will get numpad value (7,8,9 etc) if not - match key_event.mode.unwrap() { - KeyboardMode::Map => is_rdev_numpad_key(key_event), - _ => has_numpad_key(key_event), - } -} - fn map_keyboard_mode(evt: &KeyEvent) { #[cfg(windows)] crate::platform::windows::try_change_desktop(); @@ -1383,15 +1363,29 @@ fn skip_led_sync_control_key(_evt: &KeyEvent) -> bool { // https://github.com/rustdesk/rustdesk/issues/3928#issuecomment-1500773473 #[cfg(any(target_os = "windows", target_os = "linux"))] fn skip_led_sync_control_key(key: &ControlKey) -> bool { - [ - ControlKey::Control, - ControlKey::Meta, - ControlKey::Shift, - ControlKey::Alt, - ControlKey::Tab, - ControlKey::Return, - ] - .contains(key) + matches!( + key, + ControlKey::Control + | ControlKey::RControl + | ControlKey::Meta + | ControlKey::Shift + | ControlKey::RShift + | ControlKey::Alt + | ControlKey::RAlt + | ControlKey::Tab + | ControlKey::Return + | ControlKey::Numpad0 + | ControlKey::Numpad1 + | ControlKey::Numpad2 + | ControlKey::Numpad3 + | ControlKey::Numpad4 + | ControlKey::Numpad5 + | ControlKey::Numpad6 + | ControlKey::Numpad7 + | ControlKey::Numpad8 + | ControlKey::Numpad9 + | ControlKey::NumpadEnter + ) } #[cfg(not(any(target_os = "windows", target_os = "linux")))] @@ -1401,19 +1395,20 @@ fn skip_led_sync_rdev_key(_evt: &KeyEvent) -> bool { #[cfg(any(target_os = "windows", target_os = "linux"))] fn skip_led_sync_rdev_key(key: &RdevKey) -> bool { - [ - RdevKey::ControlLeft, - RdevKey::ControlRight, - RdevKey::MetaLeft, - RdevKey::MetaRight, - RdevKey::ShiftRight, - RdevKey::ShiftRight, - RdevKey::Alt, - RdevKey::AltGr, - RdevKey::Tab, - RdevKey::Return, - ] - .contains(key) + crate::is_numpad_rdev_key(key) + || matches!( + key, + RdevKey::ControlLeft + | RdevKey::ControlRight + | RdevKey::MetaLeft + | RdevKey::MetaRight + | RdevKey::ShiftLeft + | RdevKey::ShiftRight + | RdevKey::Alt + | RdevKey::AltGr + | RdevKey::Tab + | RdevKey::Return + ) } pub fn handle_key_(evt: &KeyEvent) { @@ -1421,6 +1416,8 @@ pub fn handle_key_(evt: &KeyEvent) { return; } + println!("REMOVE ME ============================== {:?}", &evt); + let mut _lock_mode_handler = None; match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { (Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)), _) => { From e2dbfb7f1b26f75f37f930b35382231c65904835 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 19:48:27 +0800 Subject: [PATCH 241/366] refact keyboard handler Signed-off-by: fufesou --- src/server/input_service.rs | 72 +++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 12b625f9d..60c4ad8bc 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -140,7 +140,7 @@ impl LockModesHandler { } #[cfg(not(target_os = "macos"))] - fn new(key_event: &KeyEvent) -> Self { + fn new(key_event: &KeyEvent, is_numpad_key: bool) -> Self { let mut en = ENIGO.lock().unwrap(); let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock); let local_caps_enabled = en.get_key_state(enigo::Key::CapsLock); @@ -154,16 +154,9 @@ impl LockModesHandler { #[cfg(not(target_os = "windows"))] let disable_numlock = false; #[cfg(target_os = "windows")] - let disable_numlock = if key_event.mode == KeyboardMode::Legacy.into() { - // disable numlock if press home etc when numlock is on, - // because we will get numpad value (7,8,9 etc) if not - has_numpad_key(key_event) - } else { - false - }; - let disable_numlock = false; - println!("REMOVE ME ======================= event_num_enabled {}, local_num_enabled {}", event_num_enabled, local_num_enabled); - let num_lock_changed = event_num_enabled != local_num_enabled && !disable_numlock; + let disable_numlock = is_numlock_disabled(key_event); + let num_lock_changed = + is_numpad_key && event_num_enabled != local_num_enabled && !disable_numlock; if num_lock_changed { en.key_click(enigo::Key::NumLock); } @@ -213,6 +206,18 @@ impl Drop for LockModesHandler { } } +#[inline] +#[cfg(target_os = "windows")] +fn is_numlock_disabled(key_event: &KeyEvent) -> bool { + // disable numlock if press home etc when numlock is on, + // because we will get numpad value (7,8,9 etc) if not + if key_event.mode.enum_value_or(KeyboardMode::Legacy) == KeyboardMode::Legacy { + has_numpad_key(key_event) + } else { + false + } +} + pub const NAME_CURSOR: &'static str = "mouse_cursor"; pub const NAME_POS: &'static str = "mouse_pos"; pub type MouseCursorService = ServiceTmpl; @@ -1374,7 +1379,14 @@ fn skip_led_sync_control_key(key: &ControlKey) -> bool { | ControlKey::RAlt | ControlKey::Tab | ControlKey::Return - | ControlKey::Numpad0 + ) +} + +#[inline] +fn is_numpad_control_key(key: &ControlKey) -> bool { + matches!( + key, + ControlKey::Numpad0 | ControlKey::Numpad1 | ControlKey::Numpad2 | ControlKey::Numpad3 @@ -1395,20 +1407,19 @@ fn skip_led_sync_rdev_key(_evt: &KeyEvent) -> bool { #[cfg(any(target_os = "windows", target_os = "linux"))] fn skip_led_sync_rdev_key(key: &RdevKey) -> bool { - crate::is_numpad_rdev_key(key) - || matches!( - key, - RdevKey::ControlLeft - | RdevKey::ControlRight - | RdevKey::MetaLeft - | RdevKey::MetaRight - | RdevKey::ShiftLeft - | RdevKey::ShiftRight - | RdevKey::Alt - | RdevKey::AltGr - | RdevKey::Tab - | RdevKey::Return - ) + matches!( + key, + RdevKey::ControlLeft + | RdevKey::ControlRight + | RdevKey::MetaLeft + | RdevKey::MetaRight + | RdevKey::ShiftLeft + | RdevKey::ShiftRight + | RdevKey::Alt + | RdevKey::AltGr + | RdevKey::Tab + | RdevKey::Return + ) } pub fn handle_key_(evt: &KeyEvent) { @@ -1416,23 +1427,22 @@ pub fn handle_key_(evt: &KeyEvent) { return; } - println!("REMOVE ME ============================== {:?}", &evt); - let mut _lock_mode_handler = None; match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { (Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)), _) => { - _lock_mode_handler = Some(LockModesHandler::new(&evt)); + _lock_mode_handler = Some(LockModesHandler::new(&evt, false)); } (Some(key_event::Union::ControlKey(ck)), _) => { let key = ck.enum_value_or(ControlKey::Unknown); if !skip_led_sync_control_key(&key) { - _lock_mode_handler = Some(LockModesHandler::new(&evt)); + _lock_mode_handler = Some(LockModesHandler::new(&evt, is_numpad_control_key(&key))); } } (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { let key = crate::keycode_to_rdev_key(*code); if !skip_led_sync_rdev_key(&key) { - _lock_mode_handler = Some(LockModesHandler::new(evt)); + _lock_mode_handler = + Some(LockModesHandler::new(evt, crate::is_numpad_rdev_key(&key))); } } _ => {} From fa02246e1a548edc1c6941fd4596476184f13e8b Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 20:14:43 +0800 Subject: [PATCH 242/366] fix build Signed-off-by: fufesou --- src/server/input_service.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 60c4ad8bc..6c307204d 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -124,13 +124,13 @@ impl Subscriber for MouseCursorSub { } } -#[cfg(not(target_os = "macos"))] +#[cfg(any(target_os = "windows", target_os = "linux"))] struct LockModesHandler { caps_lock_changed: bool, num_lock_changed: bool, } -#[cfg(target_os = "macos")] +#[cfg(not(any(target_os = "windows", target_os = "linux")))] struct LockModesHandler; impl LockModesHandler { @@ -139,7 +139,7 @@ impl LockModesHandler { key_event.modifiers.contains(&modifier.into()) } - #[cfg(not(target_os = "macos"))] + #[cfg(any(target_os = "windows", target_os = "linux"))] fn new(key_event: &KeyEvent, is_numpad_key: bool) -> Self { let mut en = ENIGO.lock().unwrap(); let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock); @@ -167,8 +167,8 @@ impl LockModesHandler { } } - #[cfg(target_os = "macos")] - fn new(key_event: &KeyEvent) -> Self { + #[cfg(not(any(target_os = "windows", target_os = "linux")))] + fn new(key_event: &KeyEvent, _is_numpad_key: bool) -> Self { let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock); // Do not use the following code to detect `local_caps_enabled`. // Because the state of get_key_state will not affect simuation of `VIRTUAL_INPUT_STATE` in this file. @@ -193,7 +193,7 @@ impl LockModesHandler { } } -#[cfg(not(target_os = "macos"))] +#[cfg(any(target_os = "windows", target_os = "linux"))] impl Drop for LockModesHandler { fn drop(&mut self) { let mut en = ENIGO.lock().unwrap(); @@ -1357,7 +1357,7 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } #[cfg(not(any(target_os = "windows", target_os = "linux")))] -fn skip_led_sync_control_key(_evt: &KeyEvent) -> bool { +fn skip_led_sync_control_key(_key: &ControlKey) -> bool { false } @@ -1401,7 +1401,7 @@ fn is_numpad_control_key(key: &ControlKey) -> bool { } #[cfg(not(any(target_os = "windows", target_os = "linux")))] -fn skip_led_sync_rdev_key(_evt: &KeyEvent) -> bool { +fn skip_led_sync_rdev_key(_key: &RdevKey) -> bool { false } @@ -1427,7 +1427,9 @@ pub fn handle_key_(evt: &KeyEvent) { return; } + #[cfg(not(any(target_os = "android", target_os = "ios")))] let mut _lock_mode_handler = None; + #[cfg(not(any(target_os = "android", target_os = "ios")))] match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { (Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)), _) => { _lock_mode_handler = Some(LockModesHandler::new(&evt, false)); From d66ad5e0cb2c9fea9d71324d2bc9bf86ff230b36 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 20:17:55 +0800 Subject: [PATCH 243/366] remove unused Signed-off-by: fufesou --- src/server/input_service.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 6c307204d..ea29f67b0 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -8,8 +8,6 @@ use hbb_common::{config::COMPRESS_LEVEL, get_time, protobuf::EnumOrUnknown}; use rdev::{self, EventType, Key as RdevKey, KeyCode, RawKey}; #[cfg(target_os = "macos")] use rdev::{CGEventSourceStateID, CGEventTapLocation, VirtualInput}; -#[cfg(any(target_os = "windows", target_os = "linux"))] -use std::collections::HashSet; use std::{ convert::TryFrom, ops::Sub, From 0d5d073a43df54f82384a2732d342cbb54ae0d22 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 20:28:34 +0800 Subject: [PATCH 244/366] trivial changes Signed-off-by: fufesou --- src/keyboard.rs | 1 + src/lib.rs | 2 -- src/server/input_service.rs | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 7fa00da80..d683fd579 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -350,6 +350,7 @@ pub fn get_keyboard_mode_enum() -> KeyboardMode { } #[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn is_modifier(key: &rdev::Key) -> bool { matches!( key, diff --git a/src/lib.rs b/src/lib.rs index f8ee744a0..5dcd6389c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,8 +3,6 @@ pub mod platform; mod keyboard; #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub use keyboard::{keycode_to_rdev_key, is_numpad_rdev_key, is_letter_rdev_key}; -#[cfg(not(any(target_os = "android", target_os = "ios")))] pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; #[cfg(not(any(target_os = "ios")))] /// cbindgen:ignore diff --git a/src/server/input_service.rs b/src/server/input_service.rs index ea29f67b0..a3f62b20e 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1439,10 +1439,10 @@ pub fn handle_key_(evt: &KeyEvent) { } } (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { - let key = crate::keycode_to_rdev_key(*code); + let key = crate::keyboard::keycode_to_rdev_key(*code); if !skip_led_sync_rdev_key(&key) { _lock_mode_handler = - Some(LockModesHandler::new(evt, crate::is_numpad_rdev_key(&key))); + Some(LockModesHandler::new(evt, crate::keyboard::is_numpad_rdev_key(&key))); } } _ => {} From 20734f278ee07c1708ee4f2dc22f80e00d074ba5 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 21:24:29 +0800 Subject: [PATCH 245/366] remove unused call Signed-off-by: fufesou --- src/server/input_service.rs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index a3f62b20e..e64baad70 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -128,7 +128,7 @@ struct LockModesHandler { num_lock_changed: bool, } -#[cfg(not(any(target_os = "windows", target_os = "linux")))] +#[cfg(target_os = "macos")] struct LockModesHandler; impl LockModesHandler { @@ -137,6 +137,19 @@ impl LockModesHandler { key_event.modifiers.contains(&modifier.into()) } + #[inline] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + fn new_handler(key_event: &KeyEvent, _is_numpad_key: bool) -> Self { + #[cfg(any(target_os = "windows", target_os = "linux"))] + { + Self::new(key_event, _is_numpad_key) + } + #[cfg(target_os = "macos")] + { + Self::new(key_event) + } + } + #[cfg(any(target_os = "windows", target_os = "linux"))] fn new(key_event: &KeyEvent, is_numpad_key: bool) -> Self { let mut en = ENIGO.lock().unwrap(); @@ -165,8 +178,8 @@ impl LockModesHandler { } } - #[cfg(not(any(target_os = "windows", target_os = "linux")))] - fn new(key_event: &KeyEvent, _is_numpad_key: bool) -> Self { + #[cfg(target_os = "macos")] + fn new(key_event: &KeyEvent) -> Self { let event_caps_enabled = Self::is_modifier_enabled(key_event, ControlKey::CapsLock); // Do not use the following code to detect `local_caps_enabled`. // Because the state of get_key_state will not affect simuation of `VIRTUAL_INPUT_STATE` in this file. @@ -1430,19 +1443,26 @@ pub fn handle_key_(evt: &KeyEvent) { #[cfg(not(any(target_os = "android", target_os = "ios")))] match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { (Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)), _) => { - _lock_mode_handler = Some(LockModesHandler::new(&evt, false)); + _lock_mode_handler = Some(LockModesHandler::new_handler(&evt, false)); } (Some(key_event::Union::ControlKey(ck)), _) => { let key = ck.enum_value_or(ControlKey::Unknown); if !skip_led_sync_control_key(&key) { - _lock_mode_handler = Some(LockModesHandler::new(&evt, is_numpad_control_key(&key))); + #[cfg(target_os = "macos")] + let is_numpad_key = false; + #[cfg(any(target_os = "windows", target_os = "linux"))] + let is_numpad_key = is_numpad_control_key(&key); + _lock_mode_handler = Some(LockModesHandler::new_handler(&evt, is_numpad_key)); } } (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { let key = crate::keyboard::keycode_to_rdev_key(*code); if !skip_led_sync_rdev_key(&key) { - _lock_mode_handler = - Some(LockModesHandler::new(evt, crate::keyboard::is_numpad_rdev_key(&key))); + #[cfg(target_os = "macos")] + let is_numpad_key = false; + #[cfg(any(target_os = "windows", target_os = "linux"))] + let is_numpad_key = crate::keyboard::is_numpad_rdev_key(&key); + _lock_mode_handler = Some(LockModesHandler::new_handler(evt, is_numpad_key)); } } _ => {} From cee06f04252d4e0545a394dc9386b016c56a62c3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 21:26:19 +0800 Subject: [PATCH 246/366] remove warn Signed-off-by: fufesou --- src/server/input_service.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index e64baad70..431a54362 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1394,6 +1394,7 @@ fn skip_led_sync_control_key(key: &ControlKey) -> bool { } #[inline] +#[cfg(any(target_os = "windows", target_os = "linux"))] fn is_numpad_control_key(key: &ControlKey) -> bool { matches!( key, From 09ba9cddcd520d23b5fe1facd210bacdf67be057 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 22:06:24 +0800 Subject: [PATCH 247/366] debug, win, legacy mode, led sync Signed-off-by: fufesou --- src/server/input_service.rs | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 431a54362..d60574852 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -222,7 +222,7 @@ impl Drop for LockModesHandler { fn is_numlock_disabled(key_event: &KeyEvent) -> bool { // disable numlock if press home etc when numlock is on, // because we will get numpad value (7,8,9 etc) if not - if key_event.mode.enum_value_or(KeyboardMode::Legacy) == KeyboardMode::Legacy { + if is_legacy_mode(&key_event) { has_numpad_key(key_event) } else { false @@ -1434,6 +1434,12 @@ fn skip_led_sync_rdev_key(key: &RdevKey) -> bool { ) } +#[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +fn is_legacy_mode(evt: &KeyEvent) -> bool { + evt.mode.enum_value_or(KeyboardMode::Legacy) == KeyboardMode::Legacy +} + pub fn handle_key_(evt: &KeyEvent) { if EXITING.load(Ordering::SeqCst) { return; @@ -1442,11 +1448,11 @@ pub fn handle_key_(evt: &KeyEvent) { #[cfg(not(any(target_os = "android", target_os = "ios")))] let mut _lock_mode_handler = None; #[cfg(not(any(target_os = "android", target_os = "ios")))] - match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { - (Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)), _) => { + match &evt.union { + Some(key_event::Union::Unicode(..)) | Some(key_event::Union::Seq(..)) => { _lock_mode_handler = Some(LockModesHandler::new_handler(&evt, false)); } - (Some(key_event::Union::ControlKey(ck)), _) => { + Some(key_event::Union::ControlKey(ck)) => { let key = ck.enum_value_or(ControlKey::Unknown); if !skip_led_sync_control_key(&key) { #[cfg(target_os = "macos")] @@ -1456,14 +1462,18 @@ pub fn handle_key_(evt: &KeyEvent) { _lock_mode_handler = Some(LockModesHandler::new_handler(&evt, is_numpad_key)); } } - (Some(key_event::Union::Chr(code)), KeyboardMode::Map | KeyboardMode::Translate) => { - let key = crate::keyboard::keycode_to_rdev_key(*code); - if !skip_led_sync_rdev_key(&key) { - #[cfg(target_os = "macos")] - let is_numpad_key = false; - #[cfg(any(target_os = "windows", target_os = "linux"))] - let is_numpad_key = crate::keyboard::is_numpad_rdev_key(&key); - _lock_mode_handler = Some(LockModesHandler::new_handler(evt, is_numpad_key)); + Some(key_event::Union::Chr(code)) => { + if is_legacy_mode(&evt) { + _lock_mode_handler = Some(LockModesHandler::new_handler(evt, false)); + } else { + let key = crate::keyboard::keycode_to_rdev_key(*code); + if !skip_led_sync_rdev_key(&key) { + #[cfg(target_os = "macos")] + let is_numpad_key = false; + #[cfg(any(target_os = "windows", target_os = "linux"))] + let is_numpad_key = crate::keyboard::is_numpad_rdev_key(&key); + _lock_mode_handler = Some(LockModesHandler::new_handler(evt, is_numpad_key)); + } } } _ => {} From 5e8f28ae4c938811e15cbc94d83ee80154ee7eae Mon Sep 17 00:00:00 2001 From: Andrzej Rudnik Date: Sat, 8 Apr 2023 16:40:38 +0200 Subject: [PATCH 248/366] Update pl.rs --- src/lang/pl.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 73f81ffc1..4d6f7efeb 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -481,15 +481,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("show_monitors_tip", "Pokaż monitory w zasobniku"), ("View Mode", "Tryb widoku"), ("login_linux_tip", "Zaloguj do zdalnego konta Linux"), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("verify_rustdesk_password_tip", "Weryfikuj hasło RustDesk"), + ("remember_account_tip", "Zapamiętaj to konto"), + ("os_account_desk_tip", "To konto jest używane do logowania do zdalnych systemów i włącza bezobsługowe sesje pulpitu"), + ("OS Account", "Konto systemowe"), + ("another_user_login_title_tip", "Zalogowany jest inny użytkownik"), + ("another_user_login_text_tip", "Rozłącz"), + ("xorg_not_found_title_tip", "Nie znaleziono Xorg"), + ("xorg_not_found_text_tip", "Proszę zainstalować Xorg"), + ("no_desktop_title_tip", "Pulpit jest niedostępny"), + ("no_desktop_text_tip", "Proszę zainstalować pulpit GNOME"), ].iter().cloned().collect(); } From 09f9b515f6b84b48f618c949cd8bbb623269ff67 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sat, 8 Apr 2023 22:54:36 +0800 Subject: [PATCH 249/366] remove unused call Signed-off-by: fufesou --- src/server/input_service.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index d60574852..ddd7a0149 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -160,14 +160,16 @@ impl LockModesHandler { en.key_click(enigo::Key::CapsLock); } - let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock); - let local_num_enabled = en.get_key_state(enigo::Key::NumLock); - #[cfg(not(target_os = "windows"))] - let disable_numlock = false; - #[cfg(target_os = "windows")] - let disable_numlock = is_numlock_disabled(key_event); - let num_lock_changed = - is_numpad_key && event_num_enabled != local_num_enabled && !disable_numlock; + let mut num_lock_changed = false; + if is_numpad_key { + let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock); + let local_num_enabled = en.get_key_state(enigo::Key::NumLock); + #[cfg(not(target_os = "windows"))] + let disable_numlock = false; + #[cfg(target_os = "windows")] + let disable_numlock = is_numlock_disabled(key_event); + num_lock_changed = event_num_enabled != local_num_enabled && !disable_numlock; + } if num_lock_changed { en.key_click(enigo::Key::NumLock); } From 579b21066fad175646b57aa3542a8346800c9d91 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 9 Apr 2023 00:06:17 +0800 Subject: [PATCH 250/366] fix, win, legacy mode, shift + home Signed-off-by: fufesou --- src/server/input_service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index ddd7a0149..788c1ed62 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -161,7 +161,7 @@ impl LockModesHandler { } let mut num_lock_changed = false; - if is_numpad_key { + if is_numpad_key || is_legacy_mode(key_event) { let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock); let local_num_enabled = en.get_key_state(enigo::Key::NumLock); #[cfg(not(target_os = "windows"))] From 241cb1d24ee5a4919dc72dff4157c9fea0494a2c Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 9 Apr 2023 00:36:59 +0800 Subject: [PATCH 251/366] trivial changes Signed-off-by: fufesou --- src/server/input_service.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 788c1ed62..2b1f16886 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -161,7 +161,7 @@ impl LockModesHandler { } let mut num_lock_changed = false; - if is_numpad_key || is_legacy_mode(key_event) { + if is_numpad_key { let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock); let local_num_enabled = en.get_key_state(enigo::Key::NumLock); #[cfg(not(target_os = "windows"))] @@ -1461,7 +1461,12 @@ pub fn handle_key_(evt: &KeyEvent) { let is_numpad_key = false; #[cfg(any(target_os = "windows", target_os = "linux"))] let is_numpad_key = is_numpad_control_key(&key); - _lock_mode_handler = Some(LockModesHandler::new_handler(&evt, is_numpad_key)); + // Legacy mode need to disable numlock if home/end/arraws/page down/page up + // are pressed. + _lock_mode_handler = Some(LockModesHandler::new_handler( + &evt, + is_numpad_key || is_legacy_mode(evt), + )); } } Some(key_event::Union::Chr(code)) => { From 217570f47685e503ab187f77608f8d0328b05cd4 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 9 Apr 2023 08:52:22 +0800 Subject: [PATCH 252/366] fix fps control refresh flicking Signed-off-by: 21pages --- src/client/io_loop.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index f7530cc37..18a8bfe3d 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -852,7 +852,6 @@ impl Remote { if len < debounce || decode_fps == 0 { return; } - let mut refresh = false; // First setting , or the length of the queue still increases after setting, or exceed the size of the last setting again if ctl.set_times < 10 // enough && (ctl.set_times == 0 @@ -875,14 +874,14 @@ impl Remote { ctl.last_queue_size = len; ctl.set_times += 1; ctl.last_set_instant = Instant::now(); - refresh = true; } // send refresh if ctl.refresh_times < 10 // enough - && (refresh - || (len > self.video_queue.len() / 2 - && ctl.last_refresh_instant.elapsed().as_secs() > 30)) + && (len > self.video_queue.capacity() / 2 + && (ctl.refresh_times == 0 || ctl.last_refresh_instant.elapsed().as_secs() > 30)) { + // Refresh causes client set_display, left frames cause flickering. + while let Some(_) = self.video_queue.pop() {} self.handler.refresh_video(); ctl.refresh_times += 1; ctl.last_refresh_instant = Instant::now(); From afe07dde6f980f85f6a685dfc428099e8c456d01 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 9 Apr 2023 11:35:14 +0800 Subject: [PATCH 253/366] do not enable linux_headless when flatpak or appimage is enabled Signed-off-by: fufesou --- src/platform/mod.rs | 1 + src/rendezvous_mediator.rs | 2 ++ src/server/connection.rs | 17 +++++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/src/platform/mod.rs b/src/platform/mod.rs index fc27be098..e382b0b13 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -18,6 +18,7 @@ pub mod delegate; pub mod linux; #[cfg(all(target_os = "linux", feature = "linux_headless"))] +#[cfg(not(any(feature = "flatpak", feature = "appimage")))] pub mod linux_desktop_manager; #[cfg(not(any(target_os = "android", target_os = "ios")))] diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index 6dd0a1284..b6422e9a6 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -73,6 +73,7 @@ impl RendezvousMediator { }); } #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] crate::platform::linux_desktop_manager::start_xdesktop(); loop { Config::reset_online(); @@ -101,6 +102,7 @@ impl RendezvousMediator { // It should be better to call stop_xdesktop. // But for server, it also is Ok without calling this method. // #[cfg(all(target_os = "linux", feature = "linux_headless"))] + // #[cfg(not(any(feature = "flatpak", feature = "appimage")))] // crate::platform::linux_desktop_manager::stop_xdesktop(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index cf962d983..318424b3d 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -4,6 +4,7 @@ use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::update_clipboard; #[cfg(all(target_os = "linux", feature = "linux_headless"))] +#[cfg(not(any(feature = "flatpak", feature = "appimage")))] use crate::platform::linux_desktop_manager; #[cfg(windows)] use crate::portable_service::client as portable_client; @@ -19,6 +20,7 @@ use crate::{common::DEVICE_NAME, flutter::connection_manager::start_channel}; use crate::{ipc, VERSION}; use cidr_utils::cidr::IpCidr; #[cfg(all(target_os = "linux", feature = "linux_headless"))] +#[cfg(not(any(feature = "flatpak", feature = "appimage")))] use hbb_common::platform::linux::run_cmds; use hbb_common::{ config::Config, @@ -158,8 +160,10 @@ pub struct Connection { #[cfg(not(any(target_os = "android", target_os = "ios")))] pressed_modifiers: HashSet, #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] rx_cm_stream_ready: mpsc::Receiver<()>, #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] tx_desktop_ready: mpsc::Sender<()>, } @@ -279,8 +283,10 @@ impl Connection { #[cfg(not(any(target_os = "android", target_os = "ios")))] pressed_modifiers: Default::default(), #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] rx_cm_stream_ready: _rx_cm_stream_ready, #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] tx_desktop_ready: _tx_desktop_ready, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -895,6 +901,7 @@ impl Connection { platform_additions.insert("is_wayland".into(), json!(true)); } #[cfg(feature = "linux_headless")] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if linux_desktop_manager::is_headless() { platform_additions.insert("headless".into(), json!(true)); } @@ -1262,6 +1269,7 @@ impl Connection { } #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] let desktop_err = match lr.os_login.as_ref() { Some(os_login) => { linux_desktop_manager::try_start_desktop(&os_login.username, &os_login.password) @@ -1269,12 +1277,15 @@ impl Connection { None => linux_desktop_manager::try_start_desktop("", ""), }; #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] let is_headless = linux_desktop_manager::is_headless(); #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] let wait_ipc_timeout = 10_000; // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; return true; @@ -1299,6 +1310,7 @@ impl Connection { return false; } else if self.is_recent_session() { #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if desktop_err.is_empty() { #[cfg(target_os = "linux")] if is_headless { @@ -1323,6 +1335,7 @@ impl Connection { } } else if lr.password.is_empty() { #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if desktop_err.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { @@ -1372,6 +1385,7 @@ impl Connection { .unwrap() .insert(self.ip.clone(), failure); #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if desktop_err.is_empty() { self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; self.try_start_cm(lr.my_id, lr.my_name, false); @@ -1389,6 +1403,7 @@ impl Connection { LOGIN_FAILURES.lock().unwrap().remove(&self.ip); } #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if desktop_err.is_empty() { #[cfg(target_os = "linux")] if is_headless { @@ -2200,9 +2215,11 @@ async fn start_ipc( #[cfg(not(feature = "linux_headless"))] let user = None; #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] let mut user = None; // Cm run as user, wait until desktop session is ready. #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if linux_desktop_manager::is_headless() { let mut username = linux_desktop_manager::get_username(); loop { From 2ce1df1ad4f4ca7e8f4213da6ad837d69092f124 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 9 Apr 2023 12:14:01 +0800 Subject: [PATCH 254/366] fix, legacy mode, numlock Signed-off-by: fufesou --- src/server/input_service.rs | 41 ++++++++++++++----------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 2b1f16886..ab5f7e287 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -162,13 +162,15 @@ impl LockModesHandler { let mut num_lock_changed = false; if is_numpad_key { - let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock); let local_num_enabled = en.get_key_state(enigo::Key::NumLock); - #[cfg(not(target_os = "windows"))] - let disable_numlock = false; + let event_num_enabled = Self::is_modifier_enabled(key_event, ControlKey::NumLock); + num_lock_changed = event_num_enabled != local_num_enabled; + } else if is_legacy_mode(key_event) { #[cfg(target_os = "windows")] - let disable_numlock = is_numlock_disabled(key_event); - num_lock_changed = event_num_enabled != local_num_enabled && !disable_numlock; + { + num_lock_changed = + should_disable_numlock(key_event) && en.get_key_state(enigo::Key::NumLock); + } } if num_lock_changed { en.key_click(enigo::Key::NumLock); @@ -221,14 +223,16 @@ impl Drop for LockModesHandler { #[inline] #[cfg(target_os = "windows")] -fn is_numlock_disabled(key_event: &KeyEvent) -> bool { +fn should_disable_numlock(evt: &KeyEvent) -> bool { // disable numlock if press home etc when numlock is on, // because we will get numpad value (7,8,9 etc) if not - if is_legacy_mode(&key_event) { - has_numpad_key(key_event) - } else { - false + match (&evt.union, evt.mode.enum_value_or(KeyboardMode::Legacy)) { + (Some(key_event::Union::ControlKey(ck)), KeyboardMode::Legacy) => { + return NUMPAD_KEY_MAP.contains_key(&ck.value()); + } + _ => {} } + false } pub const NAME_CURSOR: &'static str = "mouse_cursor"; @@ -1037,16 +1041,6 @@ fn char_value_to_key(value: u32) -> Key { Key::Layout(std::char::from_u32(value).unwrap_or('\0')) } -#[cfg(target_os = "windows")] -fn has_numpad_key(key_event: &KeyEvent) -> bool { - key_event - .modifiers - .iter() - .filter(|&&ck| NUMPAD_KEY_MAP.get(&ck.value()).is_some()) - .count() - != 0 -} - fn map_keyboard_mode(evt: &KeyEvent) { #[cfg(windows)] crate::platform::windows::try_change_desktop(); @@ -1461,12 +1455,7 @@ pub fn handle_key_(evt: &KeyEvent) { let is_numpad_key = false; #[cfg(any(target_os = "windows", target_os = "linux"))] let is_numpad_key = is_numpad_control_key(&key); - // Legacy mode need to disable numlock if home/end/arraws/page down/page up - // are pressed. - _lock_mode_handler = Some(LockModesHandler::new_handler( - &evt, - is_numpad_key || is_legacy_mode(evt), - )); + _lock_mode_handler = Some(LockModesHandler::new_handler(&evt, is_numpad_key)); } } Some(key_event::Union::Chr(code)) => { From 6da9d2430708738bf67b79a4651b9ffd2677ab39 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 9 Apr 2023 12:38:17 +0800 Subject: [PATCH 255/366] update github workflow Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 1bc519f56..b6eca6c22 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -681,7 +681,16 @@ jobs: x86_64) # no need mock on x86_64 export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,flutter_texture_render,linux_headless,${{ matrix.job.extra-build-features }} --release + case "${{ matrix.job.extra-build-features }}" in + "flatpak") + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release + ;; + "appimage") + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release + ;; + "") + cargo build --lib --features hwcodec,flutter,flutter_texture_render,linux_headless,${{ matrix.job.extra-build-features }} --release + ;; ;; esac From 02f5ed9e56c4353373e51b9e5b6ac99f96140825 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 9 Apr 2023 12:43:44 +0800 Subject: [PATCH 256/366] update github workflow Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index b6eca6c22..e732a2a30 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -545,7 +545,7 @@ jobs: arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-20.04, - extra-build-features: "", + extra-build-features: "linux_headless", } - { arch: x86_64, @@ -681,16 +681,7 @@ jobs: x86_64) # no need mock on x86_64 export VCPKG_ROOT=/opt/artifacts/vcpkg - case "${{ matrix.job.extra-build-features }}" in - "flatpak") - cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release - ;; - "appimage") - cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release - ;; - "") - cargo build --lib --features hwcodec,flutter,flutter_texture_render,linux_headless,${{ matrix.job.extra-build-features }} --release - ;; + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release ;; esac From b6f7d56a066e881faf075d29cf1401ab3c06d8e1 Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 9 Apr 2023 13:01:02 +0800 Subject: [PATCH 257/366] fix build Signed-off-by: fufesou --- src/server/connection.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/connection.rs b/src/server/connection.rs index 318424b3d..8a6ba99b9 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2215,6 +2215,9 @@ async fn start_ipc( #[cfg(not(feature = "linux_headless"))] let user = None; #[cfg(all(target_os = "linux", feature = "linux_headless"))] + #[cfg(any(feature = "flatpak", feature = "appimage"))] + let user = None; + #[cfg(all(target_os = "linux", feature = "linux_headless"))] #[cfg(not(any(feature = "flatpak", feature = "appimage")))] let mut user = None; // Cm run as user, wait until desktop session is ready. From eba0ed6a38d1f83a96921a7f9aba9fce2bb1e8ec Mon Sep 17 00:00:00 2001 From: fufesou Date: Sun, 9 Apr 2023 14:45:42 +0800 Subject: [PATCH 258/366] add deps pam Signed-off-by: fufesou --- build.py | 2 +- res/PKGBUILD | 2 +- res/rpm-flutter-suse.spec | 2 +- res/rpm-flutter.spec | 2 +- res/rpm-suse.spec | 2 +- res/rpm.spec | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.py b/build.py index 3a06c4161..6ed3d19b3 100755 --- a/build.py +++ b/build.py @@ -264,7 +264,7 @@ Version: %s Architecture: %s Maintainer: open-trade Homepage: https://rustdesk.com -Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0 +Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0, libpam0g Description: A remote control software. """ % (version, get_arch()) diff --git a/res/PKGBUILD b/res/PKGBUILD index cff61516f..8d0816d7f 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -7,7 +7,7 @@ arch=('x86_64') url="" license=('AGPL-3.0') groups=() -depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3') +depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3', 'pam') makedepends=() checkdepends=() optdepends=() diff --git a/res/rpm-flutter-suse.spec b/res/rpm-flutter-suse.spec index 77c28a94e..93cbc548d 100644 --- a/res/rpm-flutter-suse.spec +++ b/res/rpm-flutter-suse.spec @@ -3,7 +3,7 @@ Version: 1.2.0 Release: 0 Summary: RPM package License: GPL-3.0 -Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils curl libXtst6 libappindicator-gtk3 libvdpau1 libva2 +Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils curl libXtst6 libappindicator-gtk3 libvdpau1 libva2 pam Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit) %description diff --git a/res/rpm-flutter.spec b/res/rpm-flutter.spec index 6124cbb70..fb00ee121 100644 --- a/res/rpm-flutter.spec +++ b/res/rpm-flutter.spec @@ -3,7 +3,7 @@ Version: 1.2.0 Release: 0 Summary: RPM package License: GPL-3.0 -Requires: gtk3 libxcb libxdo libXfixes alsa-lib curl libappindicator-gtk3 libvdpau libva +Requires: gtk3 libxcb libxdo libXfixes alsa-lib curl libappindicator-gtk3 libvdpau libva pam Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit) %description diff --git a/res/rpm-suse.spec b/res/rpm-suse.spec index 5d03d9c8a..60b9a6df6 100644 --- a/res/rpm-suse.spec +++ b/res/rpm-suse.spec @@ -3,7 +3,7 @@ Version: 1.1.9 Release: 0 Summary: RPM package License: GPL-3.0 -Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils curl libXtst6 libayatana-appindicator3-1 libvdpau1 libva2 +Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils curl libXtst6 libayatana-appindicator3-1 libvdpau1 libva2 pam %description The best open-source remote desktop client software, written in Rust. diff --git a/res/rpm.spec b/res/rpm.spec index b2a3e27e1..3a1b998c3 100644 --- a/res/rpm.spec +++ b/res/rpm.spec @@ -3,7 +3,7 @@ Version: 1.2.0 Release: 0 Summary: RPM package License: GPL-3.0 -Requires: gtk3 libxcb libxdo libXfixes alsa-lib curl libappindicator libvdpau1 libva2 +Requires: gtk3 libxcb libxdo libXfixes alsa-lib curl libappindicator libvdpau1 libva2 pam %description The best open-source remote desktop client software, written in Rust. From 73c7158e5605ab4537167c6c66a2adeaf00833d8 Mon Sep 17 00:00:00 2001 From: "Miguel F. G" <116861809+flusheDData@users.noreply.github.com> Date: Sun, 9 Apr 2023 12:49:42 +0200 Subject: [PATCH 259/366] Update es.rs New terms added --- src/lang/es.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/lang/es.rs b/src/lang/es.rs index 6207fac3b..9f7a358c5 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -480,16 +480,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("identical_file_tip", "Este archivo es idéntico al del par."), ("show_monitors_tip", "Mostrar monitores en la barra de herramientas"), ("View Mode", "Modo Vista"), - ("login_linux_tip", ""), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("login_linux_tip", "Necesitas iniciar sesión con la cueneta del Linux remoto para activar una sesión de escritorio X"), + ("verify_rustdesk_password_tip", "Verificar la contraseña de RustDesk"), + ("remember_account_tip", "Recordar esta cuenta"), + ("os_account_desk_tip", "Esta cueneta se usa para iniciar sesión en el sistema operativo remoto y habilitar la sesión de escritorio en headless."), + ("OS Account", "Cuenta del SO"), + ("another_user_login_title_tip", "Otro usuario ya ha iniciado sesión"), + ("another_user_login_text_tip", "Desconectar"), + ("xorg_not_found_title_tip", "Xorg no hallado"), + ("xorg_not_found_text_tip", "Por favor, instala Xorg"), + ("no_desktop_title_tip", "No hay escritorio disponible"), + ("no_desktop_text_tip", "Por favor, instala GNOME Desktop"), ].iter().cloned().collect(); } From 0c049c585e55c57c5674d373e6ba342df539d253 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Sat, 8 Apr 2023 17:46:47 +0800 Subject: [PATCH 260/366] add: initial plugin manager --- src/api.rs | 31 +++++++++++++++++++++++++++ src/lib.rs | 5 +++++ src/plugins.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/api.rs create mode 100644 src/plugins.rs diff --git a/src/api.rs b/src/api.rs new file mode 100644 index 000000000..d62dc4d65 --- /dev/null +++ b/src/api.rs @@ -0,0 +1,31 @@ +use std::{ffi::CStr, os::raw::c_char}; + +use crate::plugins::PLUGIN_REGISTRAR; + +pub type LoadPluginFunc = fn(*const i8) -> i32; +pub type UnloadPluginFunc = fn(*const i8) -> i32; + +pub struct RustDeskApiTable { + pub register_plugin: LoadPluginFunc, + pub unload_plugin: UnloadPluginFunc, +} + +#[no_mangle] +fn load_plugin(path: *const i8) -> i32 { + PLUGIN_REGISTRAR.load_plugin(path) +} + +#[no_mangle] +fn unload_plugin(path: *const i8) -> i32 { + PLUGIN_REGISTRAR.unload_plugin(path) +} + +impl Default for RustDeskApiTable { + fn default() -> Self { + let f = load_plugin; + Self { + register_plugin: load_plugin, + unload_plugin: unload_plugin, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 5dcd6389c..af9f773ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,11 @@ mod license; #[cfg(not(any(target_os = "android", target_os = "ios")))] mod port_forward; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +mod plugins; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +mod api; + mod tray; mod ui_cm_interface; diff --git a/src/plugins.rs b/src/plugins.rs new file mode 100644 index 000000000..bca77f8cc --- /dev/null +++ b/src/plugins.rs @@ -0,0 +1,57 @@ +use std::{collections::HashMap, path::Path, sync::Arc, ffi::CStr}; + +use hbb_common::anyhow::{anyhow, Error}; +use lazy_static::lazy_static; + +lazy_static! { + pub static ref PLUGIN_REGISTRAR: Arc> = + Arc::new(PluginRegistar::::default()); +} + +pub trait Plugin { + // Return: the unique ID which identifies this plugin. + fn plugin_id(&self) -> String; + // Return: the name which is human-readable. + fn plugin_name(&self) -> String; +} + +#[derive(Default, Clone)] +pub struct PluginImpl { + id: String, + name: String, +} + +impl Plugin for PluginImpl { + fn plugin_id(&self) -> String { + self.id.to_owned() + } + + fn plugin_name(&self) -> String { + self.name.to_owned() + } +} + +#[derive(Default, Clone)] +pub struct PluginRegistar { + plugins: HashMap, +} + +impl PluginRegistar

    { + pub fn load_plugin(&self, path: *const i8) -> i32 { + let p = unsafe { CStr::from_ptr(path) }; + 0 + } + + pub fn unload_plugin(&self, path: *const i8) -> i32 { + let p = unsafe { CStr::from_ptr(path) }; + 0 + } +} + +impl TryFrom<&Path> for PluginImpl { + type Error = Error; + + fn try_from(value: &Path) -> Result { + Err(anyhow!("Not implemented yet.")) + } +} From 4e7e9406f5eead3715aced7c9b2e26de82b3d5de Mon Sep 17 00:00:00 2001 From: Kingtous Date: Sat, 8 Apr 2023 18:17:13 +0800 Subject: [PATCH 261/366] feat: add vt --- Cargo.lock | 1 + Cargo.toml | 1 + src/api.rs | 8 ++++++- src/plugins.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++------ 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9d2984abe..5e14f72bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5146,6 +5146,7 @@ dependencies = [ "include_dir", "jni 0.19.0", "lazy_static", + "libloading", "libpulse-binding", "libpulse-simple-binding", "mac_address", diff --git a/Cargo.toml b/Cargo.toml index 48bd16045..5410ee9d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ hex = "0.4" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } chrono = "0.4" cidr-utils = "0.5" +libloading = "0.7" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] cpal = "0.14" diff --git a/src/api.rs b/src/api.rs index d62dc4d65..c8432f112 100644 --- a/src/api.rs +++ b/src/api.rs @@ -2,9 +2,11 @@ use std::{ffi::CStr, os::raw::c_char}; use crate::plugins::PLUGIN_REGISTRAR; +// API provided by RustDesk. pub type LoadPluginFunc = fn(*const i8) -> i32; pub type UnloadPluginFunc = fn(*const i8) -> i32; +#[repr(C)] pub struct RustDeskApiTable { pub register_plugin: LoadPluginFunc, pub unload_plugin: UnloadPluginFunc, @@ -20,9 +22,13 @@ fn unload_plugin(path: *const i8) -> i32 { PLUGIN_REGISTRAR.unload_plugin(path) } +#[no_mangle] +fn get_api_table() -> RustDeskApiTable { + RustDeskApiTable::default() +} + impl Default for RustDeskApiTable { fn default() -> Self { - let f = load_plugin; Self { register_plugin: load_plugin, unload_plugin: unload_plugin, diff --git a/src/plugins.rs b/src/plugins.rs index bca77f8cc..42c74e27f 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -1,22 +1,42 @@ -use std::{collections::HashMap, path::Path, sync::Arc, ffi::CStr}; +use std::{ + collections::HashMap, + ffi::CStr, + path::Path, + sync::{Arc, RwLock}, +}; use hbb_common::anyhow::{anyhow, Error}; use lazy_static::lazy_static; +use libloading::Library; lazy_static! { pub static ref PLUGIN_REGISTRAR: Arc> = Arc::new(PluginRegistar::::default()); } +// API needed to be implemented by plugins. +pub type PluginInitFunc = fn() -> i32; +// API needed to be implemented by plugins. +pub type PluginDisposeFunc = fn() -> i32; pub trait Plugin { // Return: the unique ID which identifies this plugin. fn plugin_id(&self) -> String; // Return: the name which is human-readable. fn plugin_name(&self) -> String; + // Return: the virtual table of the plugin. + fn plugin_vt(&self) -> &RustDeskPluginTable; +} + +#[repr(C)] +#[derive(Default, Clone)] +pub struct RustDeskPluginTable { + pub init: Option, + pub dispose: Option, } #[derive(Default, Clone)] pub struct PluginImpl { + vt: RustDeskPluginTable, id: String, name: String, } @@ -29,29 +49,57 @@ impl Plugin for PluginImpl { fn plugin_name(&self) -> String { self.name.to_owned() } + + fn plugin_vt(&self) -> &RustDeskPluginTable { + &self.vt + } } #[derive(Default, Clone)] pub struct PluginRegistar { - plugins: HashMap, + plugins: Arc>>, } impl PluginRegistar

    { pub fn load_plugin(&self, path: *const i8) -> i32 { let p = unsafe { CStr::from_ptr(path) }; - 0 + let lib_path = p.to_str().unwrap_or("").to_owned(); + let lib = unsafe { libloading::Library::new(lib_path.as_str()) }; + match lib { + Ok(lib) => match lib.try_into() { + Ok(plugin) => { + PLUGIN_REGISTRAR + .plugins + .write() + .unwrap() + .insert(lib_path, plugin); + return 0; + } + Err(err) => { + eprintln!("Load plugin failed: {}", err); + } + }, + Err(err) => { + eprintln!("Load plugin failed: {}", err); + } + } + -1 } pub fn unload_plugin(&self, path: *const i8) -> i32 { let p = unsafe { CStr::from_ptr(path) }; - 0 + let lib_path = p.to_str().unwrap_or("").to_owned(); + match PLUGIN_REGISTRAR.plugins.write().unwrap().remove(&lib_path) { + Some(_) => 0, + None => -1, + } } } -impl TryFrom<&Path> for PluginImpl { +impl TryFrom for PluginImpl { type Error = Error; - fn try_from(value: &Path) -> Result { - Err(anyhow!("Not implemented yet.")) + fn try_from(library: Library) -> Result { + todo!() } } From 34852224e9f5554c086bca47e4e71bbf719cb9b3 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 10 Apr 2023 00:07:26 +0800 Subject: [PATCH 262/366] feat: add plugins and api table --- src/api.rs | 2 -- src/plugins.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/api.rs b/src/api.rs index c8432f112..f737243e7 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,5 +1,3 @@ -use std::{ffi::CStr, os::raw::c_char}; - use crate::plugins::PLUGIN_REGISTRAR; // API provided by RustDesk. diff --git a/src/plugins.rs b/src/plugins.rs index 42c74e27f..7356380d3 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -1,13 +1,12 @@ use std::{ collections::HashMap, - ffi::CStr, - path::Path, + ffi::{c_char, CStr}, sync::{Arc, RwLock}, }; -use hbb_common::anyhow::{anyhow, Error}; +use hbb_common::{anyhow::Error, log::debug}; use lazy_static::lazy_static; -use libloading::Library; +use libloading::{Library, Symbol}; lazy_static! { pub static ref PLUGIN_REGISTRAR: Arc> = @@ -16,6 +15,10 @@ lazy_static! { // API needed to be implemented by plugins. pub type PluginInitFunc = fn() -> i32; // API needed to be implemented by plugins. +pub type PluginIdFunc = fn() -> *const c_char; +// API needed to be implemented by plugins. +pub type PluginNameFunc = fn() -> *const c_char; +// API needed to be implemented by plugins. pub type PluginDisposeFunc = fn() -> i32; pub trait Plugin { @@ -34,11 +37,22 @@ pub struct RustDeskPluginTable { pub dispose: Option, } -#[derive(Default, Clone)] pub struct PluginImpl { vt: RustDeskPluginTable, - id: String, - name: String, + pub id: String, + pub name: String, + _inner: Option, +} + +impl Default for PluginImpl { + fn default() -> Self { + Self { + _inner: None, + vt: Default::default(), + id: Default::default(), + name: Default::default(), + } + } } impl Plugin for PluginImpl { @@ -100,6 +114,73 @@ impl TryFrom for PluginImpl { type Error = Error; fn try_from(library: Library) -> Result { - todo!() + let init: Symbol = unsafe { library.get(b"plugin_init")? }; + let dispose: Symbol = unsafe { library.get(b"plugin_dispose")? }; + let id_func: Symbol = unsafe { library.get(b"plugin_id")? }; + let id_string = unsafe { + std::ffi::CStr::from_ptr(id_func()) + .to_str() + .unwrap_or("") + .to_owned() + }; + let name_func: Symbol = unsafe { library.get(b"plugin_name")? }; + let name_string = unsafe { + std::ffi::CStr::from_ptr(name_func()) + .to_str() + .unwrap_or("") + .to_owned() + }; + debug!( + "Successfully loaded the plugin called {} with id {}.", + name_string, id_string + ); + Ok(Self { + vt: RustDeskPluginTable { + init: Some(*init), + dispose: Some(*dispose), + }, + id: id_string, + name: name_string, + _inner: Some(library), + }) } } + +#[test] +#[cfg(target_os = "linux")] +fn test_plugin() { + use std::io::Write; + + let code = " + const char* plugin_name(){return \"test_name\";}; + const char* plugin_id(){return \"test_id\"; } + int plugin_init() {return 0;} + int plugin_dispose() {return 0;} + "; + let mut f = std::fs::File::create("test.c").unwrap(); + f.write_all(code.as_bytes()).unwrap(); + f.flush().unwrap(); + let mut cmd = std::process::Command::new("cc"); + cmd.arg("-fPIC") + .arg("-shared") + .arg("test.c") + .arg("-o") + .arg("libtest.so"); + // Spawn the compiler process. + let mut child = cmd.spawn().unwrap(); + // Wait for the compiler to finish. + let status = child.wait().unwrap(); + assert!(status.success()); + // Load the library. + let lib = unsafe { Library::new("./libtest.so").unwrap() }; + let plugin: PluginImpl = lib.try_into().unwrap(); + assert!(plugin._inner.is_some()); + assert!(plugin.name == "test_name"); + assert!(plugin.id == "test_id"); + assert!(PLUGIN_REGISTRAR + .plugins + .write() + .unwrap() + .insert("test".to_owned(), plugin) + .is_none()); +} From 73921e4cf5a05e76576c9a92a3152089d33a1854 Mon Sep 17 00:00:00 2001 From: Yevhen Date: Mon, 10 Apr 2023 02:01:53 +0300 Subject: [PATCH 263/366] Update Ukrainian UI translation (ua.rs) --- src/lang/ua.rs | 132 ++++++++++++++++++++++++------------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 755e7b041..0c1f453bf 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -12,7 +12,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Start Service", "Запустити службу"), ("Service is running", "Служба працює"), ("Service is not running", "Служба не запущена"), - ("not_ready_status", "Не готово. Будь ласка, перевірте підключення"), + ("not_ready_status", "Не готово. Будь ласка, перевірте ваше з'єднання"), ("Control Remote Desktop", "Керування віддаленою стільницею"), ("Transfer File", "Передати файл"), ("Connect", "Підключитися"), @@ -32,24 +32,24 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Import Server Config", "Імпортувати конфігурацію сервера"), ("Export Server Config", "Експортувати конфігурацію сервера"), ("Import server configuration successfully", "Конфігурацію сервера успішно імпортовано"), - ("Export server configuration successfully", ""), + ("Export server configuration successfully", "Конфігурацію сервера успішно експортовано"), ("Invalid server configuration", "Недійсна конфігурація сервера"), ("Clipboard is empty", "Буфер обміну порожній"), ("Stop service", "Зупинити службу"), ("Change ID", "Змінити ID"), - ("Your new ID", ""), - ("length %min% to %max%", ""), - ("starts with a letter", ""), - ("allowed characters", ""), - ("id_change_tip", "Допускаються тільки символи a-z, A-Z, 0-9 і _ (підкреслення). Перша буква повинна бути a-z, A-Z. Довжина від 6 до 16"), + ("Your new ID", "Ваш новий ID"), + ("length %min% to %max%", "від %min% до %max% символів"), + ("starts with a letter", "починається з літери"), + ("allowed characters", "дозволені символи"), + ("id_change_tip", "Допускаються лише символи a-z, A-Z, 0-9 і _ (підкреслення). Першою повинна бути літера a-z, A-Z. В межах від 6 до 16 символів"), ("Website", "Веб-сайт"), ("About", "Про RustDesk"), ("Slogan_tip", "Створено з душею в цьому хаотичному світі!"), ("Privacy Statement", "Декларація про конфіденційність"), ("Mute", "Вимкнути звук"), - ("Build Date", ""), - ("Version", ""), - ("Home", ""), + ("Build Date", "Дата збірки"), + ("Version", "Версія"), + ("Home", "Домівка"), ("Audio Input", "Аудіовхід"), ("Enhancements", "Покращення"), ("Hardware Codec", "Апаратний кодек"), @@ -60,7 +60,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("invalid_http", "Повинен починатися з http:// або https://"), ("Invalid IP", "Невірна IP-адреса"), ("Invalid format", "Невірний формат"), - ("server_not_support", "Поки не підтримується сервером"), + ("server_not_support", "Наразі не підтримується сервером"), ("Not available", "Недоступно"), ("Too frequent", "Занадто часто"), ("Cancel", "Скасувати"), @@ -154,16 +154,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("config_screen", "Для віддаленого доступу до стільниці ви повинні надати RustDesk права для \"запису екрану\""), ("Installing ...", "Встановлюється..."), ("Install", "Встановити"), - ("Installation", "Установка"), + ("Installation", "Встановлення"), ("Installation Path", "Шлях встановлення"), ("Create start menu shortcuts", "Створити ярлики меню \"Пуск\""), ("Create desktop icon", "Створити значок на стільниці"), - ("agreement_tip", "Починаючи установку, ви приймаєте умови ліцензійної угоди"), + ("agreement_tip", "Починаючи встановлення, ви приймаєте умови ліцензійної угоди"), ("Accept and Install", "Прийняти та встановити"), ("End-user license agreement", "Ліцензійна угода з кінцевим користувачем"), ("Generating ...", "Генерація..."), - ("Your installation is lower version.", "Ваша установка більш ранньої версії"), - ("not_close_tcp_tip", "Не закривати це вікно під час використання тунелю"), + ("Your installation is lower version.", "У вас встановлена більш рання версія"), + ("not_close_tcp_tip", "Не закривайте це вікно під час використання тунелю"), ("Listening ...", "Очікуємо ..."), ("Remote Host", "Віддалена машина"), ("Remote Port", "Віддалений порт"), @@ -212,16 +212,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Invalid port", "Неправильний порт"), ("Closed manually by the peer", "Закрито вузлом вручну"), ("Enable remote configuration modification", "Дозволити віддалену зміну конфігурації"), - ("Run without install", "Запустити без установки"), - ("Connect via relay", ""), + ("Run without install", "Запустити без встановлення"), + ("Connect via relay", "Підключитися через ретрансляційний сервер"), ("Always connect via relay", "Завжди підключатися через ретрансляційний сервер"), ("whitelist_tip", "Тільки IP-адреси з білого списку можуть отримати доступ до мене"), ("Login", "Увійти"), - ("Verify", ""), - ("Remember me", ""), - ("Trust this device", ""), - ("Verification code", ""), - ("verification_tip", ""), + ("Verify", "Підтвердити"), + ("Remember me", "Запам'ятати мене"), + ("Trust this device", "Довірений пристрій"), + ("Verification code", "Код підтвердження"), + ("verification_tip", "Виявлено новий пристрій, код підтвердження надіслано на зареєстровану email-адресу, введіть код підтвердження для продовження авторизації."), ("Logout", "Вийти"), ("Tags", "Ключові слова"), ("Search ID", "Пошук за ID"), @@ -311,7 +311,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Зберегти фонову службу RustDesk"), ("Ignore Battery Optimizations", "Ігнорувати оптимізацію батареї"), ("android_open_battery_optimizations_tip", "Перейдіть на наступну сторінку налаштувань"), - ("Start on Boot", ""), + ("Start on Boot", "Автозапуск"), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Підключення не дозволено"), ("Legacy mode", "Застарілий режим"), @@ -326,7 +326,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Are you sure you want to restart", "Ви впевнені, що хочете виконати перезапуск?"), ("Restarting Remote Device", "Перезавантаження віддаленого пристрою"), ("remote_restarting_tip", "Віддалений пристрій перезапускається. Будь ласка, закрийте це повідомлення та через деякий час перепідключіться, використовуючи постійний пароль."), - ("Copied", ""), + ("Copied", "Скопійовано"), ("Exit Fullscreen", "Вийти з повноекранного режиму"), ("Fullscreen", "Повноекранний"), ("Mobile Actions", "Мобільні дії"), @@ -348,7 +348,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Security", "Безпека"), ("Theme", "Тема"), ("Dark Theme", "Темна тема"), - ("Light Theme", ""), + ("Light Theme", "Світла тема"), ("Dark", "Темна"), ("Light", "Світла"), ("Follow System", "Як у системі"), @@ -385,7 +385,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("elevated_foreground_window_tip", "Поточне вікно віддаленої стільниці потребує розширених прав для роботи, тому наразі неможливо використати мишу та клавіатуру. Ви можете запропонувати віддаленому користувачу згорнути поточне вікно чи натиснути кнопку розширення прав у вікні керування з'єднаннями. Для уникнення цієї проблеми, рекомендується встановити програму на віддаленому пристрої"), ("Disconnected", "Відключено"), ("Other", "Інше"), - ("Confirm before closing multiple tabs", ""), + ("Confirm before closing multiple tabs", "Підтверджувати перед закриттям кількох вкладок"), ("Keyboard Settings", "Налаштування клавіатури"), ("Full Access", "Повний доступ"), ("Screen Share", "Демонстрація екрану"), @@ -411,19 +411,19 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("hide_cm_tip", "Дозволено приховати лише якщо сеанс підтверджується постійним паролем"), ("wayland_experiment_tip", "Підтримка Wayland на експериментальній стадії, будь ласка, використовуйте X11, якщо необхідний автоматичний доступ."), ("Right click to select tabs", "Правий клік для вибору вкладки"), - ("Skipped", ""), + ("Skipped", "Пропущено"), ("Add to Address Book", "Додати IP до Адресної книги"), ("Group", "Група"), ("Search", "Пошук"), - ("Closed manually by web console", ""), - ("Local keyboard type", ""), - ("Select local keyboard type", ""), + ("Closed manually by web console", "Закрито вручну з веб-консолі"), + ("Local keyboard type", "Тип локальної клавіатури"), + ("Select local keyboard type", "Оберіть тип локальної клавіатури"), ("software_render_tip", ""), ("Always use software rendering", ""), ("config_input", ""), ("config_microphone", ""), ("request_elevation_tip", ""), - ("Wait", ""), + ("Wait", "Зачекайте"), ("Elevation Error", ""), ("Ask the remote user for authentication", ""), ("Choose this if the remote account is administrator", ""), @@ -432,59 +432,59 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Request Elevation", ""), ("wait_accept_uac_tip", ""), ("Elevate successfully", ""), - ("uppercase", ""), - ("lowercase", ""), - ("digit", ""), - ("special character", ""), - ("length>=8", ""), - ("Weak", ""), - ("Medium", ""), - ("Strong", ""), + ("uppercase", "верхній регістр"), + ("lowercase", "нижній регістр"), + ("digit", "цифра"), + ("special character", "спецсимвол"), + ("length>=8", "довжина>=8"), + ("Weak", "Слабкий"), + ("Medium", "Середній"), + ("Strong", "Сильний"), ("Switch Sides", ""), - ("Please confirm if you want to share your desktop?", ""), - ("Display", ""), - ("Default View Style", ""), - ("Default Scroll Style", ""), - ("Default Image Quality", ""), - ("Default Codec", ""), - ("Bitrate", ""), - ("FPS", ""), - ("Auto", ""), - ("Other Default Options", ""), - ("Voice call", ""), - ("Text chat", ""), - ("Stop voice call", ""), + ("Please confirm if you want to share your desktop?", "Будь ласка, пітвердіть дозвіл на спільне використання стільниці"), + ("Display", "Екран"), + ("Default View Style", "Типовий стиль перегляду"), + ("Default Scroll Style", "Типовий стиль гортання"), + ("Default Image Quality", "Типова якість зображення"), + ("Default Codec", "Типовий кодек"), + ("Bitrate", "Бітрейт"), + ("FPS", "FPS"), + ("Auto", "Авто"), + ("Other Default Options", "Інші типові параметри"), + ("Voice call", "Голосовий дзвінок"), + ("Text chat", "Текстовий чат"), + ("Stop voice call", "Покласти слухавку"), ("relay_hint_tip", ""), - ("Reconnect", ""), - ("Codec", ""), - ("Resolution", ""), + ("Reconnect", "Перепідключитися"), + ("Codec", "Кодек"), + ("Resolution", "Роздільна здатність"), ("No transfers in progress", ""), ("Set one-time password length", ""), ("idd_driver_tip", ""), ("confirm_idd_driver_tip", ""), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), + ("RDP Settings", "Налаштування RDP"), + ("Sort by", "Сортувати за"), + ("New Connection", "Нове з'єднання"), + ("Restore", "Відновити"), + ("Minimize", "Згорнути"), + ("Maximize", "Розгорнути"), + ("Your Device", "Вам пристрій"), ("empty_recent_tip", ""), ("empty_favorite_tip", ""), ("empty_lan_tip", ""), ("empty_address_book_tip", ""), - ("eg: admin", ""), + ("eg: admin", "напр. admin"), ("Empty Username", ""), ("Empty Password", ""), - ("Me", ""), + ("Me", "Я"), ("identical_file_tip", ""), ("show_monitors_tip", ""), - ("View Mode", ""), + ("View Mode", "Режим перегляду"), ("login_linux_tip", ""), ("verify_rustdesk_password_tip", ""), ("remember_account_tip", ""), ("os_account_desk_tip", ""), - ("OS Account", ""), + ("OS Account", "Користувач ОС"), ("another_user_login_title_tip", ""), ("another_user_login_text_tip", ""), ("xorg_not_found_title_tip", ""), From fff7feec4ccada57fba1a110737f13e3e68ef4d4 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 10 Apr 2023 08:52:44 +0800 Subject: [PATCH 264/366] minor --- Cargo.toml | 1 - build.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5410ee9d7..28eacffce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -161,7 +161,6 @@ hound = "3.5" name = "RustDesk" identifier = "com.carriez.rustdesk" icon = ["res/32x32.png", "res/128x128.png", "res/128x128@2x.png"] -deb_depends = ["libgtk-3-0", "libxcb-randr0", "libxdo3", "libxfixes3", "libxcb-shape0", "libxcb-xfixes0", "libasound2", "libsystemd0", "curl", "libvdpau1", "libva2"] osx_minimum_system_version = "10.14" #https://github.com/johnthagen/min-sized-rust diff --git a/build.py b/build.py index 6ed3d19b3..fb9e213db 100755 --- a/build.py +++ b/build.py @@ -262,7 +262,7 @@ def generate_control_file(version): content = """Package: rustdesk Version: %s Architecture: %s -Maintainer: open-trade +Maintainer: rustdesk Homepage: https://rustdesk.com Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0, libpam0g Description: A remote control software. From ceb2e6614c80e146d0b40d1cac30ccbeff8f23a7 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 14:30:38 +0800 Subject: [PATCH 265/366] fix clipboard update B->A->B Signed-off-by: fufesou --- src/client.rs | 28 +++++++++++++++++++++++----- src/client/io_loop.rs | 15 ++++++--------- src/flutter.rs | 4 ++-- src/ui_session_interface.rs | 2 ++ 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/client.rs b/src/client.rs index a24c531c1..c880c2dbe 100644 --- a/src/client.rs +++ b/src/client.rs @@ -635,7 +635,7 @@ impl Client { } #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn try_start_clipboard(_conf_tx: Option<(SessionPermissionConfig, UnboundedSender)>) { + fn try_start_clipboard(_conf_tx: Option) { let mut clipboard_lock = TEXT_CLIPBOARD_STATE.lock().unwrap(); if clipboard_lock.running { return; @@ -660,11 +660,17 @@ impl Client { if let Some(msg) = check_clipboard(&mut ctx, Some(&OLD_CLIPBOARD_TEXT)) { #[cfg(feature = "flutter")] - crate::flutter::send_text_clipboard_msg(msg); + crate::flutter::send_text_clipboard_msg( + &*OLD_CLIPBOARD_TEXT.lock().unwrap(), + msg, + ); #[cfg(not(feature = "flutter"))] - if let Some((cfg, tx)) = &_conf_tx { - if cfg.is_text_clipboard_required() { - let _ = tx.send(Data::Message(msg)); + if let Some(ctx) = &_ctx { + if ctx.cfg.is_text_clipboard_required() + && *OLD_CLIPBOARD_TEXT.lock().unwrap() + != *ctx.old.lock().unwrap() + { + let _ = ctx.tx.send(Data::Message(msg)); } } } @@ -2423,3 +2429,15 @@ fn decode_id_pk(signed: &[u8], key: &sign::PublicKey) -> ResultType<(String, [u8 bail!("Wrong public length"); } } + +#[cfg(feature = "flutter")] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub(crate) struct ClientClipboardContext; + +#[cfg(not(feature = "flutter"))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub(crate) struct ClientClipboardContext { + pub cfg: SessionPermissionConfig, + pub old: Arc>, + pub tx: UnboundedSender, +} diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 18a8bfe3d..735a35e3a 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -51,8 +51,6 @@ pub struct Remote { // Stop sending local audio to remote client. stop_voice_call_sender: Option>, voice_call_request_timestamp: Option, - #[cfg(not(any(target_os = "android", target_os = "ios")))] - old_clipboard: Arc>, read_jobs: Vec, write_jobs: Vec, remove_jobs: HashMap, @@ -87,8 +85,6 @@ impl Remote { audio_sender, receiver, sender, - #[cfg(not(any(target_os = "android", target_os = "ios")))] - old_clipboard: Default::default(), read_jobs: Vec::new(), write_jobs: Vec::new(), remove_jobs: Default::default(), @@ -941,10 +937,11 @@ impl Remote { Client::try_start_clipboard(None); #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] - Client::try_start_clipboard(Some(( - permission_config.clone(), - sender.clone(), - ))); + Client::try_start_clipboard(Some(ClientClipboardContext { + cfg: permission_config.clone(), + old: self.handler.old_clipboard.clone(), + tx: sender.clone(), + })); #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -977,7 +974,7 @@ impl Remote { Some(message::Union::Clipboard(cb)) => { if !self.handler.lc.read().unwrap().disable_clipboard.v { #[cfg(not(any(target_os = "android", target_os = "ios")))] - update_clipboard(cb, Some(&self.old_clipboard)); + update_clipboard(cb, Some(&self.handler.old_clipboard)); #[cfg(any(target_os = "android", target_os = "ios"))] { let content = if cb.compress { diff --git a/src/flutter.rs b/src/flutter.rs index 6c9ff7f37..e49f04043 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -725,9 +725,9 @@ pub fn other_sessions_running(id: &str) -> bool { } #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn send_text_clipboard_msg(msg: Message) { +pub fn send_text_clipboard_msg(text: &str, msg: Message) { for (_id, session) in SESSIONS.read().unwrap().iter() { - if session.is_text_clipboard_required() { + if session.is_text_clipboard_required() && text != *session.old_clipboard.lock().unwrap() { session.send(Data::Message(msg.clone())); } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 504982f50..7ae1d5b24 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -43,6 +43,8 @@ pub struct Session { pub server_keyboard_enabled: Arc>, pub server_file_transfer_enabled: Arc>, pub server_clipboard_enabled: Arc>, + #[cfg(not(any(target_os = "android", target_os = "ios")))] + pub old_clipboard: Arc>, } #[derive(Clone)] From 7de5b7dbc07a81cb74d47eec481ba7b8db8d609a Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 15:06:11 +0800 Subject: [PATCH 266/366] remove warns Signed-off-by: fufesou --- src/client.rs | 9 +++++---- src/client/io_loop.rs | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/client.rs b/src/client.rs index c880c2dbe..44d3e0201 100644 --- a/src/client.rs +++ b/src/client.rs @@ -24,6 +24,7 @@ use sha2::{Digest, Sha256}; use uuid::Uuid; pub use file_trait::FileManager; +#[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::tokio::sync::mpsc::UnboundedSender; use hbb_common::{ @@ -58,10 +59,10 @@ use crate::{ }; #[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::{ - common::{check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}, - ui_session_interface::SessionPermissionConfig, -}; +use crate::common::{check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}; +#[cfg(not(feature = "flutter"))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::ui_session_interface::SessionPermissionConfig; pub use super::lang::*; diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 735a35e3a..d5143c0a8 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1,7 +1,5 @@ use std::collections::HashMap; use std::num::NonZeroI64; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use std::sync::Mutex; use std::sync::{ atomic::{AtomicUsize, Ordering}, Arc, From 6e61cfb381bc7e2d32cfef9748174effbd6b54ff Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 16:02:21 +0800 Subject: [PATCH 267/366] fix chain update Signed-off-by: fufesou --- src/client.rs | 5 +++++ src/common.rs | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 44d3e0201..f080eeacb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -95,6 +95,11 @@ lazy_static::lazy_static! { static ref TEXT_CLIPBOARD_STATE: Arc> = Arc::new(Mutex::new(TextClipboardState::new())); } +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn update_clipboard_text(text: String) { + *OLD_CLIPBOARD_TEXT.lock().unwrap() = text; +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_key_state(key: enigo::Key) -> bool { use enigo::KeyboardControllable; diff --git a/src/common.rs b/src/common.rs index 3cf10eaba..43776820e 100644 --- a/src/common.rs +++ b/src/common.rs @@ -187,9 +187,14 @@ pub fn update_clipboard(clipboard: Clipboard, old: Option<&Arc>>) } match ClipboardContext::new() { Ok(mut ctx) => { - let side = if old.is_none() { "host" } else { "client" }; + let host_side = "host"; + let client_side = "client"; + let side = if old.is_none() { host_side } else { client_side }; let old = if let Some(old) = old { old } else { &CONTENT }; *old.lock().unwrap() = content.clone(); + if side == client_side { + crate::client::update_clipboard_text(content.clone()); + } let _lock = ARBOARD_MTX.lock().unwrap(); allow_err!(ctx.set_text(content)); log::debug!("{} updated on {}", CLIPBOARD_NAME, side); From 9ebfe7f9ce75ea9a4e54deb31cec7e397a6b30a4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 16:16:09 +0800 Subject: [PATCH 268/366] fix build Signed-off-by: fufesou --- src/client.rs | 2 +- src/client/io_loop.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index f080eeacb..843befd4e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -641,7 +641,7 @@ impl Client { } #[cfg(not(any(target_os = "android", target_os = "ios")))] - fn try_start_clipboard(_conf_tx: Option) { + fn try_start_clipboard(_ctx: Option) { let mut clipboard_lock = TEXT_CLIPBOARD_STATE.lock().unwrap(); if clipboard_lock.running { return; diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index d5143c0a8..eb2c96b06 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -935,7 +935,7 @@ impl Remote { Client::try_start_clipboard(None); #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] - Client::try_start_clipboard(Some(ClientClipboardContext { + Client::try_start_clipboard(Some(crate::client::ClientClipboardContext { cfg: permission_config.clone(), old: self.handler.old_clipboard.clone(), tx: sender.clone(), From 48caef0952599be41318194650d91b4cc2951737 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 10 Apr 2023 09:48:16 +0800 Subject: [PATCH 269/366] fix: move linux_headless to a new option --- .github/workflows/flutter-build.yml | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index e732a2a30..b1c4c577c 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -545,19 +545,22 @@ jobs: arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-20.04, - extra-build-features: "linux_headless", + extra-build-features: "", + enable-headless: true } - { arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-features: "flatpak", + enable-headless: false } - { arch: x86_64, target: x86_64-unknown-linux-gnu, os: ubuntu-20.04, extra-build-features: "appimage", + enable-headless: false } # - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true } steps: @@ -681,7 +684,11 @@ jobs: x86_64) # no need mock on x86_64 export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release + export DEFAULT_FEAT="" + if ${{ matrix.job.enable-headless }}; then + export DEFAULT_FEAT=linux_headless + fi + cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release ;; esac @@ -706,6 +713,7 @@ jobs: os: ubuntu-20.04, # just for naming package, not running host use-cross: true, extra-build-features: "", + enable-headless: true } - { arch: aarch64, @@ -713,6 +721,7 @@ jobs: os: ubuntu-20.04, # just for naming package, not running host use-cross: true, extra-build-features: "appimage", + enable-headless: false } # - { arch: aarch64, target: aarch64-unknown-linux-gnu , os: ubuntu-20.04, use-cross: true, extra-build-features: "flatpak" } # - { @@ -839,7 +848,11 @@ jobs: # start build pushd /workspace export VCPKG_ROOT=/opt/artifacts/vcpkg - cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release + export DEFAULT_FEAT="" + if ${{ matrix.job.enable-headless }}; then + export DEFAULT_FEAT=linux_headless + fi + cargo build --lib --features flutter,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release - name: Upload Artifacts uses: actions/upload-artifact@master @@ -863,6 +876,7 @@ jobs: os: ubuntu-latest, use-cross: true, extra-build-features: "", + enable-headless: true } # - { 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 } @@ -968,7 +982,11 @@ jobs: python3 ./res/inline-sciter.py export VCPKG_ROOT=/opt/artifacts/vcpkg export ARCH=armhf - cargo build --features inline --release --bins + export DEFAULT_FEAT="" + if ${{ matrix.job.enable-headless }}; then + export DEFAULT_FEAT=linux_headless + fi + cargo build --features inline,${{ matrix.job.extra-build-features }},$DEFAULT_FEAT --release --bins # package mkdir -p ./Release mv ./target/release/rustdesk ./Release/rustdesk From 1883c05b76b6057977e9f04ccfbdd1a1845a390d Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 10 Apr 2023 16:27:09 +0800 Subject: [PATCH 270/366] fix: remove comma in PKGBUILD --- res/PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/PKGBUILD b/res/PKGBUILD index 8d0816d7f..2b520332e 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -7,7 +7,7 @@ arch=('x86_64') url="" license=('AGPL-3.0') groups=() -depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3', 'pam') +depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3' 'pam') makedepends=() checkdepends=() optdepends=() From dc7692952bc3d8afdbede0bea257eb5a2cdec5b8 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 10 Apr 2023 16:54:50 +0800 Subject: [PATCH 271/366] fix: use c_char instead of i8/u8 --- src/api.rs | 10 ++++++---- src/plugins.rs | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/api.rs b/src/api.rs index f737243e7..19779995e 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,8 +1,10 @@ +use std::ffi::c_char; + use crate::plugins::PLUGIN_REGISTRAR; // API provided by RustDesk. -pub type LoadPluginFunc = fn(*const i8) -> i32; -pub type UnloadPluginFunc = fn(*const i8) -> i32; +pub type LoadPluginFunc = fn(*const c_char) -> i32; +pub type UnloadPluginFunc = fn(*const c_char) -> i32; #[repr(C)] pub struct RustDeskApiTable { @@ -11,12 +13,12 @@ pub struct RustDeskApiTable { } #[no_mangle] -fn load_plugin(path: *const i8) -> i32 { +fn load_plugin(path: *const c_char) -> i32 { PLUGIN_REGISTRAR.load_plugin(path) } #[no_mangle] -fn unload_plugin(path: *const i8) -> i32 { +fn unload_plugin(path: *const c_char) -> i32 { PLUGIN_REGISTRAR.unload_plugin(path) } diff --git a/src/plugins.rs b/src/plugins.rs index 7356380d3..4e44a2643 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -75,7 +75,7 @@ pub struct PluginRegistar { } impl PluginRegistar

    { - pub fn load_plugin(&self, path: *const i8) -> i32 { + pub fn load_plugin(&self, path: *const c_char) -> i32 { let p = unsafe { CStr::from_ptr(path) }; let lib_path = p.to_str().unwrap_or("").to_owned(); let lib = unsafe { libloading::Library::new(lib_path.as_str()) }; @@ -100,7 +100,7 @@ impl PluginRegistar

    { -1 } - pub fn unload_plugin(&self, path: *const i8) -> i32 { + pub fn unload_plugin(&self, path: *const c_char) -> i32 { let p = unsafe { CStr::from_ptr(path) }; let lib_path = p.to_str().unwrap_or("").to_owned(); match PLUGIN_REGISTRAR.plugins.write().unwrap().remove(&lib_path) { From e0f4cdd18cad8f7f816ec53a152d944826ad7cef Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 16:58:58 +0800 Subject: [PATCH 272/366] remove session's old_clipboard Signed-off-by: fufesou --- src/client.rs | 16 +++++----------- src/client/io_loop.rs | 13 +++++++------ src/common.rs | 7 +------ src/flutter.rs | 4 ++-- src/ui_session_interface.rs | 2 -- 5 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/client.rs b/src/client.rs index 843befd4e..4b06074eb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -95,9 +95,10 @@ lazy_static::lazy_static! { static ref TEXT_CLIPBOARD_STATE: Arc> = Arc::new(Mutex::new(TextClipboardState::new())); } +#[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn update_clipboard_text(text: String) { - *OLD_CLIPBOARD_TEXT.lock().unwrap() = text; +pub fn get_old_clipboard_text() -> &'static Arc> { + &OLD_CLIPBOARD_TEXT } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -666,16 +667,10 @@ impl Client { if let Some(msg) = check_clipboard(&mut ctx, Some(&OLD_CLIPBOARD_TEXT)) { #[cfg(feature = "flutter")] - crate::flutter::send_text_clipboard_msg( - &*OLD_CLIPBOARD_TEXT.lock().unwrap(), - msg, - ); + crate::flutter::send_text_clipboard_msg(msg); #[cfg(not(feature = "flutter"))] if let Some(ctx) = &_ctx { - if ctx.cfg.is_text_clipboard_required() - && *OLD_CLIPBOARD_TEXT.lock().unwrap() - != *ctx.old.lock().unwrap() - { + if ctx.cfg.is_text_clipboard_required() { let _ = ctx.tx.send(Data::Message(msg)); } } @@ -2444,6 +2439,5 @@ pub(crate) struct ClientClipboardContext; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub(crate) struct ClientClipboardContext { pub cfg: SessionPermissionConfig, - pub old: Arc>, pub tx: UnboundedSender, } diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index eb2c96b06..1c009d84e 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -935,11 +935,12 @@ impl Remote { Client::try_start_clipboard(None); #[cfg(not(feature = "flutter"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] - Client::try_start_clipboard(Some(crate::client::ClientClipboardContext { - cfg: permission_config.clone(), - old: self.handler.old_clipboard.clone(), - tx: sender.clone(), - })); + Client::try_start_clipboard(Some( + crate::client::ClientClipboardContext { + cfg: permission_config.clone(), + tx: sender.clone(), + }, + )); #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -972,7 +973,7 @@ impl Remote { Some(message::Union::Clipboard(cb)) => { if !self.handler.lc.read().unwrap().disable_clipboard.v { #[cfg(not(any(target_os = "android", target_os = "ios")))] - update_clipboard(cb, Some(&self.handler.old_clipboard)); + update_clipboard(cb, Some(&crate::client::get_old_clipboard_text())); #[cfg(any(target_os = "android", target_os = "ios"))] { let content = if cb.compress { diff --git a/src/common.rs b/src/common.rs index 43776820e..3cf10eaba 100644 --- a/src/common.rs +++ b/src/common.rs @@ -187,14 +187,9 @@ pub fn update_clipboard(clipboard: Clipboard, old: Option<&Arc>>) } match ClipboardContext::new() { Ok(mut ctx) => { - let host_side = "host"; - let client_side = "client"; - let side = if old.is_none() { host_side } else { client_side }; + let side = if old.is_none() { "host" } else { "client" }; let old = if let Some(old) = old { old } else { &CONTENT }; *old.lock().unwrap() = content.clone(); - if side == client_side { - crate::client::update_clipboard_text(content.clone()); - } let _lock = ARBOARD_MTX.lock().unwrap(); allow_err!(ctx.set_text(content)); log::debug!("{} updated on {}", CLIPBOARD_NAME, side); diff --git a/src/flutter.rs b/src/flutter.rs index e49f04043..6c9ff7f37 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -725,9 +725,9 @@ pub fn other_sessions_running(id: &str) -> bool { } #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn send_text_clipboard_msg(text: &str, msg: Message) { +pub fn send_text_clipboard_msg(msg: Message) { for (_id, session) in SESSIONS.read().unwrap().iter() { - if session.is_text_clipboard_required() && text != *session.old_clipboard.lock().unwrap() { + if session.is_text_clipboard_required() { session.send(Data::Message(msg.clone())); } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 7ae1d5b24..504982f50 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -43,8 +43,6 @@ pub struct Session { pub server_keyboard_enabled: Arc>, pub server_file_transfer_enabled: Arc>, pub server_clipboard_enabled: Arc>, - #[cfg(not(any(target_os = "android", target_os = "ios")))] - pub old_clipboard: Arc>, } #[derive(Clone)] From 51432df0055359fec4e165f30c3e3022b26c0b1d Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 17:11:17 +0800 Subject: [PATCH 273/366] add some comments Signed-off-by: fufesou --- src/client.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/client.rs b/src/client.rs index 4b06074eb..f39110a78 100644 --- a/src/client.rs +++ b/src/client.rs @@ -641,6 +641,11 @@ impl Client { TEXT_CLIPBOARD_STATE.lock().unwrap().running = false; } + // `try_start_clipboard` is called by all session when connection is established. (When handling peer info). + // This function only create one thread with a loop, the loop is shared by all sessions. + // After all sessions are end, the loop exists. + // + // If clipboard update is detected, the text will be sent to all sessions by `send_text_clipboard_msg`. #[cfg(not(any(target_os = "android", target_os = "ios")))] fn try_start_clipboard(_ctx: Option) { let mut clipboard_lock = TEXT_CLIPBOARD_STATE.lock().unwrap(); From 1d5f65b0053e6798f37a614296525545bf6116aa Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 17:51:07 +0800 Subject: [PATCH 274/366] fix, translate mode, numpad keys Signed-off-by: fufesou --- src/keyboard.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index d683fd579..c6da18097 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -550,8 +550,9 @@ pub fn event_to_key_events( }; #[cfg(not(any(target_os = "android", target_os = "ios")))] - if keyboard_mode != KeyboardMode::Translate { - let is_numpad_key = is_numpad_key(&event); + let is_numpad_key = is_numpad_key(&event); + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if keyboard_mode != KeyboardMode::Translate || is_numpad_key { let is_letter_key = is_letter_key(&event); for key_event in &mut key_events { if let Some(lock_modes) = _lock_modes { From c83b4398afdfda0ef74abd09cbf82316d8401637 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 10 Apr 2023 18:29:33 +0800 Subject: [PATCH 275/366] minor --- libs/hbb_common/src/udp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/hbb_common/src/udp.rs b/libs/hbb_common/src/udp.rs index bb0d071a2..55a82f8f7 100644 --- a/libs/hbb_common/src/udp.rs +++ b/libs/hbb_common/src/udp.rs @@ -32,7 +32,7 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result 0 { socket.set_recv_buffer_size(buf_size).ok(); } - log::info!( + log::debug!( "Receive buf size of udp {}: {:?}", addr, socket.recv_buffer_size() From e9d5897b4bd29e1dac55f22b31aa5bb22e8adb81 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 18:33:16 +0800 Subject: [PATCH 276/366] fix clipboard init sync Signed-off-by: fufesou --- src/client.rs | 1 + src/client/io_loop.rs | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/client.rs b/src/client.rs index f39110a78..335fded0b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -690,6 +690,7 @@ impl Client { } } + #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] fn get_current_text_clipboard_msg() -> Option { let txt = &*OLD_CLIPBOARD_TEXT.lock().unwrap(); diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 1c009d84e..405fb8fbe 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -925,11 +925,6 @@ impl Remote { self.handler.handle_peer_info(pi); self.check_clipboard_file_context(); if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - let sender = self.sender.clone(); - #[cfg(not(any(target_os = "android", target_os = "ios")))] - let permission_config = self.handler.get_permission_config(); - #[cfg(feature = "flutter")] #[cfg(not(any(target_os = "android", target_os = "ios")))] Client::try_start_clipboard(None); @@ -938,21 +933,22 @@ impl Remote { Client::try_start_clipboard(Some( crate::client::ClientClipboardContext { cfg: permission_config.clone(), - tx: sender.clone(), + tx: self.sender.clone(), }, )); #[cfg(not(any(target_os = "android", target_os = "ios")))] - tokio::spawn(async move { - // due to clipboard service interval time - sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; - if permission_config.is_text_clipboard_required() { - if let Some(msg_out) = Client::get_current_text_clipboard_msg() - { + if let Some(msg_out) = Client::get_current_text_clipboard_msg() { + let sender = self.sender.clone(); + let permission_config = self.handler.get_permission_config(); + tokio::spawn(async move { + // due to clipboard service interval time + sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; + if permission_config.is_text_clipboard_required() { sender.send(Data::Message(msg_out)).ok(); } - } - }); + }); + } } if self.handler.is_file_transfer() { From ff695bd1b4229ee5bb7bc912cb370dee55cc2c53 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 10 Apr 2023 20:34:22 +0800 Subject: [PATCH 277/366] fix build Signed-off-by: fufesou --- src/client/io_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 405fb8fbe..4cc7fd825 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -932,7 +932,7 @@ impl Remote { #[cfg(not(any(target_os = "android", target_os = "ios")))] Client::try_start_clipboard(Some( crate::client::ClientClipboardContext { - cfg: permission_config.clone(), + cfg: self.handler.get_permission_config(), tx: self.sender.clone(), }, )); From 6994ace9f58dc588a2d3108103c03befef1663ce Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Mon, 10 Apr 2023 17:46:56 +0200 Subject: [PATCH 278/366] Update index.tis --- src/ui/index.tis | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/index.tis b/src/ui/index.tis index 0e2247070..b2f71dafb 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -311,7 +311,7 @@ class MyIdMenu: Reactor.Component {

  • {svg_checkmark}{translate("Enable Service")}
  • {handler.is_rdp_service_open() ? : ""} - {false && handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connected via relay')}
  • } + {false && handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • } {handler.is_ok_change_id() ?
    : ""} {username ?
  • {translate('Logout')} ({username})
  • : From 4a6880c6b73c8f1dc88350ac3b7410133d164bbf Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 10 Apr 2023 23:35:09 +0300 Subject: [PATCH 279/366] Update lt.rs --- src/lang/lt.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 9355a95a2..a4bddf75f 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -179,10 +179,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Priimti"), ("Dismiss", "Atmesti"), ("Disconnect", "Atjungti"), - ("Allow using keyboard and mouse", "Leisti naudojant klaviatūrą ir pelę"), + ("Allow using keyboard and mouse", "Leisti naudoti klaviatūrą ir pelę"), ("Allow using clipboard", "Leisti naudoti mainų sritį"), - ("Allow hearing sound", "Leisti girdėti garsą"), - ("Allow file copy and paste", "Leisti failą kopijuoti ir įklijuoti"), + ("Allow hearing sound", "Leisti girdėti kompiuterio garsą"), + ("Allow file copy and paste", "Leisti kopijuoti ir įklijuoti failus"), ("Connected", "Prisijungta"), ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), @@ -321,9 +321,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use both passwords", "Naudoti abu slaptažodžius"), ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), - ("Allow remote restart", "Leisti nuotolinį paleidimą iš naujo"), - ("Restart Remote Device", "Paleisti nuotolinį įrenginį iš naujo"), - ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo"), + ("Allow remote restart", "Leisti nuotolinio kompiuterio paleidimą iš naujo"), + ("Restart Remote Device", "Paleisti nuotolinį kompiuterį iš naujo"), + ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo?"), ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), ("remote_restarting_tip", "Nuotolinis įrenginys paleidžiamas iš naujo. Uždarykite šį pranešimą ir po kurio laiko vėl prisijunkite naudodami nuolatinį slaptažodį."), ("Copied", "Nukopijuota"), @@ -373,10 +373,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Directory", "Katalogas"), ("Automatically record incoming sessions", "Automatiškai įrašyti įeinančius seansus"), ("Change", "Keisti"), - ("Start session recording", "Pradėti seanso įrašymą"), - ("Stop session recording", "Sustabdyti seanso įrašymą"), - ("Enable Recording Session", "Įgalinti įrašymo seansą"), - ("Allow recording session", "Leisti įrašymo sesiją"), + ("Start session recording", "Pradėti seanso įrašinėjimą"), + ("Stop session recording", "Sustabdyti seanso įrašinėjimą"), + ("Enable Recording Session", "Įgalinti seanso įrašinėjimą"), + ("Allow recording session", "Leisti seanso įrašinėjimą"), ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), ("Deny LAN Discovery", "Neleisti LAN aptikimo"), ("Write a message", "Rašyti žinutę"), From 3d61175273256f3199c6dfb0cac7f400e89c5d59 Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Mon, 10 Apr 2023 23:41:43 +0300 Subject: [PATCH 280/366] Update lt.rs --- src/lang/lt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/lt.rs b/src/lang/lt.rs index a4bddf75f..bb8f1c167 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -383,7 +383,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Prompt", "Užuomina"), ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), ("elevated_foreground_window_tip", "Dabartinis nuotolinio darbalaukio langas reikalauja didesnių privilegijų, todėl laikinai neįmanoma naudoti pelės ir klaviatūros. Galite paprašyti nuotolinio vartotojo sumažinti dabartinį langą arba spustelėti aukščio mygtuką ryšio valdymo lange. Norint išvengti šios problemos ateityje, rekomenduojama programinę įrangą įdiegti nuotoliniame įrenginyje."), - ("Disconnected", "Atjungtas"), + ("Disconnected", "Atsijungęs, seanso laikas:"), ("Other", "Kita"), ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), ("Keyboard Settings", "Klaviatūros nustatymai"), From 341f36caf2e0544f6e33830ba5eef6e14959b120 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 11 Apr 2023 09:56:35 +0800 Subject: [PATCH 281/366] minimize cm before hide to send focus back Signed-off-by: 21pages --- flutter/lib/main.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 164de04d6..27987d776 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -134,8 +134,7 @@ void runMainApp(bool startService) async { await restoreWindowPosition(WindowType.Main); // Check the startup argument, if we successfully handle the argument, we keep the main window hidden. final handledByUniLinks = await initUniLinks(); - debugPrint( - "handled by uni links: $handledByUniLinks"); + debugPrint("handled by uni links: $handledByUniLinks"); if (handledByUniLinks || checkArguments()) { windowManager.hide(); } else { @@ -249,6 +248,7 @@ void hideCmWindow() { windowManager.setOpacity(0); windowManager.waitUntilReadyToShow(windowOptions, () async { bind.mainHideDocker(); + await windowManager.minimize(); await windowManager.hide(); }); } From ec66bf9c1fc5c68e46fc247e27e6b055faa3f1b4 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 11 Apr 2023 12:41:03 +0800 Subject: [PATCH 282/366] fix portable service crash caused by unwrap Option Signed-off-by: 21pages --- src/server/portable_service.rs | 78 +++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index d9acc4152..6cff2b9dd 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -479,12 +479,11 @@ pub mod client { )?); shutdown_hooks::add_shutdown_hook(drop_portable_service_shared_memory); } - let mut option = SHMEM.lock().unwrap(); - let shmem = option.as_mut().unwrap(); - unsafe { - libc::memset(shmem.as_ptr() as _, 0, shmem.len() as _); + if let Some(shmem) = SHMEM.lock().unwrap().as_mut() { + unsafe { + libc::memset(shmem.as_ptr() as _, 0, shmem.len() as _); + } } - drop(option); match para { StartPara::Direct => { if let Err(e) = crate::platform::run_background( @@ -544,8 +543,11 @@ pub mod client { } pub extern "C" fn drop_portable_service_shared_memory() { - log::info!("drop shared memory"); - *SHMEM.lock().unwrap() = None; + let mut lock = SHMEM.lock().unwrap(); + if lock.is_some() { + *lock = None; + log::info!("drop shared memory"); + } } pub fn set_quick_support(v: bool) { @@ -560,17 +562,18 @@ pub mod client { Self: Sized, { let mut option = SHMEM.lock().unwrap(); - let shmem = option.as_mut().unwrap(); - Self::set_para( - shmem, - CapturerPara { - current_display, - use_yuv, - use_yuv_set: false, - timeout_ms: 33, - }, - ); - shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); + if let Some(shmem) = option.as_mut() { + Self::set_para( + shmem, + CapturerPara { + current_display, + use_yuv, + use_yuv_set: false, + timeout_ms: 33, + }, + ); + shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); + } CapturerPortable {} } @@ -587,25 +590,29 @@ pub mod client { impl TraitCapturer for CapturerPortable { fn set_use_yuv(&mut self, use_yuv: bool) { let mut option = SHMEM.lock().unwrap(); - let shmem = option.as_mut().unwrap(); - unsafe { - let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); - let para = para_ptr as *const CapturerPara; - Self::set_para( - shmem, - CapturerPara { - current_display: (*para).current_display, - use_yuv, - use_yuv_set: true, - timeout_ms: (*para).timeout_ms, - }, - ); + if let Some(shmem) = option.as_mut() { + unsafe { + let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); + let para = para_ptr as *const CapturerPara; + Self::set_para( + shmem, + CapturerPara { + current_display: (*para).current_display, + use_yuv, + use_yuv_set: true, + timeout_ms: (*para).timeout_ms, + }, + ); + } } } fn frame<'a>(&'a mut self, timeout: Duration) -> std::io::Result> { - let mut option = SHMEM.lock().unwrap(); - let shmem = option.as_mut().unwrap(); + let mut lock = SHMEM.lock().unwrap(); + let shmem = lock.as_mut().ok_or(std::io::Error::new( + std::io::ErrorKind::Other, + "shmem dropped".to_string(), + ))?; unsafe { let base = shmem.as_ptr(); let para_ptr = base.add(ADDR_CAPTURER_PARA); @@ -801,7 +808,10 @@ pub mod client { pub fn get_cursor_info(pci: PCURSORINFO) -> BOOL { if RUNNING.lock().unwrap().clone() { - get_cursor_info_(&mut SHMEM.lock().unwrap().as_mut().unwrap(), pci) + let mut option = SHMEM.lock().unwrap(); + option + .as_mut() + .map_or(FALSE, |sheme| get_cursor_info_(sheme, pci)) } else { unsafe { winuser::GetCursorInfo(pci) } } From cb26468d2dbbe88966beb36ab872988a11b37b01 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Tue, 11 Apr 2023 06:57:46 -0700 Subject: [PATCH 283/366] Italian language update --- src/lang/it.rs | 332 ++++++++++++++++++++++++------------------------- 1 file changed, 166 insertions(+), 166 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 1211ceb3c..6a4e2fd8e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -2,18 +2,18 @@ lazy_static::lazy_static! { pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ ("Status", "Stato"), - ("Your Desktop", "Il tuo desktop"), - ("desk_tip", "Puoi accedere al tuo desktop usando l'ID e la password riportati qui."), + ("Your Desktop", "Questo desktop"), + ("desk_tip", "Puoi accedere a questo desktop usando l'ID e la password riportati qui sotto."), ("Password", "Password"), ("Ready", "Pronto"), - ("Established", "Stabilito"), - ("connecting_status", "Connessione alla rete RustDesk in corso..."), + ("Established", "Stabilita"), + ("connecting_status", "Connessione alla rete RustDesk..."), ("Enable Service", "Abilita servizio"), ("Start Service", "Avvia servizio"), ("Service is running", "Il servizio è in esecuzione"), ("Service is not running", "Il servizio non è in esecuzione"), - ("not_ready_status", "Non pronto. Verifica la tua connessione"), - ("Control Remote Desktop", "Controlla un desktop remoto"), + ("not_ready_status", "Non pronto. Verifica la connessione"), + ("Control Remote Desktop", "Controlla desktop remoto"), ("Transfer File", "Trasferisci file"), ("Connect", "Connetti"), ("Recent Sessions", "Sessioni recenti"), @@ -22,38 +22,38 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("TCP Tunneling", "Tunnel TCP"), ("Remove", "Rimuovi"), ("Refresh random password", "Nuova password casuale"), - ("Set your own password", "Imposta la tua password"), + ("Set your own password", "Imposta la password"), ("Enable Keyboard/Mouse", "Abilita tastiera/mouse"), ("Enable Clipboard", "Abilita appunti"), ("Enable File Transfer", "Abilita trasferimento file"), ("Enable TCP Tunneling", "Abilita tunnel TCP"), ("IP Whitelisting", "IP autorizzati"), ("ID/Relay Server", "Server ID/Relay"), - ("Import Server Config", "Importa configurazione Server"), - ("Export Server Config", "Esporta configurazione Server"), - ("Import server configuration successfully", "Configurazione Server importata con successo"), - ("Export server configuration successfully", "Configurazione Server esportata con successo"), - ("Invalid server configuration", "Configurazione Server non valida"), + ("Import Server Config", "Importa configurazione server"), + ("Export Server Config", "Esporta configurazione server"), + ("Import server configuration successfully", "Configurazione server importata correttamente"), + ("Export server configuration successfully", "Configurazione Server esportata correttamente"), + ("Invalid server configuration", "Configurazione server non valida"), ("Clipboard is empty", "Gli appunti sono vuoti"), ("Stop service", "Arresta servizio"), ("Change ID", "Cambia ID"), - ("Your new ID", "Il tuo nuovo ID"), + ("Your new ID", "Il nuovo ID"), ("length %min% to %max%", "da lunghezza %min% a %max%"), ("starts with a letter", "inizia con una lettera"), ("allowed characters", "caratteri consentiti"), - ("id_change_tip", "Puoi usare solo i caratteri a-z, A-Z, 0-9 e _ (underscore). Il primo carattere deve essere a-z o A-Z. La lunghezza deve essere fra 6 e 16 caratteri."), + ("id_change_tip", "Puoi usare solo i caratteri a-z, A-Z, 0-9 e _ (sottolineato).\nIl primo carattere deve essere a-z o A-Z.\nLa lunghezza deve essere fra 6 e 16 caratteri."), ("Website", "Sito web"), - ("About", "Informazioni"), - ("Slogan_tip", "Fatta con il cuore in questo mondo caotico!"), + ("About", "Info programma"), + ("Slogan_tip", "Realizzato con il cuore in questo mondo caotico!"), ("Privacy Statement", "Informativa sulla privacy"), - ("Mute", "Silenzia"), - ("Build Date", "Data della build"), + ("Mute", "Audio off"), + ("Build Date", "Data build"), ("Version", "Versione"), ("Home", "Home"), - ("Audio Input", "Input audio"), + ("Audio Input", "Ingresso audio"), ("Enhancements", "Miglioramenti"), - ("Hardware Codec", "Codifica Hardware"), - ("Adaptive Bitrate", "Bitrate Adattivo"), + ("Hardware Codec", "Codec hardware"), + ("Adaptive Bitrate", "Bitrate adattivo"), ("ID Server", "ID server"), ("Relay Server", "Server relay"), ("API Server", "Server API"), @@ -68,65 +68,65 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Close", "Chiudi"), ("Retry", "Riprova"), ("OK", "OK"), - ("Password Required", "Password Richiesta"), - ("Please enter your password", "Inserisci la tua password"), + ("Password Required", "Password richiesta"), + ("Please enter your password", "Inserisci la password"), ("Remember password", "Ricorda password"), - ("Wrong Password", "Password Errata"), + ("Wrong Password", "Password errata"), ("Do you want to enter again?", "Vuoi riprovare?"), ("Connection Error", "Errore di connessione"), ("Error", "Errore"), ("Reset by the peer", "Reimpostata dal peer"), ("Connecting...", "Connessione..."), - ("Connection in progress. Please wait.", "Connessione in corso. Attendi."), - ("Please try 1 minute later", "Per favore riprova fra 1 minuto"), - ("Login Error", "Errore Login"), - ("Successful", "Successo"), + ("Connection in progress. Please wait.", "Connessione in corso..."), + ("Please try 1 minute later", "Riprova fra 1 minuto"), + ("Login Error", "Errore accesso"), + ("Successful", "Completato"), ("Connected, waiting for image...", "Connesso, in attesa dell'immagine..."), ("Name", "Nome"), ("Type", "Tipo"), ("Modified", "Modificato"), ("Size", "Dimensione"), - ("Show Hidden Files", "Mostra file nascosti"), + ("Show Hidden Files", "Visualizza file nascosti"), ("Receive", "Ricevi"), ("Send", "Invia"), ("Refresh File", "Aggiorna file"), ("Local", "Locale"), - ("Remote", "Remote"), + ("Remote", "Remoto"), ("Remote Computer", "Computer remoto"), ("Local Computer", "Computer locale"), - ("Confirm Delete", "Conferma cancellazione"), - ("Delete", "Eliminare"), + ("Confirm Delete", "Conferma eliminazione"), + ("Delete", "Elimina"), ("Properties", "Proprietà"), ("Multi Select", "Selezione multipla"), ("Select All", "Seleziona tutto"), ("Unselect All", "Deseleziona tutto"), - ("Empty Directory", "Directory vuota"), - ("Not an empty directory", "Non una directory vuota"), - ("Are you sure you want to delete this file?", "Vuoi davvero eliminare questo file?"), - ("Are you sure you want to delete this empty directory?", "Sei sicuro di voler eliminare questa directory vuota?"), - ("Are you sure you want to delete the file of this directory?", "Sei sicuro di voler eliminare il file di questa directory?"), + ("Empty Directory", "Cartella vuota"), + ("Not an empty directory", "Non è una cartella vuota"), + ("Are you sure you want to delete this file?", "Sei sicuro di voler eliminare questo file?"), + ("Are you sure you want to delete this empty directory?", "Sei sicuro di voler eliminare questa cartella vuota?"), + ("Are you sure you want to delete the file of this directory?", "Sei sicuro di voler eliminare il file di questa cartella?"), ("Do this for all conflicts", "Ricorca questa scelta per tutti i conflitti"), ("This is irreversible!", "Questo è irreversibile!"), - ("Deleting", "Cancellazione di"), + ("Deleting", "Eliminazione di"), ("files", "file"), ("Waiting", "In attesa"), - ("Finished", "Terminato"), + ("Finished", "Completato"), ("Speed", "Velocità"), ("Custom Image Quality", "Qualità immagine personalizzata"), ("Privacy mode", "Modalità privacy"), - ("Block user input", "Blocca l'input dell'utente"), - ("Unblock user input", "Sbloccare l'input dell'utente"), - ("Adjust Window", "Adatta la finestra"), + ("Block user input", "Blocca input utente"), + ("Unblock user input", "Sblocca input utente"), + ("Adjust Window", "Adatta finestra"), ("Original", "Originale"), ("Shrink", "Restringi"), ("Stretch", "Allarga"), - ("Scrollbar", "Barra di scorrimento"), + ("Scrollbar", "Barra scorrimento"), ("ScrollAuto", "Scorri automaticamente"), - ("Good image quality", "Qualità immagine migliore"), + ("Good image quality", "Qualità immagine buona"), ("Balanced", "Bilanciato"), - ("Optimize reaction time", "Ottimizza il tempo di reazione"), + ("Optimize reaction time", "Ottimizza tempo reazione"), ("Custom", "Personalizzato"), - ("Show remote cursor", "Mostra il cursore remoto"), + ("Show remote cursor", "Visualizza cursore remoto"), ("Show quality monitor", "Visualizza qualità video"), ("Disable clipboard", "Disabilita appunti"), ("Lock after session end", "Blocca al termine della sessione"), @@ -144,25 +144,25 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Failed to connect via relay server", "Errore di connessione tramite il server relay"), ("Failed to make direct connection to remote desktop", "Impossibile connettersi direttamente al desktop remoto"), ("Set Password", "Imposta password"), - ("OS Password", "Password del sistema operativo"), - ("install_tip", "A causa del Controllo Account Utente, RustDesk potrebbe non funzionare correttamente come desktop remoto. Per evitare questo problema, fai click sul tasto qui sotto per installare RustDesk a livello di sistema."), - ("Click to upgrade", "Fai click per aggiornare"), - ("Click to download", "Cliquez per scaricare"), - ("Click to update", "Fare clic per aggiornare"), + ("OS Password", "Password sistema operativo"), + ("install_tip", "A causa del controllo account uUtente (UAC), RustDesk potrebbe non funzionare correttamente come desktop remoto.\nPer evitare questo problema, fai clic sul tasto qui sotto per installare RustDesk a livello di sistema."), + ("Click to upgrade", "Fai clic per aggiornare"), + ("Click to download", "Fai clic per il download"), + ("Click to update", "Fai clic per aggiornare"), ("Configure", "Configura"), - ("config_acc", "Per controllare il tuo desktop dall'esterno, devi fornire a RustDesk il permesso \"Accessibilità\"."), - ("config_screen", "Per controllare il tuo desktop dall'esterno, devi fornire a RustDesk il permesso \"Registrazione schermo\"."), + ("config_acc", "Per controllare il desktop dall'esterno, devi fornire a RustDesk il permesso 'Accessibilità'."), + ("config_screen", "Per controllare il desktop dall'esterno, devi fornire a RustDesk il permesso 'Registrazione schermo'."), ("Installing ...", "Installazione ..."), ("Install", "Installa"), ("Installation", "Installazione"), - ("Installation Path", "Percorso di installazione"), - ("Create start menu shortcuts", "Crea i collegamenti nel menu di avvio"), + ("Installation Path", "Percorso installazione"), + ("Create start menu shortcuts", "Crea i collegamenti nel menu Start"), ("Create desktop icon", "Crea un'icona sul desktop"), ("agreement_tip", "Avviando l'installazione, accetti i termini del contratto di licenza."), ("Accept and Install", "Accetta e installa"), - ("End-user license agreement", "Contratto di licenza con l'utente finale"), + ("End-user license agreement", "Contratto di licenza utente finale"), ("Generating ...", "Generazione ..."), - ("Your installation is lower version.", "La tua installazione non è aggiornata."), + ("Your installation is lower version.", "Questa installazione non è aggiornata."), ("not_close_tcp_tip", "Non chiudere questa finestra mentre stai usando il tunnel"), ("Listening ...", "In ascolto ..."), ("Remote Host", "Host remoto"), @@ -172,9 +172,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Local Port", "Porta locale"), ("Local Address", "Indirizzo locale"), ("Change Local Port", "Cambia porta locale"), - ("setup_server_tip", "Per una connessione più veloce, configura un tuo server"), - ("Too short, at least 6 characters.", "Troppo breve, almeno 6 caratteri"), - ("The confirmation is not identical.", "La conferma non corrisponde"), + ("setup_server_tip", "Per una connessione più veloce, configura uno specifico server"), + ("Too short, at least 6 characters.", "Troppo corta, almeno 6 caratteri"), + ("The confirmation is not identical.", "La password di conferma non corrisponde"), ("Permissions", "Permessi"), ("Accept", "Accetta"), ("Dismiss", "Rifiuta"), @@ -189,7 +189,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Direct and unencrypted connection", "Connessione diretta e non cifrata"), ("Relayed and unencrypted connection", "Connessione tramite relay e non cifrata"), ("Enter Remote ID", "Inserisci l'ID remoto"), - ("Enter your password", "Inserisci la tua password"), + ("Enter your password", "Inserisci la password"), ("Logging in...", "Autenticazione..."), ("Enable RDP session sharing", "Abilita la condivisione della sessione RDP"), ("Auto Login", "Accesso automatico"), @@ -197,31 +197,31 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Rename", "Rinomina"), ("Space", "Spazio"), ("Create Desktop Shortcut", "Crea collegamento sul desktop"), - ("Change Path", "Cambia percorso"), + ("Change Path", "Modifica percorso"), ("Create Folder", "Crea cartella"), ("Please enter the folder name", "Inserisci il nome della cartella"), ("Fix it", "Risolvi"), ("Warning", "Avviso"), - ("Login screen using Wayland is not supported", "La schermata di accesso non è supportata utilizzando Wayland"), + ("Login screen using Wayland is not supported", "La schermata di accesso non è supportata usando Wayland"), ("Reboot required", "Riavvio necessario"), ("Unsupported display server", "Display server non supportato"), - ("x11 expected", "x11 necessario"), + ("x11 expected", "necessario xll"), ("Port", "Porta"), ("Settings", "Impostazioni"), ("Username", "Nome utente"), - ("Invalid port", "Numero di porta non valido"), + ("Invalid port", "Numero porta non valido"), ("Closed manually by the peer", "Chiuso manualmente dal peer"), ("Enable remote configuration modification", "Abilita la modifica remota della configurazione"), ("Run without install", "Esegui senza installare"), ("Connect via relay", "Collegati tramite relay"), ("Always connect via relay", "Collegati sempre tramite relay"), - ("whitelist_tip", "Solo gli indirizzi IP autorizzati possono connettersi a questo desktop"), + ("whitelist_tip", "Possono connettersi a questo desktop solo gli indirizzi IP autorizzati"), ("Login", "Accedi"), ("Verify", "Verifica"), ("Remember me", "Ricordami"), ("Trust this device", "Registra questo dispositivo come attendibile"), ("Verification code", "Codice di verifica"), - ("verification_tip", "È stato rilevato un nuovo dispositivo e un codice di verifica è stato inviato all'indirizzo e-mail registrato; inserire il codice di verifica per continuare l'accesso."), + ("verification_tip", "È stato rilevato un nuovo dispositivo ed è stato inviato un codice di verifica all'indirizzo email registrato.\nPer continuare l'accesso inserisci il codice di verifica."), ("Logout", "Esci"), ("Tags", "Tag"), ("Search ID", "Cerca ID"), @@ -243,10 +243,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Socks5 Proxy", "Proxy Socks5"), ("Hostname", "Nome host"), ("Discovered", "Rilevati"), - ("install_daemon_tip", "Per avviarsi all'accensione, è necessario installare il servizio di sistema."), + ("install_daemon_tip", "Per avviare il programma all'accensione, è necessario installarlo come servizio di sistema."), ("Remote ID", "ID remoto"), ("Paste", "Incolla"), - ("Paste here?", "Incolla qui?"), + ("Paste here?", "Incollare qui?"), ("Are you sure to close the connection?", "Sei sicuro di voler chiudere la connessione?"), ("Download new version", "Scarica nuova versione"), ("Touch mode", "Modalità tocco"), @@ -264,9 +264,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Two-Finger Move", "Movimento con due dita"), ("Canvas Move", "Sposta tela"), ("Pinch to Zoom", "Pizzica per zoomare"), - ("Canvas Zoom", "Zoom canvas"), - ("Reset canvas", "Ripristina canvas"), - ("No permission of file transfer", "Nessun permesso di trasferimento di file"), + ("Canvas Zoom", "Zoom tela"), + ("Reset canvas", "Ripristina tela"), + ("No permission of file transfer", "Nessun permesso per il trasferimento file"), ("Note", "Nota"), ("Connection", "Connessione"), ("Share Screen", "Condividi schermo"), @@ -275,48 +275,48 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("items", "Oggetti"), ("Selected", "Selezionato"), ("Screen Capture", "Cattura schermo"), - ("Input Control", "Controllo di input"), + ("Input Control", "Controllo input"), ("Audio Capture", "Acquisizione audio"), ("File Connection", "Connessione file"), ("Screen Connection", "Connessione schermo"), ("Do you accept?", "Accetti?"), ("Open System Setting", "Apri impostazioni di sistema"), - ("How to get Android input permission?", "Come ottenere l'autorizzazione di input su Android?"), - ("android_input_permission_tip1", "Affinché un dispositivo remoto possa controllare il tuo dispositivo Android tramite mouse o tocco, devi consentire a RustDesk di utilizzare il servizio \"Accessibilità\"."), - ("android_input_permission_tip2", "Vai alla pagina delle impostazioni di sistema che si aprirà di seguito, trova e accedi a [Servizi installati], attiva il servizio [RustDesk Input]."), - ("android_new_connection_tip", "È stata ricevuta una nuova richiesta di controllo per il dispositivo corrente."), + ("How to get Android input permission?", "Come ottenere l'autorizzazione input in Android?"), + ("android_input_permission_tip1", "Affinché un dispositivo remoto possa controllare un dispositivo Android tramite mouse o tocco, devi consentire a RustDesk di usare il servizio 'Accessibilità'."), + ("android_input_permission_tip2", "Vai nella pagina delle impostazioni di sistema che si aprirà di seguito, trova e accedi a [Servizi installati], attiva il servizio [RustDesk Input]."), + ("android_new_connection_tip", "È stata ricevuta una nuova richiesta di controllo per il dispositivo attuale."), ("android_service_will_start_tip", "L'attivazione di Cattura schermo avvierà automaticamente il servizio, consentendo ad altri dispositivi di richiedere una connessione da questo dispositivo."), ("android_stop_service_tip", "La chiusura del servizio chiuderà automaticamente tutte le connessioni stabilite."), - ("android_version_audio_tip", "L'attuale versione di Android non supporta l'acquisizione audio, esegui l'upgrade ad Android 10 o versioni successive."), + ("android_version_audio_tip", "L'attuale versione di Android non supporta l'acquisizione audio, esegui l'aggiornamento ad Android 10 o versioni successive."), ("android_start_service_tip", ""), ("android_permission_may_not_change_tip", ""), ("Account", "Account"), ("Overwrite", "Sovrascrivi"), - ("This file exists, skip or overwrite this file?", "Questo file esiste, saltare o sovrascrivere questo file?"), + ("This file exists, skip or overwrite this file?", "Questo file esiste, vuoi ignorarlo o sovrascrivere questo file?"), ("Quit", "Esci"), ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), ("Help", "Aiuto"), ("Failed", "Fallito"), - ("Succeeded", "Successo"), - ("Someone turns on privacy mode, exit", "Qualcuno attiva la modalità privacy, esci"), + ("Succeeded", "Completato"), + ("Someone turns on privacy mode, exit", "Qualcuno ha attivato la modalità privacy, uscita"), ("Unsupported", "Non supportato"), ("Peer denied", "Peer negato"), - ("Please install plugins", "Si prega di installare i plugin"), - ("Peer exit", "Uscita tra pari"), + ("Please install plugins", "Installa i plugin"), + ("Peer exit", "Uscita peer"), ("Failed to turn off", "Impossibile spegnere"), ("Turned off", "Spegni"), ("In privacy mode", "In modalità privacy"), - ("Out privacy mode", "Fuori modalità privacy"), - ("Language", "Linguaggio"), + ("Out privacy mode", "Uscita dalla modalità privacy"), + ("Language", "Lingua"), ("Keep RustDesk background service", "Mantieni il servizio di RustDesk in background"), ("Ignore Battery Optimizations", "Ignora le ottimizzazioni della batteria"), - ("android_open_battery_optimizations_tip", "Se si desidera disabilitare questa funzione, andare nelle impostazioni dell'applicazione RustDesk, aprire la sezione [Batteria] e deselezionare [Senza restrizioni]."), + ("android_open_battery_optimizations_tip", "Se vuoi disabilitare questa funzione, vai nelle impostazioni dell'applicazione RustDesk, apri la sezione 'Batteria' e deseleziona 'Senza restrizioni'."), ("Start on Boot", "Avvia all'accensione"), - ("Start the screen sharing service on boot, requires special permissions", "L'avvio del servizio di condivisione dello schermo all'accensione, richiede autorizzazioni speciali"), + ("Start the screen sharing service on boot, requires special permissions", "L'avvio del servizio di condivisione dello schermo all'accensione richiede autorizzazioni speciali"), ("Connection not allowed", "Connessione non consentita"), ("Legacy mode", "Modalità legacy"), ("Map mode", "Modalità mappa"), - ("Translate mode", "Modalità di traduzione"), + ("Translate mode", "Modalità traduzione"), ("Use permanent password", "Usa password permanente"), ("Use both passwords", "Usa entrambe le password"), ("Set permanent password", "Imposta password permanente"), @@ -325,18 +325,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Restart Remote Device", "Riavvia dispositivo remoto"), ("Are you sure you want to restart", "Sei sicuro di voler riavviare?"), ("Restarting Remote Device", "Il dispositivo remoto si sta riavviando"), - ("remote_restarting_tip", "Riavviare il dispositivo remoto"), + ("remote_restarting_tip", "Riavvia il dispositivo remoto"), ("Copied", "Copiato"), ("Exit Fullscreen", "Esci dalla modalità schermo intero"), ("Fullscreen", "A schermo intero"), ("Mobile Actions", "Azioni mobili"), ("Select Monitor", "Seleziona schermo"), - ("Control Actions", "Azioni di controllo"), - ("Display Settings", "Impostazioni di visualizzazione"), + ("Control Actions", "Azioni controllo"), + ("Display Settings", "Impostazioni visualizzazione"), ("Ratio", "Rapporto"), - ("Image Quality", "Qualità dell'immagine"), - ("Scroll Style", "Stile di scorrimento"), - ("Show Menubar", "Mostra la barra dei menu"), + ("Image Quality", "Qualità immagine"), + ("Scroll Style", "Stile scorrimento"), + ("Show Menubar", "Visualizza barra menu"), ("Hide Menubar", "nascondi la barra dei menu"), ("Direct Connection", "Connessione diretta"), ("Relay Connection", "Connessione relay"), @@ -347,91 +347,91 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("General", "Generale"), ("Security", "Sicurezza"), ("Theme", "Tema"), - ("Dark Theme", "Tema Scuro"), - ("Light Theme", ""), + ("Dark Theme", "Tema scuro"), + ("Light Theme", "Tema chiaro"), ("Dark", "Scuro"), ("Light", "Chiaro"), - ("Follow System", "Segui il sistema"), + ("Follow System", "Sistema"), ("Enable hardware codec", "Abilita codec hardware"), - ("Unlock Security Settings", "Sblocca impostazioni di sicurezza"), + ("Unlock Security Settings", "Sblocca impostazioni sicurezza"), ("Enable Audio", "Abilita audio"), ("Unlock Network Settings", "Sblocca impostazioni di rete"), ("Server", "Server"), ("Direct IP Access", "Accesso IP diretto"), ("Proxy", "Proxy"), ("Apply", "Applica"), - ("Disconnect all devices?", "Disconnettere tutti i dispositivi?"), - ("Clear", "Ripulisci"), - ("Audio Input Device", "Dispositivo di input audio"), + ("Disconnect all devices?", "Vuoi disconnettere tutti i dispositivi?"), + ("Clear", "Pulisci"), + ("Audio Input Device", "Dispositivo ingresso audio"), ("Deny remote access", "Nega accesso remoto"), - ("Use IP Whitelisting", "Utilizza la whitelist di IP"), + ("Use IP Whitelisting", "Usa elenco IP autorizzati"), ("Network", "Rete"), ("Enable RDP", "Abilita RDP"), - ("Pin menubar", "Blocca la barra dei menu"), - ("Unpin menubar", "Sblocca la barra dei menu"), + ("Pin menubar", "Blocca barra menu"), + ("Unpin menubar", "Sblocca barra menu"), ("Recording", "Registrazione"), ("Directory", "Cartella"), ("Automatically record incoming sessions", "Registra automaticamente le sessioni in entrata"), - ("Change", "Cambia"), - ("Start session recording", "Inizia registrazione della sessione"), - ("Stop session recording", "Ferma registrazione della sessione"), - ("Enable Recording Session", "Abilita registrazione della sessione"), - ("Allow recording session", "Permetti di registrare la sessione"), - ("Enable LAN Discovery", "Abilita il rilevamento della LAN"), - ("Deny LAN Discovery", "Nega il rilevamento della LAN"), + ("Change", "Modifica"), + ("Start session recording", "Inizia registrazione sessione"), + ("Stop session recording", "Ferma registrazione sessione"), + ("Enable Recording Session", "Abilita registrazione sessione"), + ("Allow recording session", "Permetti registrazione sessione"), + ("Enable LAN Discovery", "Abilita rilevamento LAN"), + ("Deny LAN Discovery", "Nega rilevamento LAN"), ("Write a message", "Scrivi un messaggio"), - ("Prompt", "Richiede"), + ("Prompt", "Richiedi"), ("Please wait for confirmation of UAC...", "Attendi la conferma dell'UAC..."), - ("elevated_foreground_window_tip", "La finestra corrente del desktop remoto richiede privilegi più elevati per funzionare, quindi non è in grado di utilizzare temporaneamente il mouse e la tastiera. È possibile chiedere all'utente remoto di ridurre a icona la finestra corrente o di fare clic sul pulsante di elevazione nella finestra di gestione della connessione. Per evitare questo problema, si consiglia di installare il software sul dispositivo remoto."), + ("elevated_foreground_window_tip", "La finestra attuale del desktop remoto richiede per funzionare privilegi più elevati, quindi non è possibile usare temporaneamente il mouse e la tastiera.\nÈ possibile chiedere all'utente remoto di ridurre a icona la finestra attuale o di selezionare il pulsante di elevazione nella finestra di gestione della connessione.\nPer evitare questo problema, ti consigliamo di installare il software nel dispositivo remoto."), ("Disconnected", "Disconnesso"), ("Other", "Altro"), ("Confirm before closing multiple tabs", "Conferma prima di chiudere più schede"), ("Keyboard Settings", "Impostazioni tastiera"), ("Full Access", "Accesso completo"), ("Screen Share", "Condivisione dello schermo"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland richiede Ubuntu 21.04 o successiva."), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland richiede una versione superiore della distribuzione Linux. Prova X11 desktop o cambia il tuo sistema operativo."), - ("JumpLink", "View"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland richiede Ubuntu 21.04 o versione successiva."), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland richiede una versione superiore della distribuzione Linux.\nProva X11 desktop o cambia il sistema operativo."), + ("JumpLink", "Vai a"), ("Please Select the screen to be shared(Operate on the peer side).", "Seleziona lo schermo da condividere (opera sul lato peer)."), - ("Show RustDesk", "Mostra RustDesk"), + ("Show RustDesk", "Visualizza RustDesk"), ("This PC", "Questo PC"), ("or", "O"), ("Continue with", "Continua con"), ("Elevate", "Eleva"), ("Zoom cursor", "Cursore zoom"), ("Accept sessions via password", "Accetta sessioni via password"), - ("Accept sessions via click", "Accetta sessioni via click"), + ("Accept sessions via click", "Accetta sessioni via clic"), ("Accept sessions via both", "Accetta sessioni con entrambi"), - ("Please wait for the remote side to accept your session request...", "Attendere che il lato remoto accetti la richiesta di sessione..."), + ("Please wait for the remote side to accept your session request...", "Attendi che il dispositivo remoto accetti la richiesta di sessione..."), ("One-time Password", "Password monouso"), ("Use one-time password", "Usa password monouso"), ("One-time password length", "Lunghezza password monouso"), - ("Request access to your device", "Richiedi l'accesso al tuo dispositivo"), + ("Request access to your device", "Richiedi l'accesso al dispositivo"), ("Hide connection management window", "Nascondi la finestra di gestione delle connessioni"), ("hide_cm_tip", "Permetti di nascondere solo se si accettano sessioni con password permanente"), - ("wayland_experiment_tip", "Il supporto Wayland è in fase sperimentale, utilizza X11 se necessiti di un accesso stabile."), + ("wayland_experiment_tip", "Il supporto Wayland è in fase sperimentale, se vuoi un accesso stabile usa X11."), ("Right click to select tabs", "Clic con il tasto destro per selezionare le schede"), ("Skipped", "Saltato"), ("Add to Address Book", "Aggiungi alla rubrica"), ("Group", "Gruppo"), ("Search", "Cerca"), - ("Closed manually by web console", "Chiudi manualmente dalla console Web"), - ("Local keyboard type", "Tipo di tastiera locale"), + ("Closed manually by web console", "Chiudi manualmente dalla console web"), + ("Local keyboard type", "Tipo tastiera locale"), ("Select local keyboard type", "Seleziona il tipo di tastiera locale"), - ("software_render_tip", "Se si dispone di una scheda grafica Nvidia e la finestra remota si chiude immediatamente dopo la connessione, l'installazione del driver nouveau e la scelta di utilizzare il rendering software possono aiutare. È necessario un riavvio del software."), - ("Always use software rendering", "Usa sempre il render Software"), - ("config_input", "Per controllare il desktop remoto con la tastiera, è necessario concedere le autorizzazioni a RustDesk \"Monitoraggio dell'input\"."), - ("config_microphone", "Per poter chiamare, è necessario concedere l'autorizzazione a RustDesk \"Registra audio\"."), - ("request_elevation_tip", "È possibile richiedere l'elevazione se c'è qualcuno sul lato remoto."), + ("software_render_tip", "Se disponi di una scheda grafica Nvidia e la finestra remota si chiude immediatamente dopo la connessione, l'installazione del driver aggiornato e la scelta di usare il rendering software possono aiutare.\nÈ necessario un riavvio del programma."), + ("Always use software rendering", "Usa sempre il render software"), + ("config_input", "Per controllare il desktop remoto con la tastiera, è necessario concedere le autorizzazioni a RustDesk 'Monitoraggio input'."), + ("config_microphone", "Per poter chiamare, è necessario concedere l'autorizzazione a RustDesk 'Registra audio'."), + ("request_elevation_tip", "Se c'è qualcuno nel lato remoto è possibile richiedere l'elevazione."), ("Wait", "Attendi"), ("Elevation Error", "Errore durante l'elevazione dei diritti"), - ("Ask the remote user for authentication", "Chiedere l'autenticazione all'utente remoto"), - ("Choose this if the remote account is administrator", "Scegliere questa opzione se l'account remoto è amministratore"), - ("Transmit the username and password of administrator", "Trasmettere il nome utente e la password dell'amministratore"), - ("still_click_uac_tip", "Richiede ancora che l'utente remoto faccia clic su OK nella finestra UAC dell'esecuzione di RustDesk."), + ("Ask the remote user for authentication", "Chiedi l'autenticazione all'utente remoto"), + ("Choose this if the remote account is administrator", "Scegli questa opzione se l'account remoto è amministratore"), + ("Transmit the username and password of administrator", "Trasmetti il nome utente e la password dell'amministratore"), + ("still_click_uac_tip", "Richiedi ancora che l'utente remoto faccia clic su OK nella finestra UAC dell'esecuzione di RustDesk."), ("Request Elevation", "Richiedi elevazione dei diritti"), - ("wait_accept_uac_tip", "Attendere che l'utente remoto accetti la finestra di dialogo UAC."), - ("Elevate successfully", "Elevazione dei diritti effettuata con successo"), + ("wait_accept_uac_tip", "Attendi che l'utente remoto accetti la finestra di dialogo UAC."), + ("Elevate successfully", "Elevazione dei diritti effettuata correttamente"), ("uppercase", "Maiuscola"), ("lowercase", "Minuscola"), ("digit", "Numero"), @@ -441,55 +441,55 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Medium", "Media"), ("Strong", "Forte"), ("Switch Sides", "Cambia lato"), - ("Please confirm if you want to share your desktop?", "Vuoi condividere il tuo desktop?"), + ("Please confirm if you want to share your desktop?", "Vuoi condividere il desktop?"), ("Display", "Visualizzazione"), - ("Default View Style", "Stile Visualizzazione Predefinito"), - ("Default Scroll Style", "Stile Scorrimento Predefinito"), - ("Default Image Quality", "Qualità Immagine Predefinita"), - ("Default Codec", "Codec Predefinito"), + ("Default View Style", "Stile visualizzazione predefinito"), + ("Default Scroll Style", "Stile scorrimento predefinito"), + ("Default Image Quality", "Qualitài immagine predefinita"), + ("Default Codec", "Codec predefinito"), ("Bitrate", "Bitrate"), ("FPS", "FPS"), - ("Auto", "Auto"), - ("Other Default Options", "Altre Opzioni Predefinite"), + ("Auto", "Automatico"), + ("Other Default Options", "Altre opzioni predefinite"), ("Voice call", "Chiamata vocale"), ("Text chat", "Chat testuale"), - ("Stop voice call", "Interrompi la chiamata vocale"), - ("relay_hint_tip", "Se non è possibile connettersi direttamente, si può provare a farlo tramite relay.\nInoltre, se si desidera utilizzare il relay al primo tentativo, è possibile aggiungere il suffisso \"/r\" all'ID o selezionare l'opzione \"Collegati sempre tramite relay\" nella scheda peer."), + ("Stop voice call", "Interrompi chiamata vocale"), + ("relay_hint_tip", "Se non è possibile connettersi direttamente, puoi provare a farlo tramite relay.\nInoltre, se si vuoi usare il relay al primo tentativo, è possibile aggiungere all'ID il suffisso '/r\' o selezionare nella scheda peer l'opzione 'Collegati sempre tramite relay'."), ("Reconnect", "Riconnetti"), ("Codec", "Codec"), ("Resolution", "Risoluzione"), ("No transfers in progress", "Nessun trasferimento in corso"), - ("Set one-time password length", "Imposta la lunghezza della password monouso"), - ("idd_driver_tip", "Installa il driver per lo schermo virtuale che sarà utilizzato quando non si dispone di schermi fisici."), - ("confirm_idd_driver_tip", "L'opzione per installare il driver per lo schermo virtuale è selezionata. Nota che un certificato di test sarà installato per l'attendibilità del driver dello schermo virtuale. Questo certificato di test verrà utilizzato solo per l'attendibilità dei driver di RustDesk."), + ("Set one-time password length", "Imposta lunghezza password monouso"), + ("idd_driver_tip", "Installa il driver per lo schermo virtuale che sarà usato quando non si dispone di schermi fisici."), + ("confirm_idd_driver_tip", "È selezionata l'opzione per installare il driver per lo schermo virtuale.\nNota che verrà installato un certificato di test per l'attendibilità del driver dello schermo virtuale.\nQuesto certificato di test verrà utilizzato solo per l'attendibilità dei driver di RustDesk."), ("RDP Settings", "Impostazioni RDP"), ("Sort by", "Ordina per"), ("New Connection", "Nuova connessione"), ("Restore", "Ripristina"), ("Minimize", "Minimizza"), ("Maximize", "Massimizza"), - ("Your Device", "Il tuo dispositivo"), - ("empty_recent_tip", "Oops, non c'è nessuna sessione recente!\nTempo di pianificarne una."), - ("empty_favorite_tip", "Ancora nessun peer?\nTrova qualcuno con cui connetterti e aggiungilo ai tuoi preferiti!"), - ("empty_lan_tip", "Oh no, sembra proprio che non abbiamo ancora rilevato nessun peer."), - ("empty_address_book_tip", "Oh diamine, sembra che per ora non ci siano peer nella tua rubrica."), + ("Your Device", "Questo dispositivo"), + ("empty_recent_tip", "Non c'è nessuna sessione recente!\nPianificane una."), + ("empty_favorite_tip", "Ancora nessun peer?\nTrova qualcuno con cui connetterti e aggiungilo ai preferiti!"), + ("empty_lan_tip", "Sembra proprio che non sia stato rilevato nessun peer."), + ("empty_address_book_tip", "Sembra che per ora nella rubrica non ci siano peer."), ("eg: admin", "es: admin"), - ("Empty Username", "Nome Utente Vuoto"), - ("Empty Password", "Password Vuota"), + ("Empty Username", "Nome utente vuoto"), + ("Empty Password", "Password vuota"), ("Me", "Io"), ("identical_file_tip", "Questo file è identico a quello del peer."), - ("show_monitors_tip", "Mostra schermi nella barra degli strumenti"), - ("View Mode", "Modalità di visualizzazione"), - ("login_linux_tip", ""), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), + ("show_monitors_tip", "Visualizza schermi nella barra strumenti"), + ("View Mode", "Modalità visualizzazione"), + ("login_linux_tip", "Accedi all'account Linux remoto"), + ("verify_rustdesk_password_tip", "Conferma password RustDesk"), + ("remember_account_tip", "Ricorda questo account"), + ("os_account_desk_tip", "Questo account viene usato per accedere al sistema operativo remoto e attivare la sessione desktop in modalità non presidiata."), + ("OS Account", "Account sistema operativo"), + ("another_user_login_title_tip", "È già loggato un altro utente."), + ("another_user_login_text_tip", "Separato"), + ("xorg_not_found_title_tip", "Xorg non trovato."), + ("xorg_not_found_text_tip", "Installa Xorg."), + ("no_desktop_title_tip", "Non c'è nessun desktop disponibile."), + ("no_desktop_text_tip", "Installa il desktop GNOME."), ].iter().cloned().collect(); } From 01816baffd7e68119c3aed023dad218c10f12d7e Mon Sep 17 00:00:00 2001 From: MrVinyla <55458509+MrVinyla@users.noreply.github.com> Date: Tue, 11 Apr 2023 22:43:39 +0300 Subject: [PATCH 284/366] Update lt.rs --- src/lang/lt.rs | 980 ++++++++++++++++++++++++------------------------- 1 file changed, 490 insertions(+), 490 deletions(-) diff --git a/src/lang/lt.rs b/src/lang/lt.rs index bb8f1c167..b284479f3 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -1,495 +1,495 @@ lazy_static::lazy_static! { pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ - ("Status", "Būsena"), - ("Your Desktop", "Jūsų darbalaukis"), - ("desk_tip", "Jūsų darbalaukis pasiekiamas naudojant šį ID ir slaptažodį"), - ("Password", "Slaptažodis"), - ("Ready", "Pasiruošęs"), - ("Established", "Įsteigta"), - ("connecting_status", "Prisijungiama prie RustDesk tinklo..."), - ("Enable Service", "Įgalinti paslaugą"), - ("Start Service", "Pradėti paslaugą"), - ("Service is running", "Paslauga veikia"), - ("Service is not running", "Paslauga neveikia"), - ("not_ready_status", "Neprisijungęs. Patikrinkite ryšį."), - ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), - ("Transfer File", "Perkelti failą"), - ("Connect", "Prisijungti"), - ("Recent Sessions", "Seansų istorija"), - ("Address Book", "Adresų knyga"), - ("Confirmation", "Patvirtinimas"), - ("TCP Tunneling", "TCP tuneliavimas"), - ("Remove", "Pašalinti"), - ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), - ("Set your own password", "Nustatykite savo slaptažodį"), - ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), - ("Enable Clipboard", "Įgalinti iškarpinę"), - ("Enable File Transfer", "Įgalinti failų perdavimą"), - ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), - ("IP Whitelisting", "IP baltasis sąrašas"), - ("ID/Relay Server", "ID / perdavimo serveris"), - ("Import Server Config", "Importuoti serverio konfigūraciją"), - ("Export Server Config", "Eksportuoti serverio konfigūraciją"), - ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), - ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), - ("Invalid server configuration", "Netinkama serverio konfigūracija"), - ("Clipboard is empty", "Iškarpinė tuščia"), - ("Stop service", "Sustabdyti paslaugą"), - ("Change ID", "Keisti ID"), - ("Your new ID", "Jūsų naujasis ID"), - ("length %min% to %max%", "ilgis %min% iki %max%"), - ("starts with a letter", "prasideda raide"), - ("allowed characters", "leistini simboliai"), - ("id_change_tip", "Leidžiami tik simboliai a–z, A–Z, 0–9 ir _ (pabraukimas). Pirmoji raidė turi būti a-z, A-Z. Ilgis nuo 6 iki 16."), - ("Website", "Interneto svetainė"), - ("About", "Apie"), - ("Slogan_tip", "Sukurta su siela šiame beprotiškame pasaulyje!"), - ("Privacy Statement", "Privatumo pareiškimas"), - ("Mute", "Nutildyti"), - ("Build Date", "Sukūrimo data"), - ("Version", "Versija"), - ("Home", "Namai"), - ("Audio Input", "Garso įvestis"), - ("Enhancements", "Patobulinimai"), - ("Hardware Codec", "Aparatinės įrangos paspartinimas"), - ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), - ("ID Server", "ID serveris"), - ("Relay Server", "Perdavimo serveris"), - ("API Server", "API serveris"), - ("invalid_http", "Turi prasidėti http:// arba https://"), - ("Invalid IP", "Netinkamas IP"), - ("Invalid format", "Neteisingas formatas"), - ("server_not_support", "Serveris dar nepalaikomas"), - ("Not available", "Nepasiekiamas"), - ("Too frequent", "Per dažnai"), - ("Cancel", "Atšaukti"), - ("Skip", "Praleisti"), - ("Close", "Uždaryti"), - ("Retry", "Bandykite dar kartą"), - ("OK", "GERAI"), - ("Password Required", "Reikalingas slaptažodis"), - ("Please enter your password", "Prašome įvesti savo slaptažodį"), - ("Remember password", "Prisiminti slaptažodį"), - ("Wrong Password", "Neteisingas slaptažodis"), - ("Do you want to enter again?", "Ar norite įeiti dar kartą?"), - ("Connection Error", "Ryšio klaida"), - ("Error", "Klaida"), - ("Reset by the peer", "Atmetė nuotolinis kompiuteris"), - ("Connecting...", "Jungiamasi..."), - ("Connection in progress. Please wait.", "Jungiamasi. Palaukite."), - ("Please try 1 minute later", "Prašome pabandyti po 1 minutės"), - ("Login Error", "Prisijungimo klaida"), - ("Successful", "Sėkmingai"), - ("Connected, waiting for image...", "Prisijungta, laukiama vaizdo..."), - ("Name", "Vardas"), - ("Type", "Tipas"), - ("Modified", "Pakeista"), - ("Size", "Dydis"), - ("Show Hidden Files", "Rodyti paslėptus failus"), - ("Receive", "Gauti"), - ("Send", "Siųsti"), - ("Refresh File", "Atnaujinti failą"), - ("Local", "Vietinis"), - ("Remote", "Nuotolinis"), - ("Remote Computer", "Nuotolinis kompiuteris"), - ("Local Computer", "Šis kompiuteris"), - ("Confirm Delete", "Patvirtinti ištrynimą"), - ("Delete", "Ištrinti"), - ("Properties", "Ypatybės"), - ("Multi Select", "Keli pasirinkimas"), - ("Select All", "Pasirinkti viską"), - ("Unselect All", "Atšaukti visų pasirinkimą"), - ("Empty Directory", "Tuščias katalogas"), - ("Not an empty directory", "Ne tuščias katalogas"), - ("Are you sure you want to delete this file?", "Ar tikrai norite ištrinti šį failą?"), - ("Are you sure you want to delete this empty directory?", "Ar tikrai norite ištrinti šį tuščią katalogą?"), - ("Are you sure you want to delete the file of this directory?", "Ar tikrai norite ištrinti šio katalogo failą?"), - ("Do this for all conflicts", "Taikyti visiems konfliktams"), - ("This is irreversible!", "Tai negrįžtama!"), - ("Deleting", "Ištrinama"), - ("files", "failai"), - ("Waiting", "Laukiu"), - ("Finished", "Baigta"), - ("Speed", "Greitis"), - ("Custom Image Quality", "Tinkinta vaizdo kokybė"), - ("Privacy mode", "Privatumo režimas"), - ("Block user input", "Blokuoti naudotojo įvestį"), - ("Unblock user input", "Atblokuoti naudotojo įvestį"), - ("Adjust Window", "Koreguoti langą"), - ("Original", "Originalas"), - ("Shrink", "Susitraukti"), - ("Stretch", "Ištempti"), - ("Scrollbar", "Slinkties juosta"), - ("ScrollAuto", "Automatinis slinkimas"), - ("Good image quality", "Gera vaizdo kokybė"), - ("Balanced", "Subalansuotas"), - ("Optimize reaction time", "Optimizuoti reakcijos laiką"), - ("Custom", "Tinkintas"), - ("Show remote cursor", "Rodyti nuotolinį žymeklį"), - ("Show quality monitor", "Rodyti kokybės monitorių"), - ("Disable clipboard", "Išjungti mainų sritį"), - ("Lock after session end", "Užrakinti pasibaigus seansui"), - ("Insert", "Įdėti"), - ("Insert Lock", "Įterpti užraktą"), - ("Refresh", "Atnaujinti"), - ("ID does not exist", "ID neegzistuoja"), - ("Failed to connect to rendezvous server", "Nepavyko prisijungti prie susitikimo serverio"), - ("Please try later", "Prašome pabandyti vėliau"), - ("Remote desktop is offline", "Nuotolinis darbalaukis neprisijungęs"), - ("Key mismatch", "Raktų neatitikimas"), - ("Timeout", "Laikas baigėsi"), - ("Failed to connect to relay server", "Nepavyko prisijungti prie perdavimo serverio"), - ("Failed to connect via rendezvous server", "Nepavyko prisijungti per susitikimo serverį"), - ("Failed to connect via relay server", "Nepavyko prisijungti per perdavimo serverį"), - ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), - ("Set Password", "Nustatyti slaptažodį"), - ("OS Password", "OS slaptažodis"), - ("install_tip", "Kai kuriais atvejais UAC gali priversti RustDesk netinkamai veikti nuotoliniame pagrindiniame kompiuteryje. Norėdami apeiti UAC, spustelėkite toliau esantį mygtuką, kad įdiegtumėte RustDesk į savo kompiuterį."), - ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), - ("Click to download", "Spustelėkite norėdami atsisiųsti"), - ("Click to update", "Spustelėkite norėdami atnaujinti"), - ("Configure", "Konfigūruoti"), - ("config_acc", "Norėdami nuotoliniu būdu valdyti darbalaukį, turite suteikti RustDesk \"prieigos\" leidimus"), - ("config_screen", "Norėdami nuotoliniu būdu pasiekti darbalaukį, turite suteikti RustDesk leidimus \"ekrano kopija\""), - ("Installing ...", "Diegiama ..."), - ("Install", "Diegti"), - ("Installation", "Įdiegimas"), - ("Installation Path", "Įdiegimo kelias"), - ("Create start menu shortcuts", "Sukurti pradžios meniu sparčiuosius klavišus"), - ("Create desktop icon", "Sukurti darbalaukio piktogramą"), - ("agreement_tip", "Pradėdami diegimą sutinkate su licencijos sutarties sąlygomis"), - ("Accept and Install", "Priimti ir įdiegti"), - ("End-user license agreement", "Galutinio vartotojo licencijos sutartis"), - ("Generating ...", "Generuojamas..."), - ("Your installation is lower version.", "Jūsų įdiegta versija senesnė."), - ("not_close_tcp_tip", "Naudodami tunelį neuždarykite šio lango"), - ("Listening ...", "Laukimas..."), - ("Remote Host", "Nuotolinis pagrindinis kompiuteris"), - ("Remote Port", "Nuotolinis prievadas"), - ("Action", "Veiksmas"), - ("Add", "Papildyti"), - ("Local Port", "Vietinis prievadas"), - ("Local Address", "Vietinis adresas"), - ("Change Local Port", "Keisti vietinį prievadą"), - ("setup_server_tip", "Kad ryšys būtų greitesnis, nustatykite savo serverį"), - ("Too short, at least 6 characters.", "Per trumpas, mažiausiai 6 simboliai."), - ("The confirmation is not identical.", "Patvirtinimas nėra tapatus."), - ("Permissions", "Leidimai"), - ("Accept", "Priimti"), - ("Dismiss", "Atmesti"), - ("Disconnect", "Atjungti"), - ("Allow using keyboard and mouse", "Leisti naudoti klaviatūrą ir pelę"), - ("Allow using clipboard", "Leisti naudoti mainų sritį"), - ("Allow hearing sound", "Leisti girdėti kompiuterio garsą"), - ("Allow file copy and paste", "Leisti kopijuoti ir įklijuoti failus"), - ("Connected", "Prisijungta"), - ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), - ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), - ("Direct and unencrypted connection", "Tiesioginis ir nešifruotas ryšys"), - ("Relayed and unencrypted connection", "Perduotas ir nešifruotas ryšys"), - ("Enter Remote ID", "Įveskite nuotolinio ID"), - ("Enter your password", "Įveskite savo slaptažodį"), - ("Logging in...", "Prisijungiama..."), - ("Enable RDP session sharing", "Įgalinti RDP seansų bendrinimą"), - ("Auto Login", "Automatinis prisijungimas"), - ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), - ("Rename", "Pervardyti"), - ("Space", "Erdvė"), - ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), - ("Change Path", "Keisti kelią"), - ("Create Folder", "Sukurti aplanką"), - ("Please enter the folder name", "Įveskite aplanko pavadinimą"), - ("Fix it", "Pataisyk tai"), - ("Warning", "Įspėjimas"), - ("Login screen using Wayland is not supported", "Prisijungimo ekranas naudojant Wayland nepalaikomas"), - ("Reboot required", "Reikia paleisti iš naujo"), - ("Unsupported display server", "Nepalaikomas rodymo serveris"), - ("x11 expected", "reikalingas x11"), - ("Port", "Prievadas"), - ("Settings", "Nustatymai"), - ("Username", "Vartotojo vardas"), - ("Invalid port", "Netinkamas prievadas"), - ("Closed manually by the peer", "Partneris atmetė prašymą prisijungti"), - ("Enable remote configuration modification", "Įgalinti nuotolinį konfigūracijos modifikavimą"), - ("Run without install", "Vykdyti be diegimo"), - ("Connect via relay", "Prisijungti per relę"), - ("Always connect via relay", "Visada prisijunkite per relę"), - ("whitelist_tip", "Mane gali pasiekti tik baltajame sąraše esantys IP adresai"), - ("Login", "Prisijungti"), - ("Verify", "Patvirtinti"), - ("Remember me", "Prisimink mane"), - ("Trust this device", "Pasitikėk šiuo įrenginiu"), - ("Verification code", "Patvirtinimo kodas"), - ("verification_tip", "Aptiktas naujas įrenginys ir registruotu el. pašto adresu išsiųstas patvirtinimo kodas. Įveskite jį norėdami tęsti prisijungimą."), - ("Logout", "Atsijungti"), - ("Tags", "Žymos"), - ("Search ID", "Paieškos ID"), - ("whitelist_sep", "Atskirti kableliu, kabliataškiu, tarpu arba nauja eilute"), - ("Add ID", "Pridėti ID"), - ("Add Tag", "Pridėti žymą"), - ("Unselect all tags", "Atšaukti visų žymų pasirinkimą"), - ("Network error", "Tinklo klaida"), - ("Username missed", "Prarastas vartotojo vardas"), - ("Password missed", "Slaptažodis praleistas"), - ("Wrong credentials", "Klaidingi kredencialai"), - ("Edit Tag", "Redaguoti žymą"), - ("Unremember Password", "Nebeprisiminti slaptažodžio"), - ("Favorites", "Parankiniai"), - ("Add to Favorites", "Įtraukti į parankinius"), - ("Remove from Favorites", "Pašalinti iš parankinių"), - ("Empty", "Tuščia"), - ("Invalid folder name", "Neteisingas aplanko pavadinimas"), - ("Socks5 Proxy", "Socks5 Proxy"), - ("Hostname", "Pagrindinio kompiuterio pavadinimas"), - ("Discovered", "Aptikta tinkle"), - ("install_daemon_tip", "Norėdami, kad RustDesk startuotų automatiškai, turite ją įdiegti"), - ("Remote ID", "Nuotolinis ID"), - ("Paste", "Įklijuoti"), - ("Paste here?", "Įklijuoti čia?"), - ("Are you sure to close the connection?", "Ar tikrai norite atsijungti?"), - ("Download new version", "Atsisiųsti naują versiją"), - ("Touch mode", "Palietimo režimas"), - ("Mouse mode", "Pelės režimas"), - ("One-Finger Tap", "Palietimas vienu pirštu"), - ("Left Mouse", "Kairysis pelės kl."), - ("One-Long Tap", "Vienas palietimas"), - ("Two-Finger Tap", "Palietimas dviem pirštais"), - ("Right Mouse", "Dešinysis pelės kl."), - ("One-Finger Move", "Vieno piršto judesys"), - ("Double Tap & Move", "Dukart palieskite ir perkelkite"), - ("Mouse Drag", "Pelės vilkimas"), - ("Three-Finger vertically", "Trys pirštai vertikaliai"), - ("Mouse Wheel", "Pelės ratukas"), - ("Two-Finger Move", "Dviejų pirštų judesys"), - ("Canvas Move", "Drobės perkėlimas"), - ("Pinch to Zoom", "Suimkite, kad padidintumėte"), - ("Canvas Zoom", "Drobės mastelis"), - ("Reset canvas", "Atstatyti drobę"), - ("No permission of file transfer", "Nėra leidimo perkelti failus"), - ("Note", "Pastaba"), - ("Connection", "Ryšys"), - ("Share Screen", "Bendrinti ekraną"), - ("Chat", "Pokalbis"), - ("Total", "Iš viso"), - ("items", "elementai"), - ("Selected", "Pasirinkta"), - ("Screen Capture", "Ekrano nuotrauka"), - ("Input Control", "Įvesties valdymas"), - ("Audio Capture", "Garso fiksavimas"), - ("File Connection", "Failo ryšys"), - ("Screen Connection", "Ekrano jungtis"), - ("Do you accept?", "Ar sutinki?"), - ("Open System Setting", "Atviros sistemos nustatymas"), - ("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"), - ("android_input_permission_tip1", "Kad nuotolinis įrenginys galėtų valdyti Android įrenginį pele arba liesti, turite leisti RustDesk naudoti \"Prieinamumo\" paslaugą."), - ("android_input_permission_tip2", "Eikite į kitą sistemos nustatymų puslapį, suraskite \"Įdiegtos paslaugos\" ir įgalinkite \"RustDesk įvestis\" paslaugą."), - ("android_new_connection_tip", "Gauta nauja užklausa tvarkyti dabartinį įrenginį."), - ("android_service_will_start_tip", "Įgalinus ekrano fiksavimo paslaugą, kiti įrenginiai gali pateikti užklausą prisijungti prie to įrenginio."), - ("android_stop_service_tip", "Uždarius paslaugą automatiškai bus uždaryti visi užmegzti ryšiai."), - ("android_version_audio_tip", "Dabartinė Android versija nepalaiko garso įrašymo, atnaujinkite į Android 10 ar naujesnę versiją."), - ("android_start_service_tip", "Spustelėkite [Paleisti paslaugą] arba įjunkite [Fiksuoti ekraną], kad paleistumėte ekrano bendrinimo paslaugą."), - ("android_permission_may_not_change_tip", "Užmegztų ryšių leidimų keisti negalima, reikia prisijungti iš naujo."), - ("Account", "Paskyra"), - ("Overwrite", "Perrašyti"), - ("This file exists, skip or overwrite this file?", "Šis failas egzistuoja, praleisti arba perrašyti šį failą?"), - ("Quit", "Išeiti"), - ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/"), - ("Help", "Pagalba"), - ("Failed", "Nepavyko"), - ("Succeeded", "Pavyko"), - ("Someone turns on privacy mode, exit", "Kažkas įjungė privatumo režimą, išeiti"), - ("Unsupported", "Nepalaikomas"), - ("Peer denied", "Atšaukė"), - ("Please install plugins", "Įdiekite papildinius"), - ("Peer exit", "Nuotolinis mazgas neveikia"), - ("Failed to turn off", "Nepavyko išjungti"), - ("Turned off", "Išjungti"), - ("In privacy mode", "Privatumo režimas"), - ("Out privacy mode", "Išėjimas iš privatumo režimo"), - ("Language", "Kalba"), - ("Keep RustDesk background service", "Palikti RustDesk fonine paslauga"), - ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), - ("android_open_battery_optimizations_tip", "Eikite į kitą nustatymų puslapį"), - ("Start on Boot", "Pradėti paleidžiant"), - ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), - ("Connection not allowed", "Ryšys neleidžiamas"), - ("Legacy mode", "Senasis režimas"), - ("Map mode", "Žemėlapio režimas"), - ("Translate mode", "Vertimo režimas"), - ("Use permanent password", "Naudoti nuolatinį slaptažodį"), - ("Use both passwords", "Naudoti abu slaptažodžius"), - ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), - ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), - ("Allow remote restart", "Leisti nuotolinio kompiuterio paleidimą iš naujo"), - ("Restart Remote Device", "Paleisti nuotolinį kompiuterį iš naujo"), - ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo?"), - ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), - ("remote_restarting_tip", "Nuotolinis įrenginys paleidžiamas iš naujo. Uždarykite šį pranešimą ir po kurio laiko vėl prisijunkite naudodami nuolatinį slaptažodį."), - ("Copied", "Nukopijuota"), - ("Exit Fullscreen", "Išeiti iš pilno ekrano"), - ("Fullscreen", "Per visą ekraną"), - ("Mobile Actions", "Veiksmai mobiliesiems"), - ("Select Monitor", "Pasirinkite monitorių"), - ("Control Actions", "Valdymo veiksmai"), - ("Display Settings", "Ekrano nustatymai"), - ("Ratio", "Santykis"), - ("Image Quality", "Vaizdo kokybė"), - ("Scroll Style", "Slinkimo stilius"), - ("Show Menubar", "Rodyti meniu juostą"), - ("Hide Menubar", "Slėpti meniu juostą"), - ("Direct Connection", "Tiesioginis ryšys"), - ("Relay Connection", "Tarpinė jungtis"), - ("Secure Connection", "Saugus ryšys"), - ("Insecure Connection", "Nesaugus ryšys"), - ("Scale original", "Pakeisti originalų mastelį"), - ("Scale adaptive", "Pritaikomas mastelis"), - ("General", "Bendra"), - ("Security", "Sauga"), - ("Theme", "Tema"), - ("Dark Theme", "Tamsioji tema"), - ("Light Theme", "Šviesi tema"), - ("Dark", "Tamsi"), - ("Light", "Šviesi"), - ("Follow System", "Kaip sistemos"), - ("Enable hardware codec", "Įgalinti"), - ("Unlock Security Settings", "Atrakinti saugos nustatymus"), - ("Enable Audio", "Įgalinti garsą"), - ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), - ("Server", "Serveris"), - ("Direct IP Access", "Tiesioginė IP prieiga"), - ("Proxy", "Tarpinis serveris"), - ("Apply", "Taikyti"), - ("Disconnect all devices?", "Atjungti visus įrenginius?"), - ("Clear", "Išvalyti"), - ("Audio Input Device", "Garso įvestis"), - ("Deny remote access", "Uždrausti nuotolinę prieigą"), - ("Use IP Whitelisting", "Naudoti patikimą IP sąrašą"), - ("Network", "Tinklas"), - ("Enable RDP", "Įgalinti RDP"), - ("Pin menubar", "Prisegti meniu juostą"), - ("Unpin menubar", "Atsegti meniu juostą"), - ("Recording", "Įrašymas"), - ("Directory", "Katalogas"), - ("Automatically record incoming sessions", "Automatiškai įrašyti įeinančius seansus"), - ("Change", "Keisti"), - ("Start session recording", "Pradėti seanso įrašinėjimą"), - ("Stop session recording", "Sustabdyti seanso įrašinėjimą"), - ("Enable Recording Session", "Įgalinti seanso įrašinėjimą"), - ("Allow recording session", "Leisti seanso įrašinėjimą"), - ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), - ("Deny LAN Discovery", "Neleisti LAN aptikimo"), - ("Write a message", "Rašyti žinutę"), - ("Prompt", "Užuomina"), - ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), - ("elevated_foreground_window_tip", "Dabartinis nuotolinio darbalaukio langas reikalauja didesnių privilegijų, todėl laikinai neįmanoma naudoti pelės ir klaviatūros. Galite paprašyti nuotolinio vartotojo sumažinti dabartinį langą arba spustelėti aukščio mygtuką ryšio valdymo lange. Norint išvengti šios problemos ateityje, rekomenduojama programinę įrangą įdiegti nuotoliniame įrenginyje."), - ("Disconnected", "Atsijungęs, seanso laikas:"), - ("Other", "Kita"), - ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), - ("Keyboard Settings", "Klaviatūros nustatymai"), - ("Full Access", "Pilna prieiga"), - ("Screen Share", "Ekrano bendrinimas"), - ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland reikalauja Ubuntu 21.04 arba naujesnės versijos."), - ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland reikalinga naujesnės Linux Distro versijos. Išbandykite X11 darbalaukį arba pakeiskite OS."), - ("JumpLink", "Peržiūra"), - ("Please Select the screen to be shared(Operate on the peer side).", "Prašome pasirinkti ekraną, kurį norite bendrinti (veikiantį kitoje pusėje)."), - ("Show RustDesk", "Rodyti RustDesk"), - ("This PC", "Šis kompiuteris"), - ("or", "arba"), - ("Continue with", "Tęsti su"), - ("Elevate", "Pakelti"), - ("Zoom cursor", "Mastelio keitimo žymeklis"), - ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), - ("Accept sessions via click", "Priimti seansus spustelėjus"), - ("Accept sessions via both", "Priimti seansus abiem variantais"), - ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), - ("One-time Password", "Vienkartinis slaptažodis"), - ("Use one-time password", "Naudoti vienkartinį slaptažodį"), - ("One-time password length", "Vienkartinio slaptažodžio ilgis"), - ("Request access to your device", "Prašo leidimo valdyti jūsų įrenginį"), - ("Hide connection management window", "Slėpti ryšio valdymo langą"), - ("hide_cm_tip", "Leisti paslėpti didžiąją ir mažąją raidę, jei priimamos slaptažodžio sesijos arba naudojamas nuolatinis slaptažodis"), - ("wayland_experiment_tip", "Wayland palaikymas yra eksperimentinis, naudokite X11, jei jums reikalingas automatinis prisijungimas."), - ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), - ("Skipped", "Praleisti"), - ("Add to Address Book", "Pridėti prie adresų knygos"), - ("Group", "Grupė"), - ("Search", "Paieška"), - ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), - ("Local keyboard type", "Vietinės klaviatūros tipas"), - ("Select local keyboard type", "Pasirinkite vietinės klaviatūros tipą"), - ("software_render_tip", "Jei turite Nvidia vaizdo plokštę ir nuotolinis langas iškart užsidaro prisijungus, gali padėti „Nouveau“ tvarkyklės įdiegimas ir programinės įrangos atvaizdavimo pasirinkimas. Būtina paleisti iš naujo."), - ("Always use software rendering", "Visada naudoti programinį spartintuvą"), - ("config_input", "Norėdami valdyti nuotolinį darbalaukį naudodami klaviatūrą, turite suteikti RustDesk leidimus \"Įvesties monitoringas\"."), - ("config_microphone", "Norėdami kalbėtis su nuotoline puse, turite suteikti RustDesk leidimą \"Įrašyti garsą\"."), - ("request_elevation_tip", "Taip pat galite prašyti tesių suteikimo, jeigu kas nors yra nuotolinėje pusėje."), - ("Wait", "Laukti"), - ("Elevation Error", "Teisių suteikimo klaida"), - ("Ask the remote user for authentication", "Klauskite nuotolinio vartotojo autentifikavimo"), - ("Choose this if the remote account is administrator", "Pasirinkite tai, jei nuotolinė paskyra yra administratorius"), - ("Transmit the username and password of administrator", "Persiųsti administratoriaus vartotojo vardą ir slaptažodį"), - ("still_click_uac_tip", "Vis tiek reikia, kad nuotolinis vartotojas paleidžiant RustDesk UAC lange paspaustų \"OK\"."), - ("Request Elevation", "Prašyti teisių"), - ("wait_accept_uac_tip", "Palaukite, kol nuotolinis vartotojas patvirtins UAC užklausą."), - ("Elevate successfully", "Teisės suteiktos"), - ("uppercase", "didžiosios raidės"), - ("lowercase", "mažosios raidės"), - ("digit", "skaitmuo"), - ("special character", "specialusis simbolis"), - ("length>=8", "ilgis>=8"), - ("Weak", "Silpnas"), - ("Medium", "Vidutinis"), - ("Strong", "Stiprus"), - ("Switch Sides", "Perjungti puses"), - ("Please confirm if you want to share your desktop?", "Prašome patvirtinti, jeigu norite bendrinti darbalaukį?"), - ("Display", "Ekranas"), - ("Default View Style", "Numatytasis peržiūros stilius"), - ("Default Scroll Style", "Numatytasis slinkties stilius"), - ("Default Image Quality", "Numatytoji vaizdo kokybė"), - ("Default Codec", "Numatytasis kodekas"), - ("Bitrate", "Sparta"), - ("FPS", "FPS"), - ("Auto", "Automatinis"), - ("Other Default Options", "Kitos numatytosios parinktys"), - ("Voice call", "Balso skambutis"), - ("Text chat", "Tekstinis pokalbis"), - ("Stop voice call", "Sustabdyti balso skambutį"), - ("relay_hint_tip", "Tiesioginis ryšys gali būti neįmanomas. Tokiu atveju galite pabandyti prisijungti per perdavimo serverį. \nArba, jei norite iš karto naudoti perdavimo serverį, prie ID galite pridėti priesagą \"/r\" arba nuotolinio pagrindinio kompiuterio nustatymuose įgalinti \"Visada prisijungti per relę\"."), - ("Reconnect", "Prisijungti iš naujo"), - ("Codec", "Kodekas"), - ("Resolution", "Rezoliucija"), - ("No transfers in progress", "Nevyksta jokių perdavimų"), - ("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"), - ("idd_driver_tip", "Įdiekite virtualaus ekrano tvarkyklę (naudojama, kai nėra fizinių ekranų)"), - ("confirm_idd_driver_tip", "Įjungta virtualaus ekrano tvarkyklės diegimo funkcija. Atminkite, kad bus įdiegtas bandomasis sertifikatas, kad būtų galima pasitikėti tvarkykle. Šis sertifikatas bus naudojamas tik pasitikėjimui Rustdesk tvarkyklėmis patikrinti."), - ("RDP Settings", "RDP nustatymai"), - ("Sort by", "Rūšiuoti pagal"), - ("New Connection", "Naujas ryšys"), - ("Restore", "Atkurti"), - ("Minimize", "Sumažinti"), - ("Maximize", "Padidinti"), - ("Your Device", "Jūsų įrenginys"), - ("empty_recent_tip", "Nėra paskutinių seansų!\nLaikas suplanuoti naują."), - ("empty_favorite_tip", "Dar neturite parankinių nuotolinių seansų."), - ("empty_lan_tip", "Nuotolinių mazgų nerasta."), - ("empty_address_book_tip", "Adresų knygelėje nėra nuotolinių kompiuterių."), - ("eg: admin", "pvz.: administratorius"), - ("Empty Username", "Tuščias naudotojo vardas"), - ("Empty Password", "Tuščias slaptažodis"), - ("Me", "Aš"), - ("identical_file_tip", "Failas yra identiškas nuotoliniame kompiuteryje esančiam failui."), - ("show_monitors_tip", "Rodyti monitorius įrankių juostoje"), - ("View Mode", "Peržiūros režimas"), - ("login_linux_tip", "Norėdami įjungti X darbalaukio seansą, turite būti prisijungę prie nuotolinės Linux paskyros."), - ("verify_rustdesk_password_tip", "Įveskite kliento RustDesk slaptažodį"), - ("remember_account_tip", "Prisiminti šią paskyrą"), - ("os_account_desk_tip", "Ši paskyra naudojama norint prisijungti prie nuotolinės OS ir įgalinti darbalaukio seansą režimu headless"), - ("OS Account", "OS paskyra"), - ("another_user_login_title_tip", "Kitas vartotojas jau yra prisijungęs"), - ("another_user_login_text_tip", "Atjungti"), - ("xorg_not_found_title_tip", "Xorg nerastas"), - ("xorg_not_found_text_tip", "Prašom įdiegti Xorg"), - ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), - ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), + ("Status", "Būsena"), + ("Your Desktop", "Jūsų darbalaukis"), + ("desk_tip", "Jūsų darbalaukis pasiekiamas naudojant šį ID ir slaptažodį"), + ("Password", "Slaptažodis"), + ("Ready", "Pasiruošęs"), + ("Established", "Įsteigta"), + ("connecting_status", "Prisijungiama prie RustDesk tinklo..."), + ("Enable Service", "Įgalinti paslaugą"), + ("Start Service", "Pradėti paslaugą"), + ("Service is running", "Paslauga veikia"), + ("Service is not running", "Paslauga neveikia"), + ("not_ready_status", "Neprisijungęs. Patikrinkite ryšį."), + ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), + ("Transfer File", "Perkelti failą"), + ("Connect", "Prisijungti"), + ("Recent Sessions", "Seansų istorija"), + ("Address Book", "Adresų knyga"), + ("Confirmation", "Patvirtinimas"), + ("TCP Tunneling", "TCP tuneliavimas"), + ("Remove", "Pašalinti"), + ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), + ("Set your own password", "Nustatykite savo slaptažodį"), + ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), + ("Enable Clipboard", "Įgalinti iškarpinę"), + ("Enable File Transfer", "Įgalinti failų perdavimą"), + ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), + ("IP Whitelisting", "IP baltasis sąrašas"), + ("ID/Relay Server", "ID / perdavimo serveris"), + ("Import Server Config", "Importuoti serverio konfigūraciją"), + ("Export Server Config", "Eksportuoti serverio konfigūraciją"), + ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), + ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), + ("Invalid server configuration", "Netinkama serverio konfigūracija"), + ("Clipboard is empty", "Iškarpinė tuščia"), + ("Stop service", "Sustabdyti paslaugą"), + ("Change ID", "Keisti ID"), + ("Your new ID", "Jūsų naujasis ID"), + ("length %min% to %max%", "ilgis %min% iki %max%"), + ("starts with a letter", "prasideda raide"), + ("allowed characters", "leistini simboliai"), + ("id_change_tip", "Leidžiami tik simboliai a–z, A–Z, 0–9 ir _ (pabraukimas). Pirmoji raidė turi būti a-z, A-Z. Ilgis nuo 6 iki 16."), + ("Website", "Interneto svetainė"), + ("About", "Apie"), + ("Slogan_tip", "Sukurta su siela šiame beprotiškame pasaulyje!"), + ("Privacy Statement", "Privatumo pareiškimas"), + ("Mute", "Nutildyti"), + ("Build Date", "Sukūrimo data"), + ("Version", "Versija"), + ("Home", "Namai"), + ("Audio Input", "Garso įvestis"), + ("Enhancements", "Patobulinimai"), + ("Hardware Codec", "Aparatinės įrangos paspartinimas"), + ("Adaptive Bitrate", "Adaptyvusis pralaidumas"), + ("ID Server", "ID serveris"), + ("Relay Server", "Perdavimo serveris"), + ("API Server", "API serveris"), + ("invalid_http", "Turi prasidėti http:// arba https://"), + ("Invalid IP", "Netinkamas IP"), + ("Invalid format", "Neteisingas formatas"), + ("server_not_support", "Serveris dar nepalaikomas"), + ("Not available", "Nepasiekiamas"), + ("Too frequent", "Per dažnai"), + ("Cancel", "Atšaukti"), + ("Skip", "Praleisti"), + ("Close", "Uždaryti"), + ("Retry", "Bandykite dar kartą"), + ("OK", "GERAI"), + ("Password Required", "Reikalingas slaptažodis"), + ("Please enter your password", "Prašome įvesti savo slaptažodį"), + ("Remember password", "Prisiminti slaptažodį"), + ("Wrong Password", "Neteisingas slaptažodis"), + ("Do you want to enter again?", "Ar norite įeiti dar kartą?"), + ("Connection Error", "Ryšio klaida"), + ("Error", "Klaida"), + ("Reset by the peer", "Atmetė nuotolinis kompiuteris"), + ("Connecting...", "Jungiamasi..."), + ("Connection in progress. Please wait.", "Jungiamasi. Palaukite."), + ("Please try 1 minute later", "Prašome pabandyti po 1 minutės"), + ("Login Error", "Prisijungimo klaida"), + ("Successful", "Sėkmingai"), + ("Connected, waiting for image...", "Prisijungta, laukiama vaizdo..."), + ("Name", "Vardas"), + ("Type", "Tipas"), + ("Modified", "Pakeista"), + ("Size", "Dydis"), + ("Show Hidden Files", "Rodyti paslėptus failus"), + ("Receive", "Gauti"), + ("Send", "Siųsti"), + ("Refresh File", "Atnaujinti failą"), + ("Local", "Vietinis"), + ("Remote", "Nuotolinis"), + ("Remote Computer", "Nuotolinis kompiuteris"), + ("Local Computer", "Šis kompiuteris"), + ("Confirm Delete", "Patvirtinti ištrynimą"), + ("Delete", "Ištrinti"), + ("Properties", "Ypatybės"), + ("Multi Select", "Keli pasirinkimas"), + ("Select All", "Pasirinkti viską"), + ("Unselect All", "Atšaukti visų pasirinkimą"), + ("Empty Directory", "Tuščias katalogas"), + ("Not an empty directory", "Ne tuščias katalogas"), + ("Are you sure you want to delete this file?", "Ar tikrai norite ištrinti šį failą?"), + ("Are you sure you want to delete this empty directory?", "Ar tikrai norite ištrinti šį tuščią katalogą?"), + ("Are you sure you want to delete the file of this directory?", "Ar tikrai norite ištrinti šio katalogo failą?"), + ("Do this for all conflicts", "Taikyti visiems konfliktams"), + ("This is irreversible!", "Tai negrįžtama!"), + ("Deleting", "Ištrinama"), + ("files", "failai"), + ("Waiting", "Laukiu"), + ("Finished", "Baigta"), + ("Speed", "Greitis"), + ("Custom Image Quality", "Tinkinta vaizdo kokybė"), + ("Privacy mode", "Privatumo režimas"), + ("Block user input", "Blokuoti naudotojo įvestį"), + ("Unblock user input", "Atblokuoti naudotojo įvestį"), + ("Adjust Window", "Koreguoti langą"), + ("Original", "Originalas"), + ("Shrink", "Susitraukti"), + ("Stretch", "Ištempti"), + ("Scrollbar", "Slinkties juosta"), + ("ScrollAuto", "Automatinis slinkimas"), + ("Good image quality", "Gera vaizdo kokybė"), + ("Balanced", "Subalansuotas"), + ("Optimize reaction time", "Optimizuoti reakcijos laiką"), + ("Custom", "Tinkintas"), + ("Show remote cursor", "Rodyti nuotolinį žymeklį"), + ("Show quality monitor", "Rodyti kokybės monitorių"), + ("Disable clipboard", "Išjungti mainų sritį"), + ("Lock after session end", "Užrakinti pasibaigus seansui"), + ("Insert", "Įdėti"), + ("Insert Lock", "Įterpti užraktą"), + ("Refresh", "Atnaujinti"), + ("ID does not exist", "ID neegzistuoja"), + ("Failed to connect to rendezvous server", "Nepavyko prisijungti prie susitikimo serverio"), + ("Please try later", "Prašome pabandyti vėliau"), + ("Remote desktop is offline", "Nuotolinis darbalaukis neprisijungęs"), + ("Key mismatch", "Raktų neatitikimas"), + ("Timeout", "Laikas baigėsi"), + ("Failed to connect to relay server", "Nepavyko prisijungti prie perdavimo serverio"), + ("Failed to connect via rendezvous server", "Nepavyko prisijungti per susitikimo serverį"), + ("Failed to connect via relay server", "Nepavyko prisijungti per perdavimo serverį"), + ("Failed to make direct connection to remote desktop", "Nepavyko tiesiogiai prisijungti prie nuotolinio darbalaukio"), + ("Set Password", "Nustatyti slaptažodį"), + ("OS Password", "OS slaptažodis"), + ("install_tip", "Kai kuriais atvejais UAC gali priversti RustDesk netinkamai veikti nuotoliniame pagrindiniame kompiuteryje. Norėdami apeiti UAC, spustelėkite toliau esantį mygtuką, kad įdiegtumėte RustDesk į savo kompiuterį."), + ("Click to upgrade", "Spustelėkite, jei norite atnaujinti"), + ("Click to download", "Spustelėkite norėdami atsisiųsti"), + ("Click to update", "Spustelėkite norėdami atnaujinti"), + ("Configure", "Konfigūruoti"), + ("config_acc", "Norėdami nuotoliniu būdu valdyti darbalaukį, turite suteikti RustDesk \"prieigos\" leidimus"), + ("config_screen", "Norėdami nuotoliniu būdu pasiekti darbalaukį, turite suteikti RustDesk leidimus \"ekrano kopija\""), + ("Installing ...", "Diegiama ..."), + ("Install", "Diegti"), + ("Installation", "Įdiegimas"), + ("Installation Path", "Įdiegimo kelias"), + ("Create start menu shortcuts", "Sukurti pradžios meniu sparčiuosius klavišus"), + ("Create desktop icon", "Sukurti darbalaukio piktogramą"), + ("agreement_tip", "Pradėdami diegimą sutinkate su licencijos sutarties sąlygomis"), + ("Accept and Install", "Priimti ir įdiegti"), + ("End-user license agreement", "Galutinio vartotojo licencijos sutartis"), + ("Generating ...", "Generuojamas..."), + ("Your installation is lower version.", "Jūsų įdiegta versija senesnė."), + ("not_close_tcp_tip", "Naudodami tunelį neuždarykite šio lango"), + ("Listening ...", "Laukimas..."), + ("Remote Host", "Nuotolinis pagrindinis kompiuteris"), + ("Remote Port", "Nuotolinis prievadas"), + ("Action", "Veiksmas"), + ("Add", "Papildyti"), + ("Local Port", "Vietinis prievadas"), + ("Local Address", "Vietinis adresas"), + ("Change Local Port", "Keisti vietinį prievadą"), + ("setup_server_tip", "Kad ryšys būtų greitesnis, nustatykite savo serverį"), + ("Too short, at least 6 characters.", "Per trumpas, mažiausiai 6 simboliai."), + ("The confirmation is not identical.", "Patvirtinimas nėra tapatus."), + ("Permissions", "Leidimai"), + ("Accept", "Priimti"), + ("Dismiss", "Atmesti"), + ("Disconnect", "Atjungti"), + ("Allow using keyboard and mouse", "Leisti naudoti klaviatūrą ir pelę"), + ("Allow using clipboard", "Leisti naudoti mainų sritį"), + ("Allow hearing sound", "Leisti girdėti kompiuterio garsą"), + ("Allow file copy and paste", "Leisti kopijuoti ir įklijuoti failus"), + ("Connected", "Prisijungta"), + ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), + ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), + ("Direct and unencrypted connection", "Tiesioginis ir nešifruotas ryšys"), + ("Relayed and unencrypted connection", "Perduotas ir nešifruotas ryšys"), + ("Enter Remote ID", "Įveskite nuotolinio ID"), + ("Enter your password", "Įveskite savo slaptažodį"), + ("Logging in...", "Prisijungiama..."), + ("Enable RDP session sharing", "Įgalinti RDP seansų bendrinimą"), + ("Auto Login", "Automatinis prisijungimas"), + ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), + ("Rename", "Pervardyti"), + ("Space", "Erdvė"), + ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), + ("Change Path", "Keisti kelią"), + ("Create Folder", "Sukurti aplanką"), + ("Please enter the folder name", "Įveskite aplanko pavadinimą"), + ("Fix it", "Pataisyk tai"), + ("Warning", "Įspėjimas"), + ("Login screen using Wayland is not supported", "Prisijungimo ekranas naudojant Wayland nepalaikomas"), + ("Reboot required", "Reikia paleisti iš naujo"), + ("Unsupported display server", "Nepalaikomas rodymo serveris"), + ("x11 expected", "reikalingas x11"), + ("Port", "Prievadas"), + ("Settings", "Nustatymai"), + ("Username", "Vartotojo vardas"), + ("Invalid port", "Netinkamas prievadas"), + ("Closed manually by the peer", "Partneris atmetė prašymą prisijungti"), + ("Enable remote configuration modification", "Įgalinti nuotolinį konfigūracijos modifikavimą"), + ("Run without install", "Vykdyti be diegimo"), + ("Connect via relay", "Prisijungti per relę"), + ("Always connect via relay", "Visada prisijunkite per relę"), + ("whitelist_tip", "Mane gali pasiekti tik baltajame sąraše esantys IP adresai"), + ("Login", "Prisijungti"), + ("Verify", "Patvirtinti"), + ("Remember me", "Prisimink mane"), + ("Trust this device", "Pasitikėk šiuo įrenginiu"), + ("Verification code", "Patvirtinimo kodas"), + ("verification_tip", "Aptiktas naujas įrenginys ir registruotu el. pašto adresu išsiųstas patvirtinimo kodas. Įveskite jį norėdami tęsti prisijungimą."), + ("Logout", "Atsijungti"), + ("Tags", "Žymos"), + ("Search ID", "Paieškos ID"), + ("whitelist_sep", "Atskirti kableliu, kabliataškiu, tarpu arba nauja eilute"), + ("Add ID", "Pridėti ID"), + ("Add Tag", "Pridėti žymą"), + ("Unselect all tags", "Atšaukti visų žymų pasirinkimą"), + ("Network error", "Tinklo klaida"), + ("Username missed", "Prarastas vartotojo vardas"), + ("Password missed", "Slaptažodis praleistas"), + ("Wrong credentials", "Klaidingi kredencialai"), + ("Edit Tag", "Redaguoti žymą"), + ("Unremember Password", "Nebeprisiminti slaptažodžio"), + ("Favorites", "Parankiniai"), + ("Add to Favorites", "Įtraukti į parankinius"), + ("Remove from Favorites", "Pašalinti iš parankinių"), + ("Empty", "Tuščia"), + ("Invalid folder name", "Neteisingas aplanko pavadinimas"), + ("Socks5 Proxy", "Socks5 Proxy"), + ("Hostname", "Pagrindinio kompiuterio pavadinimas"), + ("Discovered", "Aptikta tinkle"), + ("install_daemon_tip", "Norėdami, kad RustDesk startuotų automatiškai, turite ją įdiegti"), + ("Remote ID", "Nuotolinis ID"), + ("Paste", "Įklijuoti"), + ("Paste here?", "Įklijuoti čia?"), + ("Are you sure to close the connection?", "Ar tikrai norite atsijungti?"), + ("Download new version", "Atsisiųsti naują versiją"), + ("Touch mode", "Palietimo režimas"), + ("Mouse mode", "Pelės režimas"), + ("One-Finger Tap", "Palietimas vienu pirštu"), + ("Left Mouse", "Kairysis pelės kl."), + ("One-Long Tap", "Vienas palietimas"), + ("Two-Finger Tap", "Palietimas dviem pirštais"), + ("Right Mouse", "Dešinysis pelės kl."), + ("One-Finger Move", "Vieno piršto judesys"), + ("Double Tap & Move", "Dukart palieskite ir perkelkite"), + ("Mouse Drag", "Pelės vilkimas"), + ("Three-Finger vertically", "Trys pirštai vertikaliai"), + ("Mouse Wheel", "Pelės ratukas"), + ("Two-Finger Move", "Dviejų pirštų judesys"), + ("Canvas Move", "Drobės perkėlimas"), + ("Pinch to Zoom", "Suimkite, kad padidintumėte"), + ("Canvas Zoom", "Drobės mastelis"), + ("Reset canvas", "Atstatyti drobę"), + ("No permission of file transfer", "Nėra leidimo perkelti failus"), + ("Note", "Pastaba"), + ("Connection", "Ryšys"), + ("Share Screen", "Bendrinti ekraną"), + ("Chat", "Pokalbis"), + ("Total", "Iš viso"), + ("items", "elementai"), + ("Selected", "Pasirinkta"), + ("Screen Capture", "Ekrano nuotrauka"), + ("Input Control", "Įvesties valdymas"), + ("Audio Capture", "Garso fiksavimas"), + ("File Connection", "Failo ryšys"), + ("Screen Connection", "Ekrano jungtis"), + ("Do you accept?", "Ar sutinki?"), + ("Open System Setting", "Atviros sistemos nustatymas"), + ("How to get Android input permission?", "Kaip gauti Android įvesties leidimą?"), + ("android_input_permission_tip1", "Kad nuotolinis įrenginys galėtų valdyti Android įrenginį pele arba liesti, turite leisti RustDesk naudoti \"Prieinamumo\" paslaugą."), + ("android_input_permission_tip2", "Eikite į kitą sistemos nustatymų puslapį, suraskite \"Įdiegtos paslaugos\" ir įgalinkite \"RustDesk įvestis\" paslaugą."), + ("android_new_connection_tip", "Gauta nauja užklausa tvarkyti dabartinį įrenginį."), + ("android_service_will_start_tip", "Įgalinus ekrano fiksavimo paslaugą, kiti įrenginiai gali pateikti užklausą prisijungti prie to įrenginio."), + ("android_stop_service_tip", "Uždarius paslaugą automatiškai bus uždaryti visi užmegzti ryšiai."), + ("android_version_audio_tip", "Dabartinė Android versija nepalaiko garso įrašymo, atnaujinkite į Android 10 ar naujesnę versiją."), + ("android_start_service_tip", "Spustelėkite [Paleisti paslaugą] arba įjunkite [Fiksuoti ekraną], kad paleistumėte ekrano bendrinimo paslaugą."), + ("android_permission_may_not_change_tip", "Užmegztų ryšių leidimų keisti negalima, reikia prisijungti iš naujo."), + ("Account", "Paskyra"), + ("Overwrite", "Perrašyti"), + ("This file exists, skip or overwrite this file?", "Šis failas egzistuoja, praleisti arba perrašyti šį failą?"), + ("Quit", "Išeiti"), + ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/"), + ("Help", "Pagalba"), + ("Failed", "Nepavyko"), + ("Succeeded", "Pavyko"), + ("Someone turns on privacy mode, exit", "Kažkas įjungė privatumo režimą, išeiti"), + ("Unsupported", "Nepalaikomas"), + ("Peer denied", "Atšaukė"), + ("Please install plugins", "Įdiekite papildinius"), + ("Peer exit", "Nuotolinis mazgas neveikia"), + ("Failed to turn off", "Nepavyko išjungti"), + ("Turned off", "Išjungti"), + ("In privacy mode", "Privatumo režimas"), + ("Out privacy mode", "Išėjimas iš privatumo režimo"), + ("Language", "Kalba"), + ("Keep RustDesk background service", "Palikti RustDesk fonine paslauga"), + ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), + ("android_open_battery_optimizations_tip", "Eikite į kitą nustatymų puslapį"), + ("Start on Boot", "Pradėti paleidžiant"), + ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), + ("Connection not allowed", "Ryšys neleidžiamas"), + ("Legacy mode", "Senasis režimas"), + ("Map mode", "Žemėlapio režimas"), + ("Translate mode", "Vertimo režimas"), + ("Use permanent password", "Naudoti nuolatinį slaptažodį"), + ("Use both passwords", "Naudoti abu slaptažodžius"), + ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), + ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), + ("Allow remote restart", "Leisti nuotolinio kompiuterio paleidimą iš naujo"), + ("Restart Remote Device", "Paleisti nuotolinį kompiuterį iš naujo"), + ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo?"), + ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), + ("remote_restarting_tip", "Nuotolinis įrenginys paleidžiamas iš naujo. Uždarykite šį pranešimą ir po kurio laiko vėl prisijunkite naudodami nuolatinį slaptažodį."), + ("Copied", "Nukopijuota"), + ("Exit Fullscreen", "Išeiti iš pilno ekrano"), + ("Fullscreen", "Per visą ekraną"), + ("Mobile Actions", "Veiksmai mobiliesiems"), + ("Select Monitor", "Pasirinkite monitorių"), + ("Control Actions", "Valdymo veiksmai"), + ("Display Settings", "Ekrano nustatymai"), + ("Ratio", "Santykis"), + ("Image Quality", "Vaizdo kokybė"), + ("Scroll Style", "Slinkimo stilius"), + ("Show Menubar", "Rodyti meniu juostą"), + ("Hide Menubar", "Slėpti meniu juostą"), + ("Direct Connection", "Tiesioginis ryšys"), + ("Relay Connection", "Tarpinė jungtis"), + ("Secure Connection", "Saugus ryšys"), + ("Insecure Connection", "Nesaugus ryšys"), + ("Scale original", "Pakeisti originalų mastelį"), + ("Scale adaptive", "Pritaikomas mastelis"), + ("General", "Bendra"), + ("Security", "Sauga"), + ("Theme", "Tema"), + ("Dark Theme", "Tamsioji tema"), + ("Light Theme", "Šviesi tema"), + ("Dark", "Tamsi"), + ("Light", "Šviesi"), + ("Follow System", "Kaip sistemos"), + ("Enable hardware codec", "Įgalinti"), + ("Unlock Security Settings", "Atrakinti saugos nustatymus"), + ("Enable Audio", "Įgalinti garsą"), + ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), + ("Server", "Serveris"), + ("Direct IP Access", "Tiesioginė IP prieiga"), + ("Proxy", "Tarpinis serveris"), + ("Apply", "Taikyti"), + ("Disconnect all devices?", "Atjungti visus įrenginius?"), + ("Clear", "Išvalyti"), + ("Audio Input Device", "Garso įvestis"), + ("Deny remote access", "Uždrausti nuotolinę prieigą"), + ("Use IP Whitelisting", "Naudoti patikimą IP sąrašą"), + ("Network", "Tinklas"), + ("Enable RDP", "Įgalinti RDP"), + ("Pin menubar", "Prisegti meniu juostą"), + ("Unpin menubar", "Atsegti meniu juostą"), + ("Recording", "Įrašymas"), + ("Directory", "Katalogas"), + ("Automatically record incoming sessions", "Automatiškai įrašyti įeinančius seansus"), + ("Change", "Keisti"), + ("Start session recording", "Pradėti seanso įrašinėjimą"), + ("Stop session recording", "Sustabdyti seanso įrašinėjimą"), + ("Enable Recording Session", "Įgalinti seanso įrašinėjimą"), + ("Allow recording session", "Leisti seanso įrašinėjimą"), + ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), + ("Deny LAN Discovery", "Neleisti LAN aptikimo"), + ("Write a message", "Rašyti žinutę"), + ("Prompt", "Užuomina"), + ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), + ("elevated_foreground_window_tip", "Dabartinis nuotolinio darbalaukio langas reikalauja didesnių privilegijų, todėl laikinai neįmanoma naudoti pelės ir klaviatūros. Galite paprašyti nuotolinio vartotojo sumažinti dabartinį langą arba spustelėti aukščio mygtuką ryšio valdymo lange. Norint išvengti šios problemos ateityje, rekomenduojama programinę įrangą įdiegti nuotoliniame įrenginyje."), + ("Disconnected", "Atsijungęs"), + ("Other", "Kita"), + ("Confirm before closing multiple tabs", "Patvirtinti prieš uždarant kelis skirtukus"), + ("Keyboard Settings", "Klaviatūros nustatymai"), + ("Full Access", "Pilna prieiga"), + ("Screen Share", "Ekrano bendrinimas"), + ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland reikalauja Ubuntu 21.04 arba naujesnės versijos."), + ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland reikalinga naujesnės Linux Distro versijos. Išbandykite X11 darbalaukį arba pakeiskite OS."), + ("JumpLink", "Peržiūra"), + ("Please Select the screen to be shared(Operate on the peer side).", "Prašome pasirinkti ekraną, kurį norite bendrinti (veikiantį kitoje pusėje)."), + ("Show RustDesk", "Rodyti RustDesk"), + ("This PC", "Šis kompiuteris"), + ("or", "arba"), + ("Continue with", "Tęsti su"), + ("Elevate", "Pakelti"), + ("Zoom cursor", "Mastelio keitimo žymeklis"), + ("Accept sessions via password", "Priimti seansus naudojant slaptažodį"), + ("Accept sessions via click", "Priimti seansus spustelėjus"), + ("Accept sessions via both", "Priimti seansus abiem variantais"), + ("Please wait for the remote side to accept your session request...", "Palaukite, kol nuotolinė pusė priims jūsų seanso užklausą..."), + ("One-time Password", "Vienkartinis slaptažodis"), + ("Use one-time password", "Naudoti vienkartinį slaptažodį"), + ("One-time password length", "Vienkartinio slaptažodžio ilgis"), + ("Request access to your device", "Prašo leidimo valdyti jūsų įrenginį"), + ("Hide connection management window", "Slėpti ryšio valdymo langą"), + ("hide_cm_tip", "Leisti paslėpti didžiąją ir mažąją raidę, jei priimamos slaptažodžio sesijos arba naudojamas nuolatinis slaptažodis"), + ("wayland_experiment_tip", "Wayland palaikymas yra eksperimentinis, naudokite X11, jei jums reikalingas automatinis prisijungimas."), + ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), + ("Skipped", "Praleisti"), + ("Add to Address Book", "Pridėti prie adresų knygos"), + ("Group", "Grupė"), + ("Search", "Paieška"), + ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), + ("Local keyboard type", "Vietinės klaviatūros tipas"), + ("Select local keyboard type", "Pasirinkite vietinės klaviatūros tipą"), + ("software_render_tip", "Jei turite Nvidia vaizdo plokštę ir nuotolinis langas iškart užsidaro prisijungus, gali padėti „Nouveau“ tvarkyklės įdiegimas ir programinės įrangos atvaizdavimo pasirinkimas. Būtina paleisti iš naujo."), + ("Always use software rendering", "Visada naudoti programinį spartintuvą"), + ("config_input", "Norėdami valdyti nuotolinį darbalaukį naudodami klaviatūrą, turite suteikti RustDesk leidimus \"Įvesties monitoringas\"."), + ("config_microphone", "Norėdami kalbėtis su nuotoline puse, turite suteikti RustDesk leidimą \"Įrašyti garsą\"."), + ("request_elevation_tip", "Taip pat galite prašyti tesių suteikimo, jeigu kas nors yra nuotolinėje pusėje."), + ("Wait", "Laukti"), + ("Elevation Error", "Teisių suteikimo klaida"), + ("Ask the remote user for authentication", "Klauskite nuotolinio vartotojo autentifikavimo"), + ("Choose this if the remote account is administrator", "Pasirinkite tai, jei nuotolinė paskyra yra administratorius"), + ("Transmit the username and password of administrator", "Persiųsti administratoriaus vartotojo vardą ir slaptažodį"), + ("still_click_uac_tip", "Vis tiek reikia, kad nuotolinis vartotojas paleidžiant RustDesk UAC lange paspaustų \"OK\"."), + ("Request Elevation", "Prašyti teisių"), + ("wait_accept_uac_tip", "Palaukite, kol nuotolinis vartotojas patvirtins UAC užklausą."), + ("Elevate successfully", "Teisės suteiktos"), + ("uppercase", "didžiosios raidės"), + ("lowercase", "mažosios raidės"), + ("digit", "skaitmuo"), + ("special character", "specialusis simbolis"), + ("length>=8", "ilgis>=8"), + ("Weak", "Silpnas"), + ("Medium", "Vidutinis"), + ("Strong", "Stiprus"), + ("Switch Sides", "Perjungti puses"), + ("Please confirm if you want to share your desktop?", "Prašome patvirtinti, jeigu norite bendrinti darbalaukį?"), + ("Display", "Ekranas"), + ("Default View Style", "Numatytasis peržiūros stilius"), + ("Default Scroll Style", "Numatytasis slinkties stilius"), + ("Default Image Quality", "Numatytoji vaizdo kokybė"), + ("Default Codec", "Numatytasis kodekas"), + ("Bitrate", "Sparta"), + ("FPS", "FPS"), + ("Auto", "Automatinis"), + ("Other Default Options", "Kitos numatytosios parinktys"), + ("Voice call", "Balso skambutis"), + ("Text chat", "Tekstinis pokalbis"), + ("Stop voice call", "Sustabdyti balso skambutį"), + ("relay_hint_tip", "Tiesioginis ryšys gali būti neįmanomas. Tokiu atveju galite pabandyti prisijungti per perdavimo serverį. \nArba, jei norite iš karto naudoti perdavimo serverį, prie ID galite pridėti priesagą \"/r\" arba nuotolinio pagrindinio kompiuterio nustatymuose įgalinti \"Visada prisijungti per relę\"."), + ("Reconnect", "Prisijungti iš naujo"), + ("Codec", "Kodekas"), + ("Resolution", "Rezoliucija"), + ("No transfers in progress", "Nevyksta jokių perdavimų"), + ("Set one-time password length", "Nustatyti vienkartinio slaptažodžio ilgį"), + ("idd_driver_tip", "Įdiekite virtualaus ekrano tvarkyklę (naudojama, kai nėra fizinių ekranų)"), + ("confirm_idd_driver_tip", "Įjungta virtualaus ekrano tvarkyklės diegimo funkcija. Atminkite, kad bus įdiegtas bandomasis sertifikatas, kad būtų galima pasitikėti tvarkykle. Šis sertifikatas bus naudojamas tik pasitikėjimui Rustdesk tvarkyklėmis patikrinti."), + ("RDP Settings", "RDP nustatymai"), + ("Sort by", "Rūšiuoti pagal"), + ("New Connection", "Naujas ryšys"), + ("Restore", "Atkurti"), + ("Minimize", "Sumažinti"), + ("Maximize", "Padidinti"), + ("Your Device", "Jūsų įrenginys"), + ("empty_recent_tip", "Nėra paskutinių seansų!\nLaikas suplanuoti naują."), + ("empty_favorite_tip", "Dar neturite parankinių nuotolinių seansų."), + ("empty_lan_tip", "Nuotolinių mazgų nerasta."), + ("empty_address_book_tip", "Adresų knygelėje nėra nuotolinių kompiuterių."), + ("eg: admin", "pvz.: administratorius"), + ("Empty Username", "Tuščias naudotojo vardas"), + ("Empty Password", "Tuščias slaptažodis"), + ("Me", "Aš"), + ("identical_file_tip", "Failas yra identiškas nuotoliniame kompiuteryje esančiam failui."), + ("show_monitors_tip", "Rodyti monitorius įrankių juostoje"), + ("View Mode", "Peržiūros režimas"), + ("login_linux_tip", "Norėdami įjungti X darbalaukio seansą, turite būti prisijungę prie nuotolinės Linux paskyros."), + ("verify_rustdesk_password_tip", "Įveskite kliento RustDesk slaptažodį"), + ("remember_account_tip", "Prisiminti šią paskyrą"), + ("os_account_desk_tip", "Ši paskyra naudojama norint prisijungti prie nuotolinės OS ir įgalinti darbalaukio seansą režimu headless"), + ("OS Account", "OS paskyra"), + ("another_user_login_title_tip", "Kitas vartotojas jau yra prisijungęs"), + ("another_user_login_text_tip", "Atjungti"), + ("xorg_not_found_title_tip", "Xorg nerastas"), + ("xorg_not_found_text_tip", "Prašom įdiegti Xorg"), + ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), + ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), ].iter().cloned().collect(); } From e3c828a6f1ba18283b81db5bd4e340efb1d4d9ec Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 10 Apr 2023 18:06:22 +0800 Subject: [PATCH 285/366] add: init/dispose hook when load/unload plugins --- src/plugins.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/plugins.rs b/src/plugins.rs index 4e44a2643..9ca2ad8f0 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -4,7 +4,10 @@ use std::{ sync::{Arc, RwLock}, }; -use hbb_common::{anyhow::Error, log::debug}; +use hbb_common::{ + anyhow::Error, + log::{debug, error}, +}; use lazy_static::lazy_static; use libloading::{Library, Symbol}; @@ -82,6 +85,18 @@ impl PluginRegistar

    { match lib { Ok(lib) => match lib.try_into() { Ok(plugin) => { + let plugin: PluginImpl = plugin; + // try to initialize this plugin + if let Some(init) = plugin.plugin_vt().init { + let init_ret = init(); + if init_ret != 0 { + error!( + "Error when initializing the plugin {} with error code {}.", + plugin.name, init_ret + ); + return init_ret; + } + } PLUGIN_REGISTRAR .plugins .write() @@ -104,7 +119,12 @@ impl PluginRegistar

    { let p = unsafe { CStr::from_ptr(path) }; let lib_path = p.to_str().unwrap_or("").to_owned(); match PLUGIN_REGISTRAR.plugins.write().unwrap().remove(&lib_path) { - Some(_) => 0, + Some(plugin) => { + if let Some(dispose) = plugin.plugin_vt().dispose { + return dispose(); + } + 0 + } None => -1, } } From f2f39e31a1435986be3d4f46050db0478f63bd8c Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 13 Apr 2023 02:06:42 +0800 Subject: [PATCH 286/366] feat: change dummy c to a rust plugin example --- .gitignore | 2 ++ Cargo.toml | 2 +- examples/custom_plugin/Cargo.toml | 23 +++++++++++++++++++++ examples/custom_plugin/src/lib.rs | 30 ++++++++++++++++++++++++++++ src/api.rs | 9 ++------- src/lib.rs | 13 ++++++++---- src/plugins.rs | 33 +++++++++++++------------------ 7 files changed, 81 insertions(+), 31 deletions(-) create mode 100644 examples/custom_plugin/Cargo.toml create mode 100644 examples/custom_plugin/src/lib.rs diff --git a/.gitignore b/.gitignore index f17f8155b..ec6251084 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ lib/generated_bridge.dart .vscode-server/ .ssh .devcontainer/.* +# build cache in examples +examples/**/target/ \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 28eacffce..b8c4f59f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,7 +136,7 @@ flutter_rust_bridge = "1.61.1" [workspace] members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/simple_rc", "libs/portable"] -exclude = ["vdi/host"] +exclude = ["vdi/host", "examples/custom_plugin"] [package.metadata.winres] LegalCopyright = "Copyright © 2022 Purslane, Inc." diff --git a/examples/custom_plugin/Cargo.toml b/examples/custom_plugin/Cargo.toml new file mode 100644 index 000000000..106e48ebb --- /dev/null +++ b/examples/custom_plugin/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "custom_plugin" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "custom_plugin" +path = "src/lib.rs" +crate-type = ["cdylib"] + +[dependencies] +lazy_static = "1.4.0" +rustdesk = { path = "../../", version = "1.2.0"} + +[profile.release] +lto = true +codegen-units = 1 +panic = 'abort' +strip = true +#opt-level = 'z' # only have smaller size after strip +rpath = true \ No newline at end of file diff --git a/examples/custom_plugin/src/lib.rs b/examples/custom_plugin/src/lib.rs new file mode 100644 index 000000000..be051c1d9 --- /dev/null +++ b/examples/custom_plugin/src/lib.rs @@ -0,0 +1,30 @@ +use librustdesk::{api::RustDeskApiTable}; +/// This file demonstrates how to write a custom plugin for RustDesk. +use std::ffi::{c_char, c_int, CString}; + +lazy_static::lazy_static! { + pub static ref PLUGIN_NAME: CString = CString::new("A Template Rust Plugin").unwrap(); + pub static ref PLUGIN_ID: CString = CString::new("TemplatePlugin").unwrap(); + // Do your own logic based on the API provided by RustDesk. + pub static ref API: RustDeskApiTable = RustDeskApiTable::default(); +} + +#[no_mangle] +fn plugin_name() -> *const c_char { + return PLUGIN_NAME.as_ptr(); +} + +#[no_mangle] +fn plugin_id() -> *const c_char { + return PLUGIN_ID.as_ptr(); +} + +#[no_mangle] +fn plugin_init() -> c_int { + return 0 as _; +} + +#[no_mangle] +fn plugin_dispose() -> c_int { + return 0 as _; +} diff --git a/src/api.rs b/src/api.rs index 19779995e..1c993a5ee 100644 --- a/src/api.rs +++ b/src/api.rs @@ -8,8 +8,8 @@ pub type UnloadPluginFunc = fn(*const c_char) -> i32; #[repr(C)] pub struct RustDeskApiTable { - pub register_plugin: LoadPluginFunc, - pub unload_plugin: UnloadPluginFunc, + pub(crate) register_plugin: LoadPluginFunc, + pub(crate) unload_plugin: UnloadPluginFunc, } #[no_mangle] @@ -22,11 +22,6 @@ fn unload_plugin(path: *const c_char) -> i32 { PLUGIN_REGISTRAR.unload_plugin(path) } -#[no_mangle] -fn get_api_table() -> RustDeskApiTable { - RustDeskApiTable::default() -} - impl Default for RustDeskApiTable { fn default() -> Self { Self { diff --git a/src/lib.rs b/src/lib.rs index af9f773ce..15c0ca037 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ +mod keyboard; #[cfg(not(any(target_os = "ios")))] /// cbindgen:ignore pub mod platform; -mod keyboard; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use platform::{get_cursor, get_cursor_data, get_cursor_pos, start_os_service}; #[cfg(not(any(target_os = "ios")))] @@ -20,7 +20,12 @@ pub use self::rendezvous_mediator::*; pub mod common; #[cfg(not(any(target_os = "ios")))] pub mod ipc; -#[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli", feature = "flutter")))] +#[cfg(not(any( + target_os = "android", + target_os = "ios", + feature = "cli", + feature = "flutter" +)))] pub mod ui; mod version; pub use version::*; @@ -44,9 +49,9 @@ mod license; mod port_forward; #[cfg(not(any(target_os = "android", target_os = "ios")))] -mod plugins; +pub mod api; #[cfg(not(any(target_os = "android", target_os = "ios")))] -mod api; +pub mod plugins; mod tray; diff --git a/src/plugins.rs b/src/plugins.rs index 9ca2ad8f0..3235bce4a 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -170,33 +170,28 @@ impl TryFrom for PluginImpl { #[cfg(target_os = "linux")] fn test_plugin() { use std::io::Write; - - let code = " - const char* plugin_name(){return \"test_name\";}; - const char* plugin_id(){return \"test_id\"; } - int plugin_init() {return 0;} - int plugin_dispose() {return 0;} - "; - let mut f = std::fs::File::create("test.c").unwrap(); - f.write_all(code.as_bytes()).unwrap(); - f.flush().unwrap(); - let mut cmd = std::process::Command::new("cc"); - cmd.arg("-fPIC") - .arg("-shared") - .arg("test.c") - .arg("-o") - .arg("libtest.so"); + let mut cmd = std::process::Command::new("cargo"); + cmd.current_dir("./examples/custom_plugin"); + // Strip this shared library. + cmd.env("RUSTFLAGS", "-C link-arg=-s"); + cmd.arg("build"); // Spawn the compiler process. let mut child = cmd.spawn().unwrap(); // Wait for the compiler to finish. let status = child.wait().unwrap(); assert!(status.success()); // Load the library. - let lib = unsafe { Library::new("./libtest.so").unwrap() }; + let lib = unsafe { + Library::new("./examples/custom_plugin/target/debug/libcustom_plugin.so").unwrap() + }; let plugin: PluginImpl = lib.try_into().unwrap(); assert!(plugin._inner.is_some()); - assert!(plugin.name == "test_name"); - assert!(plugin.id == "test_id"); + assert!(plugin.name == "A Template Rust Plugin"); + assert!(plugin.id == "TemplatePlugin"); + println!( + "plugin vt size: {}", + std::mem::size_of::() + ); assert!(PLUGIN_REGISTRAR .plugins .write() From 82681de633bb1d39ab531c4a920e12ec4f8fa0b6 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Wed, 12 Apr 2023 22:16:39 +0200 Subject: [PATCH 287/366] Add files via upload --- src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 5 +++-- src/lang/el.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + 34 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index f4a8a1204..9347dac82 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 11b3144c7..3ec042928 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "请安装 Xorg"), ("no_desktop_title_tip", "desktop 未安装"), ("no_desktop_text_tip", "请安装 desktop"), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index acfbed636..31bd83533 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index b165540e8..c566a1284 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 6777ad733..caf0dd5a1 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -113,7 +113,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Finished", "Fertiggestellt"), ("Speed", "Geschwindigkeit"), ("Custom Image Quality", "Benutzerdefinierte Bildqualität"), - ("Privacy mode", "Datenschutz-Modus"), + ("Privacy mode", "Datenschutzmodus"), ("Block user input", "Benutzereingaben blockieren"), ("Unblock user input", "Benutzereingaben freigeben"), ("Adjust Window", "Fenster anpassen"), @@ -357,7 +357,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable Audio", "Audio aktivieren"), ("Unlock Network Settings", "Netzwerkeinstellungen entsperren"), ("Server", "Server"), - ("Direct IP Access", "Direkter IP-Zugriff"), + ("Direct IP Access", "Direkter IP-Zugang"), ("Proxy", "Proxy"), ("Apply", "Anwenden"), ("Disconnect all devices?", "Alle Geräte trennen?"), @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Bitte installieren Sie Xorg."), ("no_desktop_title_tip", "Es ist kein Desktop verfügbar."), ("no_desktop_text_tip", "Bitte installieren Sie den GNOME-Desktop."), + ("No need to elevate", "Erhöhung der Rechte nicht erforderlich"), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index e74551046..84d6a3c42 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 435bc6b0a..1969c545f 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 9f7a358c5..fde3934ee 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Por favor, instala Xorg"), ("no_desktop_title_tip", "No hay escritorio disponible"), ("no_desktop_text_tip", "Por favor, instala GNOME Desktop"), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 79f39154a..c00fb3ab6 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "لطفا Xorg را نصب کنید"), ("no_desktop_title_tip", "هیچ دسکتاپی در دسترس نیست"), ("no_desktop_text_tip", "لطفا دسکتاپ گنوم را نصب کنید"), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index cd2a6c3cc..fd4598d04 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index e8efc6aa3..20adb1d6e 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 97cab077c..32f350472 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 6a4e2fd8e..7181ffab1 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Installa Xorg."), ("no_desktop_title_tip", "Non c'è nessun desktop disponibile."), ("no_desktop_text_tip", "Installa il desktop GNOME."), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index cfe029cc4..5a32110dc 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 9e1278c6e..e6a840ba0 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index a86d1c81a..cbea5826d 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index b284479f3..f403baaf4 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Prašom įdiegti Xorg"), ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 329c4dd70..7ba78b470 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 4d6f7efeb..3f969dd15 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Proszę zainstalować Xorg"), ("no_desktop_title_tip", "Pulpit jest niedostępny"), ("no_desktop_text_tip", "Proszę zainstalować pulpit GNOME"), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 6a44584a0..77d1f1aec 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 51ef174f5..58bc99959 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 5de632ec4..7d0b809cf 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 447e5223b..cfb37813b 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Установите, пожалуйста, Xorg"), ("no_desktop_title_tip", "Нет доступных рабочих столов"), ("no_desktop_text_tip", "Установите, пожалуйста, GNOME Desktop"), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 3b7f62b74..920fc53ab 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 2f050333c..2f74b0d55 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 453534a86..676e97b6d 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index e2728caaa..04b99433a 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 802eff33d..8419f4dee 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 8941c00d4..61d2e415d 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 1bdece3b7..942c0de64 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 99977515b..3347da6a9 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 1eb74d7b2..47d43167f 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "請安裝 Xorg"), ("no_desktop_title_tip", "沒有可用的桌面"), ("no_desktop_text_tip", "請安裝 GNOME 桌面"), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 0c1f453bf..06e9acfee 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index b8086f126..211080a2b 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", ""), ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), + ("No need to elevate", ""), ].iter().cloned().collect(); } From b2c05908986defa93232e7f613e5e575fb9e31df Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 12 Apr 2023 09:41:13 +0800 Subject: [PATCH 288/366] merge mobile/desktop remote toobar code Signed-off-by: 21pages --- flutter/lib/common.dart | 26 +- flutter/lib/common/shared_state.dart | 20 + flutter/lib/common/widgets/dialog.dart | 224 ++++- flutter/lib/common/widgets/toolbar.dart | 449 ++++++++++ flutter/lib/desktop/pages/remote_page.dart | 22 +- .../lib/desktop/widgets/remote_toolbar.dart | 830 +++--------------- flutter/lib/mobile/pages/remote_page.dart | 298 ++----- flutter/lib/mobile/pages/settings_page.dart | 12 +- 8 files changed, 882 insertions(+), 999 deletions(-) create mode 100644 flutter/lib/common/widgets/toolbar.dart diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 49cbd6091..daffe261f 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -43,6 +43,7 @@ final isIOS = Platform.isIOS; final isDesktop = Platform.isWindows || Platform.isMacOS || Platform.isLinux; var isWeb = false; var isWebDesktop = false; +var isMobile = isAndroid || isIOS; var version = ""; int androidVersion = 0; @@ -1158,40 +1159,19 @@ class AndroidPermissionManager { // Used only for mobile, pages remote, settings, dialog // TODO remove argument contentPadding, it’s not used, getToggle() has not RadioListTile getRadio( - String name, T toValue, T curValue, void Function(T?) onChange, + Widget title, T toValue, T curValue, ValueChanged? onChange, {EdgeInsetsGeometry? contentPadding}) { return RadioListTile( contentPadding: contentPadding ?? EdgeInsets.zero, visualDensity: VisualDensity.compact, controlAffinity: ListTileControlAffinity.trailing, - title: Text(translate(name)), + title: title, value: toValue, groupValue: curValue, onChanged: onChange, ); } -// TODO move this to mobile/widgets. -// Used only for mobile, pages remote, settings, dialog -CheckboxListTile getToggle( - String id, void Function(void Function()) setState, option, name, - {FFI? ffi}) { - final opt = bind.sessionGetToggleOptionSync(id: id, arg: option); - return CheckboxListTile( - contentPadding: EdgeInsets.zero, - visualDensity: VisualDensity.compact, - value: opt, - onChanged: (v) { - setState(() { - bind.sessionToggleOption(id: id, value: option); - }); - if (option == "show-quality-monitor") { - (ffi ?? gFFI).qualityMonitorModel.checkShowQualityMonitor(id); - } - }, - title: Text(translate(name))); -} - /// find ffi, tag is Remote ID /// for session specific usage FFI ffi(String? tag) { diff --git a/flutter/lib/common/shared_state.dart b/flutter/lib/common/shared_state.dart index bc1a562b9..4659a01ce 100644 --- a/flutter/lib/common/shared_state.dart +++ b/flutter/lib/common/shared_state.dart @@ -261,3 +261,23 @@ class PeerStringOption { static RxString find(String id, String opt) => Get.find(tag: tag(id, opt)); } + +initSharedStates(String id) { + PrivacyModeState.init(id); + BlockInputState.init(id); + CurrentDisplayState.init(id); + KeyboardEnabledState.init(id); + ShowRemoteCursorState.init(id); + RemoteCursorMovedState.init(id); + PeerBoolOption.init(id, 'zoom-cursor', () => false); +} + +removeSharedStates(String id) { + PrivacyModeState.delete(id); + BlockInputState.delete(id); + CurrentDisplayState.delete(id); + ShowRemoteCursorState.delete(id); + KeyboardEnabledState.delete(id); + RemoteCursorMovedState.delete(id); + PeerBoolOption.delete(id, 'zoom-cursor'); +} diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index cf1ced92f..7228bb585 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'package:debounce_throttle/debounce_throttle.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_hbb/common/shared_state.dart'; import 'package:get/get.dart'; import '../../common.dart'; @@ -879,7 +881,9 @@ void showRestartRemoteDevice( await dialogManager.show((setState, close) => CustomAlertDialog( title: Row(children: [ Icon(Icons.warning_rounded, color: Colors.redAccent, size: 28), - Text(translate("Restart Remote Device")).paddingOnly(left: 10), + Flexible( + child: Text(translate("Restart Remote Device")) + .paddingOnly(left: 10)), ]), content: Text( "${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"), @@ -1047,3 +1051,221 @@ showSetOSAccount( ); }); } + +showAuditDialog(String id, dialogManager) async { + final controller = TextEditingController(); + dialogManager.show((setState, close) { + submit() { + var text = controller.text.trim(); + if (text != '') { + bind.sessionSendNote(id: id, note: text); + } + close(); + } + + late final focusNode = FocusNode( + onKey: (FocusNode node, RawKeyEvent evt) { + if (evt.logicalKey.keyLabel == 'Enter') { + if (evt is RawKeyDownEvent) { + int pos = controller.selection.base.offset; + controller.text = + '${controller.text.substring(0, pos)}\n${controller.text.substring(pos)}'; + controller.selection = + TextSelection.fromPosition(TextPosition(offset: pos + 1)); + } + return KeyEventResult.handled; + } + if (evt.logicalKey.keyLabel == 'Esc') { + if (evt is RawKeyDownEvent) { + close(); + } + return KeyEventResult.handled; + } else { + return KeyEventResult.ignored; + } + }, + ); + + return CustomAlertDialog( + title: Text(translate('Note')), + content: SizedBox( + width: 250, + height: 120, + child: TextField( + autofocus: true, + keyboardType: TextInputType.multiline, + textInputAction: TextInputAction.newline, + decoration: const InputDecoration.collapsed( + hintText: 'input note here', + ), + maxLines: null, + maxLength: 256, + controller: controller, + focusNode: focusNode, + )), + actions: [ + dialogButton('Cancel', onPressed: close, isOutline: true), + dialogButton('OK', onPressed: submit) + ], + onSubmit: submit, + onCancel: close, + ); + }); +} + +void showConfirmSwitchSidesDialog( + String id, OverlayDialogManager dialogManager) async { + dialogManager.show((setState, close) { + submit() async { + await bind.sessionSwitchSides(id: id); + closeConnection(id: id); + } + + return CustomAlertDialog( + content: msgboxContent('info', 'Switch Sides', + 'Please confirm if you want to share your desktop?'), + actions: [ + dialogButton('Cancel', onPressed: close, isOutline: true), + dialogButton('OK', onPressed: submit), + ], + onSubmit: submit, + onCancel: close, + ); + }); +} + +customImageQualityDialog(String id, FFI ffi) async { + double qualityInitValue = 50; + double fpsInitValue = 30; + bool qualitySet = false; + bool fpsSet = false; + setCustomValues({double? quality, double? fps}) async { + if (quality != null) { + qualitySet = true; + await bind.sessionSetCustomImageQuality(id: id, value: quality.toInt()); + } + if (fps != null) { + fpsSet = true; + await bind.sessionSetCustomFps(id: id, fps: fps.toInt()); + } + if (!qualitySet) { + qualitySet = true; + await bind.sessionSetCustomImageQuality( + id: id, value: qualityInitValue.toInt()); + } + if (!fpsSet) { + fpsSet = true; + await bind.sessionSetCustomFps(id: id, fps: fpsInitValue.toInt()); + } + } + + final btnClose = dialogButton('Close', onPressed: () async { + await setCustomValues(); + ffi.dialogManager.dismissAll(); + }); + + // quality + final quality = await bind.sessionGetCustomImageQuality(id: id); + qualityInitValue = + quality != null && quality.isNotEmpty ? quality[0].toDouble() : 50.0; + const qualityMinValue = 10.0; + const qualityMaxValue = 100.0; + if (qualityInitValue < qualityMinValue) { + qualityInitValue = qualityMinValue; + } + if (qualityInitValue > qualityMaxValue) { + qualityInitValue = qualityMaxValue; + } + final RxDouble qualitySliderValue = RxDouble(qualityInitValue); + final debouncerQuality = Debouncer( + Duration(milliseconds: 1000), + onChanged: (double v) { + setCustomValues(quality: v); + }, + initialValue: qualityInitValue, + ); + final qualitySlider = Obx(() => Row( + children: [ + Expanded( + flex: 3, + child: Slider( + value: qualitySliderValue.value, + min: qualityMinValue, + max: qualityMaxValue, + divisions: 18, + onChanged: (double value) { + qualitySliderValue.value = value; + debouncerQuality.value = value; + }, + )), + Expanded( + flex: 1, + child: Text( + '${qualitySliderValue.value.round()}%', + style: const TextStyle(fontSize: 15), + )), + Expanded( + flex: 2, + child: Text( + translate('Bitrate'), + style: const TextStyle(fontSize: 15), + )), + ], + )); + // fps + final fpsOption = await bind.sessionGetOption(id: id, arg: 'custom-fps'); + fpsInitValue = fpsOption == null ? 30 : double.tryParse(fpsOption) ?? 30; + if (fpsInitValue < 5 || fpsInitValue > 120) { + fpsInitValue = 30; + } + final RxDouble fpsSliderValue = RxDouble(fpsInitValue); + final debouncerFps = Debouncer( + Duration(milliseconds: 1000), + onChanged: (double v) { + setCustomValues(fps: v); + }, + initialValue: qualityInitValue, + ); + bool? direct; + try { + direct = + ConnectionTypeState.find(id).direct.value == ConnectionType.strDirect; + } catch (_) {} + final fpsSlider = Offstage( + offstage: (await bind.mainIsUsingPublicServer() && direct != true) || + version_cmp(ffi.ffiModel.pi.version, '1.2.0') < 0, + child: Row( + children: [ + Expanded( + flex: 3, + child: Obx((() => Slider( + value: fpsSliderValue.value, + min: 5, + max: 120, + divisions: 23, + onChanged: (double value) { + fpsSliderValue.value = value; + debouncerFps.value = value; + }, + )))), + Expanded( + flex: 1, + child: Obx(() => Text( + '${fpsSliderValue.value.round()}', + style: const TextStyle(fontSize: 15), + ))), + Expanded( + flex: 2, + child: Text( + translate('FPS'), + style: const TextStyle(fontSize: 15), + )) + ], + ), + ); + + final content = Column( + children: [qualitySlider, fpsSlider], + ); + msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]); +} diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart new file mode 100644 index 000000000..abf12de30 --- /dev/null +++ b/flutter/lib/common/widgets/toolbar.dart @@ -0,0 +1,449 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/common/shared_state.dart'; +import 'package:flutter_hbb/common/widgets/dialog.dart'; +import 'package:flutter_hbb/consts.dart'; +import 'package:flutter_hbb/models/model.dart'; +import 'package:flutter_hbb/models/platform_model.dart'; +import 'package:get/get.dart'; + +class TTextMenu { + final Widget child; + final VoidCallback onPressed; + Widget? trailingIcon; + bool divider; + TTextMenu( + {required this.child, + required this.onPressed, + this.trailingIcon, + this.divider = false}); +} + +class TRadioMenu { + final Widget child; + final T value; + final T groupValue; + final ValueChanged? onChanged; + + TRadioMenu( + {required this.child, + required this.value, + required this.groupValue, + required this.onChanged}); +} + +class TToggleMenu { + final Widget child; + final bool value; + final ValueChanged? onChanged; + TToggleMenu( + {required this.child, required this.value, required this.onChanged}); +} + +List toolbarControls(BuildContext context, String id, FFI ffi) { + final ffiModel = ffi.ffiModel; + final pi = ffiModel.pi; + final perms = ffiModel.permissions; + + List v = []; + // elevation + if (ffi.elevationModel.showRequestMenu) { + v.add( + TTextMenu( + child: Text(translate('Request Elevation')), + onPressed: () => showRequestElevationDialog(id, ffi.dialogManager)), + ); + } + // osAccount / osPassword + v.add( + TTextMenu( + child: Row(children: [ + Text(translate(pi.is_headless ? 'OS Account' : 'OS Password')), + Offstage( + offstage: isDesktop, + child: + Icon(Icons.edit, color: MyTheme.accent).marginOnly(left: 12)) + ]), + trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), + onPressed: () => pi.is_headless + ? showSetOSAccount(id, ffi.dialogManager) + : showSetOSPassword(id, false, ffi.dialogManager)), + ); + // paste + if (isMobile && perms['keyboard'] != false && perms['clipboard'] != false) { + v.add(TTextMenu( + child: Text(translate('Paste')), + onPressed: () async { + ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain); + if (data != null && data.text != null) { + bind.sessionInputString(id: id, value: data.text ?? ""); + } + })); + } + // reset canvas + if (isMobile) { + v.add(TTextMenu( + child: Text(translate('Reset canvas')), + onPressed: () => ffi.cursorModel.reset())); + } + // transferFile + if (isDesktop) { + v.add( + TTextMenu( + child: Text(translate('Transfer File')), + onPressed: () => connect(context, id, isFileTransfer: true)), + ); + } + // tcpTunneling + if (isDesktop) { + v.add( + TTextMenu( + child: Text(translate('TCP Tunneling')), + onPressed: () => connect(context, id, isTcpTunneling: true)), + ); + } + // note + if (bind.sessionGetAuditServerSync(id: id, typ: "conn").isNotEmpty) { + v.add( + TTextMenu( + child: Text(translate('Note')), + onPressed: () => showAuditDialog(id, ffi.dialogManager)), + ); + } + // divider + if (isDesktop) { + v.add(TTextMenu(child: Offstage(), onPressed: () {}, divider: true)); + } + // ctrlAltDel + if (!ffiModel.viewOnly && + ffiModel.keyboard && + (pi.platform == kPeerPlatformLinux || pi.sasEnabled)) { + v.add( + TTextMenu( + child: Text('${translate("Insert")} Ctrl + Alt + Del'), + onPressed: () => bind.sessionCtrlAltDel(id: id)), + ); + } + // restart + if (perms['restart'] != false && + (pi.platform == kPeerPlatformLinux || + pi.platform == kPeerPlatformWindows || + pi.platform == kPeerPlatformMacOS)) { + v.add( + TTextMenu( + child: Text(translate('Restart Remote Device')), + onPressed: () => showRestartRemoteDevice(pi, id, ffi.dialogManager)), + ); + } + // insertLock + if (!ffiModel.viewOnly && ffi.ffiModel.keyboard) { + v.add( + TTextMenu( + child: Text(translate('Insert Lock')), + onPressed: () => bind.sessionLockScreen(id: id)), + ); + } + // blockUserInput + if (ffi.ffiModel.keyboard && + pi.platform == kPeerPlatformWindows) // privacy-mode != true ?? + { + v.add(TTextMenu( + child: Obx(() => Text(translate( + '${BlockInputState.find(id).value ? 'Unb' : 'B'}lock user input'))), + onPressed: () { + RxBool blockInput = BlockInputState.find(id); + bind.sessionToggleOption( + id: id, value: '${blockInput.value ? 'un' : ''}block-input'); + blockInput.value = !blockInput.value; + })); + } + // switchSides + if (isDesktop && + ffiModel.keyboard && + pi.platform != kPeerPlatformAndroid && + pi.platform != kPeerPlatformMacOS && + version_cmp(pi.version, '1.2.0') >= 0) { + v.add(TTextMenu( + child: Text(translate('Switch Sides')), + onPressed: () => showConfirmSwitchSidesDialog(id, ffi.dialogManager))); + } + // refresh + if (pi.version.isNotEmpty) { + v.add(TTextMenu( + child: Text(translate('Refresh')), + onPressed: () => bind.sessionRefresh(id: id))); + } + // record + var codecFormat = ffi.qualityMonitorModel.data.codecFormat; + if (!isDesktop && + (ffi.recordingModel.start || + (perms["recording"] != false && + (codecFormat == "VP8" || codecFormat == "VP9")))) { + v.add(TTextMenu( + child: Row( + children: [ + Text(translate(ffi.recordingModel.start + ? 'Stop session recording' + : 'Start session recording')), + Padding( + padding: EdgeInsets.only(left: 12), + child: Icon( + ffi.recordingModel.start + ? Icons.pause_circle_filled + : Icons.videocam_outlined, + color: MyTheme.accent), + ) + ], + ), + onPressed: () => ffi.recordingModel.toggle())); + } + return v; +} + +Future>> toolbarViewStyle( + BuildContext context, String id, FFI ffi) async { + final groupValue = await bind.sessionGetViewStyle(id: id) ?? ''; + void onChanged(String? value) async { + if (value == null) return; + bind + .sessionSetViewStyle(id: id, value: value) + .then((_) => ffi.canvasModel.updateViewStyle()); + } + + return [ + TRadioMenu( + child: Text(translate('Scale original')), + value: kRemoteViewStyleOriginal, + groupValue: groupValue, + onChanged: onChanged), + TRadioMenu( + child: Text(translate('Scale adaptive')), + value: kRemoteViewStyleAdaptive, + groupValue: groupValue, + onChanged: onChanged) + ]; +} + +Future>> toolbarImageQuality( + BuildContext context, String id, FFI ffi) async { + final groupValue = await bind.sessionGetImageQuality(id: id) ?? ''; + onChanged(String? value) async { + if (value == null) return; + await bind.sessionSetImageQuality(id: id, value: value); + } + + return [ + TRadioMenu( + child: Text(translate('Good image quality')), + value: kRemoteImageQualityBest, + groupValue: groupValue, + onChanged: onChanged), + TRadioMenu( + child: Text(translate('Balanced')), + value: kRemoteImageQualityBalanced, + groupValue: groupValue, + onChanged: onChanged), + TRadioMenu( + child: Text(translate('Optimize reaction time')), + value: kRemoteImageQualityLow, + groupValue: groupValue, + onChanged: onChanged), + TRadioMenu( + child: Text(translate('Custom')), + value: kRemoteImageQualityCustom, + groupValue: groupValue, + onChanged: (value) { + onChanged(value); + customImageQualityDialog(id, ffi); + }, + ), + ]; +} + +Future>> toolbarCodec( + BuildContext context, String id, FFI ffi) async { + final alternativeCodecs = await bind.sessionAlternativeCodecs(id: id); + final groupValue = + await bind.sessionGetOption(id: id, arg: 'codec-preference') ?? ''; + final List codecs = []; + try { + final Map codecsJson = jsonDecode(alternativeCodecs); + final vp8 = codecsJson['vp8'] ?? false; + final h264 = codecsJson['h264'] ?? false; + final h265 = codecsJson['h265'] ?? false; + codecs.add(vp8); + codecs.add(h264); + codecs.add(h265); + } catch (e) { + debugPrint("Show Codec Preference err=$e"); + } + final visible = codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2]); + if (!visible) return []; + onChanged(String? value) async { + if (value == null) return; + await bind.sessionPeerOption( + id: id, name: 'codec-preference', value: value); + bind.sessionChangePreferCodec(id: id); + } + + TRadioMenu radio(String label, String value, bool enabled) { + return TRadioMenu( + child: Text(translate(label)), + value: value, + groupValue: groupValue, + onChanged: enabled ? onChanged : null); + } + + return [ + radio('Auto', 'auto', true), + if (isDesktop || codecs[0]) radio('VP8', 'vp8', codecs[0]), + radio('VP9', 'vp9', true), + if (isDesktop || codecs[1]) radio('H264', 'h264', codecs[1]), + if (isDesktop || codecs[2]) radio('H265', 'h265', codecs[2]), + ]; +} + +Future> toolbarDisplayToggle( + BuildContext context, String id, FFI ffi) async { + List v = []; + final ffiModel = ffi.ffiModel; + final pi = ffiModel.pi; + final perms = ffiModel.permissions; + + // show remote cursor + if (pi.platform != kPeerPlatformAndroid && + !ffi.canvasModel.cursorEmbedded && + !pi.is_wayland) { + final state = ShowRemoteCursorState.find(id); + final enabled = !ffiModel.viewOnly; + final option = 'show-remote-cursor'; + v.add(TToggleMenu( + child: Text(translate('Show remote cursor')), + value: state.value, + onChanged: enabled + ? (value) async { + if (value == null) return; + await bind.sessionToggleOption(id: id, value: option); + state.value = + bind.sessionGetToggleOptionSync(id: id, arg: option); + } + : null)); + } + // zoom cursor + final viewStyle = await bind.sessionGetViewStyle(id: id) ?? ''; + if (!isMobile && + pi.platform != kPeerPlatformAndroid && + viewStyle != kRemoteViewStyleOriginal) { + final option = 'zoom-cursor'; + final peerState = PeerBoolOption.find(id, option); + v.add(TToggleMenu( + child: Text(translate('Zoom cursor')), + value: peerState.value, + onChanged: (value) async { + if (value == null) return; + await bind.sessionToggleOption(id: id, value: option); + peerState.value = bind.sessionGetToggleOptionSync(id: id, arg: option); + }, + )); + } + // show quality monitor + final option = 'show-quality-monitor'; + v.add(TToggleMenu( + value: bind.sessionGetToggleOptionSync(id: id, arg: option), + onChanged: (value) async { + if (value == null) return; + await bind.sessionToggleOption(id: id, value: option); + ffi.qualityMonitorModel.checkShowQualityMonitor(id); + }, + child: Text(translate('Show quality monitor')))); + // mute + if (perms['audio'] != false) { + final option = 'disable-audio'; + final value = bind.sessionGetToggleOptionSync(id: id, arg: option); + v.add(TToggleMenu( + value: value, + onChanged: (value) { + if (value == null) return; + bind.sessionToggleOption(id: id, value: option); + }, + child: Text(translate('Mute')))); + } + // file copy and paste + if (Platform.isWindows && + pi.platform == kPeerPlatformWindows && + perms['file'] != false) { + final option = 'enable-file-transfer'; + final value = bind.sessionGetToggleOptionSync(id: id, arg: option); + v.add(TToggleMenu( + value: value, + onChanged: (value) { + if (value == null) return; + bind.sessionToggleOption(id: id, value: option); + }, + child: Text(translate('Allow file copy and paste')))); + } + // disable clipboard + if (ffiModel.keyboard && perms['clipboard'] != false) { + final enabled = !ffiModel.viewOnly; + final option = 'disable-clipboard'; + var value = bind.sessionGetToggleOptionSync(id: id, arg: option); + if (ffiModel.viewOnly) value = true; + v.add(TToggleMenu( + value: value, + onChanged: enabled + ? (value) { + if (value == null) return; + bind.sessionToggleOption(id: id, value: option); + } + : null, + child: Text(translate('Disable clipboard')))); + } + // lock after session end + if (ffiModel.keyboard) { + final option = 'lock-after-session-end'; + final value = bind.sessionGetToggleOptionSync(id: id, arg: option); + v.add(TToggleMenu( + value: value, + onChanged: (value) { + if (value == null) return; + bind.sessionToggleOption(id: id, value: option); + }, + child: Text(translate('Lock after session end')))); + } + // privacy mode + if (ffiModel.keyboard && pi.features.privacyMode) { + final option = 'privacy-mode'; + final rxValue = PrivacyModeState.find(id); + v.add(TToggleMenu( + value: rxValue.value, + onChanged: (value) { + if (value == null) return; + if (ffiModel.pi.currentDisplay != 0) { + msgBox(id, 'custom-nook-nocancel-hasclose', 'info', + 'Please switch to Display 1 first', '', ffi.dialogManager); + return; + } + bind.sessionToggleOption(id: id, value: option); + }, + child: Text(translate('Privacy mode')))); + } + // swap key + if (ffiModel.keyboard && + ((Platform.isMacOS && pi.platform != kPeerPlatformMacOS) || + (!Platform.isMacOS && pi.platform == kPeerPlatformMacOS))) { + final option = 'allow_swap_key'; + final value = bind.sessionGetToggleOptionSync(id: id, arg: option); + v.add(TToggleMenu( + value: value, + onChanged: (value) { + if (value == null) return; + bind.sessionToggleOption(id: id, value: option); + }, + child: Text(translate('Swap control-command key')))); + } + return v; +} diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 312f9bbed..bd026874b 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -77,15 +77,8 @@ class _RemotePageState extends State late FFI _ffi; void _initStates(String id) { - PrivacyModeState.init(id); - BlockInputState.init(id); - CurrentDisplayState.init(id); - KeyboardEnabledState.init(id); - ShowRemoteCursorState.init(id); - RemoteCursorMovedState.init(id); - final optZoomCursor = 'zoom-cursor'; - PeerBoolOption.init(id, optZoomCursor, () => false); - _zoomCursor = PeerBoolOption.find(id, optZoomCursor); + initSharedStates(id); + _zoomCursor = PeerBoolOption.find(id, 'zoom-cursor'); _showRemoteCursor = ShowRemoteCursorState.find(id); _keyboardEnabled = KeyboardEnabledState.find(id); _remoteCursorMoved = RemoteCursorMovedState.find(id); @@ -93,15 +86,6 @@ class _RemotePageState extends State _textureId = RxInt(-1); } - void _removeStates(String id) { - PrivacyModeState.delete(id); - BlockInputState.delete(id); - CurrentDisplayState.delete(id); - ShowRemoteCursorState.delete(id); - KeyboardEnabledState.delete(id); - RemoteCursorMovedState.delete(id); - } - @override void initState() { super.initState(); @@ -217,7 +201,7 @@ class _RemotePageState extends State } Get.delete(tag: widget.id); super.dispose(); - _removeStates(widget.id); + removeSharedStates(widget.id); } Widget buildBody(BuildContext context) { diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 9627b107b..f8a38c830 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -4,6 +4,7 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_hbb/common/widgets/toolbar.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/consts.dart'; @@ -31,7 +32,6 @@ class MenubarState { final kStoreKey = 'remoteMenubarState'; late RxBool show; late RxBool _pin; - RxString viewStyle = RxString(kRemoteViewStyleOriginal); MenubarState() { final s = bind.getLocalFlutterConfig(k: kStoreKey); @@ -456,7 +456,7 @@ class _RemoteMenubarState extends State { return Theme.of(context).copyWith( menuButtonTheme: MenuButtonThemeData( style: ButtonStyle( - minimumSize: MaterialStatePropertyAll(Size(64, 36)), + minimumSize: MaterialStatePropertyAll(Size(64, 32)), textStyle: MaterialStatePropertyAll( TextStyle(fontWeight: FontWeight.normal), ), @@ -637,229 +637,17 @@ class _ControlMenu extends StatelessWidget { color: _MenubarTheme.blueColor, hoverColor: _MenubarTheme.hoverBlueColor, ffi: ffi, - menuChildren: [ - requestElevation(), - ffi.ffiModel.pi.is_headless ? osAccount() : osPassword(), - transferFile(context), - tcpTunneling(context), - note(), - Divider(), - ctrlAltDel(), - restart(), - insertLock(), - blockUserInput(), - switchSides(), - refresh(), - ]); - } - - requestElevation() { - final visible = ffi.elevationModel.showRequestMenu; - if (!visible) return Offstage(); - return _MenuItemButton( - child: Text(translate('Request Elevation')), - ffi: ffi, - onPressed: () => showRequestElevationDialog(id, ffi.dialogManager)); - } - - osAccount() { - return _MenuItemButton( - child: Text(translate('OS Account')), - trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), - ffi: ffi, - onPressed: () => showSetOSAccount(id, ffi.dialogManager)); - } - - osPassword() { - return _MenuItemButton( - child: Text(translate('OS Password')), - trailingIcon: Transform.scale(scale: 0.8, child: Icon(Icons.edit)), - ffi: ffi, - onPressed: () => showSetOSPassword(id, false, ffi.dialogManager)); - } - - transferFile(BuildContext context) { - return _MenuItemButton( - child: Text(translate('Transfer File')), - ffi: ffi, - onPressed: () => connect(context, id, isFileTransfer: true)); - } - - tcpTunneling(BuildContext context) { - return _MenuItemButton( - child: Text(translate('TCP Tunneling')), - ffi: ffi, - onPressed: () => connect(context, id, isTcpTunneling: true)); - } - - note() { - final auditServer = bind.sessionGetAuditServerSync(id: id, typ: "conn"); - final visible = auditServer.isNotEmpty; - if (!visible) return Offstage(); - return _MenuItemButton( - child: Text(translate('Note')), - ffi: ffi, - onPressed: () => _showAuditDialog(id, ffi.dialogManager), - ); - } - - _showAuditDialog(String id, dialogManager) async { - final controller = TextEditingController(); - dialogManager.show((setState, close) { - submit() { - var text = controller.text.trim(); - if (text != '') { - bind.sessionSendNote(id: id, note: text); - } - close(); - } - - late final focusNode = FocusNode( - onKey: (FocusNode node, RawKeyEvent evt) { - if (evt.logicalKey.keyLabel == 'Enter') { - if (evt is RawKeyDownEvent) { - int pos = controller.selection.base.offset; - controller.text = - '${controller.text.substring(0, pos)}\n${controller.text.substring(pos)}'; - controller.selection = - TextSelection.fromPosition(TextPosition(offset: pos + 1)); - } - return KeyEventResult.handled; - } - if (evt.logicalKey.keyLabel == 'Esc') { - if (evt is RawKeyDownEvent) { - close(); - } - return KeyEventResult.handled; + menuChildren: toolbarControls(context, id, ffi).map((e) { + if (e.divider) { + return Divider(); } else { - return KeyEventResult.ignored; + return _MenuItemButton( + child: e.child, + onPressed: e.onPressed, + ffi: ffi, + trailingIcon: e.trailingIcon); } - }, - ); - - return CustomAlertDialog( - title: Text(translate('Note')), - content: SizedBox( - width: 250, - height: 120, - child: TextField( - autofocus: true, - keyboardType: TextInputType.multiline, - textInputAction: TextInputAction.newline, - decoration: const InputDecoration.collapsed( - hintText: 'input note here', - ), - maxLines: null, - maxLength: 256, - controller: controller, - focusNode: focusNode, - )), - actions: [ - dialogButton('Cancel', onPressed: close, isOutline: true), - dialogButton('OK', onPressed: submit) - ], - onSubmit: submit, - onCancel: close, - ); - }); - } - - ctrlAltDel() { - final viewOnly = ffi.ffiModel.viewOnly; - final pi = ffi.ffiModel.pi; - final visible = !viewOnly && - ffi.ffiModel.keyboard && - (pi.platform == kPeerPlatformLinux || pi.sasEnabled); - if (!visible) return Offstage(); - return _MenuItemButton( - child: Text('${translate("Insert")} Ctrl + Alt + Del'), - ffi: ffi, - onPressed: () => bind.sessionCtrlAltDel(id: id)); - } - - restart() { - final perms = ffi.ffiModel.permissions; - final pi = ffi.ffiModel.pi; - final visible = perms['restart'] != false && - (pi.platform == kPeerPlatformLinux || - pi.platform == kPeerPlatformWindows || - pi.platform == kPeerPlatformMacOS); - if (!visible) return Offstage(); - return _MenuItemButton( - child: Text(translate('Restart Remote Device')), - ffi: ffi, - onPressed: () => showRestartRemoteDevice(pi, id, ffi.dialogManager)); - } - - insertLock() { - final viewOnly = ffi.ffiModel.viewOnly; - final visible = !viewOnly && ffi.ffiModel.keyboard; - if (!visible) return Offstage(); - return _MenuItemButton( - child: Text(translate('Insert Lock')), - ffi: ffi, - onPressed: () => bind.sessionLockScreen(id: id)); - } - - blockUserInput() { - final pi = ffi.ffiModel.pi; - final visible = - ffi.ffiModel.keyboard && pi.platform == kPeerPlatformWindows; - if (!visible) return Offstage(); - return _MenuItemButton( - child: Obx(() => Text(translate( - '${BlockInputState.find(id).value ? 'Unb' : 'B'}lock user input'))), - ffi: ffi, - onPressed: () { - RxBool blockInput = BlockInputState.find(id); - bind.sessionToggleOption( - id: id, value: '${blockInput.value ? 'un' : ''}block-input'); - blockInput.value = !blockInput.value; - }); - } - - switchSides() { - final pi = ffi.ffiModel.pi; - final visible = ffi.ffiModel.keyboard && - pi.platform != kPeerPlatformAndroid && - pi.platform != kPeerPlatformMacOS && - version_cmp(pi.version, '1.2.0') >= 0; - if (!visible) return Offstage(); - return _MenuItemButton( - child: Text(translate('Switch Sides')), - ffi: ffi, - onPressed: () => _showConfirmSwitchSidesDialog(id, ffi.dialogManager)); - } - - void _showConfirmSwitchSidesDialog( - String id, OverlayDialogManager dialogManager) async { - dialogManager.show((setState, close) { - submit() async { - await bind.sessionSwitchSides(id: id); - closeConnection(id: id); - } - - return CustomAlertDialog( - content: msgboxContent('info', 'Switch Sides', - 'Please confirm if you want to share your desktop?'), - actions: [ - dialogButton('Cancel', onPressed: close, isOutline: true), - dialogButton('OK', onPressed: submit), - ], - onSubmit: submit, - onCancel: close, - ); - }); - } - - refresh() { - final pi = ffi.ffiModel.pi; - final visible = pi.version.isNotEmpty; - if (!visible) return Offstage(); - return _MenuItemButton( - child: Text(translate('Refresh')), - ffi: ffi, - onPressed: () => bind.sessionRefresh(id: id)); + }).toList()); } } @@ -891,6 +679,8 @@ class _DisplayMenuState extends State<_DisplayMenu> { PeerInfo get pi => widget.ffi.ffiModel.pi; FfiModel get ffiModel => widget.ffi.ffiModel; + FFI get ffi => widget.ffi; + String get id => widget.id; @override Widget build(BuildContext context) { @@ -909,30 +699,26 @@ class _DisplayMenuState extends State<_DisplayMenu> { codec(), resolutions(), Divider(), - showRemoteCursor(), - zoomCursor(), - showQualityMonitor(), - mute(), - fileCopyAndPaste(), - disableClipboard(), - lockAfterSessionEnd(), - privacyMode(), - swapKey(), + toggles(), ]); } adjustWindow() { - final visible = _isWindowCanBeAdjusted(); - if (!visible) return Offstage(); - return Column( - children: [ - _MenuItemButton( - child: Text(translate('Adjust Window')), - onPressed: _doAdjustWindow, - ffi: widget.ffi), - Divider(), - ], - ); + return futureBuilder( + future: _isWindowCanBeAdjusted(), + hasData: (data) { + final visible = data as bool; + if (!visible) return Offstage(); + return Column( + children: [ + _MenuItemButton( + child: Text(translate('Adjust Window')), + onPressed: _doAdjustWindow, + ffi: widget.ffi), + Divider(), + ], + ); + }); } _doAdjustWindow() async { @@ -1004,8 +790,9 @@ class _DisplayMenuState extends State<_DisplayMenu> { } } - _isWindowCanBeAdjusted() { - if (widget.state.viewStyle.value != kRemoteViewStyleOriginal) { + Future _isWindowCanBeAdjusted() async { + final viewStyle = await bind.sessionGetViewStyle(id: widget.id) ?? ''; + if (viewStyle != kRemoteViewStyleOriginal) { return false; } final remoteCount = RemoteCountState.find().value; @@ -1035,47 +822,34 @@ class _DisplayMenuState extends State<_DisplayMenu> { } viewStyle() { - return futureBuilder(future: () async { - final viewStyle = await bind.sessionGetViewStyle(id: widget.id) ?? ''; - widget.state.viewStyle.value = viewStyle; - return viewStyle; - }(), hasData: (data) { - final groupValue = data as String; - onChanged(String? value) async { - if (value == null) return; - await bind.sessionSetViewStyle(id: widget.id, value: value); - widget.state.viewStyle.value = value; - widget.ffi.canvasModel.updateViewStyle(); - } - - return Column(children: [ - _RadioMenuButton( - child: Text(translate('Scale original')), - value: kRemoteViewStyleOriginal, - groupValue: groupValue, - onChanged: onChanged, - ffi: widget.ffi, - ), - _RadioMenuButton( - child: Text(translate('Scale adaptive')), - value: kRemoteViewStyleAdaptive, - groupValue: groupValue, - onChanged: onChanged, - ffi: widget.ffi, - ), - Divider(), - ]); - }); + return futureBuilder( + future: toolbarViewStyle(context, widget.id, widget.ffi), + hasData: (data) { + final v = data as List>; + return Column(children: [ + ...v + .map((e) => _RadioMenuButton( + value: e.value, + groupValue: e.groupValue, + onChanged: e.onChanged, + child: e.child, + ffi: ffi)) + .toList(), + Divider(), + ]); + }); } scrollStyle() { - final visible = widget.state.viewStyle.value == kRemoteViewStyleOriginal; - if (!visible) return Offstage(); return futureBuilder(future: () async { + final viewStyle = await bind.sessionGetViewStyle(id: id) ?? ''; + final visible = viewStyle == kRemoteViewStyleOriginal; final scrollStyle = await bind.sessionGetScrollStyle(id: widget.id) ?? ''; - return scrollStyle; + return {'visible': visible, 'scrollStyle': scrollStyle}; }(), hasData: (data) { - final groupValue = data as String; + final visible = data['visible'] as bool; + if (!visible) return Offstage(); + final groupValue = data['scrollStyle'] as String; onChange(String? value) async { if (value == null) return; await bind.sessionSetScrollStyle(id: widget.id, value: value); @@ -1104,269 +878,44 @@ class _DisplayMenuState extends State<_DisplayMenu> { } imageQuality() { - return futureBuilder(future: () async { - final imageQuality = - await bind.sessionGetImageQuality(id: widget.id) ?? ''; - return imageQuality; - }(), hasData: (data) { - final groupValue = data as String; - onChanged(String? value) async { - if (value == null) return; - await bind.sessionSetImageQuality(id: widget.id, value: value); - } - - return _SubmenuButton( - ffi: widget.ffi, - child: Text(translate('Image Quality')), - menuChildren: [ - _RadioMenuButton( - child: Text(translate('Good image quality')), - value: kRemoteImageQualityBest, - groupValue: groupValue, - onChanged: onChanged, + return futureBuilder( + future: toolbarImageQuality(context, widget.id, widget.ffi), + hasData: (data) { + final v = data as List>; + return _SubmenuButton( ffi: widget.ffi, - ), - _RadioMenuButton( - child: Text(translate('Balanced')), - value: kRemoteImageQualityBalanced, - groupValue: groupValue, - onChanged: onChanged, - ffi: widget.ffi, - ), - _RadioMenuButton( - child: Text(translate('Optimize reaction time')), - value: kRemoteImageQualityLow, - groupValue: groupValue, - onChanged: onChanged, - ffi: widget.ffi, - ), - _RadioMenuButton( - child: Text(translate('Custom')), - value: kRemoteImageQualityCustom, - groupValue: groupValue, - onChanged: (value) { - onChanged(value); - _customImageQualityDialog(); - }, - ffi: widget.ffi, - ), - ], - ); - }); - } - - _customImageQualityDialog() async { - double qualityInitValue = 50; - double fpsInitValue = 30; - bool qualitySet = false; - bool fpsSet = false; - setCustomValues({double? quality, double? fps}) async { - if (quality != null) { - qualitySet = true; - await bind.sessionSetCustomImageQuality( - id: widget.id, value: quality.toInt()); - } - if (fps != null) { - fpsSet = true; - await bind.sessionSetCustomFps(id: widget.id, fps: fps.toInt()); - } - if (!qualitySet) { - qualitySet = true; - await bind.sessionSetCustomImageQuality( - id: widget.id, value: qualityInitValue.toInt()); - } - if (!fpsSet) { - fpsSet = true; - await bind.sessionSetCustomFps( - id: widget.id, fps: fpsInitValue.toInt()); - } - } - - final btnClose = dialogButton('Close', onPressed: () async { - await setCustomValues(); - widget.ffi.dialogManager.dismissAll(); - }); - - // quality - final quality = await bind.sessionGetCustomImageQuality(id: widget.id); - qualityInitValue = - quality != null && quality.isNotEmpty ? quality[0].toDouble() : 50.0; - const qualityMinValue = 10.0; - const qualityMaxValue = 100.0; - if (qualityInitValue < qualityMinValue) { - qualityInitValue = qualityMinValue; - } - if (qualityInitValue > qualityMaxValue) { - qualityInitValue = qualityMaxValue; - } - final RxDouble qualitySliderValue = RxDouble(qualityInitValue); - final debouncerQuality = Debouncer( - Duration(milliseconds: 1000), - onChanged: (double v) { - setCustomValues(quality: v); - }, - initialValue: qualityInitValue, - ); - final qualitySlider = Obx(() => Row( - children: [ - Slider( - value: qualitySliderValue.value, - min: qualityMinValue, - max: qualityMaxValue, - divisions: 18, - onChanged: (double value) { - qualitySliderValue.value = value; - debouncerQuality.value = value; - }, - ), - SizedBox( - width: 40, - child: Text( - '${qualitySliderValue.value.round()}%', - style: const TextStyle(fontSize: 15), - )), - SizedBox( - width: 50, - child: Text( - translate('Bitrate'), - style: const TextStyle(fontSize: 15), - )) - ], - )); - // fps - final fpsOption = - await bind.sessionGetOption(id: widget.id, arg: 'custom-fps'); - fpsInitValue = fpsOption == null ? 30 : double.tryParse(fpsOption) ?? 30; - if (fpsInitValue < 5 || fpsInitValue > 120) { - fpsInitValue = 30; - } - final RxDouble fpsSliderValue = RxDouble(fpsInitValue); - final debouncerFps = Debouncer( - Duration(milliseconds: 1000), - onChanged: (double v) { - setCustomValues(fps: v); - }, - initialValue: qualityInitValue, - ); - bool? direct; - try { - direct = ConnectionTypeState.find(widget.id).direct.value == - ConnectionType.strDirect; - } catch (_) {} - final fpsSlider = Offstage( - offstage: (await bind.mainIsUsingPublicServer() && direct != true) || - version_cmp(pi.version, '1.2.0') < 0, - child: Row( - children: [ - Obx((() => Slider( - value: fpsSliderValue.value, - min: 5, - max: 120, - divisions: 23, - onChanged: (double value) { - fpsSliderValue.value = value; - debouncerFps.value = value; - }, - ))), - SizedBox( - width: 40, - child: Obx(() => Text( - '${fpsSliderValue.value.round()}', - style: const TextStyle(fontSize: 15), - ))), - SizedBox( - width: 50, - child: Text( - translate('FPS'), - style: const TextStyle(fontSize: 15), - )) - ], - ), - ); - - final content = Column( - children: [qualitySlider, fpsSlider], - ); - msgBoxCommon( - widget.ffi.dialogManager, 'Custom Image Quality', content, [btnClose]); + child: Text(translate('Image Quality')), + menuChildren: v + .map((e) => _RadioMenuButton( + value: e.value, + groupValue: e.groupValue, + onChanged: e.onChanged, + child: e.child, + ffi: ffi)) + .toList(), + ); + }); } codec() { - return futureBuilder(future: () async { - final alternativeCodecs = - await bind.sessionAlternativeCodecs(id: widget.id); - final codecPreference = - await bind.sessionGetOption(id: widget.id, arg: 'codec-preference') ?? - ''; - return { - 'alternativeCodecs': alternativeCodecs, - 'codecPreference': codecPreference - }; - }(), hasData: (data) { - final List codecs = []; - try { - final Map codecsJson = jsonDecode(data['alternativeCodecs']); - final vp8 = codecsJson['vp8'] ?? false; - final h264 = codecsJson['h264'] ?? false; - final h265 = codecsJson['h265'] ?? false; - codecs.add(vp8); - codecs.add(h264); - codecs.add(h265); - } catch (e) { - debugPrint("Show Codec Preference err=$e"); - } - final visible = - codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2]); - if (!visible) return Offstage(); - final groupValue = data['codecPreference'] as String; - onChanged(String? value) async { - if (value == null) return; - await bind.sessionPeerOption( - id: widget.id, name: 'codec-preference', value: value); - bind.sessionChangePreferCodec(id: widget.id); - } + return futureBuilder( + future: toolbarCodec(context, id, ffi), + hasData: (data) { + final v = data as List>; + if (v.isEmpty) return Offstage(); - return _SubmenuButton( - ffi: widget.ffi, - child: Text(translate('Codec')), - menuChildren: [ - _RadioMenuButton( - child: Text(translate('Auto')), - value: 'auto', - groupValue: groupValue, - onChanged: onChanged, + return _SubmenuButton( ffi: widget.ffi, - ), - _RadioMenuButton( - child: Text(translate('VP8')), - value: 'vp8', - groupValue: groupValue, - onChanged: codecs[0] ? onChanged : null, - ffi: widget.ffi, - ), - _RadioMenuButton( - child: Text(translate('VP9')), - value: 'vp9', - groupValue: groupValue, - onChanged: onChanged, - ffi: widget.ffi, - ), - _RadioMenuButton( - child: Text(translate('H264')), - value: 'h264', - groupValue: groupValue, - onChanged: codecs[1] ? onChanged : null, - ffi: widget.ffi, - ), - _RadioMenuButton( - child: Text(translate('H265')), - value: 'h265', - groupValue: groupValue, - onChanged: codecs[2] ? onChanged : null, - ffi: widget.ffi, - ), - ]); - }); + child: Text(translate('Codec')), + menuChildren: v + .map((e) => _RadioMenuButton( + value: e.value, + groupValue: e.groupValue, + onChanged: e.onChanged, + child: e.child, + ffi: ffi)) + .toList()); + }); } resolutions() { @@ -1387,7 +936,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { Future.delayed(Duration(seconds: 3), () async { final display = ffiModel.display; if (w == display.width && h == display.height) { - if (_isWindowCanBeAdjusted()) { + if (await _isWindowCanBeAdjusted()) { _doAdjustWindow(); } } @@ -1409,169 +958,21 @@ class _DisplayMenuState extends State<_DisplayMenu> { child: Text(translate("Resolution"))); } - showRemoteCursor() { - if (pi.platform == kPeerPlatformAndroid) { - return Offstage(); - } - final visible = - !widget.ffi.canvasModel.cursorEmbedded && !ffiModel.pi.is_wayland; - if (!visible) return Offstage(); - final enabled = !ffiModel.viewOnly; - final state = ShowRemoteCursorState.find(widget.id); - final option = 'show-remote-cursor'; - return _CheckboxMenuButton( - value: state.value, - onChanged: enabled - ? (value) async { - if (value == null) return; - await bind.sessionToggleOption(id: widget.id, value: option); - state.value = - bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - } - : null, - ffi: widget.ffi, - child: Text(translate('Show remote cursor'))); - } - - zoomCursor() { - if (pi.platform == kPeerPlatformAndroid) { - return Offstage(); - } - final visible = widget.state.viewStyle.value != kRemoteViewStyleOriginal; - if (!visible) return Offstage(); - final option = 'zoom-cursor'; - final peerState = PeerBoolOption.find(widget.id, option); - return _CheckboxMenuButton( - value: peerState.value, - onChanged: (value) async { - if (value == null) return; - await bind.sessionToggleOption(id: widget.id, value: option); - peerState.value = - bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - }, - ffi: widget.ffi, - child: Text(translate('Zoom cursor'))); - } - - showQualityMonitor() { - final option = 'show-quality-monitor'; - final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - return _CheckboxMenuButton( - value: value, - onChanged: (value) async { - if (value == null) return; - await bind.sessionToggleOption(id: widget.id, value: option); - widget.ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); - }, - ffi: widget.ffi, - child: Text(translate('Show quality monitor'))); - } - - mute() { - final visible = perms['audio'] != false; - if (!visible) return Offstage(); - final option = 'disable-audio'; - final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - return _CheckboxMenuButton( - value: value, - onChanged: (value) { - if (value == null) return; - bind.sessionToggleOption(id: widget.id, value: option); - }, - ffi: widget.ffi, - child: Text(translate('Mute'))); - } - - fileCopyAndPaste() { - final visible = Platform.isWindows && - pi.platform == kPeerPlatformWindows && - perms['file'] != false; - if (!visible) return Offstage(); - final option = 'enable-file-transfer'; - final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - return _CheckboxMenuButton( - value: value, - onChanged: (value) { - if (value == null) return; - bind.sessionToggleOption(id: widget.id, value: option); - }, - ffi: widget.ffi, - child: Text(translate('Allow file copy and paste'))); - } - - disableClipboard() { - final visible = ffiModel.keyboard && perms['clipboard'] != false; - if (!visible) return Offstage(); - final enabled = !ffiModel.viewOnly; - final option = 'disable-clipboard'; - var value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - if (ffiModel.viewOnly) value = true; - return _CheckboxMenuButton( - value: value, - onChanged: enabled - ? (value) { - if (value == null) return; - bind.sessionToggleOption(id: widget.id, value: option); - } - : null, - ffi: widget.ffi, - child: Text(translate('Disable clipboard'))); - } - - lockAfterSessionEnd() { - if (!ffiModel.keyboard) return Offstage(); - final option = 'lock-after-session-end'; - final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - return _CheckboxMenuButton( - value: value, - onChanged: (value) { - if (value == null) return; - bind.sessionToggleOption(id: widget.id, value: option); - }, - ffi: widget.ffi, - child: Text(translate('Lock after session end'))); - } - - privacyMode() { - bool visible = ffiModel.keyboard && pi.features.privacyMode; - if (!visible) return Offstage(); - final option = 'privacy-mode'; - final rxValue = PrivacyModeState.find(widget.id); - return _CheckboxMenuButton( - value: rxValue.value, - onChanged: (value) { - if (value == null) return; - if (ffiModel.pi.currentDisplay != 0) { - msgBox( - widget.id, - 'custom-nook-nocancel-hasclose', - 'info', - 'Please switch to Display 1 first', - '', - widget.ffi.dialogManager); - return; - } - bind.sessionToggleOption(id: widget.id, value: option); - }, - ffi: widget.ffi, - child: Text(translate('Privacy mode'))); - } - - swapKey() { - final visible = ffiModel.keyboard && - ((Platform.isMacOS && pi.platform != kPeerPlatformMacOS) || - (!Platform.isMacOS && pi.platform == kPeerPlatformMacOS)); - if (!visible) return Offstage(); - final option = 'allow_swap_key'; - final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - return _CheckboxMenuButton( - value: value, - onChanged: (value) { - if (value == null) return; - bind.sessionToggleOption(id: widget.id, value: option); - }, - ffi: widget.ffi, - child: Text(translate('Swap control-command key'))); + toggles() { + return futureBuilder( + future: toolbarDisplayToggle(context, id, ffi), + hasData: (data) { + final v = data as List; + if (v.isEmpty) return Offstage(); + return Column( + children: v + .map((e) => _CheckboxMenuButton( + value: e.value, + onChanged: e.onChanged, + child: e.child, + ffi: ffi)) + .toList()); + }); } } @@ -1799,19 +1200,22 @@ class _RecordMenu extends StatelessWidget { @override Widget build(BuildContext context) { var ffi = Provider.of(context); - final visible = ffi.permissions['recording'] != false; + var recordingModel = Provider.of(context); + final visible = + recordingModel.start || ffi.permissions['recording'] != false; if (!visible) return Offstage(); - return Consumer( - builder: (context, value, child) => _IconMenuButton( - assetName: 'assets/rec.svg', - tooltip: - value.start ? 'Stop session recording' : 'Start session recording', - onPressed: () => value.toggle(), - color: value.start ? _MenubarTheme.redColor : _MenubarTheme.blueColor, - hoverColor: value.start - ? _MenubarTheme.hoverRedColor - : _MenubarTheme.hoverBlueColor, - ), + return _IconMenuButton( + assetName: 'assets/rec.svg', + tooltip: recordingModel.start + ? 'Stop session recording' + : 'Start session recording', + onPressed: () => recordingModel.toggle(), + color: recordingModel.start + ? _MenubarTheme.redColor + : _MenubarTheme.blueColor, + hoverColor: recordingModel.start + ? _MenubarTheme.hoverRedColor + : _MenubarTheme.hoverBlueColor, ); } } diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 04852c98c..32c736dba 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -4,10 +4,13 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_hbb/common/shared_state.dart'; +import 'package:flutter_hbb/common/widgets/toolbar.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/mobile/widgets/gesture_help.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; +import 'package:get/get.dart'; import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; import 'package:provider/provider.dart'; import 'package:wakelock/wakelock.dart'; @@ -69,6 +72,7 @@ class _RemotePageState extends State { keyboardSubscription = keyboardVisibilityController.onChange.listen(onSoftKeyboardChanged); _blockableOverlayState.applyFfi(gFFI); + initSharedStates(widget.id); } @override @@ -85,6 +89,7 @@ class _RemotePageState extends State { overlays: SystemUiOverlay.values); Wakelock.disable(); keyboardSubscription.cancel(); + removeSharedStates(widget.id); super.dispose(); } @@ -543,150 +548,21 @@ class _RemotePageState extends State { final size = MediaQuery.of(context).size; final x = 120.0; final y = size.height; - final more = >[]; - final pi = gFFI.ffiModel.pi; - final perms = gFFI.ffiModel.permissions; - if (pi.version.isNotEmpty) { - more.add(PopupMenuItem( - child: Text(translate('Refresh')), value: 'refresh')); - } - if (gFFI.ffiModel.pi.is_headless) { - more.add( - PopupMenuItem( - child: Row( - children: ([ - Text(translate('OS Account')), - TextButton( - style: flatButtonStyle, - onPressed: () { - showSetOSAccount(id, gFFI.dialogManager); - }, - child: Icon(Icons.edit, color: MyTheme.accent), - ) - ])), - value: 'enter_os_account'), - ); - } else { - more.add( - PopupMenuItem( - child: Row( - children: ([ - Text(translate('OS Password')), - TextButton( - style: flatButtonStyle, - onPressed: () { - showSetOSPassword(id, false, gFFI.dialogManager); - }, - child: Icon(Icons.edit, color: MyTheme.accent), - ) - ])), - value: 'enter_os_password'), - ); - } - if (!isWebDesktop) { - if (perms['keyboard'] != false && perms['clipboard'] != false) { - more.add(PopupMenuItem( - child: Text(translate('Paste')), value: 'paste')); - } - more.add(PopupMenuItem( - child: Text(translate('Reset canvas')), value: 'reset_canvas')); - } - if (perms['keyboard'] != false) { - // * Currently mobile does not enable map mode - // more.add(PopupMenuItem( - // child: Text(translate('Physical Keyboard Input Mode')), - // value: 'input-mode')); - if (pi.platform == kPeerPlatformLinux || pi.sasEnabled) { - more.add(PopupMenuItem( - child: Text('${translate('Insert')} Ctrl + Alt + Del'), - value: 'cad')); - } - more.add(PopupMenuItem( - child: Text(translate('Insert Lock')), value: 'lock')); - if (pi.platform == kPeerPlatformWindows && - await bind.sessionGetToggleOption(id: id, arg: 'privacy-mode') != - true) { - more.add(PopupMenuItem( - child: Text(translate( - '${gFFI.ffiModel.inputBlocked ? 'Unb' : 'B'}lock user input')), - value: 'block-input')); - } - } - if (perms["restart"] != false && - (pi.platform == kPeerPlatformLinux || - pi.platform == kPeerPlatformWindows || - pi.platform == kPeerPlatformMacOS)) { - more.add(PopupMenuItem( - child: Text(translate('Restart Remote Device')), value: 'restart')); - } - // Currently only support VP9 - if (gFFI.recordingModel.start || - (perms["recording"] != false && - gFFI.qualityMonitorModel.data.codecFormat == "VP9")) { - more.add(PopupMenuItem( - child: Row( - children: [ - Text(translate(gFFI.recordingModel.start - ? 'Stop session recording' - : 'Start session recording')), - Padding( - padding: EdgeInsets.only(left: 12), - child: Icon( - gFFI.recordingModel.start - ? Icons.pause_circle_filled - : Icons.videocam_outlined, - color: MyTheme.accent), - ) - ], - ), - value: 'record')); - } + final menus = toolbarControls(context, id, gFFI); + final more = menus + .asMap() + .entries + .map((e) => PopupMenuItem(child: e.value.child, value: e.key)) + .toList(); () async { - var value = await showMenu( + var index = await showMenu( context: context, position: RelativeRect.fromLTRB(x, y, x, y), items: more, elevation: 8, ); - if (value == 'cad') { - bind.sessionCtrlAltDel(id: widget.id); - // * Currently mobile does not enable map mode - // } else if (value == 'input-mode') { - // changePhysicalKeyboardInputMode(); - } else if (value == 'lock') { - bind.sessionLockScreen(id: widget.id); - } else if (value == 'block-input') { - bind.sessionToggleOption( - id: widget.id, - value: '${gFFI.ffiModel.inputBlocked ? 'un' : ''}block-input'); - gFFI.ffiModel.inputBlocked = !gFFI.ffiModel.inputBlocked; - } else if (value == 'refresh') { - bind.sessionRefresh(id: widget.id); - } else if (value == 'paste') { - () async { - ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain); - if (data != null && data.text != null) { - bind.sessionInputString(id: widget.id, value: data.text ?? ""); - } - }(); - } else if (value == 'enter_os_password') { - // FIXME: - // null means no session of id - // empty string means no password - var password = await bind.sessionGetOption(id: id, arg: 'os-password'); - if (password != null) { - bind.sessionInputOsPassword(id: widget.id, value: password); - } else { - showSetOSPassword(id, true, gFFI.dialogManager); - } - } else if (value == 'enter_os_account') { - showSetOSAccount(id, gFFI.dialogManager); - } else if (value == 'reset_canvas') { - gFFI.cursorModel.reset(); - } else if (value == 'restart') { - showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager); - } else if (value == 'record') { - gFFI.recordingModel.toggle(); + if (index != null && index < menus.length) { + menus[index].onPressed.call(); } }(); } @@ -941,14 +817,6 @@ class CursorPaint extends StatelessWidget { void showOptions( BuildContext context, String id, OverlayDialogManager dialogManager) async { - String quality = - await bind.sessionGetImageQuality(id: id) ?? kRemoteImageQualityBalanced; - if (quality == '') quality = kRemoteImageQualityBalanced; - String codec = - await bind.sessionGetOption(id: id, arg: 'codec-preference') ?? 'auto'; - if (codec == '') codec = 'auto'; - String viewStyle = await bind.sessionGetViewStyle(id: id) ?? ''; - var displays = []; final pi = gFFI.ffiModel.pi; final image = gFFI.ffiModel.getConnectionImage(); @@ -991,107 +859,61 @@ void showOptions( if (displays.isNotEmpty) { displays.add(const Divider(color: MyTheme.border)); } - final perms = gFFI.ffiModel.permissions; - final hasHwcodec = bind.mainHasHwcodec(); - final List codecs = []; - try { - final Map codecsJson = - jsonDecode(await bind.sessionAlternativeCodecs(id: id)); - final vp8 = codecsJson['vp8'] ?? false; - final h264 = codecsJson['h264'] ?? false; - final h265 = codecsJson['h265'] ?? false; - codecs.add(vp8); - codecs.add(h264); - codecs.add(h265); - } catch (e) { - debugPrint("Show Codec Preference err=$e"); - } + + List> viewStyleRadios = + await toolbarViewStyle(context, id, gFFI); + List> imageQualityRadios = + await toolbarImageQuality(context, id, gFFI); + List> codecRadios = await toolbarCodec(context, id, gFFI); + List displayToggles = + await toolbarDisplayToggle(context, id, gFFI); dialogManager.show((setState, close) { - final more = []; - if (perms['audio'] != false) { - more.add(getToggle(id, setState, 'disable-audio', 'Mute')); - } - if (perms['keyboard'] != false) { - if (perms['clipboard'] != false) { - more.add( - getToggle(id, setState, 'disable-clipboard', 'Disable clipboard')); - } - more.add(getToggle( - id, setState, 'lock-after-session-end', 'Lock after session end')); - if (pi.platform == kPeerPlatformWindows) { - more.add(getToggle(id, setState, 'privacy-mode', 'Privacy mode')); - } - } - setQuality(String? value) { - if (value == null) return; - setState(() { - quality = value; - bind.sessionSetImageQuality(id: id, value: value); - }); - } - - setViewStyle(String? value) { - if (value == null) return; - setState(() { - viewStyle = value; - bind - .sessionSetViewStyle(id: id, value: value) - .then((_) => gFFI.canvasModel.updateViewStyle()); - }); - } - - setCodec(String? value) { - if (value == null) return; - setState(() { - codec = value; - bind - .sessionPeerOption(id: id, name: "codec-preference", value: value) - .then((_) => bind.sessionChangePreferCodec(id: id)); - }); - } - + var viewStyle = + (viewStyleRadios.isNotEmpty ? viewStyleRadios[0].groupValue : '').obs; + var imageQuality = + (imageQualityRadios.isNotEmpty ? imageQualityRadios[0].groupValue : '') + .obs; + var codec = (codecRadios.isNotEmpty ? codecRadios[0].groupValue : '').obs; final radios = [ - getRadio( - 'Scale original', kRemoteViewStyleOriginal, viewStyle, setViewStyle), - getRadio( - 'Scale adaptive', kRemoteViewStyleAdaptive, viewStyle, setViewStyle), + for (var e in viewStyleRadios) + Obx(() => getRadio(e.child, e.value, viewStyle.value, (v) { + e.onChanged?.call(v); + if (v != null) viewStyle.value = v; + })), const Divider(color: MyTheme.border), - getRadio( - 'Good image quality', kRemoteImageQualityBest, quality, setQuality), - getRadio('Balanced', kRemoteImageQualityBalanced, quality, setQuality), - getRadio('Optimize reaction time', kRemoteImageQualityLow, quality, - setQuality), - const Divider(color: MyTheme.border) + for (var e in imageQualityRadios) + Obx(() => getRadio(e.child, e.value, imageQuality.value, (v) { + e.onChanged?.call(v); + if (v != null) imageQuality.value = v; + })), + const Divider(color: MyTheme.border), + for (var e in codecRadios) + Obx(() => getRadio(e.child, e.value, codec.value, (v) { + e.onChanged?.call(v); + if (v != null) codec.value = v; + })), + if (codecRadios.isNotEmpty) const Divider(color: MyTheme.border), ]; - - if (codecs.length == 3 && (codecs[0] || codecs[1] || codecs[2])) { - radios.add(getRadio(translate('Auto'), 'auto', codec, setCodec)); - if (codecs[0]) { - radios.add(getRadio('VP8', 'vp8', codec, setCodec)); - } - radios.add(getRadio('VP9', 'vp9', codec, setCodec)); - if (codecs[1]) { - radios.add(getRadio('H264', 'h264', codec, setCodec)); - } - if (codecs[2]) { - radios.add(getRadio('H265', 'h265', codec, setCodec)); - } - radios.add(const Divider(color: MyTheme.border)); - } - - final toggles = [ - getToggle(id, setState, 'show-quality-monitor', 'Show quality monitor'), - ]; - if (!gFFI.canvasModel.cursorEmbedded && !pi.is_wayland) { - toggles.insert(0, - getToggle(id, setState, 'show-remote-cursor', 'Show remote cursor')); - } + final rxToggleValues = displayToggles.map((e) => e.value.obs).toList(); + final toggles = displayToggles + .asMap() + .entries + .map((e) => Obx(() => CheckboxListTile( + contentPadding: EdgeInsets.zero, + visualDensity: VisualDensity.compact, + value: rxToggleValues[e.key].value, + onChanged: (v) { + e.value.onChanged?.call(v); + if (v != null) rxToggleValues[e.key].value = v; + }, + title: e.value.child))) + .toList(); return CustomAlertDialog( content: Column( mainAxisSize: MainAxisSize.min, - children: displays + radios + toggles + more), + children: displays + radios + toggles), ); }, clickMaskDismiss: true, backDismiss: true); } diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 96038f983..ff7c9e240 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -504,13 +504,13 @@ void showLanguageSettings(OverlayDialogManager dialogManager) async { return CustomAlertDialog( content: Column( children: [ - getRadio('Default', '', lang, setLang), + getRadio(Text(translate('Default')), '', lang, setLang), Divider(color: MyTheme.border), ] + langs.map((e) { final key = e[0] as String; final name = e[1] as String; - return getRadio(name, key, lang, setLang); + return getRadio(Text(translate(name)), key, lang, setLang); }).toList(), ), ); @@ -536,9 +536,11 @@ void showThemeSettings(OverlayDialogManager dialogManager) async { return CustomAlertDialog( content: Column(children: [ - getRadio('Light', ThemeMode.light, themeMode, setTheme), - getRadio('Dark', ThemeMode.dark, themeMode, setTheme), - getRadio('Follow System', ThemeMode.system, themeMode, setTheme) + getRadio( + Text(translate('Light')), ThemeMode.light, themeMode, setTheme), + getRadio(Text(translate('Dark')), ThemeMode.dark, themeMode, setTheme), + getRadio(Text(translate('Follow System')), ThemeMode.system, themeMode, + setTheme) ]), ); }, backDismiss: true, clickMaskDismiss: true); From bb128e2dd8bd7c14ee879bb93c6650f4b96e65d1 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Thu, 13 Apr 2023 00:51:46 -0700 Subject: [PATCH 289/366] Update Italian language --- src/lang/it.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 7181ffab1..2e0d474cc 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -3,7 +3,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ ("Status", "Stato"), ("Your Desktop", "Questo desktop"), - ("desk_tip", "Puoi accedere a questo desktop usando l'ID e la password riportati qui sotto."), + ("desk_tip", "Puoi accedere a questo desktop usando l'ID e la password indicati qui sotto."), ("Password", "Password"), ("Ready", "Pronto"), ("Established", "Stabilita"), @@ -29,20 +29,20 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable TCP Tunneling", "Abilita tunnel TCP"), ("IP Whitelisting", "IP autorizzati"), ("ID/Relay Server", "Server ID/Relay"), - ("Import Server Config", "Importa configurazione server"), - ("Export Server Config", "Esporta configurazione server"), - ("Import server configuration successfully", "Configurazione server importata correttamente"), - ("Export server configuration successfully", "Configurazione Server esportata correttamente"), + ("Import Server Config", "Importa configurazione server dagli appunti"), + ("Export Server Config", "Esporta configurazione server negli appunti"), + ("Import server configuration successfully", "Configurazione server importata completata"), + ("Export server configuration successfully", "Configurazione Server esportata completata"), ("Invalid server configuration", "Configurazione server non valida"), ("Clipboard is empty", "Gli appunti sono vuoti"), ("Stop service", "Arresta servizio"), ("Change ID", "Cambia ID"), ("Your new ID", "Il nuovo ID"), - ("length %min% to %max%", "da lunghezza %min% a %max%"), + ("length %min% to %max%", "lunghezza da %min% a %max%"), ("starts with a letter", "inizia con una lettera"), ("allowed characters", "caratteri consentiti"), ("id_change_tip", "Puoi usare solo i caratteri a-z, A-Z, 0-9 e _ (sottolineato).\nIl primo carattere deve essere a-z o A-Z.\nLa lunghezza deve essere fra 6 e 16 caratteri."), - ("Website", "Sito web"), + ("Website", "Sito web programma"), ("About", "Info programma"), ("Slogan_tip", "Realizzato con il cuore in questo mondo caotico!"), ("Privacy Statement", "Informativa sulla privacy"), @@ -123,9 +123,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Scrollbar", "Barra scorrimento"), ("ScrollAuto", "Scorri automaticamente"), ("Good image quality", "Qualità immagine buona"), - ("Balanced", "Bilanciato"), + ("Balanced", "Bilanciata qualità/velocità"), ("Optimize reaction time", "Ottimizza tempo reazione"), - ("Custom", "Personalizzato"), + ("Custom", "Qualità personalizzata"), ("Show remote cursor", "Visualizza cursore remoto"), ("Show quality monitor", "Visualizza qualità video"), ("Disable clipboard", "Disabilita appunti"), @@ -146,9 +146,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Set Password", "Imposta password"), ("OS Password", "Password sistema operativo"), ("install_tip", "A causa del controllo account uUtente (UAC), RustDesk potrebbe non funzionare correttamente come desktop remoto.\nPer evitare questo problema, fai clic sul tasto qui sotto per installare RustDesk a livello di sistema."), - ("Click to upgrade", "Fai clic per aggiornare"), - ("Click to download", "Fai clic per il download"), - ("Click to update", "Fai clic per aggiornare"), + ("Click to upgrade", "Aggiorna"), + ("Click to download", "Download"), + ("Click to update", "Aggiorna"), ("Configure", "Configura"), ("config_acc", "Per controllare il desktop dall'esterno, devi fornire a RustDesk il permesso 'Accessibilità'."), ("config_screen", "Per controllare il desktop dall'esterno, devi fornire a RustDesk il permesso 'Registrazione schermo'."), @@ -318,7 +318,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Map mode", "Modalità mappa"), ("Translate mode", "Modalità traduzione"), ("Use permanent password", "Usa password permanente"), - ("Use both passwords", "Usa entrambe le password"), + ("Use both passwords", "Usa password monouso e permanente"), ("Set permanent password", "Imposta password permanente"), ("Enable Remote Restart", "Abilita riavvio da remoto"), ("Allow remote restart", "Consenti riavvio da remoto"), @@ -361,7 +361,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Proxy", "Proxy"), ("Apply", "Applica"), ("Disconnect all devices?", "Vuoi disconnettere tutti i dispositivi?"), - ("Clear", "Pulisci"), + ("Clear", "Azzera"), ("Audio Input Device", "Dispositivo ingresso audio"), ("Deny remote access", "Nega accesso remoto"), ("Use IP Whitelisting", "Usa elenco IP autorizzati"), @@ -445,11 +445,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Display", "Visualizzazione"), ("Default View Style", "Stile visualizzazione predefinito"), ("Default Scroll Style", "Stile scorrimento predefinito"), - ("Default Image Quality", "Qualitài immagine predefinita"), + ("Default Image Quality", "Qualità immagine predefinita"), ("Default Codec", "Codec predefinito"), ("Bitrate", "Bitrate"), ("FPS", "FPS"), - ("Auto", "Automatico"), + ("Auto", "Codec automatico"), ("Other Default Options", "Altre opzioni predefinite"), ("Voice call", "Chiamata vocale"), ("Text chat", "Chat testuale"), @@ -491,6 +491,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Installa Xorg."), ("no_desktop_title_tip", "Non c'è nessun desktop disponibile."), ("no_desktop_text_tip", "Installa il desktop GNOME."), - ("No need to elevate", ""), ].iter().cloned().collect(); } From a692038991c1360928c21af6219f4030083342f3 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 13 Apr 2023 16:06:46 +0800 Subject: [PATCH 290/366] fix #4037 --- flutter/lib/desktop/pages/desktop_setting_page.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index fe5b2fbf4..93fffe3da 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -301,7 +301,7 @@ class _GeneralState extends State<_General> { Widget audio(BuildContext context) { String getDefault() { - if (Platform.isWindows) return 'System Sound'; + if (Platform.isWindows) return translate('System Sound'); return ''; } @@ -322,7 +322,7 @@ class _GeneralState extends State<_General> { return futureBuilder(future: () async { List devices = (await bind.mainGetSoundInputs()).toList(); if (Platform.isWindows) { - devices.insert(0, 'System Sound'); + devices.insert(0, translate('System Sound')); } String current = await getValue(); return {'devices': devices, 'current': current}; @@ -415,7 +415,7 @@ class _GeneralState extends State<_General> { List keys = langsMap.keys.toList(); List values = langsMap.values.toList(); keys.insert(0, ''); - values.insert(0, 'Default'); + values.insert(0, translate('Default')); String currentKey = data['lang']!; if (!keys.contains(currentKey)) { currentKey = ''; From 35f5af94c4ac3897b4053305824283492ebc9ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=85=C3=BC?= <53787985+LaiYueTing@users.noreply.github.com> Date: Thu, 13 Apr 2023 18:09:00 +0800 Subject: [PATCH 291/366] Update tw.rs --- src/lang/tw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 47d43167f..dadc6918b 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -491,6 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "請安裝 Xorg"), ("no_desktop_title_tip", "沒有可用的桌面"), ("no_desktop_text_tip", "請安裝 GNOME 桌面"), - ("No need to elevate", ""), + ("No need to elevate", "不需要提升權限"), ].iter().cloned().collect(); } From 7b6f6bc1b3191d372caa7d60ae3f665cfa4ded7d Mon Sep 17 00:00:00 2001 From: rustdesk Date: Thu, 13 Apr 2023 18:18:09 +0800 Subject: [PATCH 292/366] revert Auto translation in IT --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 2e0d474cc..4f1f10e7e 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -449,7 +449,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Default Codec", "Codec predefinito"), ("Bitrate", "Bitrate"), ("FPS", "FPS"), - ("Auto", "Codec automatico"), + ("Auto", "Automatico"), ("Other Default Options", "Altre opzioni predefinite"), ("Voice call", "Chiamata vocale"), ("Text chat", "Chat testuale"), From 9eaf745f18a2fdbd8d5205c4035184fc6b0fe529 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 13 Apr 2023 20:23:30 +0800 Subject: [PATCH 293/366] fix audio encode Signed-off-by: 21pages --- src/server/audio_service.rs | 50 +++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index 3d2c226c5..d05b349e8 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -109,6 +109,7 @@ mod cpal_impl { lazy_static::lazy_static! { static ref HOST: Host = cpal::default_host(); + static ref INPUT_BUFFER: Arc>> = Default::default(); } #[derive(Default)] @@ -242,18 +243,31 @@ mod cpal_impl { LowDelay, )?; let channels = config.channels(); + // https://www.opus-codec.org/docs/html_api/group__opusencoder.html#gace941e4ef26ed844879fde342ffbe546 + // https://chromium.googlesource.com/chromium/deps/opus/+/1.1.1/include/opus.h + let encode_len = sample_rate as usize * channels as usize / 100; // 10 ms + INPUT_BUFFER.lock().unwrap().clear(); + let mut send_input_stream = move || { + let mut lock = INPUT_BUFFER.lock().unwrap(); + while lock.len() >= encode_len { + let frame: Vec = lock.drain(0..encode_len).collect(); + send( + &frame, + sample_rate_0, + sample_rate, + channels, + &mut encoder, + &sp, + ); + } + }; + let stream = match config.sample_format() { cpal::SampleFormat::F32 => device.build_input_stream( &config.into(), move |data, _: &_| { - send( - data, - sample_rate_0, - sample_rate, - channels, - &mut encoder, - &sp, - ); + INPUT_BUFFER.lock().unwrap().extend(data); + send_input_stream(); }, err_fn, )?, @@ -261,14 +275,8 @@ mod cpal_impl { &config.into(), move |data: &[i16], _: &_| { let buffer: Vec<_> = data.iter().map(|s| cpal::Sample::to_f32(s)).collect(); - send( - &buffer, - sample_rate_0, - sample_rate, - channels, - &mut encoder, - &sp, - ); + INPUT_BUFFER.lock().unwrap().extend(buffer); + send_input_stream(); }, err_fn, )?, @@ -276,14 +284,8 @@ mod cpal_impl { &config.into(), move |data: &[u16], _: &_| { let buffer: Vec<_> = data.iter().map(|s| cpal::Sample::to_f32(s)).collect(); - send( - &buffer, - sample_rate_0, - sample_rate, - channels, - &mut encoder, - &sp, - ); + INPUT_BUFFER.lock().unwrap().extend(buffer); + send_input_stream(); }, err_fn, )?, From e35011da987199af69a3aaf1b04c53ecbbd540d4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 13 Apr 2023 22:39:41 +0800 Subject: [PATCH 294/366] add deps for wayland Signed-off-by: fufesou --- res/PKGBUILD | 2 +- res/rpm-flutter-suse.spec | 2 +- res/rpm-flutter.spec | 2 +- res/rpm-suse.spec | 2 +- res/rpm.spec | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/res/PKGBUILD b/res/PKGBUILD index 2b520332e..559f1c025 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -7,7 +7,7 @@ arch=('x86_64') url="" license=('AGPL-3.0') groups=() -depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3' 'pam') +depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3' 'pam' 'gst-plugins-base' 'gst-plugin-pipewire') makedepends=() checkdepends=() optdepends=() diff --git a/res/rpm-flutter-suse.spec b/res/rpm-flutter-suse.spec index 93cbc548d..cdee3d901 100644 --- a/res/rpm-flutter-suse.spec +++ b/res/rpm-flutter-suse.spec @@ -3,7 +3,7 @@ Version: 1.2.0 Release: 0 Summary: RPM package License: GPL-3.0 -Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils curl libXtst6 libappindicator-gtk3 libvdpau1 libva2 pam +Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils curl libXtst6 libappindicator-gtk3 libvdpau1 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit) %description diff --git a/res/rpm-flutter.spec b/res/rpm-flutter.spec index fb00ee121..d81bb4b89 100644 --- a/res/rpm-flutter.spec +++ b/res/rpm-flutter.spec @@ -3,7 +3,7 @@ Version: 1.2.0 Release: 0 Summary: RPM package License: GPL-3.0 -Requires: gtk3 libxcb libxdo libXfixes alsa-lib curl libappindicator-gtk3 libvdpau libva pam +Requires: gtk3 libxcb libxdo libXfixes alsa-lib curl libappindicator-gtk3 libvdpau libva pam gstreamer1-plugins-base Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libflutter_custom_cursor_plugin.so()(64bit), libflutter_linux_gtk.so()(64bit), libscreen_retriever_plugin.so()(64bit), libtray_manager_plugin.so()(64bit), liburl_launcher_linux_plugin.so()(64bit), libwindow_manager_plugin.so()(64bit), libwindow_size_plugin.so()(64bit), libtexture_rgba_renderer_plugin.so()(64bit) %description diff --git a/res/rpm-suse.spec b/res/rpm-suse.spec index 60b9a6df6..496e47b19 100644 --- a/res/rpm-suse.spec +++ b/res/rpm-suse.spec @@ -3,7 +3,7 @@ Version: 1.1.9 Release: 0 Summary: RPM package License: GPL-3.0 -Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils curl libXtst6 libayatana-appindicator3-1 libvdpau1 libva2 pam +Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils curl libXtst6 libayatana-appindicator3-1 libvdpau1 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire %description The best open-source remote desktop client software, written in Rust. diff --git a/res/rpm.spec b/res/rpm.spec index 3a1b998c3..988b8270b 100644 --- a/res/rpm.spec +++ b/res/rpm.spec @@ -3,7 +3,7 @@ Version: 1.2.0 Release: 0 Summary: RPM package License: GPL-3.0 -Requires: gtk3 libxcb libxdo libXfixes alsa-lib curl libappindicator libvdpau1 libva2 pam +Requires: gtk3 libxcb libxdo libXfixes alsa-lib curl libappindicator libvdpau1 libva2 pam gstreamer1-plugins-base %description The best open-source remote desktop client software, written in Rust. From c6820153ffa8f995021f49ab83958e1f78a038a3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 14 Apr 2023 09:53:51 +0800 Subject: [PATCH 295/366] fix build archlinux Signed-off-by: fufesou --- res/PKGBUILD | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/PKGBUILD b/res/PKGBUILD index 559f1c025..4ffbc4eb2 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -7,7 +7,7 @@ arch=('x86_64') url="" license=('AGPL-3.0') groups=() -depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3' 'pam' 'gst-plugins-base' 'gst-plugin-pipewire') +depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3') makedepends=() checkdepends=() optdepends=() @@ -22,6 +22,7 @@ noextract=() md5sums=() #generate with 'makepkg -g' package() { + pacman -S --noconfirm pam gst-plugins-base gst-plugin-pipewire if [[ ${FLUTTER} ]]; then mkdir -p "${pkgdir}/usr/lib/rustdesk" && cp -r ${HBB}/flutter/build/linux/x64/release/bundle/* -t "${pkgdir}/usr/lib/rustdesk" fi From 9e3b76b399202b7fd5158a64b6dcfa16baf89604 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 14 Apr 2023 11:03:39 +0800 Subject: [PATCH 296/366] ignore errors on installing gst Signed-off-by: fufesou --- res/PKGBUILD | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/res/PKGBUILD b/res/PKGBUILD index 4ffbc4eb2..338ba5b77 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -22,7 +22,9 @@ noextract=() md5sums=() #generate with 'makepkg -g' package() { - pacman -S --noconfirm pam gst-plugins-base gst-plugin-pipewire + pacman -S --noconfirm gst-plugins-base || true + pacman -S --noconfirm gst-plugin-pipewire || true + pacman -S --noconfirm pam if [[ ${FLUTTER} ]]; then mkdir -p "${pkgdir}/usr/lib/rustdesk" && cp -r ${HBB}/flutter/build/linux/x64/release/bundle/* -t "${pkgdir}/usr/lib/rustdesk" fi From e83a24eb994b87f853d48ae732050de5fc31d561 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 14 Apr 2023 11:07:27 +0800 Subject: [PATCH 297/366] fix archlinux deps Signed-off-by: fufesou --- .github/workflows/flutter-build.yml | 3 +++ res/PKGBUILD | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index b1c4c577c..7ea06ba8e 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -1447,6 +1447,9 @@ jobs: python ttf-arphic-uming libappindicator-gtk3 + pam + gst-plugins-base + gst-plugin-pipewire scripts: | cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f diff --git a/res/PKGBUILD b/res/PKGBUILD index 338ba5b77..559f1c025 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -7,7 +7,7 @@ arch=('x86_64') url="" license=('AGPL-3.0') groups=() -depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3') +depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'curl' 'libva' 'libvdpau' 'libappindicator-gtk3' 'pam' 'gst-plugins-base' 'gst-plugin-pipewire') makedepends=() checkdepends=() optdepends=() @@ -22,9 +22,6 @@ noextract=() md5sums=() #generate with 'makepkg -g' package() { - pacman -S --noconfirm gst-plugins-base || true - pacman -S --noconfirm gst-plugin-pipewire || true - pacman -S --noconfirm pam if [[ ${FLUTTER} ]]; then mkdir -p "${pkgdir}/usr/lib/rustdesk" && cp -r ${HBB}/flutter/build/linux/x64/release/bundle/* -t "${pkgdir}/usr/lib/rustdesk" fi From fa7ea7e2810eb4c1686e529f5c4db6ea8f388095 Mon Sep 17 00:00:00 2001 From: solokot Date: Fri, 14 Apr 2023 13:57:52 +0300 Subject: [PATCH 298/366] Update ru.rs --- src/lang/ru.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index cfb37813b..12eeb7b03 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -483,14 +483,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("login_linux_tip", "Чтобы включить сеанс рабочего стола X, необходимо войти в удалённый аккаунт Linux."), ("verify_rustdesk_password_tip", "Подтвердить пароль RustDesk"), ("remember_account_tip", "Запомнить этот аккаунт"), - ("os_account_desk_tip", "Эта учетная запись используется для входа в удаленную ОС и включения сеанса рабочего стола в режиме headless"), + ("os_account_desk_tip", "Этот аккаунт используется для входа в удалённую ОС и включения сеанса рабочего стола в режиме headless"), ("OS Account", "Аккаунт ОС"), - ("another_user_login_title_tip", "Другой пользователь уже вошел в систему"), + ("another_user_login_title_tip", "Другой пользователь уже вошёл в систему"), ("another_user_login_text_tip", "Отключить"), ("xorg_not_found_title_tip", "Xorg не найден"), - ("xorg_not_found_text_tip", "Установите, пожалуйста, Xorg"), + ("xorg_not_found_text_tip", "Установите Xorg"), ("no_desktop_title_tip", "Нет доступных рабочих столов"), - ("no_desktop_text_tip", "Установите, пожалуйста, GNOME Desktop"), - ("No need to elevate", ""), + ("no_desktop_text_tip", "Установите GNOME Desktop"), + ("No need to elevate", "Повышение прав не требуется"), ].iter().cloned().collect(); } From b32e05420d59b9f3409486a019994c3a6db2b6f6 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Fri, 14 Apr 2023 05:18:23 -0700 Subject: [PATCH 299/366] Update Italian language --- src/lang/it.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 4f1f10e7e..ea04d8ce8 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -28,7 +28,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable File Transfer", "Abilita trasferimento file"), ("Enable TCP Tunneling", "Abilita tunnel TCP"), ("IP Whitelisting", "IP autorizzati"), - ("ID/Relay Server", "Server ID/Relay"), + ("ID/Relay Server", "ID server/Relay"), ("Import Server Config", "Importa configurazione server dagli appunti"), ("Export Server Config", "Esporta configurazione server negli appunti"), ("Import server configuration successfully", "Configurazione server importata completata"), @@ -460,8 +460,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Resolution", "Risoluzione"), ("No transfers in progress", "Nessun trasferimento in corso"), ("Set one-time password length", "Imposta lunghezza password monouso"), - ("idd_driver_tip", "Installa il driver per lo schermo virtuale che sarà usato quando non si dispone di schermi fisici."), - ("confirm_idd_driver_tip", "È selezionata l'opzione per installare il driver per lo schermo virtuale.\nNota che verrà installato un certificato di test per l'attendibilità del driver dello schermo virtuale.\nQuesto certificato di test verrà utilizzato solo per l'attendibilità dei driver di RustDesk."), + ("idd_driver_tip", "Installa driver schermo virtuale che verrà usato quando non sono disponibili schermi fisici."), + ("confirm_idd_driver_tip", "È stata selezionata l'opzione per installare il driver schermo virtuale.\nNota che verrà installato un certificato di test per l'attendibilità del driver dello schermo virtuale.\nQuesto certificato di test verrà utilizzato solo per l'attendibilità dei driver di RustDesk."), ("RDP Settings", "Impostazioni RDP"), ("Sort by", "Ordina per"), ("New Connection", "Nuova connessione"), @@ -491,5 +491,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Installa Xorg."), ("no_desktop_title_tip", "Non c'è nessun desktop disponibile."), ("no_desktop_text_tip", "Installa il desktop GNOME."), + ("No need to elevate", "Elevazione dei privilegi non richiesta"), ].iter().cloned().collect(); } From ef9864c5a04964011594b3f8e7a6c5a3903afab7 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:47:49 +0200 Subject: [PATCH 300/366] Update Italian language --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index ea04d8ce8..521c4ef08 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -28,7 +28,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable File Transfer", "Abilita trasferimento file"), ("Enable TCP Tunneling", "Abilita tunnel TCP"), ("IP Whitelisting", "IP autorizzati"), - ("ID/Relay Server", "ID server/Relay"), + ("ID/Relay Server", "Server ID/Relay"), ("Import Server Config", "Importa configurazione server dagli appunti"), ("Export Server Config", "Esporta configurazione server negli appunti"), ("Import server configuration successfully", "Configurazione server importata completata"), From cb342f86e6d5fbaf338392b430ff538050d72523 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 15 Apr 2023 09:41:56 +0800 Subject: [PATCH 301/366] bump cpal to 0.15 Signed-off-by: 21pages --- Cargo.lock | 201 ++++++------------------------------ Cargo.toml | 2 +- src/client.rs | 20 +++- src/server/audio_service.rs | 115 +++++++++++---------- 4 files changed, 112 insertions(+), 226 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e14f72bb..505a6f2d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,14 +65,14 @@ dependencies = [ [[package]] name = "alsa" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5915f52fe2cf65e83924d037b6c5290b7cee097c6b5c8700746e6168a343fd6b" +checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44" dependencies = [ "alsa-sys", "bitflags", "libc", - "nix 0.23.2", + "nix 0.24.3", ] [[package]] @@ -401,12 +401,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base64" version = "0.21.0" @@ -626,7 +620,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.17", + "semver", "serde 1.0.159", "serde_json 1.0.95", ] @@ -995,6 +989,12 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation-sys" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" + [[package]] name = "core-foundation-sys" version = "0.7.0" @@ -1059,11 +1059,12 @@ dependencies = [ [[package]] name = "coreaudio-rs" -version = "0.10.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88" +checksum = "cb17e2d1795b1996419648915df94bc7103c28f7b48062d7acf4652fc371b2ff" dependencies = [ "bitflags", + "core-foundation-sys 0.6.2", "coreaudio-sys", ] @@ -1078,26 +1079,27 @@ dependencies = [ [[package]] name = "cpal" -version = "0.14.2" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f342c1b63e185e9953584ff2199726bf53850d96610a310e3aca09e9405a2d0b" +checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" dependencies = [ "alsa", "core-foundation-sys 0.8.3", "coreaudio-rs", + "dasp_sample", "jni 0.19.0", "js-sys", "libc", - "mach", + "mach2", "ndk 0.7.0", "ndk-context", "oboe", "once_cell", "parking_lot 0.12.1", - "stdweb", - "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", - "windows 0.37.0", + "windows 0.46.0", ] [[package]] @@ -1575,12 +1577,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "dispatch" version = "0.2.0" @@ -1706,7 +1702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e" dependencies = [ "cc", - "rustc_version 0.4.0", + "rustc_version", "toml 0.5.11", "vswhom", "winreg 0.10.1", @@ -1961,7 +1957,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" dependencies = [ "memoffset 0.8.0", - "rustc_version 0.4.0", + "rustc_version", ] [[package]] @@ -3444,10 +3440,10 @@ dependencies = [ ] [[package]] -name = "mach" -version = "0.3.2" +name = "mach2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8" dependencies = [ "libc", ] @@ -4071,12 +4067,12 @@ dependencies = [ [[package]] name = "oboe" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f63c358b4fa0fbcfefd7c8be5cfc39c08ce2389f5325687e7762a48d30a5c1" +checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0" dependencies = [ - "jni 0.19.0", - "ndk 0.6.0", + "jni 0.20.0", + "ndk 0.7.0", "ndk-context", "num-derive", "num-traits 0.2.15", @@ -4085,9 +4081,9 @@ dependencies = [ [[package]] name = "oboe-sys" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3370abb7372ed744232c12954d920d1a40f1c4686de9e79e800021ef492294bd" +checksum = "7f44155e7fb718d3cfddcf70690b2b51ac4412f347cd9e4fbe511abe9cd7b5f2" dependencies = [ "cc", ] @@ -5085,22 +5081,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.17", + "semver", ] [[package]] @@ -5404,15 +5391,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.17" @@ -5422,12 +5400,6 @@ dependencies = [ "serde 1.0.159", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "0.9.15" @@ -5521,15 +5493,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - [[package]] name = "sha1" version = "0.10.5" @@ -5541,12 +5504,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - [[package]] name = "sha2" version = "0.10.6" @@ -5719,55 +5676,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2 1.0.54", - "quote 1.0.26", - "serde 1.0.159", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2 1.0.54", - "quote 1.0.26", - "serde 1.0.159", - "serde_derive", - "serde_json 1.0.95", - "sha1 0.6.1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "str-buf" version = "1.0.6" @@ -6916,19 +6824,6 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] -[[package]] -name = "windows" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647" -dependencies = [ - "windows_aarch64_msvc 0.37.0", - "windows_i686_gnu 0.37.0", - "windows_i686_msvc 0.37.0", - "windows_x86_64_gnu 0.37.0", - "windows_x86_64_msvc 0.37.0", -] - [[package]] name = "windows" version = "0.44.0" @@ -7059,12 +6954,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" -[[package]] -name = "windows_aarch64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -7089,12 +6978,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" -[[package]] -name = "windows_i686_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -7119,12 +7002,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" -[[package]] -name = "windows_i686_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -7149,12 +7026,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" -[[package]] -name = "windows_x86_64_gnu" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -7185,12 +7056,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" -[[package]] -name = "windows_x86_64_msvc" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -7396,7 +7261,7 @@ dependencies = [ "rand 0.8.5", "serde 1.0.159", "serde_repr", - "sha1 0.10.5", + "sha1", "static_assertions", "tracing", "uds_windows", diff --git a/Cargo.toml b/Cargo.toml index b8c4f59f0..3579501c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,7 +73,7 @@ cidr-utils = "0.5" libloading = "0.7" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] -cpal = "0.14" +cpal = "0.15" ringbuf = "0.3" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] diff --git a/src/client.rs b/src/client.rs index 335fded0b..d889e86cd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -644,7 +644,7 @@ impl Client { // `try_start_clipboard` is called by all session when connection is established. (When handling peer info). // This function only create one thread with a loop, the loop is shared by all sessions. // After all sessions are end, the loop exists. - // + // // If clipboard update is detected, the text will be sent to all sessions by `send_text_clipboard_msg`. #[cfg(not(any(target_os = "android", target_os = "ios")))] fn try_start_clipboard(_ctx: Option) { @@ -801,9 +801,17 @@ impl AudioHandler { let mut config: StreamConfig = config.into(); config.channels = format0.channels as _; match sample_format { - cpal::SampleFormat::F32 => self.build_output_stream::(&config, &device)?, + cpal::SampleFormat::I8 => self.build_output_stream::(&config, &device)?, cpal::SampleFormat::I16 => self.build_output_stream::(&config, &device)?, + cpal::SampleFormat::I32 => self.build_output_stream::(&config, &device)?, + cpal::SampleFormat::I64 => self.build_output_stream::(&config, &device)?, + cpal::SampleFormat::U8 => self.build_output_stream::(&config, &device)?, cpal::SampleFormat::U16 => self.build_output_stream::(&config, &device)?, + cpal::SampleFormat::U32 => self.build_output_stream::(&config, &device)?, + cpal::SampleFormat::U64 => self.build_output_stream::(&config, &device)?, + cpal::SampleFormat::F32 => self.build_output_stream::(&config, &device)?, + cpal::SampleFormat::F64 => self.build_output_stream::(&config, &device)?, + f => bail!("unsupported audio format: {:?}", f), } self.sample_rate = (format0.sample_rate, config.sample_rate.0); Ok(()) @@ -880,7 +888,7 @@ impl AudioHandler { /// Build audio output stream for current device. #[cfg(not(any(target_os = "android", target_os = "linux")))] - fn build_output_stream( + fn build_output_stream>( &mut self, config: &StreamConfig, device: &Device, @@ -891,6 +899,7 @@ impl AudioHandler { }; let audio_buffer = self.audio_buffer.0.clone(); let ready = self.ready.clone(); + let timeout = None; let stream = device.build_output_stream( config, move |data: &mut [T], _: &_| { @@ -908,12 +917,13 @@ impl AudioHandler { let mut input = elems.into_iter(); for sample in data.iter_mut() { *sample = match input.next() { - Some(x) => T::from(&x), - _ => T::from(&0.), + Some(x) => T::from_sample(x), + _ => T::from_sample(0.), }; } }, err_fn, + timeout, )?; stream.play()?; self.audio_stream = Some(Box::new(stream)); diff --git a/src/server/audio_service.rs b/src/server/audio_service.rs index d05b349e8..c2b1d80f9 100644 --- a/src/server/audio_service.rs +++ b/src/server/audio_service.rs @@ -104,7 +104,7 @@ mod cpal_impl { use super::*; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, - Device, Host, SupportedStreamConfig, + BufferSize, Device, Host, InputCallbackInfo, StreamConfig, SupportedStreamConfig, }; lazy_static::lazy_static! { @@ -214,12 +214,9 @@ mod cpal_impl { } fn play(sp: &GenericService) -> ResultType<(Box, Arc)> { + use cpal::SampleFormat::*; let (device, config) = get_device()?; let sp = sp.clone(); - let err_fn = move |err| { - // too many UnknownErrno, will improve later - log::trace!("an error occurred on stream: {}", err); - }; // Sample rate must be one of 8000, 12000, 16000, 24000, or 48000. let sample_rate_0 = config.sample_rate().0; let sample_rate = if sample_rate_0 < 12000 { @@ -233,6 +230,40 @@ mod cpal_impl { } else { 48000 }; + let stream = match config.sample_format() { + I8 => build_input_stream::(device, &config, sp, sample_rate)?, + I16 => build_input_stream::(device, &config, sp, sample_rate)?, + I32 => build_input_stream::(device, &config, sp, sample_rate)?, + I64 => build_input_stream::(device, &config, sp, sample_rate)?, + U8 => build_input_stream::(device, &config, sp, sample_rate)?, + U16 => build_input_stream::(device, &config, sp, sample_rate)?, + U32 => build_input_stream::(device, &config, sp, sample_rate)?, + U64 => build_input_stream::(device, &config, sp, sample_rate)?, + F32 => build_input_stream::(device, &config, sp, sample_rate)?, + F64 => build_input_stream::(device, &config, sp, sample_rate)?, + f => bail!("unsupported audio format: {:?}", f), + }; + stream.play()?; + Ok(( + Box::new(stream), + Arc::new(create_format_msg(sample_rate, config.channels())), + )) + } + + fn build_input_stream( + device: cpal::Device, + config: &cpal::SupportedStreamConfig, + sp: GenericService, + sample_rate: u32, + ) -> ResultType + where + T: cpal::SizedSample + dasp::sample::ToSample, + { + let err_fn = move |err| { + // too many UnknownErrno, will improve later + log::trace!("an error occurred on stream: {}", err); + }; + let sample_rate_0 = config.sample_rate().0; log::debug!("Audio sample rate : {}", sample_rate); unsafe { AUDIO_ZERO_COUNT = 0; @@ -247,54 +278,34 @@ mod cpal_impl { // https://chromium.googlesource.com/chromium/deps/opus/+/1.1.1/include/opus.h let encode_len = sample_rate as usize * channels as usize / 100; // 10 ms INPUT_BUFFER.lock().unwrap().clear(); - let mut send_input_stream = move || { - let mut lock = INPUT_BUFFER.lock().unwrap(); - while lock.len() >= encode_len { - let frame: Vec = lock.drain(0..encode_len).collect(); - send( - &frame, - sample_rate_0, - sample_rate, - channels, - &mut encoder, - &sp, - ); - } + let timeout = None; + let stream_config = StreamConfig { + channels, + sample_rate: config.sample_rate(), + buffer_size: BufferSize::Default, }; - - let stream = match config.sample_format() { - cpal::SampleFormat::F32 => device.build_input_stream( - &config.into(), - move |data, _: &_| { - INPUT_BUFFER.lock().unwrap().extend(data); - send_input_stream(); - }, - err_fn, - )?, - cpal::SampleFormat::I16 => device.build_input_stream( - &config.into(), - move |data: &[i16], _: &_| { - let buffer: Vec<_> = data.iter().map(|s| cpal::Sample::to_f32(s)).collect(); - INPUT_BUFFER.lock().unwrap().extend(buffer); - send_input_stream(); - }, - err_fn, - )?, - cpal::SampleFormat::U16 => device.build_input_stream( - &config.into(), - move |data: &[u16], _: &_| { - let buffer: Vec<_> = data.iter().map(|s| cpal::Sample::to_f32(s)).collect(); - INPUT_BUFFER.lock().unwrap().extend(buffer); - send_input_stream(); - }, - err_fn, - )?, - }; - stream.play()?; - Ok(( - Box::new(stream), - Arc::new(create_format_msg(sample_rate, channels)), - )) + let stream = device.build_input_stream( + &stream_config, + move |data: &[T], _: &InputCallbackInfo| { + let buffer: Vec = data.iter().map(|s| T::to_sample(*s)).collect(); + let mut lock = INPUT_BUFFER.lock().unwrap(); + lock.extend(buffer); + while lock.len() >= encode_len { + let frame: Vec = lock.drain(0..encode_len).collect(); + send( + &frame, + sample_rate_0, + sample_rate, + channels, + &mut encoder, + &sp, + ); + } + }, + err_fn, + timeout, + )?; + Ok(stream) } } From 225a549e4ee79b60d7a575a669d380d43254734f Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Sat, 15 Apr 2023 18:11:14 +0200 Subject: [PATCH 302/366] Add files via upload --- src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/el.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + 34 files changed, 34 insertions(+) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 9347dac82..995b56f1b 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 3ec042928..7f94173f0 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "desktop 未安装"), ("no_desktop_text_tip", "请安装 desktop"), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 31bd83533..94cbc0c24 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index c566a1284..d567799c7 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index caf0dd5a1..619814d8d 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Es ist kein Desktop verfügbar."), ("no_desktop_text_tip", "Bitte installieren Sie den GNOME-Desktop."), ("No need to elevate", "Erhöhung der Rechte nicht erforderlich"), + ("New RDP", "Neue RDP-Verbindung"), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 84d6a3c42..8929ca5e7 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 1969c545f..5762ca118 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index fde3934ee..26f7b4243 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "No hay escritorio disponible"), ("no_desktop_text_tip", "Por favor, instala GNOME Desktop"), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index c00fb3ab6..4a39cd31e 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "هیچ دسکتاپی در دسترس نیست"), ("no_desktop_text_tip", "لطفا دسکتاپ گنوم را نصب کنید"), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index fd4598d04..dcee91876 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 20adb1d6e..b82469c62 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 32f350472..0316965af 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 521c4ef08..bd2cd04d4 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Non c'è nessun desktop disponibile."), ("no_desktop_text_tip", "Installa il desktop GNOME."), ("No need to elevate", "Elevazione dei privilegi non richiesta"), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 5a32110dc..f980138b0 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index e6a840ba0..beded1c1e 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index cbea5826d..ead19ea1f 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index f403baaf4..8bc81a01e 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 7ba78b470..fc6ff1e45 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 3f969dd15..92a5c1c77 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Pulpit jest niedostępny"), ("no_desktop_text_tip", "Proszę zainstalować pulpit GNOME"), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 77d1f1aec..04e5c79bd 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 58bc99959..02c92875e 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 7d0b809cf..73e7161a3 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 12eeb7b03..a42ef5009 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Нет доступных рабочих столов"), ("no_desktop_text_tip", "Установите GNOME Desktop"), ("No need to elevate", "Повышение прав не требуется"), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 920fc53ab..18a77b790 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 2f74b0d55..a6b3f76f6 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 676e97b6d..317b8c3be 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 04b99433a..adf5c539c 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 8419f4dee..702cbab16 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 61d2e415d..d97e60d9f 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 942c0de64..1255c888b 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 3347da6a9..d1389a9fa 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index dadc6918b..5337749a6 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "沒有可用的桌面"), ("no_desktop_text_tip", "請安裝 GNOME 桌面"), ("No need to elevate", "不需要提升權限"), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 06e9acfee..bcda47591 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 211080a2b..543155418 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -492,5 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("New RDP", ""), ].iter().cloned().collect(); } From a9705c5be68dd83198087c582ece5f386242f79a Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Sat, 15 Apr 2023 18:16:03 +0200 Subject: [PATCH 303/366] Update de.rs --- src/lang/de.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lang/de.rs b/src/lang/de.rs index 619814d8d..b703880fd 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -493,5 +493,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_text_tip", "Bitte installieren Sie den GNOME-Desktop."), ("No need to elevate", "Erhöhung der Rechte nicht erforderlich"), ("New RDP", "Neue RDP-Verbindung"), + ("Failed to listen on {}: {}", "Lauschen fehlgeschlagen auf Port {}: {}"), ].iter().cloned().collect(); } From 3bac9adbd9b371764e6cf73f82f6ff00d137c28d Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Sat, 15 Apr 2023 19:50:24 +0200 Subject: [PATCH 304/366] Add new strings --- src/lang/template.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lang/template.rs b/src/lang/template.rs index 61d2e415d..b15bff45e 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -492,5 +492,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ].iter().cloned().collect(); } From 3e52da46db11ca7cd3898de5f3223ef3a55c149d Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Sat, 15 Apr 2023 19:52:13 +0200 Subject: [PATCH 305/366] Update Italian language --- src/lang/it.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lang/it.rs b/src/lang/it.rs index 521c4ef08..b0532638c 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -492,5 +492,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Non c'è nessun desktop disponibile."), ("no_desktop_text_tip", "Installa il desktop GNOME."), ("No need to elevate", "Elevazione dei privilegi non richiesta"), + ("System Sound", "Dispositivo audio sistema"), + ("Default", "Predefinita"), ].iter().cloned().collect(); } From 0a220150e3c82a2a0906a127e0da40e38445f774 Mon Sep 17 00:00:00 2001 From: mehdi-song Date: Sun, 16 Apr 2023 04:12:34 +0000 Subject: [PATCH 306/366] Update fa.rs --- src/lang/fa.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 4a39cd31e..474113d3a 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -491,7 +491,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "لطفا Xorg را نصب کنید"), ("no_desktop_title_tip", "هیچ دسکتاپی در دسترس نیست"), ("no_desktop_text_tip", "لطفا دسکتاپ گنوم را نصب کنید"), - ("No need to elevate", ""), - ("New RDP", ""), + ("No need to elevate", "نیازی به ارتقاء نیست"), + ("New RDP", "ریموت جدید"), ].iter().cloned().collect(); } From b4eb8a580b739008718a9dc5a79febab66b94ecb Mon Sep 17 00:00:00 2001 From: mehdi-song Date: Sun, 16 Apr 2023 06:06:15 +0000 Subject: [PATCH 307/366] Update fa.rs --- src/lang/fa.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 474113d3a..9daeec94b 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -191,7 +191,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter Remote ID", "شناسه از راه دور را وارد کنید"), ("Enter your password", "زمر عبور خود را وارد کنید"), ("Logging in...", "...در حال ورود"), - ("Enable RDP session sharing", "اشتراک گذاری جلسه RDP را فعال کنید"), + ("Enable RDP session sharing", "را فعال کنید RDP اشتراک گذاری جلسه"), ("Auto Login", "ورود خودکار"), ("Enable Direct IP Access", "را فعال کنید IP دسترسی مستقیم"), ("Rename", "تغییر نام"), @@ -240,7 +240,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Remove from Favorites", "از علاقه مندی ها حذف شود"), ("Empty", "موردی وجود ندارد"), ("Invalid folder name", "نام پوشه نامعتبر است"), - ("Socks5 Proxy", "Socks5 Proxy"), + ("Socks5 Proxy", "Socks5 پروکسی"), ("Hostname", "نام هاست"), ("Discovered", "پیدا شده"), ("install_daemon_tip", "برای شروع در هنگام راه اندازی، باید سرویس سیستم را نصب کنید"), @@ -353,9 +353,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Light", "روشن"), ("Follow System", "پیروی از سیستم"), ("Enable hardware codec", "فعال سازی کدک سخت افزاری"), - ("Unlock Security Settings", "آنلاک شدن تنظیمات امنیتی"), + ("Unlock Security Settings", "دسترسی کامل به تنظیمات امنیتی"), ("Enable Audio", "فعال شدن صدا"), - ("Unlock Network Settings", "آنلاک شدن تنظیمات شبکه"), + ("Unlock Network Settings", "دسترسی کامل به تنظیمات شبکه"), ("Server", "سرور"), ("Direct IP Access", "IP دسترسی مستقیم به"), ("Proxy", "پروکسی"), From 274098debb6aeb27a56e7b6e5a49031cc4ce2033 Mon Sep 17 00:00:00 2001 From: NicKoehler <53040044+NicKoehler@users.noreply.github.com> Date: Sun, 16 Apr 2023 08:44:37 +0200 Subject: [PATCH 308/366] Update it.rs --- src/lang/it.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index e24da4d88..f18df3dec 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -494,6 +494,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("No need to elevate", "Elevazione dei privilegi non richiesta"), ("System Sound", "Dispositivo audio sistema"), ("Default", "Predefinita"), - ("New RDP", ""), + ("New RDP", "Nuovo RDP"), ].iter().cloned().collect(); } From 6a21b98f734a05af575f18938368f4d800bd0e9c Mon Sep 17 00:00:00 2001 From: "Miguel F. G" <116861809+flusheDData@users.noreply.github.com> Date: Sun, 16 Apr 2023 16:14:39 +0200 Subject: [PATCH 309/366] Update es.rs A few corrections and new terms added --- src/lang/es.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lang/es.rs b/src/lang/es.rs index 26f7b4243..772ae03a4 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -397,7 +397,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("This PC", "Este PC"), ("or", "o"), ("Continue with", "Continuar con"), - ("Elevate", "Elevar"), + ("Elevate", "Elevar privilegios"), ("Zoom cursor", "Ampliar cursor"), ("Accept sessions via password", "Aceptar sesiones a través de contraseña"), ("Accept sessions via click", "Aceptar sesiones a través de clic"), @@ -422,16 +422,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Always use software rendering", "Usar siempre renderizado por software"), ("config_input", "Para controlar el escritorio remoto con el teclado necesitas dar a RustDesk permisos de \"Monitorización de entrada\"."), ("config_microphone", "Para poder hablar de forma remota necesitas darle a RustDesk permisos de \"Grabar Audio\"."), - ("request_elevation_tip", "También puedes solicitar elevación si hay alguien en el lado remoto."), + ("request_elevation_tip", "También puedes solicitar elevación de privilegios si hay alguien en el lado remoto."), ("Wait", "Esperar"), - ("Elevation Error", "Error de elevación"), + ("Elevation Error", "Error de elevación de privilegios"), ("Ask the remote user for authentication", "Pida autenticación al usuario remoto"), ("Choose this if the remote account is administrator", "Elegir si la cuenta remota es de administrador"), ("Transmit the username and password of administrator", "Transmitir usuario y contraseña del administrador"), ("still_click_uac_tip", "Aún se necesita que el usuario remoto haga click en OK en la ventana UAC del RusDesk en ejecución."), - ("Request Elevation", "Solicitar Elevación"), + ("Request Elevation", "Solicitar Elevación de privilegios"), ("wait_accept_uac_tip", "Por favor espere a que el usuario remoto acepte el diálogo UAC."), - ("Elevate successfully", "Elevar con éxito"), + ("Elevate successfully", "Elevación de privilegios exitosa"), ("uppercase", "mayúsculas"), ("lowercase", "minúsculas"), ("digit", "dígito"), @@ -491,7 +491,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("xorg_not_found_text_tip", "Por favor, instala Xorg"), ("no_desktop_title_tip", "No hay escritorio disponible"), ("no_desktop_text_tip", "Por favor, instala GNOME Desktop"), - ("No need to elevate", ""), - ("New RDP", ""), + ("No need to elevate", "No es necesario elevar privilegios"), + ("New RDP", "Nuevo RDP"), ].iter().cloned().collect(); } From 06a52e1b540e1502ae088803244f270e6b73400b Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 13 Apr 2023 23:00:24 +0800 Subject: [PATCH 310/366] tmp commit Signed-off-by: fufesou --- Cargo.toml | 3 +- src/lib.rs | 5 +- src/platform/windows.rs | 62 ++++-- src/privacy_mode/mod.rs | 43 ++++ src/privacy_mode/privacy_win_idd.rs | 188 ++++++++++++++++++ .../privacy_win_mag.rs} | 15 +- src/server/connection.rs | 16 +- src/server/video_service.rs | 47 +++-- src/ui_cm_interface.rs | 4 +- 9 files changed, 318 insertions(+), 65 deletions(-) create mode 100644 src/privacy_mode/mod.rs create mode 100644 src/privacy_mode/privacy_win_idd.rs rename src/{win_privacy.rs => privacy_mode/privacy_win_mag.rs} (96%) diff --git a/Cargo.toml b/Cargo.toml index 3579501c7..f1c39a64d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ default = ["use_dasp"] hwcodec = ["scrap/hwcodec"] mediacodec = ["scrap/mediacodec"] linux_headless = ["pam", "users"] +virtual_display_driver = ["virtual_display"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -94,7 +95,7 @@ winit = "0.26" winapi = { version = "0.3", features = ["winuser", "wincrypt"] } winreg = "0.10" windows-service = "0.4" -virtual_display = { path = "libs/virtual_display" } +virtual_display = { path = "libs/virtual_display", optional = true } impersonate_system = { git = "https://github.com/21pages/impersonate-system" } shared_memory = "0.12" shutdown_hooks = "0.1" diff --git a/src/lib.rs b/src/lib.rs index 15c0ca037..7a7062b27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,5 +66,6 @@ pub mod clipboard_file; #[cfg(all(windows, feature = "with_rc"))] pub mod rc; -#[cfg(target_os = "windows")] -pub mod win_privacy; + +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub mod privacy_mode; diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 383259a32..ab76c8559 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1,7 +1,8 @@ use super::{CursorData, ResultType}; use crate::common::PORTABLE_APPNAME_RUNTIME_ENV_KEY; -use crate::ipc; -use crate::license::*; +#[cfg(feature = "privacy_win_mag")] +use crate::privacy_mode::privacy_win_mag; +use crate::{ipc, license::*, privacy_mode::WIN_MAG_INJECTED_PROCESS_EXE}; use hbb_common::{ allow_err, bail, config::{self, Config}, @@ -9,10 +10,12 @@ use hbb_common::{ message_proto::Resolution, sleep, timeout, tokio, }; +#[cfg(feature = "privacy_win_mag")] +use std::fs; use std::{ collections::HashMap, ffi::OsString, - fs, io, + io, io::prelude::*, mem, os::windows::process::CommandExt, @@ -836,10 +839,11 @@ fn get_default_install_path() -> String { format!("{}\\{}", pf, crate::get_app_name()) } +#[cfg(feature = "privacy_win_mag")] pub fn check_update_broker_process() -> ResultType<()> { // let (_, path, _, _) = get_install_info(); - let process_exe = crate::win_privacy::INJECTED_PROCESS_EXE; - let origin_process_exe = crate::win_privacy::ORIGIN_PROCESS_EXE; + let process_exe = privacy_win_mag::INJECTED_PROCESS_EXE; + let origin_process_exe = privacy_win_mag::ORIGIN_PROCESS_EXE; let exe_file = std::env::current_exe()?; if exe_file.parent().is_none() { @@ -922,17 +926,31 @@ pub fn copy_raw_cmd(src_raw: &str, _raw: &str, _path: &str) -> String { pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { let main_exe = copy_raw_cmd(src_exe, exe, path); - return format!( - " - {main_exe} - copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" - \"{src_exe}\" --extract \"{path}\" - ", - main_exe = main_exe, - path = path, - ORIGIN_PROCESS_EXE = crate::win_privacy::ORIGIN_PROCESS_EXE, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, - ); + #[cfg(feature = "privacy_win_mag")] + { + format!( + " + {main_exe} + copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" + \"{src_exe}\" --extract \"{path}\" + ", + main_exe = main_exe, + path = path, + ORIGIN_PROCESS_EXE = privacy_win_mag::ORIGIN_PROCESS_EXE, + broker_exe = privacy_win_mag::INJECTED_PROCESS_EXE, + ) + } + #[cfg(not(feature = "privacy_win_mag"))] + { + format!( + " + {main_exe} + \"{src_exe}\" --extract \"{path}\" + ", + main_exe = main_exe, + path = path, + ) + } } pub fn update_me() -> ResultType<()> { @@ -963,7 +981,7 @@ pub fn update_me() -> ResultType<()> { ", copy_exe = copy_exe_cmd(&src_exe, &exe, &path), copy_dll = copy_dll, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, + broker_exe = WIN_MAG_INJECTED_PROCESS_EXE, app_name = crate::get_app_name(), lic = register_licence(), cur_pid = get_current_pid(), @@ -1257,7 +1275,7 @@ fn get_before_uninstall(kill_self: bool) -> String { netsh advfirewall firewall delete rule name=\"{app_name} Service\" ", app_name = app_name, - broker_exe = crate::win_privacy::INJECTED_PROCESS_EXE, + broker_exe = WIN_MAG_INJECTED_PROCESS_EXE, ext = ext, filter = filter, ) @@ -2180,6 +2198,14 @@ pub fn get_unicode_from_vk(vk: u32) -> Option { } } +pub fn is_process_consent_running() -> ResultType { + let output = std::process::Command::new("cmd") + .args(&["/C", "tasklist | findstr consent.exe"]) + .creation_flags(CREATE_NO_WINDOW) + .output()?; + Ok(output.status.success() && !output.stdout.is_empty()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/privacy_mode/mod.rs b/src/privacy_mode/mod.rs new file mode 100644 index 000000000..9b21177f8 --- /dev/null +++ b/src/privacy_mode/mod.rs @@ -0,0 +1,43 @@ +use crate::ipc::PrivacyModeState; +use hbb_common::{bail, ResultType}; + +#[cfg(all(windows, feature = "privacy_win_mag"))] +pub mod privacy_win_mag; +pub const WIN_MAG_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; + +pub const OCCUPIED: &'static str = "Privacy occupied by another one"; +pub const TURN_OFF_OTHER_ID: &'static str = + "Failed to turn off privacy mode that belongs to someone else"; +pub const NO_DISPLAYS: &'static str = "No displays"; + +#[cfg(all(windows, feature = "virtual_display_driver"))] +pub mod privacy_win_idd; + +pub trait PrivacyMode { + fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType; + fn turn_off_privacy(&mut self, conn_id: i32, state: Option) + -> ResultType<()>; + + fn cur_conn_id(&self) -> i32; + + #[inline] + fn check_on_conn_id(&self, conn_id: i32) -> ResultType { + let pre_conn_id = self.cur_conn_id(); + if pre_conn_id == conn_id { + return Ok(true); + } + if pre_conn_id != 0 { + bail!(OCCUPIED); + } + Ok(false) + } + + #[inline] + fn check_off_conn_id(&self, conn_id: i32) -> ResultType<()> { + let pre_conn_id = self.cur_conn_id(); + if pre_conn_id != 0 && conn_id != 0 && pre_conn_id != conn_id { + bail!(TURN_OFF_OTHER_ID) + } + Ok(()) + } +} diff --git a/src/privacy_mode/privacy_win_idd.rs b/src/privacy_mode/privacy_win_idd.rs new file mode 100644 index 000000000..13798bf16 --- /dev/null +++ b/src/privacy_mode/privacy_win_idd.rs @@ -0,0 +1,188 @@ +use super::{PrivacyModeState, NO_DISPLAYS}; +use hbb_common::{allow_err, bail, lazy_static, log, ResultType}; +use scrap::dxgi; +use winapi::{ + shared::{ + minwindef::{DWORD, FALSE}, + ntdef::{NULL, WCHAR}, + }, + um::{ + errhandlingapi::GetLastError, + wingdi::{DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ATTACHED_TO_DESKTOP, DM_POSITION}, + winuser::{ + ChangeDisplaySettingsExW, EnumDisplayDevicesW, EnumDisplaySettingsW, CDS_NORESET, + CDS_SET_PRIMARY, CDS_UPDATEREGISTRY, DISP_CHANGE_SUCCESSFUL, + EDD_GET_DEVICE_INTERFACE_NAME, ENUM_CURRENT_SETTINGS, + }, + }, +}; + +const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device"; + +struct Display { + dm: DEVMODEW, + name: [WCHAR; 32], + primary: bool, +} + +pub struct PrivacyModeImpl { + conn_id: i32, + displays: Vec, + virtual_displays: Vec, + virtual_displays_added: Option, +} + +impl PrivacyModeImpl { + fn set_displays(&mut self) { + self.displays.clear(); + self.virtual_displays.clear(); + for display in dxgi::Displays::get_from_gdi().into_iter() { + if let Some(gdi_info) = display.gdi() { + if let Ok(s) = std::string::String::from_utf16(&gdi_info.dd.DeviceString) { + if s == IDD_DEVICE_STRING { + self.virtual_displays.push(Display { + dm: gdi_info.dm, + name: gdi_info.dd.DeviceName, + primary: gdi_info.is_primary, + }); + continue; + } + } + self.displays.push(Display { + dm: gdi_info.dm, + name: gdi_info.dd.DeviceName, + primary: gdi_info.is_primary, + }); + } + } + } + + fn restore(&self) {} + + fn set_primary_display(&mut self) -> ResultType<()> { + self.ensure_virtual_display()?; + if self.virtual_displays.is_empty() { + bail!("No virtual displays"); + } + let display = &self.virtual_displays[0]; + + let mut new_primary_dm: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; + new_primary_dm.dmSize = std::mem::size_of::() as _; + new_primary_dm.dmDriverExtra = 0; + unsafe { + if FALSE + == EnumDisplaySettingsW( + display.name.as_ptr(), + ENUM_CURRENT_SETTINGS, + &mut new_primary_dm, + ) + { + bail!( + "Failed EnumDisplaySettingsW, device name: {:?}, error code: {}", + std::string::String::from_utf16(&display.name), + GetLastError() + ); + } + + let mut i: DWORD = 0; + loop { + let mut flags = CDS_UPDATEREGISTRY | CDS_NORESET; + let mut dd: DISPLAY_DEVICEW = std::mem::MaybeUninit::uninit().assume_init(); + dd.cb = std::mem::size_of::() as _; + if FALSE + == EnumDisplayDevicesW(NULL as _, i, &mut dd, EDD_GET_DEVICE_INTERFACE_NAME) + { + break; + } + i += 1; + if (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0 { + continue; + } + + if dd.DeviceName == display.name { + flags |= CDS_SET_PRIMARY; + } + + let mut dm: DEVMODEW = std::mem::MaybeUninit::uninit().assume_init(); + dm.dmSize = std::mem::size_of::() as _; + dm.dmDriverExtra = 0; + if FALSE + == EnumDisplaySettingsW(dd.DeviceName.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) + { + bail!( + "Failed EnumDisplaySettingsW, device name: {:?}, error code: {}", + std::string::String::from_utf16(&dd.DeviceName), + GetLastError() + ); + } + + dm.u1.s2_mut().dmPosition.x -= new_primary_dm.u1.s2().dmPosition.x; + dm.u1.s2_mut().dmPosition.y -= new_primary_dm.u1.s2().dmPosition.y; + dm.dmFields |= DM_POSITION; + let rc = ChangeDisplaySettingsExW( + dd.DeviceName.as_ptr(), + &mut dm, + NULL as _, + flags, + NULL, + ); + + if rc != DISP_CHANGE_SUCCESSFUL { + log::error!( + "Failed ChangeDisplaySettingsEx, device name: {:?}, flags: {}, ret: {}", + std::string::String::from_utf16(&dd.DeviceName), + flags, + rc + ); + bail!("Failed ChangeDisplaySettingsEx, ret: {}", rc); + } + } + } + + Ok(()) + } + + fn ensure_virtual_display(&mut self) -> ResultType<()> { + if self.virtual_displays.is_empty() { + virtual_display::create_device()?; + } + if virtual_display::is_device_created() { + // to-do: add monitor index here + virtual_display::plug_in_monitor()?; + self.virtual_displays_added = Some(0); + } + + self.set_displays(); + Ok(()) + } +} + +impl super::PrivacyMode for PrivacyModeImpl { + fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType { + if self.check_on_conn_id(conn_id)? { + return Ok(true); + } + self.set_displays(); + if self.displays.is_empty() { + bail!(NO_DISPLAYS); + } + self.set_primary_display()?; + + bail!("unimplemented") + } + + fn turn_off_privacy( + &mut self, + conn_id: i32, + state: Option, + ) -> ResultType<()> { + self.check_off_conn_id(conn_id)?; + self.restore(); + bail!("unimplemented") + } + + #[inline] + fn cur_conn_id(&self) -> i32 { + self.conn_id + } +} diff --git a/src/win_privacy.rs b/src/privacy_mode/privacy_win_mag.rs similarity index 96% rename from src/win_privacy.rs rename to src/privacy_mode/privacy_win_mag.rs index 9944bf262..923c42859 100644 --- a/src/win_privacy.rs +++ b/src/privacy_mode/privacy_win_mag.rs @@ -5,7 +5,6 @@ use crate::{ use hbb_common::{allow_err, bail, lazy_static, log, tokio, ResultType}; use std::{ ffi::CString, - os::windows::process::CommandExt, sync::Mutex, time::{Duration, Instant}, }; @@ -25,16 +24,14 @@ use winapi::{ CreateProcessAsUserW, GetCurrentThreadId, QueueUserAPC, ResumeThread, PROCESS_INFORMATION, STARTUPINFOW, }, - winbase::{ - WTSGetActiveConsoleSessionId, CREATE_NO_WINDOW, CREATE_SUSPENDED, DETACHED_PROCESS, - }, + winbase::{WTSGetActiveConsoleSessionId, CREATE_SUSPENDED, DETACHED_PROCESS}, winnt::{MEM_COMMIT, PAGE_READWRITE}, winuser::*, }, }; pub const ORIGIN_PROCESS_EXE: &'static str = "C:\\Windows\\System32\\RuntimeBroker.exe"; -pub const INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; +pub const INJECTED_PROCESS_EXE: &'static str = super::WIN_MAG_INJECTED_PROCESS_EXE; pub const PRIVACY_WINDOW_NAME: &'static str = "RustDeskPrivacyWindow"; pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2; @@ -317,14 +314,6 @@ fn wait_find_privacy_hwnd(msecs: u128) -> ResultType { } } -pub fn is_process_consent_running() -> ResultType { - let output = std::process::Command::new("cmd") - .args(&["/C", "tasklist | findstr consent.exe"]) - .creation_flags(CREATE_NO_WINDOW) - .output()?; - Ok(output.status.success() && !output.stdout.is_empty()) -} - #[tokio::main(flavor = "current_thread")] async fn set_privacy_mode_state( conn_id: i32, diff --git a/src/server/connection.rs b/src/server/connection.rs index 8a6ba99b9..25dea58ad 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2346,13 +2346,13 @@ fn try_activate_screen() { mod privacy_mode { use super::*; + #[cfg(all(windows, feature = "privacy_win_mag"))] + use crate::privacy_mode::privacy_win_mag; pub(super) fn turn_off_privacy(_conn_id: i32) -> Message { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] { - use crate::win_privacy::*; - - let res = turn_off_privacy(_conn_id, None); + let res = privacy_win_mag::turn_off_privacy(_conn_id, None); match res { Ok(_) => crate::common::make_privacy_mode_msg( back_notification::PrivacyModeState::PrvOffSucceeded, @@ -2365,19 +2365,19 @@ mod privacy_mode { } } } - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] { crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::PrvOffFailed) } } pub(super) fn turn_on_privacy(_conn_id: i32) -> ResultType { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] { - let plugin_exist = crate::win_privacy::turn_on_privacy(_conn_id)?; + let plugin_exist = privacy_win_mag::turn_on_privacy(_conn_id)?; Ok(plugin_exist) } - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] { Ok(true) } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 4abeafff5..e6a4e3806 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -20,6 +20,10 @@ use super::{video_qos::VideoQoS, *}; #[cfg(windows)] +use crate::platform::windows::is_process_consent_running; +#[cfg(all(windows, feature = "privacy_win_mag"))] +use crate::privacy_mode::privacy_win_mag; +#[cfg(all(windows, feature = "privacy_win_mag"))] use hbb_common::get_version_number; use hbb_common::tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, @@ -41,7 +45,7 @@ use std::{ ops::{Deref, DerefMut}, time::{self, Duration, Instant}, }; -#[cfg(windows)] +#[cfg(all(windows, feature = "virtual_display_driver"))] use virtual_display; pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; @@ -69,9 +73,9 @@ lazy_static::lazy_static! { } fn is_capturer_mag_supported() -> bool { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] return scrap::CapturerMag::is_supported(); - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] false } @@ -92,10 +96,10 @@ pub fn get_privacy_mode_conn_id() -> i32 { } pub fn is_privacy_mode_supported() -> bool { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] return *IS_CAPTURER_MAGNIFIER_SUPPORTED && get_version_number(&crate::VERSION) > get_version_number("1.1.9"); - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] return false; } @@ -201,15 +205,13 @@ fn create_capturer( _current: usize, _portable_service_running: bool, ) -> ResultType> { - #[cfg(not(windows))] + #[cfg(not(all(windows, feature = "privacy_win_mag")))] let c: Option> = None; - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] let mut c: Option> = None; if privacy_mode_id > 0 { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] { - use crate::win_privacy::*; - match scrap::CapturerMag::new( display.origin(), display.width(), @@ -220,7 +222,7 @@ fn create_capturer( let mut ok = false; let check_begin = Instant::now(); while check_begin.elapsed().as_secs() < 5 { - match c1.exclude("", PRIVACY_WINDOW_NAME) { + match c1.exclude("", privacy_win_mag::PRIVACY_WINDOW_NAME) { Ok(false) => { ok = false; std::thread::sleep(std::time::Duration::from_millis(500)); @@ -229,7 +231,7 @@ fn create_capturer( bail!( "Failed to exclude privacy window {} - {}, err: {}", "", - PRIVACY_WINDOW_NAME, + privacy_win_mag::PRIVACY_WINDOW_NAME, e ); } @@ -243,7 +245,7 @@ fn create_capturer( bail!( "Failed to exclude privacy window {} - {} ", "", - PRIVACY_WINDOW_NAME + privacy_win_mag::PRIVACY_WINDOW_NAME ); } log::debug!("Create magnifier capture for {}", privacy_mode_id); @@ -275,7 +277,8 @@ fn create_capturer( }; } -#[cfg(windows)] +// to-do: do not close if in privacy mode. +#[cfg(all(windows, feature = "virtual_display_driver"))] fn ensure_close_virtual_device() -> ResultType<()> { let num_displays = Display::all()?.len(); if num_displays == 0 { @@ -309,11 +312,11 @@ pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool { fn check_uac_switch(privacy_mode_id: i32, capturer_privacy_mode_id: i32) -> ResultType<()> { if capturer_privacy_mode_id != 0 { if privacy_mode_id != capturer_privacy_mode_id { - if !crate::win_privacy::is_process_consent_running()? { + if !is_process_consent_running()? { bail!("consent.exe is running"); } } - if crate::win_privacy::is_process_consent_running()? { + if is_process_consent_running()? { bail!("consent.exe is running"); } } @@ -374,7 +377,7 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType Option { } fn run(sp: GenericService) -> ResultType<()> { - #[cfg(windows)] + #[cfg(all(windows, feature = "virtual_display_driver"))] ensure_close_virtual_device()?; // ensure_inited() is needed because release_resource() may be called. @@ -922,12 +925,12 @@ pub async fn switch_to_primary() { } #[inline] -#[cfg(not(windows))] +#[cfg(not(all(windows, feature = "virtual_display_driver")))] fn try_get_displays() -> ResultType> { Ok(Display::all()?) } -#[cfg(windows)] +#[cfg(all(windows, feature = "virtual_display_driver"))] fn try_get_displays() -> ResultType> { let mut displays = Display::all()?; if displays.len() == 0 { @@ -949,6 +952,8 @@ fn try_get_displays() -> ResultType> { } displays = Display::all()?; } else if displays.len() > 1 { + // to-do: do not close if in privacy mode. + // If more than one displays exists, close RustDeskVirtualDisplay if virtual_display::is_device_created() { virtual_display::close_device() @@ -991,7 +996,7 @@ fn start_uac_elevation_check() { if !crate::platform::is_installed() && !crate::platform::is_root() { std::thread::spawn(|| loop { std::thread::sleep(std::time::Duration::from_secs(1)); - if let Ok(uac) = crate::win_privacy::is_process_consent_running() { + if let Ok(uac) = is_process_consent_running() { *IS_UAC_RUNNING.lock().unwrap() = uac; } if !crate::platform::is_elevated(None).unwrap_or(false) { diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 8612b5751..b738bd4ac 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -495,7 +495,7 @@ impl IpcTaskRunner { #[cfg(not(any(target_os = "android", target_os = "ios")))] #[tokio::main(flavor = "current_thread")] pub async fn start_ipc(cm: ConnectionManager) { - #[cfg(windows)] + #[cfg(all(windows, feature = "privacy_win_mag"))] std::thread::spawn(move || { log::info!("try create privacy mode window"); if let Err(e) = crate::platform::windows::check_update_broker_process() { @@ -504,7 +504,7 @@ pub async fn start_ipc(cm: ConnectionManager) { e ); } - allow_err!(crate::win_privacy::start()); + allow_err!(crate::privacy_mode::privacy_win_mag::start()); }); match ipc::new_listener("_cm").await { From 29887440346fdf8b61c792795625e2abbe41198a Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 14 Apr 2023 09:42:46 +0800 Subject: [PATCH 311/366] mid commit Signed-off-by: fufesou --- libs/scrap/src/dxgi/mod.rs | 78 ++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs index 4a0a53402..2d9bd636f 100644 --- a/libs/scrap/src/dxgi/mod.rs +++ b/libs/scrap/src/dxgi/mod.rs @@ -62,7 +62,7 @@ impl Capturer { let mut desc = unsafe { mem::MaybeUninit::uninit().assume_init() }; let mut gdi_capturer = None; - let mut res = if display.gdi { + let mut res = if display.gdi.is_some() { wrap_hresult(1) } else { wrap_hresult(unsafe { @@ -367,48 +367,60 @@ impl Displays { let mut i: DWORD = 0; loop { #[allow(invalid_value)] - let mut d: DISPLAY_DEVICEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; - d.cb = std::mem::size_of::() as _; - let ok = unsafe { EnumDisplayDevicesW(std::ptr::null(), i, &mut d as _, 0) }; + let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; + dd.cb = std::mem::size_of::() as _; + let ok = unsafe { EnumDisplayDevicesW(std::ptr::null(), i, &mut dd as _, 0) }; if ok == FALSE { break; } i += 1; - if 0 == (d.StateFlags & DISPLAY_DEVICE_ACTIVE) - || (d.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) > 0 + if 0 == (dd.StateFlags & DISPLAY_DEVICE_ACTIVE) + || (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) > 0 { continue; } - // let is_primary = (d.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) > 0; let mut disp = Display { inner: ComPtr(std::ptr::null_mut()), adapter: ComPtr(std::ptr::null_mut()), desc: unsafe { std::mem::zeroed() }, - gdi: true, + gdi: None, }; - disp.desc.DeviceName = d.DeviceName; + disp.desc.DeviceName = dd.DeviceName.clone(); #[allow(invalid_value)] - let mut m: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; - m.dmSize = std::mem::size_of::() as _; - m.dmDriverExtra = 0; - let ok = unsafe { - EnumDisplaySettingsExW( - disp.desc.DeviceName.as_ptr(), - ENUM_CURRENT_SETTINGS, - &mut m as _, - 0, - ) - }; - if ok == FALSE { - continue; + let mut dm: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; + dm.dmSize = std::mem::size_of::() as _; + dm.dmDriverExtra = 0; + unsafe { + if FALSE + == EnumDisplaySettingsExW( + disp.desc.DeviceName.as_ptr(), + ENUM_CURRENT_SETTINGS, + &mut dm as _, + 0, + ) + { + if FALSE + == EnumDisplaySettingsExW( + disp.desc.DeviceName.as_ptr(), + ENUM_REGISTRY_SETTINGS, + &mut dm as _, + 0, + ) + { + continue; + } + } } - disp.desc.DesktopCoordinates.left = unsafe { m.u1.s2().dmPosition.x }; - disp.desc.DesktopCoordinates.top = unsafe { m.u1.s2().dmPosition.y }; + disp.desc.DesktopCoordinates.left = unsafe { dm.u1.s2().dmPosition.x }; + disp.desc.DesktopCoordinates.top = unsafe { dm.u1.s2().dmPosition.y }; disp.desc.DesktopCoordinates.right = - disp.desc.DesktopCoordinates.left + m.dmPelsWidth as i32; + disp.desc.DesktopCoordinates.left + dm.dmPelsWidth as i32; disp.desc.DesktopCoordinates.bottom = - disp.desc.DesktopCoordinates.top + m.dmPelsHeight as i32; + disp.desc.DesktopCoordinates.top + dm.dmPelsHeight as i32; disp.desc.AttachedToDesktop = 1; + + let is_primary = (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) > 0; + disp.gdi = Some(GdiDisplayInfo { dd, dm, is_primary }); all.push(disp); } all @@ -476,7 +488,7 @@ impl Displays { inner: ComPtr(inner), adapter: ComPtr(self.adapter.0), desc, - gdi: false, + gdi: None, })) } } @@ -512,7 +524,13 @@ pub struct Display { inner: ComPtr, adapter: ComPtr, desc: DXGI_OUTPUT_DESC, - gdi: bool, + gdi: Option, +} + +pub struct GdiDisplayInfo { + pub dd: DISPLAY_DEVICEW, + pub dm: DEVMODEW, + pub is_primary: bool, } // optimized for updated region @@ -537,6 +555,10 @@ impl Display { self.desc.Rotation } + pub fn gdi(&self) -> &Option { + &self.gdi + } + fn create_gdi(&self) -> Option { if let Ok(res) = CapturerGDI::new(self.name(), self.width(), self.height()) { Some(res) From 35ec3ffef6d404b5744be7e849886a06e42451b4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 14 Apr 2023 21:31:12 +0800 Subject: [PATCH 312/366] tmp commit Signed-off-by: fufesou --- Cargo.lock | 3 +- Cargo.toml | 1 - libs/hbb_common/Cargo.toml | 1 + libs/hbb_common/src/config.rs | 15 ++ libs/hbb_common/src/lib.rs | 2 + libs/virtual_display/Cargo.toml | 1 - libs/virtual_display/dylib/src/lib.rs | 101 +++----- .../examples/virtual_display_1.rs | 19 +- libs/virtual_display/src/lib.rs | 243 +++++++++++------- src/flutter.rs | 15 +- src/server/video_service.rs | 28 +- 11 files changed, 253 insertions(+), 176 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 505a6f2d6..098189693 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2776,6 +2776,7 @@ dependencies = [ "confy", "directories-next", "dirs-next", + "dlopen", "env_logger 0.10.0", "filetime", "flexi_logger", @@ -5118,7 +5119,6 @@ dependencies = [ "dbus-crossroads", "default-net", "dispatch", - "dlopen", "enigo", "errno", "evdev", @@ -6451,7 +6451,6 @@ version = "0.1.0" dependencies = [ "hbb_common", "lazy_static", - "libloading", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index f1c39a64d..94ef44e0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,7 +65,6 @@ flutter_rust_bridge = { version = "1.61.1", optional = true } errno = "0.3" rdev = { git = "https://github.com/fufesou/rdev" } url = { version = "2.1", features = ["serde"] } -dlopen = "0.1" crossbeam-queue = "0.3" hex = "0.4" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index ed9ec73be..4ce9ef37e 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -34,6 +34,7 @@ tokio-socks = { git = "https://github.com/open-trade/tokio-socks" } chrono = "0.4" backtrace = "0.3" libc = "0.2" +dlopen = "0.1" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] mac_address = "1.1" diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 369920982..8786858e2 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1141,6 +1141,8 @@ pub struct LocalConfig { // Various data for flutter ui #[serde(default)] ui_flutter: HashMap, + #[serde(default)] + virtual_display_num: usize, } impl LocalConfig { @@ -1243,6 +1245,19 @@ impl LocalConfig { config.store(); } } + + pub fn get_virtual_display_num() -> usize { + LOCAL_CONFIG.read().unwrap().virtual_display_num + } + + pub fn set_virtual_display_num(virtual_display_num: usize) { + let mut lock = LOCAL_CONFIG.write().unwrap(); + if lock.virtual_display_num == virtual_display_num { + return; + } + lock.virtual_display_num = virtual_display_num; + lock.store(); + } } #[derive(Debug, Default, Serialize, Deserialize, Clone)] diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index 799093d24..7d841613d 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -44,6 +44,8 @@ pub use libc; pub mod keyboard; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use sysinfo; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub use dlopen; #[cfg(feature = "quic")] pub type Stream = quic::Connection; diff --git a/libs/virtual_display/Cargo.toml b/libs/virtual_display/Cargo.toml index c700bd12a..2c2372212 100644 --- a/libs/virtual_display/Cargo.toml +++ b/libs/virtual_display/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" [dependencies] lazy_static = "1.4" -libloading = "0.7" hbb_common = { path = "../hbb_common" } diff --git a/libs/virtual_display/dylib/src/lib.rs b/libs/virtual_display/dylib/src/lib.rs index 4a95e3461..3b83d297c 100644 --- a/libs/virtual_display/dylib/src/lib.rs +++ b/libs/virtual_display/dylib/src/lib.rs @@ -2,18 +2,21 @@ pub mod win10; use hbb_common::{bail, lazy_static, ResultType}; -use std::{path::Path, sync::Mutex}; +use std::path::Path; +#[cfg(windows)] +use std::sync::Mutex; + +#[cfg(windows)] lazy_static::lazy_static! { // If device is uninstalled though "Device Manager" Window. // Rustdesk is unable to handle device any more... static ref H_SW_DEVICE: Mutex = Mutex::new(0); - static ref MONITOR_PLUGIN: Mutex> = Mutex::new(Vec::new()); } #[no_mangle] #[cfg(windows)] -pub fn get_dirver_install_path() -> &'static str { +pub fn get_driver_install_path() -> &'static str { win10::DRIVER_INSTALL_PATH } @@ -137,68 +140,48 @@ pub fn close_device() { unsafe { win10::idd::DeviceClose(*H_SW_DEVICE.lock().unwrap() as win10::idd::HSWDEVICE); *H_SW_DEVICE.lock().unwrap() = 0; - MONITOR_PLUGIN.lock().unwrap().clear(); } } #[no_mangle] -pub fn plug_in_monitor() -> ResultType<()> { +pub fn plug_in_monitor(_monitor_index: u32, _edid: u32, _retries: u32) -> ResultType<()> { #[cfg(windows)] unsafe { - let monitor_index = 0 as u32; - let mut plug_in_monitors = MONITOR_PLUGIN.lock().unwrap(); - for i in 0..plug_in_monitors.len() { - if let Some(d) = plug_in_monitors.get(i) { - if *d == monitor_index { - return Ok(()); - } - }; - } - if win10::idd::MonitorPlugIn(monitor_index, 0, 30) == win10::idd::FALSE { - bail!("{}", win10::get_last_msg()?); - } - (*plug_in_monitors).push(monitor_index); - } - Ok(()) -} - -#[no_mangle] -pub fn plug_out_monitor() -> ResultType<()> { - #[cfg(windows)] - unsafe { - let monitor_index = 0 as u32; - if win10::idd::MonitorPlugOut(monitor_index) == win10::idd::FALSE { - bail!("{}", win10::get_last_msg()?); - } - let mut plug_in_monitors = MONITOR_PLUGIN.lock().unwrap(); - for i in 0..plug_in_monitors.len() { - if let Some(d) = plug_in_monitors.get(i) { - if *d == monitor_index { - plug_in_monitors.remove(i); - break; - } - }; - } - } - Ok(()) -} - -#[no_mangle] -pub fn update_monitor_modes() -> ResultType<()> { - #[cfg(windows)] - unsafe { - let monitor_index = 0 as u32; - let mut modes = vec![win10::idd::MonitorMode { - width: 1920, - height: 1080, - sync: 60, - }]; - if win10::idd::FALSE - == win10::idd::MonitorModesUpdate( - monitor_index as win10::idd::UINT, - modes.len() as win10::idd::UINT, - modes.as_mut_ptr(), - ) + if win10::idd::MonitorPlugIn(_monitor_index as _, _edid as _, _retries as _) + == win10::idd::FALSE + { + bail!("{}", win10::get_last_msg()?); + } + } + Ok(()) +} + +#[no_mangle] +pub fn plug_out_monitor(_monitor_index: u32) -> ResultType<()> { + #[cfg(windows)] + unsafe { + if win10::idd::MonitorPlugOut(_monitor_index) == win10::idd::FALSE { + bail!("{}", win10::get_last_msg()?); + } + } + Ok(()) +} + +#[cfg(windows)] +type PMonitorMode = win10::idd::PMonitorMode; +#[cfg(not(windows))] +type PMonitorMode = *mut std::ffi::c_void; + +#[no_mangle] +pub fn update_monitor_modes( + _monitor_index: u32, + _mode_count: u32, + _modes: PMonitorMode, +) -> ResultType<()> { + #[cfg(windows)] + unsafe { + if win10::idd::FALSE + == win10::idd::MonitorModesUpdate(_monitor_index as _, _mode_count as _, _modes) { bail!("{}", win10::get_last_msg()?); } diff --git a/libs/virtual_display/examples/virtual_display_1.rs b/libs/virtual_display/examples/virtual_display_1.rs index 31fdbe06e..1471e3faf 100644 --- a/libs/virtual_display/examples/virtual_display_1.rs +++ b/libs/virtual_display/examples/virtual_display_1.rs @@ -18,18 +18,18 @@ fn prompt_input() -> u8 { .unwrap_or(0) } -fn plug_in() { +fn plug_in(monitor_index: u32) { println!("Plug in monitor begin"); - if let Err(e) = virtual_display::plug_in_monitor() { + if let Err(e) = virtual_display::plug_in_monitor(monitor_index as _) { println!("{}", e); } else { println!("Plug in monitor done"); } } -fn plug_out() { +fn plug_out(monitor_index: u32) { println!("Plug out monitor begin"); - if let Err(e) = virtual_display::plug_out_monitor() { + if let Err(e) = virtual_display::plug_out_monitor(monitor_index as _) { println!("{}", e); } else { println!("Plug out monitor done"); @@ -38,7 +38,8 @@ fn plug_out() { fn main() { loop { - match prompt_input() as char { + let chr = prompt_input(); + match chr as char { 'x' => break, 'i' => { println!("Install or update driver begin"); @@ -81,8 +82,12 @@ fn main() { virtual_display::close_device(); println!("Close device done"); } - '1' => plug_in(), - '4' => plug_out(), + '1' => plug_in(0), + '2' => plug_in(1), + '3' => plug_in(2), + '4' => plug_out(0), + '5' => plug_out(1), + '6' => plug_out(2), _ => {} } } diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs index cd9402c69..f7e904537 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -1,12 +1,93 @@ -use hbb_common::{bail, ResultType}; -use std::sync::{Arc, Mutex}; +use hbb_common::{anyhow, dlopen::symbor::Library, log, ResultType}; +use std::{ + collections::HashSet, + sync::{Arc, Mutex}, +}; const LIB_NAME_VIRTUAL_DISPLAY: &str = "dylib_virtual_display"; +pub type DWORD = ::std::os::raw::c_ulong; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _MonitorMode { + pub width: DWORD, + pub height: DWORD, + pub sync: DWORD, +} +pub type MonitorMode = _MonitorMode; +pub type PMonitorMode = *mut MonitorMode; + +pub type GetDriverInstallPath = fn() -> &'static str; +pub type IsDeviceCreated = fn() -> bool; +pub type CloseDevice = fn(); +pub type DownLoadDriver = fn() -> ResultType<()>; +pub type CreateDevice = fn() -> ResultType<()>; +pub type InstallUpdateDriver = fn(&mut bool) -> ResultType<()>; +pub type UninstallDriver = fn(&mut bool) -> ResultType<()>; +pub type PlugInMonitor = fn(u32) -> ResultType<()>; +pub type PlugOutMonitor = fn(u32) -> ResultType<()>; +pub type UpdateMonitorModes = fn(u32, u32, PMonitorMode) -> ResultType<()>; + +macro_rules! make_lib_wrapper { + ($($field:ident : $tp:ty),+) => { + struct LibWrapper { + _lib: Option, + $($field: Option<$tp>),+ + } + + impl LibWrapper { + fn new() -> Self { + let lib = match Library::open(get_lib_name()) { + Ok(lib) => Some(lib), + Err(e) => { + log::warn!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e); + None + } + }; + + $(let $field = if let Some(lib) = &lib { + match unsafe { lib.symbol::<$tp>(stringify!($field)) } { + Ok(m) => Some(*m), + Err(e) => { + log::warn!("Failed to load func {}, {}", stringify!($field), e); + None + } + } + } else { + None + };)+ + + Self { + _lib: lib, + $( $field ),+ + } + } + } + + impl Default for LibWrapper { + fn default() -> Self { + Self::new() + } + } + } +} + +make_lib_wrapper!( + get_driver_install_path: GetDriverInstallPath, + is_device_created: IsDeviceCreated, + close_device: CloseDevice, + download_driver: DownLoadDriver, + create_device: CreateDevice, + install_update_driver: InstallUpdateDriver, + uninstall_driver: UninstallDriver, + plug_in_monitor: PlugInMonitor, + plug_out_monitor: PlugOutMonitor, + update_monitor_modes: UpdateMonitorModes +); + lazy_static::lazy_static! { - static ref LIB_VIRTUAL_DISPLAY: Arc>> = { - Arc::new(Mutex::new(unsafe { libloading::Library::new(get_lib_name()) })) - }; + static ref LIB_WRAPPER: Arc> = Default::default(); + static ref MONITOR_INDICES: Mutex> = Mutex::new(HashSet::new()); } #[cfg(target_os = "windows")] @@ -24,102 +105,90 @@ fn get_lib_name() -> String { format!("lib{}.dylib", LIB_NAME_VIRTUAL_DISPLAY) } -fn try_reload_lib() { - let mut lock = LIB_VIRTUAL_DISPLAY.lock().unwrap(); - if lock.is_err() { - *lock = unsafe { libloading::Library::new(get_lib_name()) }; - } -} - #[cfg(windows)] -pub fn get_dirver_install_path() -> ResultType<&'static str> { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get:: &'static str>>(b"get_dirver_install_path") { - Ok(func) => Ok(func()), - Err(e) => bail!("Failed to load func get_dirver_install_path, {}", e), - } - }, - Err(e) => bail!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e), - } +pub fn get_driver_install_path() -> Option<&'static str> { + Some(LIB_WRAPPER.lock().unwrap().get_driver_install_path?()) } pub fn is_device_created() -> bool { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get:: bool>>(b"is_device_created") { - Ok(func) => func(), - Err(..) => false, - } - }, - Err(..) => false, - } + LIB_WRAPPER + .lock() + .unwrap() + .is_device_created + .map(|f| f()) + .unwrap_or(false) } pub fn close_device() { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get::>(b"close_device") { - Ok(func) => func(), - Err(..) => {} - } - }, - Err(..) => {} - } + let _r = LIB_WRAPPER.lock().unwrap().close_device.map(|f| f()); } -macro_rules! def_func_result { - ($func:ident, $name: tt) => { - pub fn $func() -> ResultType<()> { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get:: ResultType<()>>>($name.as_bytes()) { - Ok(func) => func(), - Err(e) => bail!("Failed to load func {}, {}", $name, e), - } - }, - Err(e) => bail!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e), - } - } - }; +pub fn download_driver() -> ResultType<()> { + LIB_WRAPPER + .lock() + .unwrap() + .download_driver + .ok_or(anyhow::Error::msg("download_driver method not found"))?() +} + +pub fn create_device() -> ResultType<()> { + LIB_WRAPPER + .lock() + .unwrap() + .create_device + .ok_or(anyhow::Error::msg("create_device method not found"))?() } pub fn install_update_driver(reboot_required: &mut bool) -> ResultType<()> { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib.get:: ResultType<()>>>( - b"install_update_driver", - ) { - Ok(func) => func(reboot_required), - Err(e) => bail!("Failed to load func install_update_driver, {}", e), - } - }, - Err(e) => bail!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e), - } + LIB_WRAPPER + .lock() + .unwrap() + .install_update_driver + .ok_or(anyhow::Error::msg("install_update_driver method not found"))?(reboot_required) } pub fn uninstall_driver(reboot_required: &mut bool) -> ResultType<()> { - try_reload_lib(); - match &*LIB_VIRTUAL_DISPLAY.lock().unwrap() { - Ok(lib) => unsafe { - match lib - .get:: ResultType<()>>>(b"uninstall_driver") - { - Ok(func) => func(reboot_required), - Err(e) => bail!("Failed to load func uninstall_driver, {}", e), - } - }, - Err(e) => bail!("Failed to load library {}, {}", LIB_NAME_VIRTUAL_DISPLAY, e), - } + LIB_WRAPPER + .lock() + .unwrap() + .uninstall_driver + .ok_or(anyhow::Error::msg("uninstall_driver method not found"))?(reboot_required) } -def_func_result!(download_driver, "download_driver"); -def_func_result!(create_device, "create_device"); -def_func_result!(plug_in_monitor, "plug_in_monitor"); -def_func_result!(plug_out_monitor, "plug_out_monitor"); -def_func_result!(update_monitor_modes, "update_monitor_modes"); +#[cfg(windows)] +pub fn plug_in_monitor(monitor_index: u32) -> ResultType<()> { + let mut lock = MONITOR_INDICES.lock().unwrap(); + if lock.contains(&monitor_index) { + return Ok(()); + } + let f = LIB_WRAPPER + .lock() + .unwrap() + .plug_in_monitor + .ok_or(anyhow::Error::msg("plug_in_monitor method not found"))?; + f(monitor_index)?; + lock.insert(monitor_index); + Ok(()) +} + +#[cfg(windows)] +pub fn plug_out_monitor(monitor_index: u32) -> ResultType<()> { + let f = LIB_WRAPPER + .lock() + .unwrap() + .plug_out_monitor + .ok_or(anyhow::Error::msg("plug_out_monitor method not found"))?; + f(monitor_index)?; + MONITOR_INDICES.lock().unwrap().remove(&monitor_index); + Ok(()) +} + +#[cfg(windows)] +pub fn update_monitor_modes(monitor_index: u32, modes: &[MonitorMode]) -> ResultType<()> { + let f = LIB_WRAPPER + .lock() + .unwrap() + .update_monitor_modes + .ok_or(anyhow::Error::msg("update_monitor_modes method not found"))?; + f(monitor_index, modes.len() as _, modes.as_ptr() as _) +} diff --git a/src/flutter.rs b/src/flutter.rs index 6c9ff7f37..8c4522e47 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -3,18 +3,19 @@ use crate::{ flutter_ffi::EventToUI, ui_session_interface::{io_loop, InvokeUiSession, Session}, }; -#[cfg(feature = "flutter_texture_render")] -use dlopen::{ - symbor::{Library, Symbol}, - Error as LibError, -}; use flutter_rust_bridge::StreamSink; -#[cfg(feature = "flutter_texture_render")] -use hbb_common::libc::c_void; use hbb_common::{ bail, config::LocalConfig, get_version_number, log, message_proto::*, rendezvous_proto::ConnType, ResultType, }; +#[cfg(feature = "flutter_texture_render")] +use hbb_common::{ + dlopen::{ + symbor::{Library, Symbol}, + Error as LibError, + }, + libc::c_void, +}; use serde_json::json; #[cfg(not(feature = "flutter_texture_render"))] diff --git a/src/server/video_service.rs b/src/server/video_service.rs index e6a4e3806..e5394157d 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -23,6 +23,8 @@ use super::{video_qos::VideoQoS, *}; use crate::platform::windows::is_process_consent_running; #[cfg(all(windows, feature = "privacy_win_mag"))] use crate::privacy_mode::privacy_win_mag; +#[cfg(all(windows, feature = "virtual_display_driver"))] +use hbb_common::config::LocalConfig; #[cfg(all(windows, feature = "privacy_win_mag"))] use hbb_common::get_version_number; use hbb_common::tokio::sync::{ @@ -48,6 +50,9 @@ use std::{ #[cfg(all(windows, feature = "virtual_display_driver"))] use virtual_display; +#[cfg(all(windows, feature = "virtual_display_driver"))] +const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0; + pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; pub const SCRAP_OTHER_VERSION_OR_X11_REQUIRED: &str = "Wayland requires higher version of linux distro. Please try X11 desktop or change your OS."; @@ -936,21 +941,20 @@ fn try_get_displays() -> ResultType> { if displays.len() == 0 { log::debug!("no displays, create virtual display"); // Try plugin monitor - if !virtual_display::is_device_created() { - if let Err(e) = virtual_display::create_device() { - log::debug!("Create device failed {}", e); - } - } - if virtual_display::is_device_created() { - if let Err(e) = virtual_display::plug_in_monitor() { - log::debug!("Plug in monitor failed {}", e); - } else { - if let Err(e) = virtual_display::update_monitor_modes() { - log::debug!("Update monitor modes failed {}", e); + if LocalConfig::get_virtual_display_num() > 0 { + if !virtual_display::is_device_created() { + if let Err(e) = virtual_display::create_device() { + log::debug!("Create device failed {}", e); } } + if virtual_display::is_device_created() { + if let Err(e) = virtual_display::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS) + { + log::debug!("Plug in monitor failed {}", e); + } + } + displays = Display::all()?; } - displays = Display::all()?; } else if displays.len() > 1 { // to-do: do not close if in privacy mode. From 042a4e575f5c54ee4b7ca8f89a863862141062d0 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 09:48:39 +0800 Subject: [PATCH 313/366] tmp commit Signed-off-by: fufesou --- libs/virtual_display/Cargo.toml | 2 +- .../dylib/src/win10/IddController.c | 4 +- src/lib.rs | 2 +- src/platform/windows.rs | 50 ++--- src/privacy_mode/mod.rs | 43 ---- src/privacy_mode/privacy_win_idd.rs | 188 ------------------ src/{privacy_mode => }/privacy_win_mag.rs | 8 +- src/server/connection.rs | 12 +- src/server/video_service.rs | 46 +++-- src/ui_cm_interface.rs | 4 +- 10 files changed, 67 insertions(+), 292 deletions(-) delete mode 100644 src/privacy_mode/mod.rs delete mode 100644 src/privacy_mode/privacy_win_idd.rs rename src/{privacy_mode => }/privacy_win_mag.rs (97%) diff --git a/libs/virtual_display/Cargo.toml b/libs/virtual_display/Cargo.toml index 2c2372212..2559ffbc0 100644 --- a/libs/virtual_display/Cargo.toml +++ b/libs/virtual_display/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" [dependencies] lazy_static = "1.4" -hbb_common = { path = "../hbb_common" } +hbb_common = { path = "../hbb_common" } \ No newline at end of file diff --git a/libs/virtual_display/dylib/src/win10/IddController.c b/libs/virtual_display/dylib/src/win10/IddController.c index c1faccfc2..6c240657a 100644 --- a/libs/virtual_display/dylib/src/win10/IddController.c +++ b/libs/virtual_display/dylib/src/win10/IddController.c @@ -196,7 +196,7 @@ BOOL DeviceCreate(PHSWDEVICE hSwDevice) } if (created == TRUE) { - SetLastMsg("Device is created before, please uninstall it first\n"); + SetLastMsg("Device is already created, please destroy it first\n"); if (g_printMsg) { printf(g_lastMsg); @@ -288,7 +288,7 @@ BOOL MonitorPlugIn(UINT index, UINT edid, INT retries) if (retries < 0) { - SetLastMsg("invalid tries %d\n", retries); + SetLastMsg("Invalid tries %d\n", retries); if (g_printMsg) { printf(g_lastMsg); diff --git a/src/lib.rs b/src/lib.rs index 7a7062b27..f52fbd1da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,4 +68,4 @@ pub mod clipboard_file; pub mod rc; #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub mod privacy_mode; +pub mod privacy_win_mag; diff --git a/src/platform/windows.rs b/src/platform/windows.rs index ab76c8559..b0b0d18e6 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -1,8 +1,10 @@ use super::{CursorData, ResultType}; use crate::common::PORTABLE_APPNAME_RUNTIME_ENV_KEY; -#[cfg(feature = "privacy_win_mag")] -use crate::privacy_mode::privacy_win_mag; -use crate::{ipc, license::*, privacy_mode::WIN_MAG_INJECTED_PROCESS_EXE}; +use crate::{ + ipc, + license::*, + privacy_win_mag::{self, WIN_MAG_INJECTED_PROCESS_EXE}, +}; use hbb_common::{ allow_err, bail, config::{self, Config}, @@ -10,12 +12,10 @@ use hbb_common::{ message_proto::Resolution, sleep, timeout, tokio, }; -#[cfg(feature = "privacy_win_mag")] -use std::fs; use std::{ collections::HashMap, ffi::OsString, - io, + fs, io, io::prelude::*, mem, os::windows::process::CommandExt, @@ -839,7 +839,6 @@ fn get_default_install_path() -> String { format!("{}\\{}", pf, crate::get_app_name()) } -#[cfg(feature = "privacy_win_mag")] pub fn check_update_broker_process() -> ResultType<()> { // let (_, path, _, _) = get_install_info(); let process_exe = privacy_win_mag::INJECTED_PROCESS_EXE; @@ -925,32 +924,17 @@ pub fn copy_raw_cmd(src_raw: &str, _raw: &str, _path: &str) -> String { pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { let main_exe = copy_raw_cmd(src_exe, exe, path); - - #[cfg(feature = "privacy_win_mag")] - { - format!( - " - {main_exe} - copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" - \"{src_exe}\" --extract \"{path}\" - ", - main_exe = main_exe, - path = path, - ORIGIN_PROCESS_EXE = privacy_win_mag::ORIGIN_PROCESS_EXE, - broker_exe = privacy_win_mag::INJECTED_PROCESS_EXE, - ) - } - #[cfg(not(feature = "privacy_win_mag"))] - { - format!( - " - {main_exe} - \"{src_exe}\" --extract \"{path}\" - ", - main_exe = main_exe, - path = path, - ) - } + format!( + " + {main_exe} + copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" + \"{src_exe}\" --extract \"{path}\" + ", + main_exe = main_exe, + path = path, + ORIGIN_PROCESS_EXE = privacy_win_mag::ORIGIN_PROCESS_EXE, + broker_exe = privacy_win_mag::INJECTED_PROCESS_EXE, + ) } pub fn update_me() -> ResultType<()> { diff --git a/src/privacy_mode/mod.rs b/src/privacy_mode/mod.rs deleted file mode 100644 index 9b21177f8..000000000 --- a/src/privacy_mode/mod.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::ipc::PrivacyModeState; -use hbb_common::{bail, ResultType}; - -#[cfg(all(windows, feature = "privacy_win_mag"))] -pub mod privacy_win_mag; -pub const WIN_MAG_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; - -pub const OCCUPIED: &'static str = "Privacy occupied by another one"; -pub const TURN_OFF_OTHER_ID: &'static str = - "Failed to turn off privacy mode that belongs to someone else"; -pub const NO_DISPLAYS: &'static str = "No displays"; - -#[cfg(all(windows, feature = "virtual_display_driver"))] -pub mod privacy_win_idd; - -pub trait PrivacyMode { - fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType; - fn turn_off_privacy(&mut self, conn_id: i32, state: Option) - -> ResultType<()>; - - fn cur_conn_id(&self) -> i32; - - #[inline] - fn check_on_conn_id(&self, conn_id: i32) -> ResultType { - let pre_conn_id = self.cur_conn_id(); - if pre_conn_id == conn_id { - return Ok(true); - } - if pre_conn_id != 0 { - bail!(OCCUPIED); - } - Ok(false) - } - - #[inline] - fn check_off_conn_id(&self, conn_id: i32) -> ResultType<()> { - let pre_conn_id = self.cur_conn_id(); - if pre_conn_id != 0 && conn_id != 0 && pre_conn_id != conn_id { - bail!(TURN_OFF_OTHER_ID) - } - Ok(()) - } -} diff --git a/src/privacy_mode/privacy_win_idd.rs b/src/privacy_mode/privacy_win_idd.rs deleted file mode 100644 index 13798bf16..000000000 --- a/src/privacy_mode/privacy_win_idd.rs +++ /dev/null @@ -1,188 +0,0 @@ -use super::{PrivacyModeState, NO_DISPLAYS}; -use hbb_common::{allow_err, bail, lazy_static, log, ResultType}; -use scrap::dxgi; -use winapi::{ - shared::{ - minwindef::{DWORD, FALSE}, - ntdef::{NULL, WCHAR}, - }, - um::{ - errhandlingapi::GetLastError, - wingdi::{DEVMODEW, DISPLAY_DEVICEW, DISPLAY_DEVICE_ATTACHED_TO_DESKTOP, DM_POSITION}, - winuser::{ - ChangeDisplaySettingsExW, EnumDisplayDevicesW, EnumDisplaySettingsW, CDS_NORESET, - CDS_SET_PRIMARY, CDS_UPDATEREGISTRY, DISP_CHANGE_SUCCESSFUL, - EDD_GET_DEVICE_INTERFACE_NAME, ENUM_CURRENT_SETTINGS, - }, - }, -}; - -const IDD_DEVICE_STRING: &'static str = "RustDeskIddDriver Device"; - -struct Display { - dm: DEVMODEW, - name: [WCHAR; 32], - primary: bool, -} - -pub struct PrivacyModeImpl { - conn_id: i32, - displays: Vec, - virtual_displays: Vec, - virtual_displays_added: Option, -} - -impl PrivacyModeImpl { - fn set_displays(&mut self) { - self.displays.clear(); - self.virtual_displays.clear(); - for display in dxgi::Displays::get_from_gdi().into_iter() { - if let Some(gdi_info) = display.gdi() { - if let Ok(s) = std::string::String::from_utf16(&gdi_info.dd.DeviceString) { - if s == IDD_DEVICE_STRING { - self.virtual_displays.push(Display { - dm: gdi_info.dm, - name: gdi_info.dd.DeviceName, - primary: gdi_info.is_primary, - }); - continue; - } - } - self.displays.push(Display { - dm: gdi_info.dm, - name: gdi_info.dd.DeviceName, - primary: gdi_info.is_primary, - }); - } - } - } - - fn restore(&self) {} - - fn set_primary_display(&mut self) -> ResultType<()> { - self.ensure_virtual_display()?; - if self.virtual_displays.is_empty() { - bail!("No virtual displays"); - } - let display = &self.virtual_displays[0]; - - let mut new_primary_dm: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; - new_primary_dm.dmSize = std::mem::size_of::() as _; - new_primary_dm.dmDriverExtra = 0; - unsafe { - if FALSE - == EnumDisplaySettingsW( - display.name.as_ptr(), - ENUM_CURRENT_SETTINGS, - &mut new_primary_dm, - ) - { - bail!( - "Failed EnumDisplaySettingsW, device name: {:?}, error code: {}", - std::string::String::from_utf16(&display.name), - GetLastError() - ); - } - - let mut i: DWORD = 0; - loop { - let mut flags = CDS_UPDATEREGISTRY | CDS_NORESET; - let mut dd: DISPLAY_DEVICEW = std::mem::MaybeUninit::uninit().assume_init(); - dd.cb = std::mem::size_of::() as _; - if FALSE - == EnumDisplayDevicesW(NULL as _, i, &mut dd, EDD_GET_DEVICE_INTERFACE_NAME) - { - break; - } - i += 1; - if (dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0 { - continue; - } - - if dd.DeviceName == display.name { - flags |= CDS_SET_PRIMARY; - } - - let mut dm: DEVMODEW = std::mem::MaybeUninit::uninit().assume_init(); - dm.dmSize = std::mem::size_of::() as _; - dm.dmDriverExtra = 0; - if FALSE - == EnumDisplaySettingsW(dd.DeviceName.as_ptr(), ENUM_CURRENT_SETTINGS, &mut dm) - { - bail!( - "Failed EnumDisplaySettingsW, device name: {:?}, error code: {}", - std::string::String::from_utf16(&dd.DeviceName), - GetLastError() - ); - } - - dm.u1.s2_mut().dmPosition.x -= new_primary_dm.u1.s2().dmPosition.x; - dm.u1.s2_mut().dmPosition.y -= new_primary_dm.u1.s2().dmPosition.y; - dm.dmFields |= DM_POSITION; - let rc = ChangeDisplaySettingsExW( - dd.DeviceName.as_ptr(), - &mut dm, - NULL as _, - flags, - NULL, - ); - - if rc != DISP_CHANGE_SUCCESSFUL { - log::error!( - "Failed ChangeDisplaySettingsEx, device name: {:?}, flags: {}, ret: {}", - std::string::String::from_utf16(&dd.DeviceName), - flags, - rc - ); - bail!("Failed ChangeDisplaySettingsEx, ret: {}", rc); - } - } - } - - Ok(()) - } - - fn ensure_virtual_display(&mut self) -> ResultType<()> { - if self.virtual_displays.is_empty() { - virtual_display::create_device()?; - } - if virtual_display::is_device_created() { - // to-do: add monitor index here - virtual_display::plug_in_monitor()?; - self.virtual_displays_added = Some(0); - } - - self.set_displays(); - Ok(()) - } -} - -impl super::PrivacyMode for PrivacyModeImpl { - fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType { - if self.check_on_conn_id(conn_id)? { - return Ok(true); - } - self.set_displays(); - if self.displays.is_empty() { - bail!(NO_DISPLAYS); - } - self.set_primary_display()?; - - bail!("unimplemented") - } - - fn turn_off_privacy( - &mut self, - conn_id: i32, - state: Option, - ) -> ResultType<()> { - self.check_off_conn_id(conn_id)?; - self.restore(); - bail!("unimplemented") - } - - #[inline] - fn cur_conn_id(&self) -> i32 { - self.conn_id - } -} diff --git a/src/privacy_mode/privacy_win_mag.rs b/src/privacy_win_mag.rs similarity index 97% rename from src/privacy_mode/privacy_win_mag.rs rename to src/privacy_win_mag.rs index 923c42859..fe0ee4f69 100644 --- a/src/privacy_mode/privacy_win_mag.rs +++ b/src/privacy_win_mag.rs @@ -31,9 +31,15 @@ use winapi::{ }; pub const ORIGIN_PROCESS_EXE: &'static str = "C:\\Windows\\System32\\RuntimeBroker.exe"; -pub const INJECTED_PROCESS_EXE: &'static str = super::WIN_MAG_INJECTED_PROCESS_EXE; +pub const WIN_MAG_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; +pub const INJECTED_PROCESS_EXE: &'static str = WIN_MAG_INJECTED_PROCESS_EXE; pub const PRIVACY_WINDOW_NAME: &'static str = "RustDeskPrivacyWindow"; +pub const OCCUPIED: &'static str = "Privacy occupied by another one"; +pub const TURN_OFF_OTHER_ID: &'static str = + "Failed to turn off privacy mode that belongs to someone else"; +pub const NO_DISPLAYS: &'static str = "No displays"; + pub const GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT: u32 = 2; pub const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 4; diff --git a/src/server/connection.rs b/src/server/connection.rs index 25dea58ad..d4b645078 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2346,11 +2346,11 @@ fn try_activate_screen() { mod privacy_mode { use super::*; - #[cfg(all(windows, feature = "privacy_win_mag"))] - use crate::privacy_mode::privacy_win_mag; + #[cfg(windows)] + use crate::privacy_win_mag; pub(super) fn turn_off_privacy(_conn_id: i32) -> Message { - #[cfg(all(windows, feature = "privacy_win_mag"))] + #[cfg(windows)] { let res = privacy_win_mag::turn_off_privacy(_conn_id, None); match res { @@ -2365,19 +2365,19 @@ mod privacy_mode { } } } - #[cfg(not(all(windows, feature = "privacy_win_mag")))] + #[cfg(not(windows))] { crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::PrvOffFailed) } } pub(super) fn turn_on_privacy(_conn_id: i32) -> ResultType { - #[cfg(all(windows, feature = "privacy_win_mag"))] + #[cfg(windows)] { let plugin_exist = privacy_win_mag::turn_on_privacy(_conn_id)?; Ok(plugin_exist) } - #[cfg(not(all(windows, feature = "privacy_win_mag")))] + #[cfg(not(windows))] { Ok(true) } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index e5394157d..4789d4fd6 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -20,12 +20,10 @@ use super::{video_qos::VideoQoS, *}; #[cfg(windows)] -use crate::platform::windows::is_process_consent_running; -#[cfg(all(windows, feature = "privacy_win_mag"))] -use crate::privacy_mode::privacy_win_mag; +use crate::{platform::windows::is_process_consent_running, privacy_win_mag}; #[cfg(all(windows, feature = "virtual_display_driver"))] use hbb_common::config::LocalConfig; -#[cfg(all(windows, feature = "privacy_win_mag"))] +#[cfg(windows)] use hbb_common::get_version_number; use hbb_common::tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, @@ -78,9 +76,9 @@ lazy_static::lazy_static! { } fn is_capturer_mag_supported() -> bool { - #[cfg(all(windows, feature = "privacy_win_mag"))] + #[cfg(windows)] return scrap::CapturerMag::is_supported(); - #[cfg(not(all(windows, feature = "privacy_win_mag")))] + #[cfg(not(windows))] false } @@ -101,10 +99,10 @@ pub fn get_privacy_mode_conn_id() -> i32 { } pub fn is_privacy_mode_supported() -> bool { - #[cfg(all(windows, feature = "privacy_win_mag"))] + #[cfg(windows)] return *IS_CAPTURER_MAGNIFIER_SUPPORTED && get_version_number(&crate::VERSION) > get_version_number("1.1.9"); - #[cfg(not(all(windows, feature = "privacy_win_mag")))] + #[cfg(not(windows))] return false; } @@ -210,12 +208,12 @@ fn create_capturer( _current: usize, _portable_service_running: bool, ) -> ResultType> { - #[cfg(not(all(windows, feature = "privacy_win_mag")))] + #[cfg(not(windows))] let c: Option> = None; - #[cfg(all(windows, feature = "privacy_win_mag"))] + #[cfg(windows)] let mut c: Option> = None; if privacy_mode_id > 0 { - #[cfg(all(windows, feature = "privacy_win_mag"))] + #[cfg(windows)] { match scrap::CapturerMag::new( display.origin(), @@ -942,21 +940,39 @@ fn try_get_displays() -> ResultType> { log::debug!("no displays, create virtual display"); // Try plugin monitor if LocalConfig::get_virtual_display_num() > 0 { - if !virtual_display::is_device_created() { - if let Err(e) = virtual_display::create_device() { + let mut device_already_created = false; + if let Err(e) = virtual_display::create_device() { + if e.to_string().contains("Device is already created") { + device_already_created = true; + } else { log::debug!("Create device failed {}", e); } } - if virtual_display::is_device_created() { + if device_already_created || virtual_display::is_device_created() { + // Reboot is not required for this case. + let mut _reboot_required = false; + virtual_display::install_update_driver(&mut _reboot_required)?; if let Err(e) = virtual_display::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS) { log::debug!("Plug in monitor failed {}", e); + } else { + let modes = [virtual_display::MonitorMode { + width: 1920, + height: 1080, + sync: 60, + }]; + if let Err(e) = virtual_display::update_monitor_modes( + VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, + &modes, + ) { + log::debug!("Update monitor modes failed {}", e); + } } } displays = Display::all()?; } } else if displays.len() > 1 { - // to-do: do not close if in privacy mode. + // to-do: do not close if in idd privacy mode. // If more than one displays exists, close RustDeskVirtualDisplay if virtual_display::is_device_created() { diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index b738bd4ac..b6bb3e35d 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -495,7 +495,7 @@ impl IpcTaskRunner { #[cfg(not(any(target_os = "android", target_os = "ios")))] #[tokio::main(flavor = "current_thread")] pub async fn start_ipc(cm: ConnectionManager) { - #[cfg(all(windows, feature = "privacy_win_mag"))] + #[cfg(windows)] std::thread::spawn(move || { log::info!("try create privacy mode window"); if let Err(e) = crate::platform::windows::check_update_broker_process() { @@ -504,7 +504,7 @@ pub async fn start_ipc(cm: ConnectionManager) { e ); } - allow_err!(crate::privacy_mode::privacy_win_mag::start()); + allow_err!(crate::privacy_win_mag::start()); }); match ipc::new_listener("_cm").await { From 9d8e7745e2b8977a60b5c48c620441d8144b44ce Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 12:05:36 +0800 Subject: [PATCH 314/366] refact virtual display Signed-off-by: fufesou --- build.py | 4 +- libs/hbb_common/src/config.rs | 15 --- .../examples/virtual_display_1.rs | 4 +- libs/virtual_display/src/lib.rs | 5 +- src/lib.rs | 3 + src/server/video_service.rs | 60 ++-------- src/virtual_display_manager.rs | 106 ++++++++++++++++++ 7 files changed, 125 insertions(+), 72 deletions(-) create mode 100644 src/virtual_display_manager.rs diff --git a/build.py b/build.py index fb9e213db..cc00c0793 100755 --- a/build.py +++ b/build.py @@ -41,8 +41,8 @@ def get_version(): def parse_rc_features(feature): available_features = { 'IddDriver': { - 'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/RustDeskIddDriver_x64.zip', - 'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/checksum_md5', + 'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/RustDeskIddDriver_x64.zip', + 'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/checksum_md5', 'exclude': ['README.md', 'certmgr.exe', 'install_cert_runas_admin.bat'], }, 'PrivacyMode': { diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 8786858e2..369920982 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1141,8 +1141,6 @@ pub struct LocalConfig { // Various data for flutter ui #[serde(default)] ui_flutter: HashMap, - #[serde(default)] - virtual_display_num: usize, } impl LocalConfig { @@ -1245,19 +1243,6 @@ impl LocalConfig { config.store(); } } - - pub fn get_virtual_display_num() -> usize { - LOCAL_CONFIG.read().unwrap().virtual_display_num - } - - pub fn set_virtual_display_num(virtual_display_num: usize) { - let mut lock = LOCAL_CONFIG.write().unwrap(); - if lock.virtual_display_num == virtual_display_num { - return; - } - lock.virtual_display_num = virtual_display_num; - lock.store(); - } } #[derive(Debug, Default, Serialize, Deserialize, Clone)] diff --git a/libs/virtual_display/examples/virtual_display_1.rs b/libs/virtual_display/examples/virtual_display_1.rs index 1471e3faf..e5e1ae554 100644 --- a/libs/virtual_display/examples/virtual_display_1.rs +++ b/libs/virtual_display/examples/virtual_display_1.rs @@ -3,7 +3,7 @@ use virtual_display; fn prompt_input() -> u8 { println!("Press key execute:"); - println!(" 1. 'x' 1. exit"); + println!(" 1. 'q' 1. quit"); println!(" 2. 'i' 2. install or update driver"); println!(" 3. 'u' 3. uninstall driver"); println!(" 4. 'c' 4. create device"); @@ -40,7 +40,7 @@ fn main() { loop { let chr = prompt_input(); match chr as char { - 'x' => break, + 'q' => break, 'i' => { println!("Install or update driver begin"); let mut reboot_required = false; diff --git a/libs/virtual_display/src/lib.rs b/libs/virtual_display/src/lib.rs index f7e904537..578ffa2e9 100644 --- a/libs/virtual_display/src/lib.rs +++ b/libs/virtual_display/src/lib.rs @@ -47,7 +47,10 @@ macro_rules! make_lib_wrapper { $(let $field = if let Some(lib) = &lib { match unsafe { lib.symbol::<$tp>(stringify!($field)) } { - Ok(m) => Some(*m), + Ok(m) => { + log::info!("method found {}", stringify!($field)); + Some(*m) + }, Err(e) => { log::warn!("Failed to load func {}, {}", stringify!($field), e); None diff --git a/src/lib.rs b/src/lib.rs index f52fbd1da..52e9fcd5d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,3 +69,6 @@ pub mod rc; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub mod privacy_win_mag; + +#[cfg(all(windows, feature = "virtual_display_driver"))] +pub mod virtual_display_manager; diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 4789d4fd6..e6f29a405 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -19,10 +19,10 @@ // https://slhck.info/video/2017/03/01/rate-control.html use super::{video_qos::VideoQoS, *}; +#[cfg(all(windows, feature = "virtual_display_driver"))] +use crate::virtual_display_manager; #[cfg(windows)] use crate::{platform::windows::is_process_consent_running, privacy_win_mag}; -#[cfg(all(windows, feature = "virtual_display_driver"))] -use hbb_common::config::LocalConfig; #[cfg(windows)] use hbb_common::get_version_number; use hbb_common::tokio::sync::{ @@ -45,11 +45,6 @@ use std::{ ops::{Deref, DerefMut}, time::{self, Duration, Instant}, }; -#[cfg(all(windows, feature = "virtual_display_driver"))] -use virtual_display; - -#[cfg(all(windows, feature = "virtual_display_driver"))] -const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0; pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; pub const SCRAP_OTHER_VERSION_OR_X11_REQUIRED: &str = @@ -284,15 +279,8 @@ fn create_capturer( #[cfg(all(windows, feature = "virtual_display_driver"))] fn ensure_close_virtual_device() -> ResultType<()> { let num_displays = Display::all()?.len(); - if num_displays == 0 { - // Device may sometimes be uninstalled by user in "Device Manager" Window. - // Closing device will clear the instance data. - virtual_display::close_device(); - } else if num_displays > 1 { - // Try close device, if display device changed. - if virtual_display::is_device_created() { - virtual_display::close_device(); - } + if num_displays > 1 { + virtual_display_manager::plug_out_headless(); } Ok(()) } @@ -938,46 +926,14 @@ fn try_get_displays() -> ResultType> { let mut displays = Display::all()?; if displays.len() == 0 { log::debug!("no displays, create virtual display"); - // Try plugin monitor - if LocalConfig::get_virtual_display_num() > 0 { - let mut device_already_created = false; - if let Err(e) = virtual_display::create_device() { - if e.to_string().contains("Device is already created") { - device_already_created = true; - } else { - log::debug!("Create device failed {}", e); - } - } - if device_already_created || virtual_display::is_device_created() { - // Reboot is not required for this case. - let mut _reboot_required = false; - virtual_display::install_update_driver(&mut _reboot_required)?; - if let Err(e) = virtual_display::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS) - { - log::debug!("Plug in monitor failed {}", e); - } else { - let modes = [virtual_display::MonitorMode { - width: 1920, - height: 1080, - sync: 60, - }]; - if let Err(e) = virtual_display::update_monitor_modes( - VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, - &modes, - ) { - log::debug!("Update monitor modes failed {}", e); - } - } - } + if let Err(e) = virtual_display_manager::plug_in_headless() { + log::error!("plug in headless failed {}", e); + } else { displays = Display::all()?; } } else if displays.len() > 1 { - // to-do: do not close if in idd privacy mode. - // If more than one displays exists, close RustDeskVirtualDisplay - if virtual_display::is_device_created() { - virtual_display::close_device() - } + let _res = virtual_display_manager::plug_in_headless(); } Ok(displays) } diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs new file mode 100644 index 000000000..82328915e --- /dev/null +++ b/src/virtual_display_manager.rs @@ -0,0 +1,106 @@ +use hbb_common::{allow_err, bail, lazy_static, log, ResultType}; +use std::{ + collections::HashSet, + sync::{Arc, Mutex}, +}; + +// virtual display index range: 0 - 2 are reserved for headless and other special uses. +const VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS: u32 = 0; +const VIRTUAL_DISPLAY_START_FOR_PEER: u32 = 3; +const VIRTUAL_DISPLAY_MAX_COUNT: u32 = 10; + +lazy_static::lazy_static! { + static ref VIRTUAL_DISPLAY_MANAGER: Arc> = + Arc::new(Mutex::new(VirtualDisplayManager::default())); +} + +#[derive(Default)] +struct VirtualDisplayManager { + headless_index: Option, + peer_required_indices: HashSet, +} + +impl VirtualDisplayManager { + fn prepare_driver() -> ResultType<()> { + if let Err(e) = virtual_display::create_device() { + if !e.to_string().contains("Device is already created") { + bail!("Create device failed {}", e); + } + } + // Reboot is not required for this case. + let mut _reboot_required = false; + allow_err!(virtual_display::install_update_driver( + &mut _reboot_required + )); + Ok(()) + } + + fn plug_in_monitor(index: u32, modes: &[virtual_display::MonitorMode]) -> ResultType<()> { + if let Err(e) = virtual_display::plug_in_monitor(index) { + bail!("Plug in monitor failed {}", e); + } + if let Err(e) = virtual_display::update_monitor_modes(index, &modes) { + log::error!("Update monitor modes failed {}", e); + } + Ok(()) + } +} + +pub fn plug_in_headless() -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + VirtualDisplayManager::prepare_driver()?; + let modes = [virtual_display::MonitorMode { + width: 1920, + height: 1080, + sync: 60, + }]; + VirtualDisplayManager::plug_in_monitor(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS, &modes)?; + manager.headless_index = Some(VIRTUAL_DISPLAY_INDEX_FOR_HEADLESS); + Ok(()) +} + +pub fn plug_out_headless() { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + if let Some(index) = manager.headless_index.take() { + if let Err(e) = virtual_display::plug_out_monitor(index) { + log::error!("Plug out monitor failed {}", e); + } + } +} + +pub fn plug_in_peer_required( + modes: Vec>, +) -> ResultType> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + VirtualDisplayManager::prepare_driver()?; + + let mut indices: Vec = Vec::new(); + for m in modes.iter() { + for idx in VIRTUAL_DISPLAY_START_FOR_PEER..VIRTUAL_DISPLAY_MAX_COUNT { + if !manager.peer_required_indices.contains(&idx) { + match VirtualDisplayManager::plug_in_monitor(idx, m) { + Ok(_) => { + manager.peer_required_indices.insert(idx); + indices.push(idx); + } + Err(e) => { + log::error!("Plug in monitor failed {}", e); + } + } + } + } + } + + Ok(indices) +} + +pub fn plug_out_peer_required(modes: &[u32]) -> ResultType<()> { + let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); + for idx in modes.iter() { + if manager.peer_required_indices.contains(idx) { + allow_err!(virtual_display::plug_out_monitor(*idx)); + manager.peer_required_indices.remove(idx); + } + } + Ok(()) +} From 8f7b4b198d92255e360b2fc63c0f4b00fecdcddf Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 12:10:38 +0800 Subject: [PATCH 315/366] enable virtual_display_driver on win Signed-off-by: fufesou --- build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/build.py b/build.py index cc00c0793..053d59c47 100755 --- a/build.py +++ b/build.py @@ -242,6 +242,7 @@ def get_features(args): features = ['inline'] if not args.flutter else [] if windows: features.extend(get_rc_features(args)) + features.append('virtual_display_driver') if args.hwcodec: features.append('hwcodec') if args.flutter: From 4d6358f1c87e8d2e49d920c11aed500449309d62 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 12:16:16 +0800 Subject: [PATCH 316/366] revert scrap dxgi mode.rs Signed-off-by: fufesou --- libs/scrap/src/dxgi/mod.rs | 80 ++++++++++++++------------------------ 1 file changed, 29 insertions(+), 51 deletions(-) diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs index 2d9bd636f..547f18987 100644 --- a/libs/scrap/src/dxgi/mod.rs +++ b/libs/scrap/src/dxgi/mod.rs @@ -62,7 +62,7 @@ impl Capturer { let mut desc = unsafe { mem::MaybeUninit::uninit().assume_init() }; let mut gdi_capturer = None; - let mut res = if display.gdi.is_some() { + let mut res = if display.gdi { wrap_hresult(1) } else { wrap_hresult(unsafe { @@ -367,60 +367,48 @@ impl Displays { let mut i: DWORD = 0; loop { #[allow(invalid_value)] - let mut dd: DISPLAY_DEVICEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; - dd.cb = std::mem::size_of::() as _; - let ok = unsafe { EnumDisplayDevicesW(std::ptr::null(), i, &mut dd as _, 0) }; + let mut d: DISPLAY_DEVICEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; + d.cb = std::mem::size_of::() as _; + let ok = unsafe { EnumDisplayDevicesW(std::ptr::null(), i, &mut d as _, 0) }; if ok == FALSE { break; } i += 1; - if 0 == (dd.StateFlags & DISPLAY_DEVICE_ACTIVE) - || (dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) > 0 + if 0 == (d.StateFlags & DISPLAY_DEVICE_ACTIVE) + || (d.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) > 0 { continue; } + // let is_primary = (d.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) > 0; let mut disp = Display { inner: ComPtr(std::ptr::null_mut()), adapter: ComPtr(std::ptr::null_mut()), desc: unsafe { std::mem::zeroed() }, - gdi: None, + gdi: true, }; - disp.desc.DeviceName = dd.DeviceName.clone(); + disp.desc.DeviceName = d.DeviceName; #[allow(invalid_value)] - let mut dm: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; - dm.dmSize = std::mem::size_of::() as _; - dm.dmDriverExtra = 0; - unsafe { - if FALSE - == EnumDisplaySettingsExW( - disp.desc.DeviceName.as_ptr(), - ENUM_CURRENT_SETTINGS, - &mut dm as _, - 0, - ) - { - if FALSE - == EnumDisplaySettingsExW( - disp.desc.DeviceName.as_ptr(), - ENUM_REGISTRY_SETTINGS, - &mut dm as _, - 0, - ) - { - continue; - } - } + let mut m: DEVMODEW = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; + m.dmSize = std::mem::size_of::() as _; + m.dmDriverExtra = 0; + let ok = unsafe { + EnumDisplaySettingsExW( + disp.desc.DeviceName.as_ptr(), + ENUM_CURRENT_SETTINGS, + &mut m as _, + 0, + ) + }; + if ok == FALSE { + continue; } - disp.desc.DesktopCoordinates.left = unsafe { dm.u1.s2().dmPosition.x }; - disp.desc.DesktopCoordinates.top = unsafe { dm.u1.s2().dmPosition.y }; + disp.desc.DesktopCoordinates.left = unsafe { m.u1.s2().dmPosition.x }; + disp.desc.DesktopCoordinates.top = unsafe { m.u1.s2().dmPosition.y }; disp.desc.DesktopCoordinates.right = - disp.desc.DesktopCoordinates.left + dm.dmPelsWidth as i32; + disp.desc.DesktopCoordinates.left + m.dmPelsWidth as i32; disp.desc.DesktopCoordinates.bottom = - disp.desc.DesktopCoordinates.top + dm.dmPelsHeight as i32; + disp.desc.DesktopCoordinates.top + m.dmPelsHeight as i32; disp.desc.AttachedToDesktop = 1; - - let is_primary = (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) > 0; - disp.gdi = Some(GdiDisplayInfo { dd, dm, is_primary }); all.push(disp); } all @@ -488,7 +476,7 @@ impl Displays { inner: ComPtr(inner), adapter: ComPtr(self.adapter.0), desc, - gdi: None, + gdi: false, })) } } @@ -524,13 +512,7 @@ pub struct Display { inner: ComPtr, adapter: ComPtr, desc: DXGI_OUTPUT_DESC, - gdi: Option, -} - -pub struct GdiDisplayInfo { - pub dd: DISPLAY_DEVICEW, - pub dm: DEVMODEW, - pub is_primary: bool, + gdi: bool, } // optimized for updated region @@ -555,10 +537,6 @@ impl Display { self.desc.Rotation } - pub fn gdi(&self) -> &Option { - &self.gdi - } - fn create_gdi(&self) -> Option { if let Ok(res) = CapturerGDI::new(self.name(), self.width(), self.height()) { Some(res) @@ -607,4 +585,4 @@ fn wrap_hresult(x: HRESULT) -> io::Result<()> { } }) .into()) -} +} \ No newline at end of file From 67d29e749679879f50344216ab88b4d9a55e2068 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 12:17:31 +0800 Subject: [PATCH 317/366] trivail change Signed-off-by: fufesou --- libs/scrap/src/dxgi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs index 547f18987..4a0a53402 100644 --- a/libs/scrap/src/dxgi/mod.rs +++ b/libs/scrap/src/dxgi/mod.rs @@ -585,4 +585,4 @@ fn wrap_hresult(x: HRESULT) -> io::Result<()> { } }) .into()) -} \ No newline at end of file +} From b7af404afa6b240441c6805185b672b6fad1db13 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 13:01:38 +0800 Subject: [PATCH 318/366] fix build Signed-off-by: fufesou --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 52e9fcd5d..d5f791eeb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,7 +67,7 @@ pub mod clipboard_file; #[cfg(all(windows, feature = "with_rc"))] pub mod rc; -#[cfg(not(any(target_os = "android", target_os = "ios")))] +#[cfg(windows)] pub mod privacy_win_mag; #[cfg(all(windows, feature = "virtual_display_driver"))] From 8f51e021c811c06675b19427ddcd1ae7f0e11abe Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 14:18:00 +0800 Subject: [PATCH 319/366] remove simple_rc Signed-off-by: fufesou --- Cargo.toml | 3 +- build.py | 26 ++-- build.rs | 16 --- libs/simple_rc/Cargo.toml | 13 -- libs/simple_rc/examples/generate.rs | 23 --- libs/simple_rc/simple_rc.toml | 12 -- libs/simple_rc/src/lib.rs | 208 ---------------------------- src/core_main.rs | 4 - src/lib.rs | 3 - src/rc.rs | 38 ----- 10 files changed, 12 insertions(+), 334 deletions(-) delete mode 100644 libs/simple_rc/Cargo.toml delete mode 100644 libs/simple_rc/examples/generate.rs delete mode 100644 libs/simple_rc/simple_rc.toml delete mode 100644 libs/simple_rc/src/lib.rs delete mode 100644 src/rc.rs diff --git a/Cargo.toml b/Cargo.toml index 94ef44e0f..abf578f72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ path = "src/naming.rs" inline = [] hbbs = [] cli = [] -with_rc = ["simple_rc"] flutter_texture_render = [] appimage = [] flatpak = [] @@ -135,7 +134,7 @@ jni = "0.19" flutter_rust_bridge = "1.61.1" [workspace] -members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/simple_rc", "libs/portable"] +members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable"] exclude = ["vdi/host", "examples/custom_plugin"] [package.metadata.winres] diff --git a/build.py b/build.py index 053d59c47..af2c0b3fd 100755 --- a/build.py +++ b/build.py @@ -211,11 +211,10 @@ def download_extract_features(features, res_dir): print(f'{feat} extract end') -def get_rc_features(args): - flutter = args.flutter +def pre_resources(args): features = parse_rc_features(args.feature) if not features: - return [] + return print(f'Build with features {list(features.keys())}') res_dir = 'resources' @@ -225,23 +224,18 @@ def get_rc_features(args): raise Exception(f'Find file {res_dir}, not a directory') os.makedirs(res_dir, exist_ok=True) download_extract_features(features, res_dir) - if flutter: - os.makedirs(flutter_win_target_dir, exist_ok=True) - for f in pathlib.Path(res_dir).iterdir(): - print(f'{f}') - if f.is_file(): - shutil.copy2(f, flutter_win_target_dir) - else: - shutil.copytree(f, f'{flutter_win_target_dir}{f.stem}') - return [] - else: - return ['with_rc'] + os.makedirs(flutter_win_target_dir, exist_ok=True) + for f in pathlib.Path(res_dir).iterdir(): + print(f'{f}') + if f.is_file(): + shutil.copy2(f, flutter_win_target_dir) + else: + shutil.copytree(f, f'{flutter_win_target_dir}{f.stem}') def get_features(args): features = ['inline'] if not args.flutter else [] if windows: - features.extend(get_rc_features(args)) features.append('virtual_display_driver') if args.hwcodec: features.append('hwcodec') @@ -438,6 +432,8 @@ def main(): build_deb_from_folder(version, package) return if windows: + pre_resources(args) + # build virtual display dynamic library os.chdir('libs/virtual_display/dylib') system2('cargo build --release') diff --git a/build.rs b/build.rs index bf141e539..85f21e7d0 100644 --- a/build.rs +++ b/build.rs @@ -41,20 +41,6 @@ fn build_manifest() { } } -#[cfg(all(windows, feature = "with_rc"))] -fn build_rc_source() { - use simple_rc::{generate_with_conf, Config, ConfigItem}; - generate_with_conf(&Config { - outfile: "src/rc.rs".to_owned(), - confs: vec![ConfigItem { - inc: "resources".to_owned(), - exc: vec![], - suppressed_front: "resources".to_owned(), - }], - }) - .unwrap(); -} - fn install_oboe() { let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); if target_os != "android" { @@ -133,8 +119,6 @@ fn main() { gen_flutter_rust_bridge(); // return; // } - #[cfg(all(windows, feature = "with_rc"))] - build_rc_source(); #[cfg(all(windows, feature = "inline"))] build_manifest(); #[cfg(windows)] diff --git a/libs/simple_rc/Cargo.toml b/libs/simple_rc/Cargo.toml deleted file mode 100644 index 89304524d..000000000 --- a/libs/simple_rc/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "simple_rc" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -serde_derive = "1.0" -serde = "1.0" -walkdir = "2" -confy = { git = "https://github.com/open-trade/confy" } -hbb_common = { path = "../hbb_common" } diff --git a/libs/simple_rc/examples/generate.rs b/libs/simple_rc/examples/generate.rs deleted file mode 100644 index 2de39961a..000000000 --- a/libs/simple_rc/examples/generate.rs +++ /dev/null @@ -1,23 +0,0 @@ -extern crate simple_rc; - -use simple_rc::*; - -fn main() { - { - const CONF_FILE: &str = "simple_rc.toml"; - generate(CONF_FILE).unwrap(); - } - - { - generate_with_conf(&Config { - outfile: "src/rc.rs".to_owned(), - confs: vec![ConfigItem { - inc: "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx".to_owned(), - // exc: vec!["*.dll".to_owned(), "*.exe".to_owned()], - exc: vec![], - suppressed_front: "D:/projects/windows".to_owned(), - }], - }) - .unwrap(); - } -} diff --git a/libs/simple_rc/simple_rc.toml b/libs/simple_rc/simple_rc.toml deleted file mode 100644 index bef976967..000000000 --- a/libs/simple_rc/simple_rc.toml +++ /dev/null @@ -1,12 +0,0 @@ -# The output source file -outfile = "src/rc.rs" - -# The resource config list. -[[confs]] -# The file or director to integrate. -inc = "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx" -# The exclusions. -exc = ["*.dll", "*.exe"] -# The front path that will ignore for extracting. -# The following config will make base output path to be "RustDeskTempTopMostWindow/x64/Release/xxx". -suppressed_front = "D:/projects/windows" diff --git a/libs/simple_rc/src/lib.rs b/libs/simple_rc/src/lib.rs deleted file mode 100644 index e59e0493f..000000000 --- a/libs/simple_rc/src/lib.rs +++ /dev/null @@ -1,208 +0,0 @@ -use hbb_common::{bail, ResultType}; -use serde_derive::{Deserialize, Serialize}; -use std::{collections::HashMap, fs::File, io::prelude::*, path::Path}; -use walkdir::WalkDir; - -//mod rc; - -#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)] -pub struct ConfigItem { - // include directory or file - pub inc: String, - // exclude files - pub exc: Vec, - // out_path = origin_path - suppressed_front - pub suppressed_front: String, -} - -#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)] -pub struct Config { - // output source file - pub outfile: String, - // config items - pub confs: Vec, -} - -pub fn get_outin_files<'a>(item: &'a ConfigItem) -> ResultType> { - let mut outin_filemap = HashMap::new(); - - for entry in WalkDir::new(&item.inc).follow_links(true) { - let path = entry?.into_path(); - if path.is_file() { - let mut exclude = false; - for excfile in item.exc.iter() { - if excfile.starts_with("*.") { - if let Some(ext) = path.extension().and_then(|x| x.to_str()) { - if excfile.ends_with(&format!(".{}", ext)) { - exclude = true; - break; - } - } - } else { - if path.ends_with(Path::new(excfile)) { - exclude = true; - break; - } - } - } - if exclude { - continue; - } - - let mut suppressed_front = item.suppressed_front.clone(); - if !suppressed_front.is_empty() && suppressed_front.ends_with('/') { - suppressed_front.push('/'); - } - let outpath = path.strip_prefix(Path::new(&suppressed_front))?; - let outfile = if outpath.is_absolute() { - match outpath - .file_name() - .and_then(|f| f.to_str()) - .map(|f| f.to_string()) - { - None => { - bail!("Failed to get filename of {}", outpath.display()); - } - Some(s) => s, - } - } else { - match outpath.to_str() { - None => { - bail!("Failed to convert {} to string", outpath.display()); - } - // Simple replace \ to / here. - // A better way is to use lib [path-slash](https://github.com/rhysd/path-slash) - Some(s) => s.to_string().replace("\\", "/"), - } - }; - let infile = match path.canonicalize()?.to_str() { - None => { - bail!("Failed to get file path of {}", path.display()); - } - Some(s) => s.to_string(), - }; - if let Some(_) = outin_filemap.insert(outfile.clone(), infile) { - bail!("outfile {} is set before", outfile); - } - } - } - - Ok(outin_filemap) -} - -pub fn generate(conf_file: &str) -> ResultType<()> { - let conf = confy::load_path(conf_file)?; - generate_with_conf(&conf)?; - Ok(()) -} - -pub fn generate_with_conf<'a>(conf: &'a Config) -> ResultType<()> { - let mut outfile = File::create(&conf.outfile)?; - - outfile.write( - br##"use hbb_common::{bail, ResultType}; -use std::{ - fs::{self, File}, - io::prelude::*, - path::Path, -}; - -"##, - )?; - - outfile.write(b"#[allow(dead_code)]\n")?; - outfile.write(b"pub fn extract_resources(root_path: &str) -> ResultType<()> {\n")?; - outfile.write(b" let mut resources: Vec<(&str, &[u8])> = Vec::new();\n")?; - - let mut outin_files = HashMap::new(); - for item in conf.confs.iter() { - for (o, i) in get_outin_files(item)?.into_iter() { - if let Some(_) = outin_files.insert(o.clone(), i) { - bail!("outfile {} is set before", o); - } - } - } - - let mut count = 1; - for (o, i) in outin_files.iter() { - let mut infile = File::open(&i)?; - let mut buffer = Vec::::new(); - infile.read_to_end(&mut buffer)?; - - let var_outfile = format!("outfile_{}", count); - let var_outdata = format!("outdata_{}", count); - - write!(outfile, " let {} = \"{}\";\n", var_outfile, o)?; - write!(outfile, " let {}: &[u8] = &[\n ", var_outdata)?; - - let mut line_num = 20; - for v in buffer { - if line_num == 0 { - write!(outfile, "\n ")?; - line_num = 20; - } - write!(outfile, "{:#04x}, ", v)?; - line_num -= 1; - } - write!(outfile, "\n ];\n")?; - - write!( - outfile, - " resources.push(({}, &{}));\n", - var_outfile, var_outdata - )?; - - count += 1; - } - - outfile.write(b" do_extract(root_path, resources)?;\n")?; - outfile.write(b" Ok(())\n")?; - outfile.write(b"}\n")?; - - outfile.write( - br##" -#[allow(dead_code)] -fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> { - let mut root_path = root_path.replace("\\", "/"); - if !root_path.ends_with('/') { - root_path.push('/'); - } - let root_path = Path::new(&root_path); - for (outfile, data) in resources { - let outfile_path = root_path.join(outfile); - match outfile_path.parent().and_then(|p| p.to_str()) { - None => { - bail!("Failed to get parent of {}", outfile_path.display()); - } - Some(p) => { - fs::create_dir_all(p)?; - let mut of = File::create(outfile_path)?; - of.write_all(data)?; - of.flush()?; - } - } - } - Ok(()) -} -"##, - )?; - - outfile.flush()?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } - - // #[test] - // fn test_extract() { - // use super::*; - // rc::extract_resources("D:").unwrap(); - // } -} diff --git a/src/core_main.rs b/src/core_main.rs index d6c3bbc59..c5193b566 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -145,10 +145,6 @@ pub fn core_main() -> Option> { args.len() > 1, )); return None; - } else if args[0] == "--extract" { - #[cfg(feature = "with_rc")] - hbb_common::allow_err!(crate::rc::extract_resources(&args[1])); - return None; } else if args[0] == "--install-cert" { #[cfg(windows)] hbb_common::allow_err!(crate::platform::windows::install_cert(&args[1])); diff --git a/src/lib.rs b/src/lib.rs index d5f791eeb..a6b5c4a46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,9 +64,6 @@ mod hbbs_http; #[cfg(windows)] pub mod clipboard_file; -#[cfg(all(windows, feature = "with_rc"))] -pub mod rc; - #[cfg(windows)] pub mod privacy_win_mag; diff --git a/src/rc.rs b/src/rc.rs deleted file mode 100644 index ef86caaa3..000000000 --- a/src/rc.rs +++ /dev/null @@ -1,38 +0,0 @@ -use hbb_common::{bail, ResultType}; -use std::{ - fs::{self, File}, - io::prelude::*, - path::Path, -}; - -#[allow(dead_code)] -pub fn extract_resources(root_path: &str) -> ResultType<()> { - let mut resources: Vec<(&str, &[u8])> = Vec::new(); - resources.push((outfile_4, &outdata_4)); - do_extract(root_path, resources)?; - Ok(()) -} - -#[allow(dead_code)] -fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> { - let mut root_path = root_path.replace("\\", "/"); - if !root_path.ends_with('/') { - root_path.push('/'); - } - let root_path = Path::new(&root_path); - for (outfile, data) in resources { - let outfile_path = root_path.join(outfile); - match outfile_path.parent().and_then(|p| p.to_str()) { - None => { - bail!("Failed to get parent of {}", outfile_path.display()); - } - Some(p) => { - fs::create_dir_all(p)?; - let mut of = File::create(outfile_path)?; - of.write_all(data)?; - of.flush()?; - } - } - } - Ok(()) -} From 321ada7b915628a891508188c9714fe5567b4a64 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 14:30:30 +0800 Subject: [PATCH 320/366] remove simple_rc Signed-off-by: fufesou --- Cargo.lock | 12 ------------ Cargo.toml | 1 - 2 files changed, 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 098189693..299d99c14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5163,7 +5163,6 @@ dependencies = [ "sha2", "shared_memory", "shutdown_hooks", - "simple_rc", "sys-locale", "system_shutdown", "tao", @@ -5571,17 +5570,6 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" -[[package]] -name = "simple_rc" -version = "0.1.0" -dependencies = [ - "confy", - "hbb_common", - "serde 1.0.159", - "serde_derive", - "walkdir", -] - [[package]] name = "siphasher" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index abf578f72..cd76b7d01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -149,7 +149,6 @@ winapi = { version = "0.3", features = [ "winnt" ] } [build-dependencies] cc = "1.0" hbb_common = { path = "libs/hbb_common" } -simple_rc = { path = "libs/simple_rc", optional = true } flutter_rust_bridge_codegen = "1.61.1" os-version = "0.2" From ec51047c23703c16d8b4fa431d3aac75bc66056a Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 14:33:46 +0800 Subject: [PATCH 321/366] remove --extract Signed-off-by: fufesou --- src/platform/windows.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform/windows.rs b/src/platform/windows.rs index b0b0d18e6..8fe900d11 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -928,7 +928,6 @@ pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String { " {main_exe} copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" - \"{src_exe}\" --extract \"{path}\" ", main_exe = main_exe, path = path, From 9f12f96ab942a30df7e6117de2251925e273390f Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 14:49:58 +0800 Subject: [PATCH 322/366] ignore external resources for sciter version (Win7) Signed-off-by: fufesou --- build.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/build.py b/build.py index af2c0b3fd..3ff4f1684 100755 --- a/build.py +++ b/build.py @@ -41,11 +41,13 @@ def get_version(): def parse_rc_features(feature): available_features = { 'IddDriver': { + 'platform': ['windows'], 'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/RustDeskIddDriver_x64.zip', 'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/checksum_md5', 'exclude': ['README.md', 'certmgr.exe', 'install_cert_runas_admin.bat'], }, 'PrivacyMode': { + 'platform': ['windows'], 'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.1' '/TempTopMostWindow_x64_pic_en.zip', 'checksum_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.1/checksum_md5', @@ -55,16 +57,30 @@ def parse_rc_features(feature): apply_features = {} if not feature: feature = [] + + def platform_check(platforms): + if windows: + return 'windows' in platforms + elif osx: + return 'osx' in platforms + else: + return 'linux' in platforms + if isinstance(feature, str) and feature.upper() == 'ALL': - return available_features + for (feat, feat_info) in available_features.items(): + if platform_check(feat_info['platform']): + apply_features[feat] = available_features[feat] + return apply_features elif isinstance(feature, list): - # force add PrivacyMode - feature.append('PrivacyMode') + if windows: + # force add PrivacyMode + feature.append('PrivacyMode') for feat in feature: if isinstance(feat, str) and feat.upper() == 'ALL': return available_features if feat in available_features: - apply_features[feat] = available_features[feat] + if platform_check(available_features[feat]['platform']): + apply_features[feat] = available_features[feat] else: print(f'Unrecognized feature {feat}') return apply_features @@ -211,7 +227,7 @@ def download_extract_features(features, res_dir): print(f'{feat} extract end') -def pre_resources(args): +def external_resources(args): features = parse_rc_features(args.feature) if not features: return @@ -432,14 +448,13 @@ def main(): build_deb_from_folder(version, package) return if windows: - pre_resources(args) - # build virtual display dynamic library os.chdir('libs/virtual_display/dylib') system2('cargo build --release') os.chdir('../../..') if flutter: + external_resources(args) build_flutter_windows(version, features) return system2('cargo build --release --features ' + features) From a4caffe032f643c35bb8b036054aa8aa59b18283 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 15:16:55 +0800 Subject: [PATCH 323/366] update build.py Signed-off-by: fufesou --- build.py | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/build.py b/build.py index 3ff4f1684..521630dbb 100755 --- a/build.py +++ b/build.py @@ -15,7 +15,13 @@ osx = platform.platform().startswith( 'Darwin') or platform.platform().startswith("macOS") hbb_name = 'rustdesk' + ('.exe' if windows else '') exe_path = 'target/release/' + hbb_name -flutter_win_target_dir = 'flutter/build/windows/runner/Release/' +if windows: + flutter_build_dir = 'build/windows/runner/Release/' +elif osx: + flutter_build_dir = 'build/macos/Build/Products/Release/' +else: + flutter_build_dir = 'build/linux/x64/release/bundle/' +flutter_build_dir_2 = f'flutter/{flutter_build_dir}' skip_cargo = False def get_arch() -> str: @@ -227,26 +233,26 @@ def download_extract_features(features, res_dir): print(f'{feat} extract end') -def external_resources(args): +def external_resources(flutter, args, res_dir): features = parse_rc_features(args.feature) if not features: return print(f'Build with features {list(features.keys())}') - res_dir = 'resources' if os.path.isdir(res_dir) and not os.path.islink(res_dir): shutil.rmtree(res_dir) elif os.path.exists(res_dir): raise Exception(f'Find file {res_dir}, not a directory') os.makedirs(res_dir, exist_ok=True) download_extract_features(features, res_dir) - os.makedirs(flutter_win_target_dir, exist_ok=True) - for f in pathlib.Path(res_dir).iterdir(): - print(f'{f}') - if f.is_file(): - shutil.copy2(f, flutter_win_target_dir) - else: - shutil.copytree(f, f'{flutter_win_target_dir}{f.stem}') + if flutter: + os.makedirs(flutter_build_dir_2, exist_ok=True) + for f in pathlib.Path(res_dir).iterdir(): + print(f'{f}') + if f.is_file(): + shutil.copy2(f, flutter_build_dir_2) + else: + shutil.copytree(f, f'{flutter_build_dir_2}{f.stem}') def get_features(args): @@ -305,7 +311,7 @@ def build_flutter_deb(version, features): system2('mkdir -p tmpdeb/usr/share/polkit-1/actions') system2('rm tmpdeb/usr/bin/rustdesk || true') system2( - 'cp -r build/linux/x64/release/bundle/* tmpdeb/usr/lib/rustdesk/') + f'cp -r {flutter_build_dir}/* tmpdeb/usr/lib/rustdesk/') system2( 'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/') system2( @@ -391,7 +397,7 @@ def build_flutter_arch_manjaro(version, features): ffi_bindgen_function_refactor() os.chdir('flutter') system2('flutter build linux --release') - system2('strip build/linux/x64/release/bundle/lib/librustdesk.so') + system2(f'strip {flutter_build_dir}/lib/librustdesk.so') os.chdir('../res') system2('HBB=`pwd`/.. FLUTTER=1 makepkg -f') @@ -406,11 +412,11 @@ def build_flutter_windows(version, features): system2('flutter build windows --release') os.chdir('..') shutil.copy2('target/release/deps/dylib_virtual_display.dll', - flutter_win_target_dir) + flutter_build_dir_2) os.chdir('libs/portable') system2('pip3 install -r requirements.txt') system2( - f'python3 ./generate.py -f ../../{flutter_win_target_dir} -o . -e ../../{flutter_win_target_dir}/rustdesk.exe') + f'python3 ./generate.py -f ../../{flutter_build_dir_2} -o . -e ../../{flutter_build_dir_2}/rustdesk.exe') os.chdir('../..') if os.path.exists('./rustdesk_portable.exe'): os.replace('./target/release/rustdesk-portable-packer.exe', @@ -447,6 +453,8 @@ def main(): if package: build_deb_from_folder(version, package) return + res_dir = 'resources' + external_resources(flutter, args, res_dir) if windows: # build virtual display dynamic library os.chdir('libs/virtual_display/dylib') @@ -454,7 +462,6 @@ def main(): os.chdir('../../..') if flutter: - external_resources(args) build_flutter_windows(version, features) return system2('cargo build --release --features ' + features) @@ -468,7 +475,11 @@ def main(): else: print('Not signed') system2( - f'cp -rf target/release/RustDesk.exe rustdesk-{version}-win7-install.exe') + f'cp -rf target/release/RustDesk.exe {res_dir}') + os.chdir('libs/portable') + system2('pip3 install -r requirements.txt') + system2( + f'python3 ./generate.py -f ../../{res_dir} -o . -e ../../rustdesk-{version}-win7-install.exe') elif os.path.isfile('/usr/bin/pacman'): # pacman -S -needed base-devel system2("sed -i 's/pkgver=.*/pkgver=%s/g' res/PKGBUILD" % version) From b53c74d9b52ce636aa9456ccf1a5f245d50e2821 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 15:26:50 +0800 Subject: [PATCH 324/366] update build.py Signed-off-by: fufesou --- build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.py b/build.py index 521630dbb..7a672dde5 100755 --- a/build.py +++ b/build.py @@ -479,7 +479,8 @@ def main(): os.chdir('libs/portable') system2('pip3 install -r requirements.txt') system2( - f'python3 ./generate.py -f ../../{res_dir} -o . -e ../../rustdesk-{version}-win7-install.exe') + f'python3 ./generate.py -f ../../{res_dir} -o . -e ../../{res_dir}/rustdesk-{version}-win7-install.exe') + system2('mv ../../{res_dir}/rustdesk-{version}-win7-install.exe ../..') elif os.path.isfile('/usr/bin/pacman'): # pacman -S -needed base-devel system2("sed -i 's/pkgver=.*/pkgver=%s/g' res/PKGBUILD" % version) From 904b53d598d851e2eb1124239a7e1a27631a7d10 Mon Sep 17 00:00:00 2001 From: fufesou Date: Mon, 17 Apr 2023 19:01:12 +0800 Subject: [PATCH 325/366] update build.py Signed-off-by: fufesou --- build.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/build.py b/build.py index 7a672dde5..6cb8c0e80 100755 --- a/build.py +++ b/build.py @@ -71,19 +71,23 @@ def parse_rc_features(feature): return 'osx' in platforms else: return 'linux' in platforms - - if isinstance(feature, str) and feature.upper() == 'ALL': + + def get_all_features(): + features = [] for (feat, feat_info) in available_features.items(): if platform_check(feat_info['platform']): - apply_features[feat] = available_features[feat] - return apply_features + features.append(feat) + return features + + if isinstance(feature, str) and feature.upper() == 'ALL': + return get_all_features() elif isinstance(feature, list): if windows: # force add PrivacyMode feature.append('PrivacyMode') for feat in feature: if isinstance(feat, str) and feat.upper() == 'ALL': - return available_features + return get_all_features() if feat in available_features: if platform_check(available_features[feat]['platform']): apply_features[feat] = available_features[feat] From 5645def1d2bfc969d3cdcb0116efa890dbf8f18e Mon Sep 17 00:00:00 2001 From: NicKoehler Date: Mon, 17 Apr 2023 13:53:55 +0200 Subject: [PATCH 326/366] fix sort inconsistency --- flutter/lib/common/widgets/peer_tab_page.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 0c8ac9329..fa9ed29c0 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -455,12 +455,12 @@ class _PeerSortDropdownState extends State { borderRadius: BorderRadius.circular(5), ); - final translated_text = - PeerSortType.values.map((e) => translate(e)).toList(); + final translated_text = { + for (var e in PeerSortType.values) e: translate(e) + }; final double max_width = - 50 + translated_text.map((e) => e.length).reduce(max) * 10; - + 50 + translated_text.values.map((e) => e.length).reduce(max) * 10; return Container( padding: EdgeInsets.all(4.0), decoration: deco, @@ -496,20 +496,20 @@ class _PeerSortDropdownState extends State { ), enabled: false, ), - ...translated_text + ...translated_text.entries .map>( - (String value) => DropdownMenuItem( - value: value, + (MapEntry entry) => DropdownMenuItem( + value: entry.key, child: Row( children: [ Icon( - value == peerSort.value + entry.key == peerSort.value ? Icons.radio_button_checked_rounded : Icons.radio_button_off_rounded, size: 18, ).paddingOnly(right: 12), Text( - value, + entry.value, overflow: TextOverflow.ellipsis, ), ], From d64fc18142ce5fb9f7a184116cee3ca879ff049e Mon Sep 17 00:00:00 2001 From: Mateusz Prais Date: Mon, 17 Apr 2023 16:08:01 +0200 Subject: [PATCH 327/366] Add missing Polish translations, improve punctuation and fix typos --- src/lang/pl.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 92a5c1c77..9e7c7407e 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -408,7 +408,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("One-time password length", "Długość hasła jednorazowego"), ("Request access to your device", "Żądanie dostępu do Twojego urządzenia"), ("Hide connection management window", "Ukryj okno zarządzania połączeniem"), - ("hide_cm_tip", "Pozwalaj na ukrycie tylko gdy akceptujesz sesje za pośrednictwem hasła i używasz hasła permanentnego"), + ("hide_cm_tip", "Pozwalaj na ukrycie tylko, gdy akceptujesz sesje za pośrednictwem hasła i używasz hasła permanentnego"), ("wayland_experiment_tip", "Wsparcie dla Wayland jest niekompletne, użyj X11 jeżeli chcesz korzystać z dostępu nienadzorowanego"), ("Right click to select tabs", "Kliknij prawym przyciskiem myszy by wybrać zakładkę"), ("Skipped", "Pominięte"), @@ -421,7 +421,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("software_render_tip", "Jeżeli posiadasz kartę graficzną Nvidia i okno zamyka się natychmiast po nawiązaniu połączenia, instalacja sterownika nouveau i wybór renderowania programowego mogą pomóc. Restart aplikacji jest wymagany."), ("Always use software rendering", "Zawsze używaj renderowania programowego"), ("config_input", "By kontrolować zdalne urządzenie przy pomocy klawiatury, musisz udzielić aplikacji RustDesk uprawnień do \"Urządzeń Wejściowych\"."), - ("config_microphone", "Aby umozliwić zdalne rozmowy należy przyznać RuskDesk uprawnienia do \"Nagrań audio\"."), + ("config_microphone", "Aby umożliwić zdalne rozmowy należy przyznać RuskDesk uprawnienia do \"Nagrań audio\"."), ("request_elevation_tip", "Możesz poprosić o podniesienie uprawnień jeżeli ktoś posiada dostęp do zdalnego urządzenia."), ("Wait", "Czekaj"), ("Elevation Error", "Błąd przy podnoszeniu uprawnień"), @@ -460,7 +460,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Resolution", "Rozdzielczość"), ("No transfers in progress", "Brak transferów w toku"), ("Set one-time password length", "Ustaw długość jednorazowego hasła"), - ("idd_driver_tip", "Zainstaluj sterownik wirtualnego wyświetlacza, który jest używany gdy nie masz fizycznych monitorów."), + ("idd_driver_tip", "Zainstaluj sterownik wirtualnego wyświetlacza, który jest używany, gdy nie masz fizycznych monitorów."), ("confirm_idd_driver_tip", "Opcja instalacji sterownika wirtualnego wyświetlacza jest zaznaczona. Pamiętaj, że zostanie zainstalowany testowy certyfikat, aby zaufać wirtualnemu sterownikowi. Ten certyfikat będzie używany tylko do weryfikacji sterowników RustDesk."), ("RDP Settings", "Ustawienia RDP"), ("Sort by", "Sortuj wg"), @@ -471,13 +471,13 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Your Device", "Twoje urządzenie"), ("empty_recent_tip", "Ups, żadnych nowych sesji!\nCzas zaplanować nową."), ("empty_favorite_tip", "Brak ulubionych?\nZnajdźmy kogoś, z kim możesz się połączyć i dodaj Go do ulubionych!"), - ("empty_lan_tip", "Ojej, wygląda na to że odkryliśmy żadnych urządzeń z RustDesk w Twojej sieci."), - ("empty_address_book_tip", "Ojej, wygląda na to że nie ma żadnych wpisów w Twojej książce adresowej."), + ("empty_lan_tip", "Ojej, wygląda na to, że nie odkryliśmy żadnych urządzeń z RustDesk w Twojej sieci."), + ("empty_address_book_tip", "Ojej, wygląda na to, że nie ma żadnych wpisów w Twojej książce adresowej."), ("eg: admin", "np. admin"), ("Empty Username", "Pusty użytkownik"), ("Empty Password", "Puste hasło"), ("Me", "Ja"), - ("identical_file_tip", "Ten plik jest identyczny jak plik na drugim komputerze."), + ("identical_file_tip", "Ten plik jest identyczny z plikiem na drugim komputerze."), ("show_monitors_tip", "Pokaż monitory w zasobniku"), ("View Mode", "Tryb widoku"), ("login_linux_tip", "Zaloguj do zdalnego konta Linux"), @@ -485,13 +485,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("remember_account_tip", "Zapamiętaj to konto"), ("os_account_desk_tip", "To konto jest używane do logowania do zdalnych systemów i włącza bezobsługowe sesje pulpitu"), ("OS Account", "Konto systemowe"), - ("another_user_login_title_tip", "Zalogowany jest inny użytkownik"), + ("another_user_login_title_tip", "Inny użytkownik jest już zalogowany"), ("another_user_login_text_tip", "Rozłącz"), ("xorg_not_found_title_tip", "Nie znaleziono Xorg"), ("xorg_not_found_text_tip", "Proszę zainstalować Xorg"), - ("no_desktop_title_tip", "Pulpit jest niedostępny"), + ("no_desktop_title_tip", "Żaden pulpit nie jest dostępny"), ("no_desktop_text_tip", "Proszę zainstalować pulpit GNOME"), - ("No need to elevate", ""), - ("New RDP", ""), + ("No need to elevate", "Podniesienie uprawnień nie jest wymagane"), + ("System Sound", "Dźwięk Systemowy"), + ("Default", "Domyślne"), + ("New RDP", "Nowe RDP"), ].iter().cloned().collect(); } From b0122ec74a26478a2c473424ba20aa9b539551ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=BB=85=C3=BC?= <53787985+LaiYueTing@users.noreply.github.com> Date: Tue, 18 Apr 2023 01:37:14 +0800 Subject: [PATCH 328/366] Update tw.rs --- src/lang/tw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 5337749a6..7bb5d04e4 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -492,6 +492,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "沒有可用的桌面"), ("no_desktop_text_tip", "請安裝 GNOME 桌面"), ("No need to elevate", "不需要提升權限"), - ("New RDP", ""), + ("New RDP", "新的 RDP"), ].iter().cloned().collect(); } From 8cad0b7a6caee3c679c0771c7ed556e0ce4170b7 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 13 Apr 2023 15:00:01 +0000 Subject: [PATCH 329/366] opt: add flutter feat --- examples/custom_plugin/Cargo.toml | 7 ++++++- examples/custom_plugin/src/lib.rs | 2 ++ src/plugins.rs | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/examples/custom_plugin/Cargo.toml b/examples/custom_plugin/Cargo.toml index 106e48ebb..b9ee06ae7 100644 --- a/examples/custom_plugin/Cargo.toml +++ b/examples/custom_plugin/Cargo.toml @@ -10,9 +10,14 @@ name = "custom_plugin" path = "src/lib.rs" crate-type = ["cdylib"] + +[features] +default = ["flutter"] +flutter = [] + [dependencies] lazy_static = "1.4.0" -rustdesk = { path = "../../", version = "1.2.0"} +rustdesk = { path = "../../", version = "1.2.0", features = ["flutter"]} [profile.release] lto = true diff --git a/examples/custom_plugin/src/lib.rs b/examples/custom_plugin/src/lib.rs index be051c1d9..860e0366a 100644 --- a/examples/custom_plugin/src/lib.rs +++ b/examples/custom_plugin/src/lib.rs @@ -9,6 +9,8 @@ lazy_static::lazy_static! { pub static ref API: RustDeskApiTable = RustDeskApiTable::default(); } + + #[no_mangle] fn plugin_name() -> *const c_char { return PLUGIN_NAME.as_ptr(); diff --git a/src/plugins.rs b/src/plugins.rs index 3235bce4a..dfbdc7753 100644 --- a/src/plugins.rs +++ b/src/plugins.rs @@ -36,8 +36,8 @@ pub trait Plugin { #[repr(C)] #[derive(Default, Clone)] pub struct RustDeskPluginTable { - pub init: Option, - pub dispose: Option, + pub init: Option, // NonNull + pub dispose: Option, // NonNull } pub struct PluginImpl { From 8e70e2ffe46bb87a51c4594677bf8d404e2ad1c8 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Fri, 14 Apr 2023 01:53:34 +0800 Subject: [PATCH 330/366] feat: add hooks for session --- examples/custom_plugin/src/lib.rs | 4 +- src/api.rs | 67 ++++++++++++++++++++++++++++--- src/flutter.rs | 31 ++++++++++++-- 3 files changed, 89 insertions(+), 13 deletions(-) diff --git a/examples/custom_plugin/src/lib.rs b/examples/custom_plugin/src/lib.rs index 860e0366a..0b21f3fc8 100644 --- a/examples/custom_plugin/src/lib.rs +++ b/examples/custom_plugin/src/lib.rs @@ -1,4 +1,4 @@ -use librustdesk::{api::RustDeskApiTable}; +use librustdesk::api::RustDeskApiTable; /// This file demonstrates how to write a custom plugin for RustDesk. use std::ffi::{c_char, c_int, CString}; @@ -9,8 +9,6 @@ lazy_static::lazy_static! { pub static ref API: RustDeskApiTable = RustDeskApiTable::default(); } - - #[no_mangle] fn plugin_name() -> *const c_char { return PLUGIN_NAME.as_ptr(); diff --git a/src/api.rs b/src/api.rs index 1c993a5ee..187c396d0 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,32 +1,87 @@ use std::ffi::c_char; -use crate::plugins::PLUGIN_REGISTRAR; +use crate::{ + flutter::{FlutterHandler, SESSIONS}, + plugins::PLUGIN_REGISTRAR, + ui_session_interface::Session, +}; // API provided by RustDesk. pub type LoadPluginFunc = fn(*const c_char) -> i32; pub type UnloadPluginFunc = fn(*const c_char) -> i32; +pub type AddSessionFunc = fn(session: Session) -> bool; +pub type RemoveSessionFunc = fn(session_id: &String) -> bool; +pub type AddSessionHookFunc = fn(session_id: String, key: String, hook: SessionHook) -> bool; +pub type RemoveSessionHookFunc = fn(session_id: String, key: &String) -> bool; + +/// Hooks for session. +#[repr(usize)] +#[derive(Clone)] +pub enum SessionHook { + OnSessionRgba(fn(&Session, Vec) -> Vec) = 1, +} #[repr(C)] pub struct RustDeskApiTable { - pub(crate) register_plugin: LoadPluginFunc, + pub(crate) load_plugin: LoadPluginFunc, pub(crate) unload_plugin: UnloadPluginFunc, + pub add_session: AddSessionFunc, + pub remove_session: RemoveSessionFunc, + pub add_session_hook: AddSessionHookFunc, + pub remove_session_hook: RemoveSessionHookFunc, } -#[no_mangle] fn load_plugin(path: *const c_char) -> i32 { PLUGIN_REGISTRAR.load_plugin(path) } -#[no_mangle] fn unload_plugin(path: *const c_char) -> i32 { PLUGIN_REGISTRAR.unload_plugin(path) } +fn add_session(session: Session) -> bool { + let mut sessions = SESSIONS.write().unwrap(); + if sessions.contains_key(&session.id) { + return false; + } + let _ = sessions.insert(session.id.to_owned(), session); + true +} + +fn remove_session(session_id: &String) -> bool { + let mut sessions = SESSIONS.write().unwrap(); + if !sessions.contains_key(session_id) { + return false; + } + let _ = sessions.remove(session_id); + true +} + +fn add_session_hook(session_id: String, key: String, hook: SessionHook) -> bool { + let sessions = SESSIONS.read().unwrap(); + if let Some(session) = sessions.get(&session_id) { + return session.add_session_hook(key, hook); + } + false +} + +fn remove_session_hook(session_id: String, key: &String) -> bool { + let sessions = SESSIONS.read().unwrap(); + if let Some(session) = sessions.get(&session_id) { + return session.remove_session_hook(key); + } + false +} + impl Default for RustDeskApiTable { fn default() -> Self { Self { - register_plugin: load_plugin, - unload_plugin: unload_plugin, + load_plugin, + unload_plugin, + add_session, + remove_session, + add_session_hook, + remove_session_hook, } } } diff --git a/src/flutter.rs b/src/flutter.rs index 8c4522e47..94c5e52e7 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1,4 +1,5 @@ use crate::{ + api::SessionHook, client::*, flutter_ffi::EventToUI, ui_session_interface::{io_loop, InvokeUiSession, Session}, @@ -40,9 +41,9 @@ pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; pub(super) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; lazy_static::lazy_static! { - pub static ref CUR_SESSION_ID: RwLock = Default::default(); - pub static ref SESSIONS: RwLock>> = Default::default(); - pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel + pub(crate) static ref CUR_SESSION_ID: RwLock = Default::default(); + pub(crate) static ref SESSIONS: RwLock>> = Default::default(); + pub(crate) static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } #[cfg(all(target_os = "windows", feature = "flutter_texture_render"))] @@ -146,6 +147,7 @@ pub struct FlutterHandler { notify_rendered: Arc>, renderer: Arc>, peer_info: Arc>, + hooks: Arc>>, } #[cfg(not(feature = "flutter_texture_render"))] @@ -157,6 +159,7 @@ pub struct FlutterHandler { pub rgba: Arc>>, pub rgba_valid: Arc, peer_info: Arc>, + hooks: Arc>>, } #[cfg(feature = "flutter_texture_render")] @@ -255,7 +258,7 @@ impl FlutterHandler { } } - pub fn close_event_stream(&mut self) { + pub(crate) fn close_event_stream(&mut self) { let mut stream_lock = self.event_stream.write().unwrap(); if let Some(stream) = &*stream_lock { stream.add(EventToUI::Event("close".to_owned())); @@ -277,6 +280,26 @@ impl FlutterHandler { serde_json::ser::to_string(&msg_vec).unwrap_or("".to_owned()) } + pub(crate) fn add_session_hook(&self, key: String, hook: crate::api::SessionHook) -> bool { + let mut hooks = self.hooks.write().unwrap(); + if hooks.contains_key(&key) { + // Already has the hook with this key. + return false; + } + let _ = hooks.insert(key, hook); + true + } + + pub(crate) fn remove_session_hook(&self, key: &String) -> bool { + let mut hooks = self.hooks.write().unwrap(); + if !hooks.contains_key(key) { + // The hook with this key does not found. + return false; + } + let _ = hooks.remove(key); + true + } + #[inline] #[cfg(feature = "flutter_texture_render")] pub fn register_texture(&mut self, ptr: usize) { From a3c3199dedf4e0cd7f1c122e2737f611c758236d Mon Sep 17 00:00:00 2001 From: Kingtous Date: Sun, 16 Apr 2023 23:39:43 +0800 Subject: [PATCH 331/366] feat: try add ios build --- .github/workflows/flutter-build.yml | 85 +++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 7ea06ba8e..2a483197a 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -370,6 +370,91 @@ jobs: generate-bridge-linux: uses: ./.github/workflows/bridge.yml + build-rustdesk-ios: + needs: [generate-bridge-linux] + name: build rustdesk ios ipa ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + - { + arch: aarch64, + target: aarch64-apple-ios, + os: macos-latest, + extra-build-features: "", + } + steps: + - name: Install dependencies + run: | + brew install nasm yasm + - name: Checkout source code + uses: actions/checkout@v3 + - name: Install flutter + uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ env.FLUTTER_VERSION }} + + - name: Clone deps + shell: bash + run: | + pushd /opt + git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 + + - name: Restore bridge files + uses: actions/download-artifact@master + with: + name: bridge-artifact + path: ./ + + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: ${{ matrix.job.target }} + override: true + profile: minimal # minimal component installation (ie, no documentation) + + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: rustdesk-lib-cache + key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} + + - name: Disable rust bridge build + run: | + sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs + + - name: Build rustdesk lib + env: + VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg + run: | + rustup target add ${{ matrix.job.target }} + cargo build --release --target aarch64-apple-ios --lib + + - name: Build rustdesk + shell: bash + run: | + pushd flutter + flutter build ipa --release + + - name: Upload Artifacts + # if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' + uses: actions/upload-artifact@master + with: + name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk + path: flutter/build/ios/ipa/*.ipa + + - name: Publish ipa package + # if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' + uses: softprops/action-gh-release@v1 + with: + prerelease: true + tag_name: ${{ env.TAG_NAME }} + files: | + flutter/build/ios/ipa/*.ipa + + build-rustdesk-android: needs: [generate-bridge-linux] name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}] From 05ac93ec40c1f8344963b735618bff3014fe3fdf Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 17 Apr 2023 00:28:49 +0800 Subject: [PATCH 332/366] fix: add_session fix for bridge --- src/api.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/api.rs b/src/api.rs index 187c396d0..a0ea5df1c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,4 +1,4 @@ -use std::ffi::c_char; +use std::ffi::{c_char}; use crate::{ flutter::{FlutterHandler, SESSIONS}, @@ -9,19 +9,18 @@ use crate::{ // API provided by RustDesk. pub type LoadPluginFunc = fn(*const c_char) -> i32; pub type UnloadPluginFunc = fn(*const c_char) -> i32; -pub type AddSessionFunc = fn(session: Session) -> bool; +pub type AddSessionFunc = fn(session_id: String) -> bool; pub type RemoveSessionFunc = fn(session_id: &String) -> bool; pub type AddSessionHookFunc = fn(session_id: String, key: String, hook: SessionHook) -> bool; pub type RemoveSessionHookFunc = fn(session_id: String, key: &String) -> bool; /// Hooks for session. -#[repr(usize)] #[derive(Clone)] pub enum SessionHook { - OnSessionRgba(fn(&Session, Vec) -> Vec) = 1, + OnSessionRgba(fn(String, Vec) -> Vec), } -#[repr(C)] +// #[repr(C)] pub struct RustDeskApiTable { pub(crate) load_plugin: LoadPluginFunc, pub(crate) unload_plugin: UnloadPluginFunc, @@ -39,13 +38,14 @@ fn unload_plugin(path: *const c_char) -> i32 { PLUGIN_REGISTRAR.unload_plugin(path) } -fn add_session(session: Session) -> bool { - let mut sessions = SESSIONS.write().unwrap(); - if sessions.contains_key(&session.id) { - return false; - } - let _ = sessions.insert(session.id.to_owned(), session); - true +fn add_session(session_id: String) -> bool { + // let mut sessions = SESSIONS.write().unwrap(); + // if sessions.contains_key(&session.id) { + // return false; + // } + // let _ = sessions.insert(session.id.to_owned(), session); + // true + false } fn remove_session(session_id: &String) -> bool { From f56fc6fdb0789d42bc02324683408eb6c882f77c Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 17 Apr 2023 00:54:34 +0800 Subject: [PATCH 333/366] fix: hook for pc only --- .github/workflows/flutter-build.yml | 5 +++-- src/flutter.rs | 9 ++++++--- src/lib.rs | 2 ++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 2a483197a..4b5b695b0 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -400,7 +400,7 @@ jobs: shell: bash run: | pushd /opt - git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 + sudo git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1 - name: Restore bridge files uses: actions/download-artifact@master @@ -422,8 +422,9 @@ jobs: key: ${{ matrix.job.target }}-${{ matrix.job.extra-build-features }} - name: Disable rust bridge build + shell: bash run: | - sed -i "s/gen_flutter_rust_bridge();/\/\//g" build.rs + sed -i build.rs.bak "s/gen_flutter_rust_bridge();/\/\//g" build.rs - name: Build rustdesk lib env: diff --git a/src/flutter.rs b/src/flutter.rs index 94c5e52e7..34fe469fc 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1,5 +1,4 @@ use crate::{ - api::SessionHook, client::*, flutter_ffi::EventToUI, ui_session_interface::{io_loop, InvokeUiSession, Session}, @@ -147,7 +146,8 @@ pub struct FlutterHandler { notify_rendered: Arc>, renderer: Arc>, peer_info: Arc>, - hooks: Arc>>, + #[cfg(not(any(target_os = "android", target_os = "ios")))] + hooks: Arc>>, } #[cfg(not(feature = "flutter_texture_render"))] @@ -159,7 +159,8 @@ pub struct FlutterHandler { pub rgba: Arc>>, pub rgba_valid: Arc, peer_info: Arc>, - hooks: Arc>>, + #[cfg(not(any(target_os = "android", target_os = "ios")))] + hooks: Arc>>, } #[cfg(feature = "flutter_texture_render")] @@ -280,6 +281,7 @@ impl FlutterHandler { serde_json::ser::to_string(&msg_vec).unwrap_or("".to_owned()) } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub(crate) fn add_session_hook(&self, key: String, hook: crate::api::SessionHook) -> bool { let mut hooks = self.hooks.write().unwrap(); if hooks.contains_key(&key) { @@ -290,6 +292,7 @@ impl FlutterHandler { true } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub(crate) fn remove_session_hook(&self, key: &String) -> bool { let mut hooks = self.hooks.write().unwrap(); if !hooks.contains_key(key) { diff --git a/src/lib.rs b/src/lib.rs index a6b5c4a46..3f48be87d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,8 +49,10 @@ mod license; mod port_forward; #[cfg(not(any(target_os = "android", target_os = "ios")))] +#[cfg(any(feature = "flutter"))] pub mod api; #[cfg(not(any(target_os = "android", target_os = "ios")))] +#[cfg(any(feature = "flutter"))] pub mod plugins; mod tray; From 83e63d57e14365c69b1d2f859828233a0f0e7e00 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 17 Apr 2023 19:26:39 +0800 Subject: [PATCH 334/366] fix: ios build --- libs/scrap/build.rs | 2 +- libs/scrap/examples/capture_mag.rs | 2 +- libs/scrap/examples/record-screen.rs | 2 +- libs/scrap/src/common/mod.rs | 3 + libs/scrap/src/wayland.rs | 2 +- src/client.rs | 42 ++++++++--- src/client/io_loop.rs | 104 ++++++++++++++------------- src/common.rs | 4 ++ src/flutter_ffi.rs | 18 ++++- src/hbbs_http/sync.rs | 6 +- src/main.rs | 8 +-- src/server/connection.rs | 16 ----- src/server/video_service.rs | 6 -- src/ui_cm_interface.rs | 23 +++++- src/ui_interface.rs | 9 ++- src/ui_session_interface.rs | 20 +++++- 16 files changed, 171 insertions(+), 96 deletions(-) diff --git a/libs/scrap/build.rs b/libs/scrap/build.rs index 8939cd214..b0d800545 100644 --- a/libs/scrap/build.rs +++ b/libs/scrap/build.rs @@ -15,7 +15,7 @@ fn link_vcpkg(mut path: PathBuf, name: &str) -> PathBuf { let mut target = if target_os == "macos" { if target_arch == "x64" { "x64-osx".to_owned() - } else if target_arch == "arm64"{ + } else if target_arch == "arm64" { "arm64-osx".to_owned() } else { format!("{}-{}", target_arch, target_os) diff --git a/libs/scrap/examples/capture_mag.rs b/libs/scrap/examples/capture_mag.rs index 81b2d8573..047020899 100644 --- a/libs/scrap/examples/capture_mag.rs +++ b/libs/scrap/examples/capture_mag.rs @@ -3,9 +3,9 @@ extern crate scrap; use std::fs::File; +use scrap::{i420_to_rgb, Display}; #[cfg(windows)] use scrap::{CapturerMag, TraitCapturer}; -use scrap::{i420_to_rgb, Display}; fn main() { let n = Display::all().unwrap().len(); diff --git a/libs/scrap/examples/record-screen.rs b/libs/scrap/examples/record-screen.rs index 5df97838e..10ad66e09 100644 --- a/libs/scrap/examples/record-screen.rs +++ b/libs/scrap/examples/record-screen.rs @@ -18,7 +18,7 @@ use webm::mux; use webm::mux::Track; use scrap::vpxcodec as vpx_encode; -use scrap::{TraitCapturer, Capturer, Display, STRIDE_ALIGN}; +use scrap::{Capturer, Display, TraitCapturer, STRIDE_ALIGN}; const USAGE: &'static str = " Simple WebM screen capture. diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 26b946401..292f371c6 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -64,6 +64,9 @@ pub fn would_block_if_equal(old: &mut Vec, b: &[u8]) -> std::io::Result<()> pub trait TraitCapturer { fn set_use_yuv(&mut self, use_yuv: bool); + + // We doesn't support + #[cfg(not(any(target_os = "ios")))] fn frame<'a>(&'a mut self, timeout: std::time::Duration) -> std::io::Result>; #[cfg(windows)] diff --git a/libs/scrap/src/wayland.rs b/libs/scrap/src/wayland.rs index 82b219306..a5c4a3903 100644 --- a/libs/scrap/src/wayland.rs +++ b/libs/scrap/src/wayland.rs @@ -1,3 +1,3 @@ +pub mod capturable; pub mod pipewire; mod pipewire_dbus; -pub mod capturable; diff --git a/src/client.rs b/src/client.rs index d889e86cd..1dd525984 100644 --- a/src/client.rs +++ b/src/client.rs @@ -55,7 +55,6 @@ use scrap::{ use crate::{ common::{self, is_keyboard_mode_supported}, - server::video_service::{SCRAP_X11_REF_URL, SCRAP_X11_REQUIRED}, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -74,6 +73,27 @@ pub const MILLI1: Duration = Duration::from_millis(1); pub const SEC30: Duration = Duration::from_secs(30); pub const VIDEO_QUEUE_SIZE: usize = 120; +pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session not ready"; +pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; +pub const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER: &str = "Desktop session another user login"; +pub const LOGIN_MSG_DESKTOP_XORG_NOT_FOUND: &str = "Desktop xorg not found"; +// ls /usr/share/xsessions/ +pub const LOGIN_MSG_DESKTOP_NO_DESKTOP: &str = "Desktop none"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY: &str = + "Desktop session not ready, password empty"; +pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG: &str = + "Desktop session not ready, password wrong"; +pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; +pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; +pub const LOGIN_MSG_NO_PASSWORD_ACCESS: &str = "No Password Access"; +pub const LOGIN_MSG_OFFLINE: &str = "Offline"; +pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; +pub const SCRAP_OTHER_VERSION_OR_X11_REQUIRED: &str = + "Wayland requires higher version of linux distro. Please try X11 desktop or change your OS."; +pub const SCRAP_X11_REQUIRED: &str = "x11 expected"; +pub const SCRAP_X11_REF_URL: &str = "https://rustdesk.com/docs/en/manual/linux/#x11-required"; + /// Client of the remote desktop. pub struct Client; @@ -1970,49 +1990,49 @@ struct LoginErrorMsgBox { lazy_static::lazy_static! { static ref LOGIN_ERROR_MAP: Arc> = { use hbb_common::config::LINK_HEADLESS_LINUX_SUPPORT; - let map = HashMap::from([(crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LoginErrorMsgBox{ + let map = HashMap::from([(LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LoginErrorMsgBox{ msgtype: "session-login", title: "", text: "", link: "", try_again: true, - }), (crate::server::LOGIN_MSG_DESKTOP_XSESSION_FAILED, LoginErrorMsgBox{ + }), (LOGIN_MSG_DESKTOP_XSESSION_FAILED, LoginErrorMsgBox{ msgtype: "session-re-login", title: "", text: "", link: "", try_again: true, - }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LoginErrorMsgBox{ + }), (LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LoginErrorMsgBox{ msgtype: "info-nocancel", title: "another_user_login_title_tip", text: "another_user_login_text_tip", link: "", try_again: false, - }), (crate::server::LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LoginErrorMsgBox{ + }), (LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LoginErrorMsgBox{ msgtype: "info-nocancel", title: "xorg_not_found_title_tip", text: "xorg_not_found_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, try_again: true, - }), (crate::server::LOGIN_MSG_DESKTOP_NO_DESKTOP, LoginErrorMsgBox{ + }), (LOGIN_MSG_DESKTOP_NO_DESKTOP, LoginErrorMsgBox{ msgtype: "info-nocancel", title: "no_desktop_title_tip", text: "no_desktop_text_tip", link: LINK_HEADLESS_LINUX_SUPPORT, try_again: true, - }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, LoginErrorMsgBox{ + }), (LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, LoginErrorMsgBox{ msgtype: "session-login-password", title: "", text: "", link: "", try_again: true, - }), (crate::server::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, LoginErrorMsgBox{ + }), (LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, LoginErrorMsgBox{ msgtype: "session-login-re-password", title: "", text: "", link: "", try_again: true, - }), (crate::server::LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ + }), (LOGIN_MSG_NO_PASSWORD_ACCESS, LoginErrorMsgBox{ msgtype: "wait-remote-accept-nook", title: "Prompt", text: "Please wait for the remote side to accept your session request...", @@ -2030,11 +2050,11 @@ pub fn handle_login_error( err: &str, interface: &impl Interface, ) -> bool { - if err == crate::server::LOGIN_MSG_PASSWORD_EMPTY { + if err == LOGIN_MSG_PASSWORD_EMPTY { lc.write().unwrap().password = Default::default(); interface.msgbox("input-password", "Password Required", "", ""); true - } else if err == crate::server::LOGIN_MSG_PASSWORD_WRONG { + } else if err == LOGIN_MSG_PASSWORD_WRONG { lc.write().unwrap().password = Default::default(); interface.msgbox("re-input-password", err, "Do you want to enter again?", ""); true diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 4cc7fd825..919347e5f 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -36,6 +36,7 @@ use crate::client::{ use crate::common::{self, update_clipboard}; use crate::common::{get_default_sound_input, set_sound_input}; use crate::ui_session_interface::{InvokeUiSession, Session}; +#[cfg(not(any(target_os = "ios")))] use crate::{audio_service, ConnInner, CLIENT_SERVER}; use crate::{client::Data, client::Interface}; @@ -303,60 +304,65 @@ impl Remote { if let Some(device) = default_sound_device { set_sound_input(device); } - // Create a channel to receive error or closed message - let (tx, rx) = std::sync::mpsc::channel(); - let (tx_audio_data, mut rx_audio_data) = hbb_common::tokio::sync::mpsc::unbounded_channel(); - // Create a stand-alone inner, add subscribe to audio service - let conn_id = CLIENT_SERVER.write().unwrap().get_new_id(); - let client_conn_inner = ConnInner::new(conn_id.clone(), Some(tx_audio_data), None); - // now we subscribe - CLIENT_SERVER.write().unwrap().subscribe( - audio_service::NAME, - client_conn_inner.clone(), - true, - ); - let tx_audio = self.sender.clone(); - std::thread::spawn(move || { - loop { - // check if client is closed - match rx.try_recv() { - Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { - log::debug!("Exit voice call audio service of client"); - // unsubscribe - CLIENT_SERVER.write().unwrap().subscribe( - audio_service::NAME, - client_conn_inner, - false, - ); - break; - } - _ => {} - } - match rx_audio_data.try_recv() { - Ok((_instant, msg)) => match &msg.union { - Some(message::Union::AudioFrame(frame)) => { - let mut msg = Message::new(); - msg.set_audio_frame(frame.clone()); - tx_audio.send(Data::Message(msg)).ok(); - } - Some(message::Union::Misc(misc)) => { - let mut msg = Message::new(); - msg.set_misc(misc.clone()); - tx_audio.send(Data::Message(msg)).ok(); + // iOS does not have this server. + #[cfg(not(any(target_os = "ios")))] + { + // Create a channel to receive error or closed message + let (tx, rx) = std::sync::mpsc::channel(); + let (tx_audio_data, mut rx_audio_data) = hbb_common::tokio::sync::mpsc::unbounded_channel(); + // Create a stand-alone inner, add subscribe to audio service + let conn_id = CLIENT_SERVER.write().unwrap().get_new_id(); + let client_conn_inner = ConnInner::new(conn_id.clone(), Some(tx_audio_data), None); + // now we subscribe + CLIENT_SERVER.write().unwrap().subscribe( + audio_service::NAME, + client_conn_inner.clone(), + true, + ); + let tx_audio = self.sender.clone(); + std::thread::spawn(move || { + loop { + // check if client is closed + match rx.try_recv() { + Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { + log::debug!("Exit voice call audio service of client"); + // unsubscribe + CLIENT_SERVER.write().unwrap().subscribe( + audio_service::NAME, + client_conn_inner, + false, + ); + break; } _ => {} - }, - Err(err) => { - if err == TryRecvError::Empty { - // ignore - } else { - log::debug!("Failed to record local audio channel: {}", err); + } + match rx_audio_data.try_recv() { + Ok((_instant, msg)) => match &msg.union { + Some(message::Union::AudioFrame(frame)) => { + let mut msg = Message::new(); + msg.set_audio_frame(frame.clone()); + tx_audio.send(Data::Message(msg)).ok(); + } + Some(message::Union::Misc(misc)) => { + let mut msg = Message::new(); + msg.set_misc(misc.clone()); + tx_audio.send(Data::Message(msg)).ok(); + } + _ => {} + }, + Err(err) => { + if err == TryRecvError::Empty { + // ignore + } else { + log::debug!("Failed to record local audio channel: {}", err); + } } } } - } - }); - Some(tx) + }); + return Some(tx); + } + None } async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { diff --git a/src/common.rs b/src/common.rs index 3cf10eaba..c96e42b37 100644 --- a/src/common.rs +++ b/src/common.rs @@ -782,6 +782,7 @@ pub fn make_fd_to_json(id: i32, path: String, entries: &Vec) -> Strin /// 1. Try to send the url scheme from ipc. /// 2. If failed to send the url scheme, we open a new main window to handle this url scheme. pub fn handle_url_scheme(url: String) { + #[cfg(not(target_os = "ios"))] if let Err(err) = crate::ipc::send_url_scheme(url.clone()) { log::debug!("Send the url to the existing flutter process failed, {}. Let's open a new program to handle this.", err); let _ = crate::run_me(vec![url]); @@ -801,6 +802,9 @@ pub fn decode64>(input: T) -> Result, base64::DecodeError } pub async fn get_key(sync: bool) -> String { + #[cfg(target_os = "ios")] + let mut key = Config::get_option("key"); + #[cfg(not(target_os = "ios"))] let mut key = if sync { Config::get_option("key") } else { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index a8fe66c8a..3fdc5e48f 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -844,11 +844,13 @@ fn main_broadcast_message(data: &HashMap<&str, &str>) { pub fn main_change_theme(dark: String) { main_broadcast_message(&HashMap::from([("name", "theme"), ("dark", &dark)])); + #[cfg(not(any(target_os = "ios")))] send_to_cm(&crate::ipc::Data::Theme(dark)); } pub fn main_change_language(lang: String) { main_broadcast_message(&HashMap::from([("name", "language"), ("lang", &lang)])); + #[cfg(not(any(target_os = "ios")))] send_to_cm(&crate::ipc::Data::Language(lang)); } @@ -1138,6 +1140,8 @@ pub fn main_get_mouse_time() -> f64 { } pub fn main_wol(id: String) { + // TODO: move send_wol outside. + #[cfg(not(any(target_os = "ios")))] crate::lan::send_wol(id) } @@ -1147,10 +1151,12 @@ pub fn main_create_shortcut(_id: String) { } pub fn cm_send_chat(conn_id: i32, msg: String) { + #[cfg(not(any(target_os = "ios")))] crate::ui_cm_interface::send_chat(conn_id, msg); } pub fn cm_login_res(conn_id: i32, res: bool) { + #[cfg(not(any(target_os = "ios")))] if res { crate::ui_cm_interface::authorize(conn_id); } else { @@ -1159,22 +1165,29 @@ pub fn cm_login_res(conn_id: i32, res: bool) { } pub fn cm_close_connection(conn_id: i32) { + #[cfg(not(any(target_os = "ios")))] crate::ui_cm_interface::close(conn_id); } pub fn cm_remove_disconnected_connection(conn_id: i32) { + #[cfg(not(any(target_os = "ios")))] crate::ui_cm_interface::remove(conn_id); } pub fn cm_check_click_time(conn_id: i32) { + #[cfg(not(any(target_os = "ios")))] crate::ui_cm_interface::check_click_time(conn_id) } pub fn cm_get_click_time() -> f64 { - crate::ui_cm_interface::get_click_time() as _ + #[cfg(not(any(target_os = "ios")))] + return crate::ui_cm_interface::get_click_time() as _; + #[cfg(any(target_os = "ios"))] + return 0 as _; } pub fn cm_switch_permission(conn_id: i32, name: String, enabled: bool) { + #[cfg(not(any(target_os = "ios")))] crate::ui_cm_interface::switch_permission(conn_id, name, enabled) } @@ -1183,10 +1196,12 @@ pub fn cm_can_elevate() -> SyncReturn { } pub fn cm_elevate_portable(conn_id: i32) { + #[cfg(not(any(target_os = "ios")))] crate::ui_cm_interface::elevate_portable(conn_id); } pub fn cm_switch_back(conn_id: i32) { + #[cfg(not(any(target_os = "ios")))] crate::ui_cm_interface::switch_back(conn_id); } @@ -1222,6 +1237,7 @@ fn handle_query_onlines(onlines: Vec, offlines: Vec) { } pub fn query_onlines(ids: Vec) { + #[cfg(not(any(target_os = "ios")))] crate::rendezvous_mediator::query_online_states(ids, handle_query_onlines) } diff --git a/src/hbbs_http/sync.rs b/src/hbbs_http/sync.rs index ae91c60aa..cd7f95208 100644 --- a/src/hbbs_http/sync.rs +++ b/src/hbbs_http/sync.rs @@ -6,12 +6,13 @@ use hbb_common::{ }; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; - +#[cfg(not(any(target_os = "ios")))] use crate::Connection; const TIME_HEARTBEAT: Duration = Duration::from_secs(30); const TIME_CONN: Duration = Duration::from_secs(3); +#[cfg(not(any(target_os = "ios")))] lazy_static::lazy_static! { static ref SENDER : Mutex>> = Mutex::new(start_hbbs_sync()); } @@ -21,10 +22,12 @@ pub fn start() { let _sender = SENDER.lock().unwrap(); } +#[cfg(not(target_os = "ios"))] pub fn signal_receiver() -> broadcast::Receiver> { SENDER.lock().unwrap().subscribe() } +#[cfg(not(any(target_os = "ios")))] fn start_hbbs_sync() -> broadcast::Sender> { let (tx, _rx) = broadcast::channel::>(16); std::thread::spawn(move || start_hbbs_sync_async()); @@ -37,6 +40,7 @@ pub struct StrategyOptions { pub extra: HashMap, } +#[cfg(not(any(target_os = "ios")))] #[tokio::main(flavor = "current_thread")] async fn start_hbbs_sync_async() { tokio::spawn(async move { diff --git a/src/main.rs b/src/main.rs index 3759f6056..24e708c52 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ - #![cfg_attr( - all(not(debug_assertions), target_os = "windows"), - windows_subsystem = "windows" - )] +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] use librustdesk::*; diff --git a/src/server/connection.rs b/src/server/connection.rs index d4b645078..422080d14 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -65,22 +65,6 @@ lazy_static::lazy_static! { pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); -pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; -pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session not ready"; -pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; -pub const LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER: &str = "Desktop session another user login"; -pub const LOGIN_MSG_DESKTOP_XORG_NOT_FOUND: &str = "Desktop xorg not found"; -// ls /usr/share/xsessions/ -pub const LOGIN_MSG_DESKTOP_NO_DESKTOP: &str = "Desktop none"; -pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY: &str = - "Desktop session not ready, password empty"; -pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG: &str = - "Desktop session not ready, password wrong"; -pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; -pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; -pub const LOGIN_MSG_NO_PASSWORD_ACCESS: &str = "No Password Access"; -pub const LOGIN_MSG_OFFLINE: &str = "Offline"; - #[derive(Clone, Default)] pub struct ConnInner { id: i32, diff --git a/src/server/video_service.rs b/src/server/video_service.rs index e6f29a405..f7a6625ec 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -46,12 +46,6 @@ use std::{ time::{self, Duration, Instant}, }; -pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; -pub const SCRAP_OTHER_VERSION_OR_X11_REQUIRED: &str = - "Wayland requires higher version of linux distro. Please try X11 desktop or change your OS."; -pub const SCRAP_X11_REQUIRED: &str = "x11 expected"; -pub const SCRAP_X11_REF_URL: &str = "https://rustdesk.com/docs/en/manual/linux/#x11-required"; - pub const NAME: &'static str = "video"; lazy_static::lazy_static! { diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index b6bb3e35d..d542313a5 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -17,6 +17,7 @@ use serde_derive::Serialize; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::ipc::Connection; +#[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::ipc::{self, Data}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::tokio::sync::mpsc::unbounded_channel; @@ -56,6 +57,7 @@ pub struct Client { pub in_voice_call: bool, pub incoming_voice_call: bool, #[serde(skip)] + #[cfg(not(any(target_os = "ios")))] tx: UnboundedSender, } @@ -129,6 +131,7 @@ impl ConnectionManager { restart: bool, recording: bool, from_switch: bool, + #[cfg(not(any(target_os = "ios")))] tx: mpsc::UnboundedSender, ) { let client = Client { @@ -146,7 +149,8 @@ impl ConnectionManager { restart, recording, from_switch, - tx, + #[cfg(not(any(target_os = "ios")))] + _tx, in_voice_call: false, incoming_voice_call: false, }; @@ -222,6 +226,7 @@ impl ConnectionManager { } #[inline] +#[cfg(not(any(target_os = "ios")))] pub fn check_click_time(id: i32) { if let Some(client) = CLIENTS.read().unwrap().get(&id) { allow_err!(client.tx.send(Data::ClickTime(0))); @@ -234,6 +239,7 @@ pub fn get_click_time() -> i64 { } #[inline] +#[cfg(not(any(target_os = "ios")))] pub fn authorize(id: i32) { if let Some(client) = CLIENTS.write().unwrap().get_mut(&id) { client.authorized = true; @@ -242,6 +248,7 @@ pub fn authorize(id: i32) { } #[inline] +#[cfg(not(any(target_os = "ios")))] pub fn close(id: i32) { if let Some(client) = CLIENTS.read().unwrap().get(&id) { allow_err!(client.tx.send(Data::Close)); @@ -255,6 +262,7 @@ pub fn remove(id: i32) { // server mode send chat to peer #[inline] +#[cfg(not(any(target_os = "ios")))] pub fn send_chat(id: i32, text: String) { let clients = CLIENTS.read().unwrap(); if let Some(client) = clients.get(&id) { @@ -263,6 +271,7 @@ pub fn send_chat(id: i32, text: String) { } #[inline] +#[cfg(not(any(target_os = "ios")))] pub fn switch_permission(id: i32, name: String, enabled: bool) { if let Some(client) = CLIENTS.read().unwrap().get(&id) { allow_err!(client.tx.send(Data::SwitchPermission { name, enabled })); @@ -285,6 +294,7 @@ pub fn get_clients_length() -> usize { #[inline] #[cfg(feature = "flutter")] +#[cfg(not(any(target_os = "ios")))] pub fn switch_back(id: i32) { if let Some(client) = CLIENTS.read().unwrap().get(&id) { allow_err!(client.tx.send(Data::SwitchSidesBack)); @@ -594,6 +604,7 @@ pub async fn start_listen( cm.remove_connection(current_id, true); } +#[cfg(not(any(target_os = "ios")))] async fn handle_fs(fs: ipc::FS, write_jobs: &mut Vec, tx: &UnboundedSender) { match fs { ipc::FS::ReadDir { @@ -739,6 +750,7 @@ async fn handle_fs(fs: ipc::FS, write_jobs: &mut Vec, tx: &Unbo } } +#[cfg(not(any(target_os = "ios")))] async fn read_dir(dir: &str, include_hidden: bool, tx: &UnboundedSender) { let path = { if dir.is_empty() { @@ -756,6 +768,7 @@ async fn read_dir(dir: &str, include_hidden: bool, tx: &UnboundedSender) { } } +#[cfg(not(any(target_os = "ios")))] async fn handle_result( res: std::result::Result, S>, id: i32, @@ -775,6 +788,7 @@ async fn handle_result( } } +#[cfg(not(any(target_os = "ios")))] async fn remove_file(path: String, id: i32, file_num: i32, tx: &UnboundedSender) { handle_result( spawn_blocking(move || fs::remove_file(&path)).await, @@ -785,6 +799,7 @@ async fn remove_file(path: String, id: i32, file_num: i32, tx: &UnboundedSender< .await; } +#[cfg(not(any(target_os = "ios")))] async fn create_dir(path: String, id: i32, tx: &UnboundedSender) { handle_result( spawn_blocking(move || fs::create_dir(&path)).await, @@ -795,6 +810,7 @@ async fn create_dir(path: String, id: i32, tx: &UnboundedSender) { .await; } +#[cfg(not(any(target_os = "ios")))] async fn remove_dir(path: String, id: i32, recursive: bool, tx: &UnboundedSender) { let path = fs::get_path(&path); handle_result( @@ -813,6 +829,7 @@ async fn remove_dir(path: String, id: i32, recursive: bool, tx: &UnboundedSender .await; } +#[cfg(not(any(target_os = "ios")))] fn send_raw(msg: Message, tx: &UnboundedSender) { match msg.write_to_bytes() { Ok(bytes) => { @@ -859,6 +876,8 @@ pub fn elevate_portable(_id: i32) { #[inline] pub fn handle_incoming_voice_call(id: i32, accept: bool) { if let Some(client) = CLIENTS.read().unwrap().get(&id) { + // Not handled in iOS yet. + #[cfg(not(any(target_os = "ios")))] allow_err!(client.tx.send(Data::VoiceCallResponse(accept))); }; } @@ -867,6 +886,8 @@ pub fn handle_incoming_voice_call(id: i32, accept: bool) { #[inline] pub fn close_voice_call(id: i32) { if let Some(client) = CLIENTS.read().unwrap().get(&id) { + // Not handled in iOS yet. + #[cfg(not(any(target_os = "ios")))] allow_err!(client.tx.send(Data::CloseVoiceCall("".to_owned()))); }; } diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 3749b4918..30c6b9ab4 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -26,7 +26,10 @@ use hbb_common::{ #[cfg(feature = "flutter")] use crate::hbbs_http::account; -use crate::{common::SOFTWARE_UPDATE_URL, ipc}; +use crate::{common::SOFTWARE_UPDATE_URL}; +#[cfg(not(any(target_os = "ios")))] +use crate::ipc; + type Message = RendezvousMessage; @@ -569,6 +572,7 @@ pub fn create_shortcut(_id: String) { #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] #[inline] pub fn discover() { + #[cfg(not(any(target_os = "ios")))] std::thread::spawn(move || { allow_err!(crate::lan::discover()); }); @@ -922,7 +926,8 @@ pub fn option_synced() -> bool { OPTION_SYNCED.lock().unwrap().clone() } -#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] +#[cfg(any(target_os = "android", feature = "flutter"))] +#[cfg(not(any(target_os = "ios")))] #[tokio::main(flavor = "current_thread")] pub(crate) async fn send_to_cm(data: &ipc::Data) { if let Ok(mut c) = ipc::connect(1000, "_cm").await { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 504982f50..db0aab0ae 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -263,7 +263,10 @@ impl Session { } pub fn is_xfce(&self) -> bool { - crate::platform::is_xfce() + #[cfg(not(any(target_os = "ios")))] + return crate::platform::is_xfce(); + #[cfg(any(target_os = "ios"))] + false } pub fn get_supported_keyboard_modes(&self) -> Vec { @@ -526,6 +529,17 @@ impl Session { self.send(Data::Message(msg_out)); } + #[cfg(any(target_os = "ios"))] + pub fn handle_flutter_key_event( + &self, + _name: &str, + platform_code: i32, + position_code: i32, + lock_modes: i32, + down_or_up: bool, + ) {} + + #[cfg(not(any(target_os = "ios")))] pub fn handle_flutter_key_event( &self, _name: &str, @@ -755,6 +769,10 @@ impl Session { self.send(Data::ElevateWithLogon(username, password)); } + #[cfg(any(target_os = "ios"))] + pub fn switch_sides(&self) {} + + #[cfg(not(any(target_os = "ios")))] #[tokio::main(flavor = "current_thread")] pub async fn switch_sides(&self) { match crate::ipc::connect(1000, "").await { From f0b532426f0ae401cff93cc3bdcf55ebf584c38c Mon Sep 17 00:00:00 2001 From: Kingtous Date: Mon, 17 Apr 2023 20:49:37 +0800 Subject: [PATCH 335/366] fix: connection text in client now --- src/platform/linux_desktop_manager.rs | 4 ++-- src/server/connection.rs | 14 +++++++------- src/server/wayland.rs | 2 +- src/ui_cm_interface.rs | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/platform/linux_desktop_manager.rs b/src/platform/linux_desktop_manager.rs index 25fada503..fe60964cc 100644 --- a/src/platform/linux_desktop_manager.rs +++ b/src/platform/linux_desktop_manager.rs @@ -1,5 +1,5 @@ use super::{linux::*, ResultType}; -use crate::server::{ +use crate::client::{ LOGIN_MSG_DESKTOP_NO_DESKTOP, LOGIN_MSG_DESKTOP_SESSION_ANOTHER_USER, LOGIN_MSG_DESKTOP_SESSION_NOT_READY, LOGIN_MSG_DESKTOP_XORG_NOT_FOUND, LOGIN_MSG_DESKTOP_XSESSION_FAILED, @@ -152,7 +152,7 @@ fn try_start_x_session(username: &str, password: &str) -> ResultType<(String, bo desktop_manager.is_running(), )) } else { - bail!(crate::server::LOGIN_MSG_DESKTOP_NOT_INITED); + bail!(crate::client::LOGIN_MSG_DESKTOP_NOT_INITED); } } diff --git a/src/server/connection.rs b/src/server/connection.rs index 422080d14..f3768aacf 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1270,13 +1270,13 @@ impl Connection { // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. #[cfg(all(target_os = "linux", feature = "linux_headless"))] #[cfg(not(any(feature = "flatpak", feature = "appimage")))] - if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_DESKTOP_SESSION_NOT_READY { + if !desktop_err.is_empty() && desktop_err != crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY { self.send_login_error(desktop_err).await; return true; } if !hbb_common::is_ipv4_str(&lr.username) && lr.username != Config::get_id() { - self.send_login_error(LOGIN_MSG_OFFLINE).await; + self.send_login_error(crate::client::LOGIN_MSG_OFFLINE).await; } else if password::approve_mode() == ApproveMode::Click || password::approve_mode() == ApproveMode::Both && !password::has_valid_password() { @@ -1284,7 +1284,7 @@ impl Connection { if hbb_common::get_version_number(&lr.version) >= hbb_common::get_version_number("1.2.0") { - self.send_login_error(LOGIN_MSG_NO_PASSWORD_ACCESS).await; + self.send_login_error(crate::client::LOGIN_MSG_NO_PASSWORD_ACCESS).await; } return true; } else if password::approve_mode() == ApproveMode::Password @@ -1323,7 +1323,7 @@ impl Connection { if desktop_err.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { - self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY) + self.send_login_error(crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY) .await; } #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] @@ -1371,15 +1371,15 @@ impl Connection { #[cfg(all(target_os = "linux", feature = "linux_headless"))] #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if desktop_err.is_empty() { - self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; + self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG).await; self.try_start_cm(lr.my_id, lr.my_name, false); } else { - self.send_login_error(LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG) + self.send_login_error(crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG) .await; } #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] { - self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await; + self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG).await; self.try_start_cm(lr.my_id, lr.my_name, false); } } else { diff --git a/src/server/wayland.rs b/src/server/wayland.rs index 73a022871..10b93afce 100644 --- a/src/server/wayland.rs +++ b/src/server/wayland.rs @@ -3,7 +3,7 @@ use hbb_common::{allow_err, platform::linux::DISTRO}; use scrap::{is_cursor_embedded, set_map_err, Capturer, Display, Frame, TraitCapturer}; use std::io; -use super::video_service::{ +use crate::client::{ SCRAP_OTHER_VERSION_OR_X11_REQUIRED, SCRAP_UBUNTU_HIGHER_REQUIRED, SCRAP_X11_REQUIRED, }; diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index d542313a5..d34b7a299 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -17,7 +17,7 @@ use serde_derive::Serialize; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::ipc::Connection; -#[cfg(not(any(target_os = "android", target_os = "ios")))] +#[cfg(not(any(target_os = "ios")))] use crate::ipc::{self, Data}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::tokio::sync::mpsc::unbounded_channel; @@ -150,7 +150,7 @@ impl ConnectionManager { recording, from_switch, #[cfg(not(any(target_os = "ios")))] - _tx, + tx, in_voice_call: false, incoming_voice_call: false, }; From 021612e8be2b1fc21c48d593f1de706648853f02 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Tue, 18 Apr 2023 13:40:32 +0800 Subject: [PATCH 336/366] fix: macos compile --- .github/workflows/flutter-build.yml | 2 +- build.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 4b5b695b0..839658149 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -431,7 +431,7 @@ jobs: VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg run: | rustup target add ${{ matrix.job.target }} - cargo build --release --target aarch64-apple-ios --lib + cargo build --features flutter --release --target aarch64-apple-ios --lib - name: Build rustdesk shell: bash diff --git a/build.rs b/build.rs index 85f21e7d0..7ff8f9ef6 100644 --- a/build.rs +++ b/build.rs @@ -123,9 +123,11 @@ fn main() { build_manifest(); #[cfg(windows)] build_windows(); - #[cfg(target_os = "macos")] - build_mac(); - #[cfg(target_os = "macos")] - println!("cargo:rustc-link-lib=framework=ApplicationServices"); + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + if target_os == "macos" { + #[cfg(target_os = "macos")] + build_mac(); + println!("cargo:rustc-link-lib=framework=ApplicationServices"); + } println!("cargo:rerun-if-changed=build.rs"); } From e269fb28ddd68d37995004dd89b2a2c429b1f719 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Tue, 18 Apr 2023 18:53:40 +0800 Subject: [PATCH 337/366] fix: build ipa archive first --- .github/workflows/flutter-build.yml | 30 +-- flutter/ios/Flutter/AppFrameworkInfo.plist | 2 +- flutter/ios/Podfile | 2 +- flutter/ios/Podfile.lock | 244 ++++++++----------- flutter/ios/Runner.xcodeproj/project.pbxproj | 13 +- flutter/ios/Runner/Info.plist | 4 + flutter/pubspec.lock | 2 +- 7 files changed, 129 insertions(+), 168 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 839658149..ae7db7135 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -437,23 +437,23 @@ jobs: shell: bash run: | pushd flutter - flutter build ipa --release + flutter build ios --release - - name: Upload Artifacts - # if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' - uses: actions/upload-artifact@master - with: - name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk - path: flutter/build/ios/ipa/*.ipa + # - name: Upload Artifacts + # # if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' + # uses: actions/upload-artifact@master + # with: + # name: rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.apk + # path: flutter/build/ios/ipa/*.ipa - - name: Publish ipa package - # if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' - uses: softprops/action-gh-release@v1 - with: - prerelease: true - tag_name: ${{ env.TAG_NAME }} - files: | - flutter/build/ios/ipa/*.ipa + # - name: Publish ipa package + # # if: env.ANDROID_SIGNING_KEY != null && env.UPLOAD_ARTIFACT == 'true' + # uses: softprops/action-gh-release@v1 + # with: + # prerelease: true + # tag_name: ${{ env.TAG_NAME }} + # files: | + # flutter/build/ios/ipa/*.ipa build-rustdesk-android: diff --git a/flutter/ios/Flutter/AppFrameworkInfo.plist b/flutter/ios/Flutter/AppFrameworkInfo.plist index 8d4492f97..9625e105d 100644 --- a/flutter/ios/Flutter/AppFrameworkInfo.plist +++ b/flutter/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/flutter/ios/Podfile b/flutter/ios/Podfile index 0df0a09f8..3f67abf6a 100644 --- a/flutter/ios/Podfile +++ b/flutter/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/flutter/ios/Podfile.lock b/flutter/ios/Podfile.lock index ff8c9282f..76d0bac73 100644 --- a/flutter/ios/Podfile.lock +++ b/flutter/ios/Podfile.lock @@ -1,194 +1,146 @@ PODS: - - device_info (0.0.1): + - device_info_plus (0.0.1): - Flutter - - Firebase/Analytics (8.15.0): - - Firebase/Core - - Firebase/Core (8.15.0): - - Firebase/CoreOnly - - FirebaseAnalytics (~> 8.15.0) - - Firebase/CoreOnly (8.15.0): - - FirebaseCore (= 8.15.0) - - firebase_analytics (9.1.8): - - Firebase/Analytics (= 8.15.0) - - firebase_core + - DKImagePickerController/Core (4.3.4): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.4) + - DKImagePickerController/PhotoGallery (4.3.4): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.4) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery - Flutter - - firebase_core (1.17.0): - - Firebase/CoreOnly (= 8.15.0) - - Flutter - - FirebaseAnalytics (8.15.0): - - FirebaseAnalytics/AdIdSupport (= 8.15.0) - - FirebaseCore (~> 8.0) - - FirebaseInstallations (~> 8.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (~> 2.30908.0) - - FirebaseAnalytics/AdIdSupport (8.15.0): - - FirebaseCore (~> 8.0) - - FirebaseInstallations (~> 8.0) - - GoogleAppMeasurement (= 8.15.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (~> 2.30908.0) - - FirebaseCore (8.15.0): - - FirebaseCoreDiagnostics (~> 8.0) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/Logger (~> 7.7) - - FirebaseCoreDiagnostics (8.15.0): - - GoogleDataTransport (~> 9.1) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/Logger (~> 7.7) - - nanopb (~> 2.30908.0) - - FirebaseInstallations (8.15.0): - - FirebaseCore (~> 8.0) - - GoogleUtilities/Environment (~> 7.7) - - GoogleUtilities/UserDefaults (~> 7.7) - - PromisesObjC (< 3.0, >= 1.2) - Flutter (1.0.0) - - GoogleAppMeasurement (8.15.0): - - GoogleAppMeasurement/AdIdSupport (= 8.15.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (~> 2.30908.0) - - GoogleAppMeasurement/AdIdSupport (8.15.0): - - GoogleAppMeasurement/WithoutAdIdSupport (= 8.15.0) - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (~> 2.30908.0) - - GoogleAppMeasurement/WithoutAdIdSupport (8.15.0): - - GoogleUtilities/AppDelegateSwizzler (~> 7.7) - - GoogleUtilities/MethodSwizzler (~> 7.7) - - GoogleUtilities/Network (~> 7.7) - - "GoogleUtilities/NSData+zlib (~> 7.7)" - - nanopb (~> 2.30908.0) - - GoogleDataTransport (9.1.4): - - GoogleUtilities/Environment (~> 7.7) - - nanopb (< 2.30910.0, >= 2.30908.0) - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/AppDelegateSwizzler (7.7.0): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Environment (7.7.0): - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.7.0): - - GoogleUtilities/Environment - - GoogleUtilities/MethodSwizzler (7.7.0): - - GoogleUtilities/Logger - - GoogleUtilities/Network (7.7.0): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (7.7.0)" - - GoogleUtilities/Reachability (7.7.0): - - GoogleUtilities/Logger - - GoogleUtilities/UserDefaults (7.7.0): - - GoogleUtilities/Logger + - flutter_keyboard_visibility (0.0.1): + - Flutter + - FMDB (2.7.5): + - FMDB/standard (= 2.7.5) + - FMDB/standard (2.7.5) - image_picker_ios (0.0.1): - Flutter - MTBBarcodeScanner (5.0.11) - - nanopb (2.30908.0): - - nanopb/decode (= 2.30908.0) - - nanopb/encode (= 2.30908.0) - - nanopb/decode (2.30908.0) - - nanopb/encode (2.30908.0) - - package_info (0.0.1): + - package_info_plus (0.4.5): - Flutter - - path_provider_ios (0.0.1): + - path_provider_foundation (0.0.1): - Flutter - - PromisesObjC (2.1.0) + - FlutterMacOS - qr_code_scanner (0.2.0): - Flutter - MTBBarcodeScanner - - shared_preferences_ios (0.0.1): + - SDWebImage (5.15.5): + - SDWebImage/Core (= 5.15.5) + - SDWebImage/Core (5.15.5) + - sqflite (0.0.2): + - Flutter + - FMDB (>= 2.7.5) + - SwiftyGif (5.4.4) + - uni_links (0.0.1): - Flutter - url_launcher_ios (0.0.1): - Flutter + - video_player_avfoundation (0.0.1): + - Flutter - wakelock (0.0.1): - Flutter DEPENDENCIES: - - device_info (from `.symlinks/plugins/device_info/ios`) - - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) + - flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - - package_info (from `.symlinks/plugins/package_info/ios`) - - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) - qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`) - - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) + - sqflite (from `.symlinks/plugins/sqflite/ios`) + - uni_links (from `.symlinks/plugins/uni_links/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - wakelock (from `.symlinks/plugins/wakelock/ios`) SPEC REPOS: trunk: - - Firebase - - FirebaseAnalytics - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseInstallations - - GoogleAppMeasurement - - GoogleDataTransport - - GoogleUtilities + - DKImagePickerController + - DKPhotoGallery + - FMDB - MTBBarcodeScanner - - nanopb - - PromisesObjC + - SDWebImage + - SwiftyGif EXTERNAL SOURCES: - device_info: - :path: ".symlinks/plugins/device_info/ios" - firebase_analytics: - :path: ".symlinks/plugins/firebase_analytics/ios" - firebase_core: - :path: ".symlinks/plugins/firebase_core/ios" + device_info_plus: + :path: ".symlinks/plugins/device_info_plus/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter + flutter_keyboard_visibility: + :path: ".symlinks/plugins/flutter_keyboard_visibility/ios" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" - package_info: - :path: ".symlinks/plugins/package_info/ios" - path_provider_ios: - :path: ".symlinks/plugins/path_provider_ios/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/ios" qr_code_scanner: :path: ".symlinks/plugins/qr_code_scanner/ios" - shared_preferences_ios: - :path: ".symlinks/plugins/shared_preferences_ios/ios" + sqflite: + :path: ".symlinks/plugins/sqflite/ios" + uni_links: + :path: ".symlinks/plugins/uni_links/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + video_player_avfoundation: + :path: ".symlinks/plugins/video_player_avfoundation/ios" wakelock: :path: ".symlinks/plugins/wakelock/ios" SPEC CHECKSUMS: - device_info: d7d233b645a32c40dfdc212de5cf646ca482f175 - Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d - firebase_analytics: 92d27947c7516981cabdc0acbb33cd0687bcda44 - firebase_core: aa1b92020533f5c23955e388c347c58fd64f8627 - FirebaseAnalytics: 7761cbadb00a717d8d0939363eb46041526474fa - FirebaseCore: 5743c5785c074a794d35f2fff7ecc254a91e08b1 - FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb - FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - GoogleAppMeasurement: 4c19f031220c72464d460c9daa1fb5d1acce958e - GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b - GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 + device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed + DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + file_picker: ce3938a0df3cc1ef404671531facef740d03f920 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 + FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb - nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 - path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 - PromisesObjC: 99b6f43f9e1044bd87a95a60beff28c2c44ddb72 + package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e + path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e - shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad - url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe + sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f + uni_links: d97da20c7701486ba192624d99bffaaffcfc298a + url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2 + video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f -PODFILE CHECKSUM: a00077baecbb97321490c14848fceed3893ca92a +PODFILE CHECKSUM: c649b4e69a3086d323110011d04604e416ad0dcd -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.0 diff --git a/flutter/ios/Runner.xcodeproj/project.pbxproj b/flutter/ios/Runner.xcodeproj/project.pbxproj index 953dad47b..06ecbdd13 100644 --- a/flutter/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -228,6 +228,7 @@ }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -264,6 +265,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -338,6 +340,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -351,7 +354,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -414,6 +417,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; @@ -433,7 +437,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -469,6 +473,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -482,7 +487,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/flutter/ios/Runner/Info.plist b/flutter/ios/Runner/Info.plist index 3886d2456..873fe3751 100644 --- a/flutter/ios/Runner/Info.plist +++ b/flutter/ios/Runner/Info.plist @@ -49,5 +49,9 @@ This app needs camera access to scan QR codes NSPhotoLibraryUsageDescription This app needs photo library access to get QR codes from image + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 639ee04de..1edf94527 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -1571,5 +1571,5 @@ packages: source: hosted version: "0.1.1" sdks: - dart: ">=2.18.0 <4.0.0" + dart: ">=2.18.0 <3.0.0" flutter: ">=3.3.0" From 74a3b14835be464d6d8148ac11b9f7ba6ffeffd2 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Tue, 18 Apr 2023 18:54:07 +0800 Subject: [PATCH 338/366] fix: remove the undefined symbol on iOS first --- flutter/ios/Runner/AppDelegate.swift | 8 ++++---- flutter/ios/Runner/ffi.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/flutter/ios/Runner/AppDelegate.swift b/flutter/ios/Runner/AppDelegate.swift index 203cfff11..38ac2d8cd 100644 --- a/flutter/ios/Runner/AppDelegate.swift +++ b/flutter/ios/Runner/AppDelegate.swift @@ -13,9 +13,9 @@ import Flutter } public func dummyMethodToEnforceBundling() { - get_rgba(); - free_rgba(nil); - get_by_name("", ""); - set_by_name("", ""); +// get_rgba(); +// free_rgba(nil); +// get_by_name("", ""); +// set_by_name("", ""); } } diff --git a/flutter/ios/Runner/ffi.h b/flutter/ios/Runner/ffi.h index 701ec4b09..755bf99fe 100644 --- a/flutter/ios/Runner/ffi.h +++ b/flutter/ios/Runner/ffi.h @@ -1,4 +1,4 @@ -void* get_rgba(); -void free_rgba(void*); -void set_by_name(const char*, const char*); -const char* get_by_name(const char*, const char*); +//void* get_rgba(); +//void free_rgba(void*); +//void set_by_name(const char*, const char*); +//const char* get_by_name(const char*, const char*); From ca97065eb62af45504110c079bb70fbf1b6ba049 Mon Sep 17 00:00:00 2001 From: unglazed <130832733+unglazed@users.noreply.github.com> Date: Tue, 18 Apr 2023 14:41:17 +0300 Subject: [PATCH 339/366] add password handling in uni links handler Signed-off-by: unglazed <130832733+unglazed@users.noreply.github.com> --- flutter/lib/common.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index daffe261f..e91b8fcd2 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1599,8 +1599,9 @@ bool callUniLinksUriHandler(Uri uri) { final peerId = uri.path.substring("/new/".length); var param = uri.queryParameters; String? switch_uuid = param["switch_uuid"]; + String? password = param["password"]; Future.delayed(Duration.zero, () { - rustDeskWinManager.newRemoteDesktop(peerId, switch_uuid: switch_uuid); + rustDeskWinManager.newRemoteDesktop(peerId, password: password, switch_uuid: switch_uuid); }); return true; } From ecf8c2664c30f4f0d1ab831e9e1ea0ae42696ff4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Tue, 18 Apr 2023 23:02:37 +0800 Subject: [PATCH 340/366] plugin, tmp commit Signed-off-by: fufesou --- Cargo.lock | 1 - Cargo.toml | 2 +- libs/hbb_common/protos/message.proto | 6 + src/flutter.rs | 54 +++++++-- src/flutter_ffi.rs | 132 +++++++++------------- src/lib.rs | 1 + src/plugin/callback_msg.rs | 112 ++++++++++++++++++ src/plugin/config.rs | 0 src/plugin/desc.rs | 126 +++++++++++++++++++++ src/plugin/mod.rs | 19 ++++ src/plugin/plugins.rs | 162 +++++++++++++++++++++++++++ src/server.rs | 18 ++- src/server/dbus.rs | 24 ++-- src/ui_session_interface.rs | 8 ++ 14 files changed, 547 insertions(+), 118 deletions(-) create mode 100644 src/plugin/callback_msg.rs create mode 100644 src/plugin/config.rs create mode 100644 src/plugin/desc.rs create mode 100644 src/plugin/mod.rs create mode 100644 src/plugin/plugins.rs diff --git a/Cargo.lock b/Cargo.lock index 299d99c14..45f809680 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5133,7 +5133,6 @@ dependencies = [ "include_dir", "jni 0.19.0", "lazy_static", - "libloading", "libpulse-binding", "libpulse-simple-binding", "mac_address", diff --git a/Cargo.toml b/Cargo.toml index cd76b7d01..d70c202c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ hwcodec = ["scrap/hwcodec"] mediacodec = ["scrap/mediacodec"] linux_headless = ["pam", "users"] virtual_display_driver = ["virtual_display"] +plugin_framework = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -69,7 +70,6 @@ hex = "0.4" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } chrono = "0.4" cidr-utils = "0.5" -libloading = "0.7" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] cpal = "0.15" diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 42d2d7c09..b4eb9bf4b 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -600,6 +600,11 @@ message SwitchSidesResponse { message SwitchBack {} +message Plugin { + string id = 1; + bytes content = 2; +} + message Misc { oneof union { ChatMessage chat_message = 4; @@ -621,6 +626,7 @@ message Misc { SwitchSidesRequest switch_sides_request = 21; SwitchBack switch_back = 22; Resolution change_resolution = 24; + Plugin plugin = 25; } } diff --git a/src/flutter.rs b/src/flutter.rs index 34fe469fc..d2cba9dd7 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -29,20 +29,20 @@ use std::{ /// tag "main" for [Desktop Main Page] and [Mobile (Client and Server)] (the mobile don't need multiple windows, only one global event stream is needed) /// tag "cm" only for [Desktop CM Page] -pub(super) const APP_TYPE_MAIN: &str = "main"; +pub(crate) const APP_TYPE_MAIN: &str = "main"; #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub(super) const APP_TYPE_CM: &str = "cm"; +pub(crate) const APP_TYPE_CM: &str = "cm"; #[cfg(any(target_os = "android", target_os = "ios"))] -pub(super) const APP_TYPE_CM: &str = "main"; +pub(crate) const APP_TYPE_CM: &str = "main"; -pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; -pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; -pub(super) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; +pub(crate) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; +pub(crate) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; +pub(crate) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; lazy_static::lazy_static! { pub(crate) static ref CUR_SESSION_ID: RwLock = Default::default(); pub(crate) static ref SESSIONS: RwLock>> = Default::default(); - pub(crate) static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel + static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } #[cfg(all(target_os = "windows", feature = "flutter_texture_render"))] @@ -249,14 +249,18 @@ impl FlutterHandler { /// /// * `name` - The name of the event. /// * `event` - Fields of the event content. - fn push_event(&self, name: &str, event: Vec<(&str, &str)>) { + pub fn push_event(&self, name: &str, event: Vec<(&str, &str)>) -> Option { let mut h: HashMap<&str, &str> = event.iter().cloned().collect(); assert!(h.get("name").is_none()); h.insert("name", name); let out = serde_json::ser::to_string(&h).unwrap_or("".to_owned()); - if let Some(stream) = &*self.event_stream.read().unwrap() { - stream.add(EventToUI::Event(out)); - } + Some( + self.event_stream + .read() + .unwrap() + .as_ref()? + .add(EventToUI::Event(out)), + ) } pub(crate) fn close_event_stream(&mut self) { @@ -627,7 +631,7 @@ impl InvokeUiSession for FlutterHandler { } fn on_voice_call_closed(&self, reason: &str) { - self.push_event("on_voice_call_closed", [("reason", reason)].into()) + let _res = self.push_event("on_voice_call_closed", [("reason", reason)].into()); } fn on_voice_call_waiting(&self) { @@ -1007,3 +1011,29 @@ pub fn session_register_texture(id: *const char, ptr: usize) { #[no_mangle] #[cfg(not(feature = "flutter_texture_render"))] pub fn session_register_texture(_id: *const char, _ptr: usize) {} + +pub fn push_session_event(peer: &str, name: &str, event: Vec<(&str, &str)>) -> Option { + SESSIONS.read().unwrap().get(peer)?.push_event(name, event) +} + +pub fn push_global_event(channel: &str, event: String) -> Option { + Some(GLOBAL_EVENT_STREAM.read().unwrap().get(channel)?.add(event)) +} + +pub fn start_global_event_stream(s: StreamSink, app_type: String) -> ResultType<()> { + if let Some(_) = GLOBAL_EVENT_STREAM + .write() + .unwrap() + .insert(app_type.clone(), s) + { + log::warn!( + "Global event stream of type {} is started before, but now removed", + app_type + ); + } + Ok(()) +} + +pub fn stop_global_event_stream(app_type: String) { + let _ = GLOBAL_EVENT_STREAM.write().unwrap().remove(&app_type); +} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 3fdc5e48f..fa1cf6cc8 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -53,27 +53,6 @@ pub enum EventToUI { Rgba, } -pub fn start_global_event_stream(s: StreamSink, app_type: String) -> ResultType<()> { - if let Some(_) = flutter::GLOBAL_EVENT_STREAM - .write() - .unwrap() - .insert(app_type.clone(), s) - { - log::warn!( - "Global event stream of type {} is started before, but now removed", - app_type - ); - } - Ok(()) -} - -pub fn stop_global_event_stream(app_type: String) { - let _ = flutter::GLOBAL_EVENT_STREAM - .write() - .unwrap() - .remove(&app_type); -} - pub fn host_stop_system_key_propagate(_stopped: bool) { #[cfg(windows)] crate::platform::windows::stop_system_key_propagate(_stopped); @@ -338,7 +317,13 @@ pub fn session_handle_flutter_key_event( down_or_up: bool, ) { if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.handle_flutter_key_event(&name, platform_code, position_code, lock_modes, down_or_up); + session.handle_flutter_key_event( + &name, + platform_code, + position_code, + lock_modes, + down_or_up, + ); } } @@ -739,20 +724,18 @@ pub fn main_load_recent_peers() { .drain(..) .map(|(id, _, p)| peer_to_map(id, p)) .collect(); - if let Some(s) = flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(flutter::APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "load_recent_peers".to_owned()), - ( - "peers", - serde_json::ser::to_string(&peers).unwrap_or("".to_owned()), - ), - ]); - s.add(serde_json::ser::to_string(&data).unwrap_or("".to_owned())); - }; + + let data = HashMap::from([ + ("name", "load_recent_peers".to_owned()), + ( + "peers", + serde_json::ser::to_string(&peers).unwrap_or("".to_owned()), + ), + ]); + let _res = flutter::push_global_event( + flutter::APP_TYPE_MAIN, + serde_json::ser::to_string(&data).unwrap_or("".to_owned()), + ); } } @@ -790,38 +773,33 @@ pub fn main_load_fav_peers() { } }) .collect(); - if let Some(s) = flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(flutter::APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "load_fav_peers".to_owned()), - ( - "peers", - serde_json::ser::to_string(&peers).unwrap_or("".to_owned()), - ), - ]); - s.add(serde_json::ser::to_string(&data).unwrap_or("".to_owned())); - }; + + let data = HashMap::from([ + ("name", "load_fav_peers".to_owned()), + ( + "peers", + serde_json::ser::to_string(&peers).unwrap_or("".to_owned()), + ), + ]); + let _res = flutter::push_global_event( + flutter::APP_TYPE_MAIN, + serde_json::ser::to_string(&data).unwrap_or("".to_owned()), + ); } } pub fn main_load_lan_peers() { - if let Some(s) = flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(flutter::APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "load_lan_peers".to_owned()), - ( - "peers", - serde_json::to_string(&get_lan_peers()).unwrap_or_default(), - ), - ]); - s.add(serde_json::ser::to_string(&data).unwrap_or("".to_owned())); - }; + let data = HashMap::from([ + ("name", "load_lan_peers".to_owned()), + ( + "peers", + serde_json::to_string(&get_lan_peers()).unwrap_or_default(), + ), + ]); + let _res = flutter::push_global_event( + flutter::APP_TYPE_MAIN, + serde_json::ser::to_string(&data).unwrap_or("".to_owned()), + ); } pub fn main_remove_discovered(id: String) { @@ -835,10 +813,9 @@ fn main_broadcast_message(data: &HashMap<&str, &str>) { flutter::APP_TYPE_DESKTOP_PORT_FORWARD, ]; + let event = serde_json::ser::to_string(&data).unwrap_or("".to_owned()); for app in apps { - if let Some(s) = flutter::GLOBAL_EVENT_STREAM.read().unwrap().get(app) { - s.add(serde_json::ser::to_string(data).unwrap_or("".to_owned())); - }; + let _res = flutter::push_global_event(app, event.clone()); } } @@ -1222,18 +1199,15 @@ unsafe extern "C" fn translate(name: *const c_char, locale: *const c_char) -> *c } fn handle_query_onlines(onlines: Vec, offlines: Vec) { - if let Some(s) = flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(flutter::APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "callback_query_onlines".to_owned()), - ("onlines", onlines.join(",")), - ("offlines", offlines.join(",")), - ]); - s.add(serde_json::ser::to_string(&data).unwrap_or("".to_owned())); - }; + let data = HashMap::from([ + ("name", "callback_query_onlines".to_owned()), + ("onlines", onlines.join(",")), + ("offlines", offlines.join(",")), + ]); + let _res = flutter::push_global_event( + flutter::APP_TYPE_MAIN, + serde_json::ser::to_string(&data).unwrap_or("".to_owned()), + ); } pub fn query_onlines(ids: Vec) { diff --git a/src/lib.rs b/src/lib.rs index 3f48be87d..ad9229ab5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod license; #[cfg(not(any(target_os = "android", target_os = "ios")))] mod port_forward; +#[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(any(feature = "flutter"))] pub mod api; diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs new file mode 100644 index 000000000..62962cc83 --- /dev/null +++ b/src/plugin/callback_msg.rs @@ -0,0 +1,112 @@ +use super::cstr_to_string; +use crate::flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}; +use hbb_common::{lazy_static, libc, log, message_proto::Plugin, ResultType}; +use serde_derive::{Deserialize, Serialize}; +use serde_json; +use std::{ + collections::HashMap, + ffi::{c_char, CStr}, + sync::Arc, +}; + +const MSG_TO_PEER_TARGET: &str = "peer"; +const MSG_TO_UI_TARGET: &str = "ui"; + +#[allow(dead_code)] +const MSG_TO_UI_FLUTTER_CHANNEL_MAIN: u16 = 0x01 << 0; +#[allow(dead_code)] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +const MSG_TO_UI_FLUTTER_CHANNEL_CM: u16 = 0x01 << 1; +#[cfg(any(target_os = "android", target_os = "ios"))] +const MSG_TO_UI_FLUTTER_CHANNEL_CM: u16 = 0x01; +const MSG_TO_UI_FLUTTER_CHANNEL_REMOTE: u16 = 0x01 << 2; +#[allow(dead_code)] +const MSG_TO_UI_FLUTTER_CHANNEL_TRANSFER: u16 = 0x01 << 3; +#[allow(dead_code)] +const MSG_TO_UI_FLUTTER_CHANNEL_FORWARD: u16 = 0x01 << 4; + +lazy_static::lazy_static! { + static ref MSG_TO_UI_FLUTTER_CHANNELS: Arc> = { + let channels = HashMap::from([ + (MSG_TO_UI_FLUTTER_CHANNEL_MAIN, APP_TYPE_MAIN.to_string()), + (MSG_TO_UI_FLUTTER_CHANNEL_CM, APP_TYPE_CM.to_string()), + ]); + Arc::new(channels) + }; +} + +/// Callback to send message to peer or ui. +/// peer, target, id are utf8 strings(null terminated). +/// +/// peer: The peer id. +/// target: "peer" or "ui". +/// id: The id of this plugin. +/// content: The content. +/// len: The length of the content. +pub fn callback_msg( + peer: *const c_char, + target: *const c_char, + id: *const c_char, + content: *const c_char, + len: usize, +) { + macro_rules! callback_msg_field { + ($field: ident) => { + let $field = match cstr_to_string($field) { + Err(e) => { + log::error!("Failed to convert {} to string, {}", stringify!($field), e); + return; + } + Ok(v) => v, + }; + }; + } + callback_msg_field!(peer); + callback_msg_field!(target); + callback_msg_field!(id); + + match &target as _ { + MSG_TO_PEER_TARGET => { + if let Some(session) = SESSIONS.write().unwrap().get_mut(&peer) { + let content_slice = + unsafe { std::slice::from_raw_parts(content as *const u8, len) }; + let content_vec = Vec::from(content_slice); + let plugin = Plugin { + id, + content: bytes::Bytes::from(content_vec), + ..Default::default() + }; + session.send_plugin(plugin); + } + } + MSG_TO_UI_TARGET => { + let content_slice = unsafe { std::slice::from_raw_parts(content as *const u8, len) }; + let channel = u16::from_be_bytes([content_slice[0], content_slice[1]]); + let content = std::string::String::from_utf8(content_slice[2..].to_vec()) + .unwrap_or("".to_string()); + let mut m = HashMap::new(); + m.insert("name", "plugin"); + m.insert("peer", &peer); + m.insert("content", &content); + let event = serde_json::to_string(&m).unwrap_or("".to_string()); + for (k, v) in MSG_TO_UI_FLUTTER_CHANNELS.iter() { + if channel & k != 0 { + let _res = flutter::push_global_event(v as _, event.clone()); + } + } + if channel & MSG_TO_UI_FLUTTER_CHANNEL_REMOTE != 0 + || channel & MSG_TO_UI_FLUTTER_CHANNEL_TRANSFER != 0 + || channel & MSG_TO_UI_FLUTTER_CHANNEL_FORWARD != 0 + { + let _res = flutter::push_session_event( + APP_TYPE_CM, + "plugin", + vec![("peer", &peer), ("content", &content)], + ); + } + } + _ => { + log::error!("Unknown target {}", target); + } + } +} diff --git a/src/plugin/config.rs b/src/plugin/config.rs new file mode 100644 index 000000000..e69de29bb diff --git a/src/plugin/desc.rs b/src/plugin/desc.rs new file mode 100644 index 000000000..8d345b7d2 --- /dev/null +++ b/src/plugin/desc.rs @@ -0,0 +1,126 @@ +use hbb_common::ResultType; +use serde_derive::Deserialize; +use serde_json; +use std::collections::HashMap; +use std::ffi::{c_char, CStr}; + +#[derive(Debug, Deserialize)] +pub struct UiButton { + key: String, + text: String, + icon: String, + tooltip: String, + action: String, // The action to be triggered when the button is clicked. +} + +#[derive(Debug, Deserialize)] +pub struct UiCheckbox { + key: String, + text: String, + tooltip: String, + action: String, // The action to be triggered when the checkbox is checked or unchecked. +} + +#[derive(Debug, Deserialize)] +#[serde(tag = "t", content = "c")] +pub enum UiType { + Button(UiButton), + Checkbox(UiCheckbox), +} + +#[derive(Debug, Deserialize)] +pub struct Location { + core: String, + ui: HashMap, +} + +#[derive(Debug, Deserialize)] +pub struct ConfigItem { + key: String, + value: String, + default: String, + description: String, +} + +#[derive(Debug, Deserialize)] +pub struct Configs { + pub local: Vec, + pub session: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct Config { + pub host: Configs, + pub client: Configs, +} + +#[derive(Debug, Deserialize)] +pub struct Desc { + id: String, + name: String, + version: String, + description: String, + author: String, + home: String, + license: String, + published: String, + released: String, + github: String, + location: Location, + config: Config, +} + +impl Desc { + pub fn from_cstr(s: *const c_char) -> ResultType { + let s = unsafe { CStr::from_ptr(s) }; + Ok(serde_json::from_str(s.to_str()?)?) + } + + pub fn id(&self) -> &str { + &self.id + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn version(&self) -> &str { + &self.version + } + + pub fn description(&self) -> &str { + &self.description + } + + pub fn author(&self) -> &str { + &self.author + } + + pub fn home(&self) -> &str { + &self.home + } + + pub fn license(&self) -> &str { + &self.license + } + + pub fn published(&self) -> &str { + &self.published + } + + pub fn released(&self) -> &str { + &self.released + } + + pub fn github(&self) -> &str { + &self.github + } + + pub fn location(&self) -> &Location { + &self.location + } + + pub fn config(&self) -> &Config { + &self.config + } +} diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs new file mode 100644 index 000000000..ab233a503 --- /dev/null +++ b/src/plugin/mod.rs @@ -0,0 +1,19 @@ +use hbb_common::{dlopen::symbor::Library, log, ResultType}; +use std::{ + ffi::{c_char, CStr}, + path::Path, +}; + +mod callback_msg; +mod config; +pub mod desc; +mod plugins; + +pub use plugins::load_plugins; + +#[inline] +fn cstr_to_string(cstr: *const c_char) -> ResultType { + Ok(String::from_utf8(unsafe { + CStr::from_ptr(cstr).to_bytes().to_vec() + })?) +} diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs new file mode 100644 index 000000000..d93ac5938 --- /dev/null +++ b/src/plugin/plugins.rs @@ -0,0 +1,162 @@ +use std::{ + collections::HashMap, + ffi::{c_char, CStr}, + path::PathBuf, + sync::{Arc, RwLock}, +}; + +use super::{callback_msg, desc::Desc}; +use hbb_common::{ + anyhow::Error, + bail, + dlopen::symbor::Library, + lazy_static, libc, log, + log::{debug, error}, + ResultType, +}; + +lazy_static::lazy_static! { + pub static ref PLUGINS: Arc>> = Default::default(); +} + +/// Initialize the plugins. +/// Return 0 if success. +pub type PluginFuncInit = fn() -> i32; +/// Reset the plugin. +/// Return 0 if success. +pub type PluginFuncReset = fn() -> i32; +/// Clear the plugin. +/// Return 0 if success. +pub type PluginFuncClear = fn() -> i32; +/// Get the description of the plugin. +/// Return the description. The plugin allocate memory with `libc::malloc` and return the pointer. +pub type PluginFuncDesc = fn() -> *const c_char; +/// Callback to send message to peer or ui. +/// peer, target, id are utf8 strings(null terminated). +/// +/// peer: The peer id. +/// target: "peer" or "ui". +/// id: The id of this plugin. +/// content: The content. +/// len: The length of the content. +type PluginFuncCallbackMsg = fn( + peer: *const c_char, + target: *const c_char, + id: *const c_char, + content: *const c_char, + len: usize, +); +pub type PluginFuncSetCallbackMsg = fn(PluginFuncCallbackMsg); +/// The main function of the plugin. +/// method: The method. "handle_ui" or "handle_peer" +/// args: The arguments. +/// out: The output. The plugin allocate memory with `libc::malloc` and set the pointer to `out`. +/// out_len: The length of the output. +/// Return 0 if success. +pub type PluginFuncCall = fn( + method: *const c_char, + args: *const c_char, + out: *mut *mut c_char, + out_len: *mut usize, +) -> i32; + +macro_rules! make_plugin { + ($($field:ident : $tp:ty),+) => { + pub struct Plugin { + _lib: Library, + path: String, + desc: Option, + $($field: $tp),+ + } + + impl Plugin { + fn new(path: &str) -> ResultType { + let lib = match Library::open(path) { + Ok(lib) => lib, + Err(e) => { + bail!("Failed to load library {}, {}", path, e); + } + }; + + $(let $field = match unsafe { lib.symbol::<$tp>(stringify!($field)) } { + Ok(m) => { + log::info!("method found {}", stringify!($field)); + *m + }, + Err(e) => { + bail!("Failed to load {} func {}, {}", path, stringify!($field), e); + } + } + ;)+ + + Ok(Self { + _lib: lib, + path: path.to_string(), + desc: None, + $( $field ),+ + }) + } + } + } +} + +make_plugin!( + fn_init: PluginFuncInit, + fn_reset: PluginFuncReset, + fn_clear: PluginFuncClear, + fn_desc: PluginFuncDesc, + fn_set_cb_msg: PluginFuncSetCallbackMsg, + fn_call: PluginFuncCall +); + +pub fn load_plugins(dir: &str) -> ResultType<()> { + for entry in std::fs::read_dir(dir)? { + match entry { + Ok(entry) => { + let path = entry.path(); + if path.is_file() { + let path = path.to_str().unwrap_or(""); + if path.ends_with(".so") { + if let Err(e) = load_plugin(path) { + log::error!("{e}"); + } + } + } + } + Err(e) => { + log::error!("Failed to read dir entry, {}", e); + } + } + } + Ok(()) +} + +pub fn unload_plugin(id: &str) { + if let Some(plugin) = PLUGINS.write().unwrap().remove(id) { + let _ret = (plugin.fn_clear)(); + } +} + +pub fn reload_plugin(id: &str) -> ResultType<()> { + let path = match PLUGINS.read().unwrap().get(id) { + Some(plugin) => plugin.path.clone(), + None => bail!("Plugin {} not found", id), + }; + unload_plugin(id); + load_plugin(&path) +} + +fn load_plugin(path: &str) -> ResultType<()> { + let mut plugin = Plugin::new(path)?; + let desc = (plugin.fn_desc)(); + let desc_res = Desc::from_cstr(desc); + unsafe { + libc::free(desc as _); + } + let desc = desc_res?; + let id = desc.id().to_string(); + (plugin.fn_set_cb_msg)(callback_msg::callback_msg); + plugin.desc = Some(desc); + PLUGINS.write().unwrap().insert(id, plugin); + Ok(()) +} diff --git a/src/server.rs b/src/server.rs index 9c7fb2260..6a0371757 100644 --- a/src/server.rs +++ b/src/server.rs @@ -451,17 +451,13 @@ pub async fn start_ipc_url_server() { Ok(Some(data)) => match data { #[cfg(feature = "flutter")] Data::UrlLink(url) => { - if let Some(stream) = crate::flutter::GLOBAL_EVENT_STREAM - .read() - .unwrap() - .get(crate::flutter::APP_TYPE_MAIN) - { - let mut m = HashMap::new(); - m.insert("name", "on_url_scheme_received"); - m.insert("url", url.as_str()); - stream.add(serde_json::to_string(&m).unwrap()); - } else { - log::warn!("No main window app found!"); + let mut m = HashMap::new(); + m.insert("name", "on_url_scheme_received"); + m.insert("url", url.as_str()); + let event = serde_json::to_string(&m).unwrap(); + match crate::flutter::push_global_event(crate::flutter::APP_TYPE_MAIN, event) { + None => log::warn!("No main window app found!"), + Some(..) => {} } } _ => { diff --git a/src/server/dbus.rs b/src/server/dbus.rs index 081db3e8f..79aa62a1e 100644 --- a/src/server/dbus.rs +++ b/src/server/dbus.rs @@ -72,21 +72,17 @@ fn handle_client_message(builder: &mut IfaceBuilder<()>) { #[cfg(feature = "flutter")] { use crate::flutter::{self, APP_TYPE_MAIN}; - - if let Some(stream) = flutter::GLOBAL_EVENT_STREAM - .write() - .unwrap() - .get(APP_TYPE_MAIN) - { - let data = HashMap::from([ - ("name", "new_connection"), - ("uni_links", _uni_links.as_str()), - ]); - if !stream.add(serde_json::ser::to_string(&data).unwrap_or("".to_string())) { - log::error!("failed to add dbus message to flutter global dbus stream."); + let data = HashMap::from([ + ("name", "new_connection"), + ("uni_links", _uni_links.as_str()), + ]); + let event = serde_json::ser::to_string(&data).unwrap_or("".to_string()); + match crate::flutter::push_global_event(flutter::APP_TYPE_MAIN, event) { + None => log::error!("failed to find main event stream"), + Some(false) => { + log::error!("failed to add dbus message to flutter global dbus stream.") } - } else { - log::error!("failed to find main event stream"); + Some(true) => {} } } return Ok((DBUS_METHOD_RETURN_SUCCESS.to_string(),)); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index db0aab0ae..2747a6f00 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -240,6 +240,14 @@ impl Session { self.send(Data::Message(msg)); } + pub fn send_plugin(&self, plugin: Plugin) { + let mut misc = Misc::new(); + misc.set_plugin(plugin); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + self.send(Data::Message(msg_out)); + } + pub fn get_audit_server(&self, typ: String) -> String { if self.lc.read().unwrap().conn_id <= 0 || LocalConfig::get_option("access_token").is_empty() From b9b2f76ae02671a83551918eadb4033379bf9615 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 19 Apr 2023 11:21:37 +0800 Subject: [PATCH 341/366] tmp commit Signed-off-by: fufesou --- src/plugin/callback_msg.rs | 13 +-- src/plugin/config.rs | 196 +++++++++++++++++++++++++++++++++++++ src/plugin/desc.rs | 31 +++--- src/plugin/mod.rs | 7 +- src/plugin/plugins.rs | 48 ++++++--- 5 files changed, 251 insertions(+), 44 deletions(-) diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 62962cc83..c3a5a24d6 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -1,13 +1,8 @@ use super::cstr_to_string; use crate::flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}; -use hbb_common::{lazy_static, libc, log, message_proto::Plugin, ResultType}; -use serde_derive::{Deserialize, Serialize}; +use hbb_common::{lazy_static, log, message_proto::Plugin}; use serde_json; -use std::{ - collections::HashMap, - ffi::{c_char, CStr}, - sync::Arc, -}; +use std::{collections::HashMap, ffi::c_char, sync::Arc}; const MSG_TO_PEER_TARGET: &str = "peer"; const MSG_TO_UI_TARGET: &str = "ui"; @@ -85,7 +80,7 @@ pub fn callback_msg( let content = std::string::String::from_utf8(content_slice[2..].to_vec()) .unwrap_or("".to_string()); let mut m = HashMap::new(); - m.insert("name", "plugin"); + m.insert("name", "plugin_event"); m.insert("peer", &peer); m.insert("content", &content); let event = serde_json::to_string(&m).unwrap_or("".to_string()); @@ -99,7 +94,7 @@ pub fn callback_msg( || channel & MSG_TO_UI_FLUTTER_CHANNEL_FORWARD != 0 { let _res = flutter::push_session_event( - APP_TYPE_CM, + &peer, "plugin", vec![("peer", &peer), ("content", &content)], ); diff --git a/src/plugin/config.rs b/src/plugin/config.rs index e69de29bb..7a9228425 100644 --- a/src/plugin/config.rs +++ b/src/plugin/config.rs @@ -0,0 +1,196 @@ +use super::desc::ConfigItem; +use hbb_common::{bail, config::Config as HbbConfig, lazy_static, ResultType}; +use serde_derive::{Deserialize, Serialize}; +use std::{ + ops::{Deref, DerefMut}, + sync::{Arc, Mutex}, + {collections::HashMap, path::PathBuf}, +}; + +lazy_static::lazy_static! { + static ref CONFIG_LOCAL: Arc>> = Default::default(); + static ref CONFIG_LOCAL_ITEMS: Arc>>> = Default::default(); + static ref CONFIG_PEERS: Arc>> = Default::default(); + static ref CONFIG_PEER_ITEMS: Arc>>> = Default::default(); +} + +#[derive(Debug, Default, Serialize, Deserialize)] +struct LocalConfig(HashMap); +#[derive(Debug, Default, Serialize, Deserialize)] +struct PeerConfig(HashMap); +type PeersConfig = HashMap; + +#[inline] +fn path_plugins(id: &str) -> PathBuf { + HbbConfig::path("plugins").join(id) +} + +impl Deref for LocalConfig { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for LocalConfig { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Deref for PeerConfig { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for PeerConfig { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl LocalConfig { + #[inline] + fn path(id: &str) -> PathBuf { + path_plugins(id).join("local.toml") + } + + #[inline] + pub fn load(id: &str) { + let mut conf = hbb_common::config::load_path::(Self::path(id)); + if let Some(items) = CONFIG_LOCAL_ITEMS.lock().unwrap().get(id) { + for item in items { + if !conf.contains_key(&item.key) { + conf.insert(item.key.to_owned(), item.default.to_owned()); + } + } + } + CONFIG_LOCAL.lock().unwrap().insert(id.to_owned(), conf); + } + + #[inline] + fn save(id: &str) -> ResultType<()> { + match CONFIG_LOCAL.lock().unwrap().get(id) { + Some(config) => hbb_common::config::store_path(Self::path(id), config), + None => bail!("No such plugin {}", id), + } + } + + #[inline] + pub fn get(id: &str, key: &str) -> Option { + if let Some(conf) = CONFIG_LOCAL.lock().unwrap().get(id) { + return conf.get(key).map(|s| s.to_owned()); + } + Self::load(id); + CONFIG_LOCAL + .lock() + .unwrap() + .get(id)? + .get(key) + .map(|s| s.to_owned()) + } + + #[inline] + pub fn set(id: &str, key: &str, value: &str) -> ResultType<()> { + match CONFIG_LOCAL.lock().unwrap().get_mut(id) { + Some(config) => { + config.insert(key.to_owned(), value.to_owned()); + hbb_common::config::store_path(Self::path(id), config) + } + None => bail!("No such plugin {}", id), + } + } +} + +impl PeerConfig { + #[inline] + fn path(id: &str, peer: &str) -> PathBuf { + path_plugins(id) + .join("peers") + .join(format!("{}.toml", peer)) + } + + #[inline] + pub fn load(id: &str, peer: &str) { + let mut conf = hbb_common::config::load_path::(Self::path(id, peer)); + if let Some(items) = CONFIG_PEER_ITEMS.lock().unwrap().get(id) { + for item in items { + if !conf.contains_key(&item.key) { + conf.insert(item.key.to_owned(), item.default.to_owned()); + } + } + } + match CONFIG_PEERS.lock().unwrap().get_mut(id) { + Some(peers) => { + peers.insert(peer.to_owned(), conf); + } + None => { + let mut peers = HashMap::new(); + peers.insert(peer.to_owned(), conf); + CONFIG_PEERS.lock().unwrap().insert(id.to_owned(), peers); + } + } + } + + #[inline] + fn save(id: &str, peer: &str) -> ResultType<()> { + match CONFIG_PEERS.lock().unwrap().get(id) { + Some(peers) => match peers.get(peer) { + Some(config) => hbb_common::config::store_path(Self::path(id, peer), config), + None => bail!("No such peer {}", peer), + }, + None => bail!("No such plugin {}", id), + } + } + + #[inline] + pub fn get(id: &str, peer: &str, key: &str) -> Option { + if let Some(peers) = CONFIG_PEERS.lock().unwrap().get(id) { + if let Some(conf) = peers.get(peer) { + return conf.get(key).map(|s| s.to_owned()); + } + } + Self::load(id, peer); + CONFIG_PEERS + .lock() + .unwrap() + .get(id)? + .get(peer)? + .get(key) + .map(|s| s.to_owned()) + } + + #[inline] + pub fn set(id: &str, peer: &str, key: &str, value: &str) -> ResultType<()> { + match CONFIG_PEERS.lock().unwrap().get_mut(id) { + Some(peers) => match peers.get_mut(peer) { + Some(config) => { + config.insert(key.to_owned(), value.to_owned()); + hbb_common::config::store_path(Self::path(id, peer), config) + } + None => bail!("No such peer {}", peer), + }, + None => bail!("No such plugin {}", id), + } + } +} + +#[inline] +pub(super) fn set_local_items(id: &str, items: &Vec) { + CONFIG_LOCAL_ITEMS + .lock() + .unwrap() + .insert(id.to_owned(), items.clone()); +} + +#[inline] +pub(super) fn set_peer_items(id: &str, items: &Vec) { + CONFIG_PEER_ITEMS + .lock() + .unwrap() + .insert(id.to_owned(), items.clone()); +} diff --git a/src/plugin/desc.rs b/src/plugin/desc.rs index 8d345b7d2..26eda187a 100644 --- a/src/plugin/desc.rs +++ b/src/plugin/desc.rs @@ -1,10 +1,10 @@ use hbb_common::ResultType; -use serde_derive::Deserialize; +use serde_derive::{Deserialize, Serialize}; use serde_json; use std::collections::HashMap; use std::ffi::{c_char, CStr}; -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct UiButton { key: String, text: String, @@ -13,7 +13,7 @@ pub struct UiButton { action: String, // The action to be triggered when the button is clicked. } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct UiCheckbox { key: String, text: String, @@ -21,7 +21,7 @@ pub struct UiCheckbox { action: String, // The action to be triggered when the checkbox is checked or unchecked. } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] #[serde(tag = "t", content = "c")] pub enum UiType { Button(UiButton), @@ -30,28 +30,21 @@ pub enum UiType { #[derive(Debug, Deserialize)] pub struct Location { - core: String, - ui: HashMap, + pub ui: HashMap, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Clone, Deserialize)] pub struct ConfigItem { - key: String, - value: String, - default: String, - description: String, -} - -#[derive(Debug, Deserialize)] -pub struct Configs { - pub local: Vec, - pub session: Vec, + pub key: String, + pub value: String, + pub default: String, + pub description: String, } #[derive(Debug, Deserialize)] pub struct Config { - pub host: Configs, - pub client: Configs, + pub local: Vec, + pub peer: Vec, } #[derive(Debug, Deserialize)] diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index ab233a503..9e3e5993d 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -1,8 +1,5 @@ -use hbb_common::{dlopen::symbor::Library, log, ResultType}; -use std::{ - ffi::{c_char, CStr}, - path::Path, -}; +use hbb_common::ResultType; +use std::ffi::{c_char, CStr}; mod callback_msg; mod config; diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs index d93ac5938..a26f7a77c 100644 --- a/src/plugin/plugins.rs +++ b/src/plugin/plugins.rs @@ -1,19 +1,13 @@ use std::{ collections::HashMap, - ffi::{c_char, CStr}, - path::PathBuf, + ffi::c_char, + path::Path, sync::{Arc, RwLock}, }; use super::{callback_msg, desc::Desc}; -use hbb_common::{ - anyhow::Error, - bail, - dlopen::symbor::Library, - lazy_static, libc, log, - log::{debug, error}, - ResultType, -}; +use crate::flutter; +use hbb_common::{bail, dlopen::symbor::Library, lazy_static, libc, log, ResultType}; lazy_static::lazy_static! { pub static ref PLUGINS: Arc>> = Default::default(); @@ -109,7 +103,7 @@ make_plugin!( fn_call: PluginFuncCall ); -pub fn load_plugins(dir: &str) -> ResultType<()> { +pub fn load_plugins>(dir: P) -> ResultType<()> { for entry in std::fs::read_dir(dir)? { match entry { Ok(entry) => { @@ -156,7 +150,39 @@ fn load_plugin(path: &str) -> ResultType<()> { let desc = desc_res?; let id = desc.id().to_string(); (plugin.fn_set_cb_msg)(callback_msg::callback_msg); + update_config(&desc); + reload_ui(&desc); plugin.desc = Some(desc); PLUGINS.write().unwrap().insert(id, plugin); Ok(()) } + +fn update_config(desc: &Desc) { + super::config::set_local_items(desc.id(), &desc.config().local); + super::config::set_peer_items(desc.id(), &desc.config().peer); +} + +fn reload_ui(desc: &Desc) { + for (location, ui) in desc.location().ui.iter() { + let v: Vec<&str> = location.split('|').collect(); + // The first element is the "client" or "host". + // The second element is the "main", "remote", "cm", "file transfer", "port forward". + if v.len() >= 2 { + let available_channels = vec![ + flutter::APP_TYPE_MAIN, + flutter::APP_TYPE_DESKTOP_REMOTE, + flutter::APP_TYPE_CM, + flutter::APP_TYPE_DESKTOP_FILE_TRANSFER, + flutter::APP_TYPE_DESKTOP_PORT_FORWARD, + ]; + if available_channels.contains(&v[1]) { + if let Ok(ui) = serde_json::to_string(&ui) { + let mut m = HashMap::new(); + m.insert("name", "plugin_reload"); + m.insert("ui", &ui); + flutter::push_global_event(v[1], serde_json::to_string(&m).unwrap()); + } + } + } + } +} From e0b3ef6c4bb0c44fc29ba2bf9e208dcff942f812 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 19 Apr 2023 11:31:49 +0800 Subject: [PATCH 342/366] fix missed ffi method Signed-off-by: fufesou --- src/flutter_ffi.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index fa1cf6cc8..0386c5ad3 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -48,6 +48,13 @@ fn initialize(app_dir: &str) { } } +pub fn start_global_event_stream(s: StreamSink, app_type: String) -> ResultType<()> { + super::flutter::start_global_event_stream(s, app_type) +} + +pub fn stop_global_event_stream(app_type: String) { + super::flutter::stop_global_event_stream(app_type) +} pub enum EventToUI { Event(String), Rgba, From e194e70f1a78b22a28e048884986ace0e81dca52 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 19 Apr 2023 11:34:52 +0800 Subject: [PATCH 343/366] remove warn Signed-off-by: fufesou --- src/server/dbus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/dbus.rs b/src/server/dbus.rs index 79aa62a1e..b01b11c9d 100644 --- a/src/server/dbus.rs +++ b/src/server/dbus.rs @@ -71,7 +71,7 @@ fn handle_client_message(builder: &mut IfaceBuilder<()>) { move |_, _, (_uni_links,): (String,)| { #[cfg(feature = "flutter")] { - use crate::flutter::{self, APP_TYPE_MAIN}; + use crate::flutter; let data = HashMap::from([ ("name", "new_connection"), ("uni_links", _uni_links.as_str()), From 601372520022636d5939f4edd7158ab2768cff7c Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 19 Apr 2023 12:06:01 +0800 Subject: [PATCH 344/366] remove warns Signed-off-by: fufesou --- Cargo.lock | 1 + Cargo.toml | 1 + src/client.rs | 8 +++++--- src/client/io_loop.rs | 5 ++++- src/lib.rs | 5 ++++- src/ui_session_interface.rs | 23 +++++++++++++++-------- 6 files changed, 30 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45f809680..299d99c14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5133,6 +5133,7 @@ dependencies = [ "include_dir", "jni 0.19.0", "lazy_static", + "libloading", "libpulse-binding", "libpulse-simple-binding", "mac_address", diff --git a/Cargo.toml b/Cargo.toml index d70c202c6..784b83537 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ hex = "0.4" reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false } chrono = "0.4" cidr-utils = "0.5" +libloading = "0.7" [target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies] cpal = "0.15" diff --git a/src/client.rs b/src/client.rs index 1dd525984..1c6f12108 100644 --- a/src/client.rs +++ b/src/client.rs @@ -53,9 +53,7 @@ use scrap::{ ImageFormat, }; -use crate::{ - common::{self, is_keyboard_mode_supported}, -}; +use crate::common::{self, is_keyboard_mode_supported}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::{check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}; @@ -73,6 +71,8 @@ pub const MILLI1: Duration = Duration::from_millis(1); pub const SEC30: Duration = Duration::from_secs(30); pub const VIDEO_QUEUE_SIZE: usize = 120; +#[cfg(all(target_os = "linux", feature = "linux_headless"))] +#[cfg(not(any(feature = "flatpak", feature = "appimage")))] pub const LOGIN_MSG_DESKTOP_NOT_INITED: &str = "Desktop env is not inited"; pub const LOGIN_MSG_DESKTOP_SESSION_NOT_READY: &str = "Desktop session not ready"; pub const LOGIN_MSG_DESKTOP_XSESSION_FAILED: &str = "Desktop xsession failed"; @@ -88,7 +88,9 @@ pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password"; pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password"; pub const LOGIN_MSG_NO_PASSWORD_ACCESS: &str = "No Password Access"; pub const LOGIN_MSG_OFFLINE: &str = "Offline"; +#[cfg(target_os = "linux")] pub const SCRAP_UBUNTU_HIGHER_REQUIRED: &str = "Wayland requires Ubuntu 21.04 or higher version."; +#[cfg(target_os = "linux")] pub const SCRAP_OTHER_VERSION_OR_X11_REQUIRED: &str = "Wayland requires higher version of linux distro. Please try X11 desktop or change your OS."; pub const SCRAP_X11_REQUIRED: &str = "x11 expected"; diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 919347e5f..eccc837f0 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -362,7 +362,10 @@ impl Remote { }); return Some(tx); } - None + #[cfg(target_os = "ios")] + { + None + } } async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { diff --git a/src/lib.rs b/src/lib.rs index ad9229ab5..ec70d1179 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,6 @@ mod license; #[cfg(not(any(target_os = "android", target_os = "ios")))] mod port_forward; -#[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] #[cfg(any(feature = "flutter"))] pub mod api; @@ -56,6 +55,10 @@ pub mod api; #[cfg(any(feature = "flutter"))] pub mod plugins; +#[cfg(all(feature = "flutter", feature = "plugin_framework"))] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub mod plugin; + mod tray; mod ui_cm_interface; diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 2747a6f00..779119221 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -14,10 +14,12 @@ use rdev::{Event, EventType::*, KeyCode}; use uuid::Uuid; use hbb_common::config::{Config, LocalConfig, PeerConfig}; +#[cfg(not(feature = "flutter"))] +use hbb_common::fs; use hbb_common::rendezvous_proto::ConnType; use hbb_common::tokio::{self, sync::mpsc}; use hbb_common::{allow_err, message_proto::*}; -use hbb_common::{fs, get_version_number, log, Stream}; +use hbb_common::{get_version_number, log, Stream}; use crate::client::io_loop::Remote; use crate::client::{ @@ -25,7 +27,7 @@ use crate::client::{ input_os_password, load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, }; -use crate::common::{self, GrabState}; +use crate::common::GrabState; use crate::keyboard; use crate::{client::Data, client::Interface}; @@ -155,6 +157,7 @@ impl Session { self.lc.read().unwrap().get_toggle_option(&name) } + #[cfg(not(feature = "flutter"))] pub fn is_privacy_mode_supported(&self) -> bool { self.lc.read().unwrap().is_privacy_mode_supported() } @@ -198,6 +201,7 @@ impl Session { self.lc.read().unwrap().remember } + #[cfg(not(feature = "flutter"))] pub fn set_write_override( &mut self, job_id: i32, @@ -240,6 +244,8 @@ impl Session { self.send(Data::Message(msg)); } + #[cfg(all(feature = "flutter", feature = "plugin_framework"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn send_plugin(&self, plugin: Plugin) { let mut misc = Misc::new(); misc.set_plugin(plugin); @@ -270,6 +276,8 @@ impl Session { }); } + #[cfg(not(feature = "flutter"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn is_xfce(&self) -> bool { #[cfg(not(any(target_os = "ios")))] return crate::platform::is_xfce(); @@ -277,11 +285,6 @@ impl Session { false } - pub fn get_supported_keyboard_modes(&self) -> Vec { - let version = self.get_peer_version(); - common::get_supported_keyboard_modes(version) - } - pub fn remove_port_forward(&self, port: i32) { let mut config = self.load_config(); config.port_forwards = config @@ -310,6 +313,7 @@ impl Session { self.send(Data::AddPortForward(pf)); } + #[cfg(not(feature = "flutter"))] pub fn get_id(&self) -> String { self.id.clone() } @@ -369,6 +373,7 @@ impl Session { input_os_password(pass, activate, self.clone()); } + #[cfg(not(feature = "flutter"))] pub fn get_chatbox(&self) -> String { #[cfg(feature = "inline")] return crate::ui::inline::get_chatbox(); @@ -545,7 +550,8 @@ impl Session { position_code: i32, lock_modes: i32, down_or_up: bool, - ) {} + ) { + } #[cfg(not(any(target_os = "ios")))] pub fn handle_flutter_key_event( @@ -675,6 +681,7 @@ impl Session { })); } + #[cfg(not(feature = "flutter"))] pub fn get_icon_path(&self, file_type: i32, ext: String) -> String { let mut path = Config::icon_path(); if file_type == FileType::DirLink as i32 { From 1100b2a465e7f092e14069ce85a583392abd8897 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 19 Apr 2023 14:39:22 +0800 Subject: [PATCH 345/366] show fingerprint Signed-off-by: 21pages --- flutter/lib/common.dart | 13 +++++- flutter/lib/common/shared_state.dart | 25 ++++++++++ flutter/lib/common/widgets/toolbar.dart | 7 +++ .../desktop/pages/desktop_setting_page.dart | 12 ++++- .../lib/desktop/pages/remote_tab_page.dart | 46 +++++++++++++++---- flutter/lib/mobile/pages/settings_page.dart | 16 +++++++ flutter/lib/models/model.dart | 2 + src/client.rs | 25 ++++++---- src/client/io_loop.rs | 4 +- src/common.rs | 14 ++++++ src/flutter.rs | 4 ++ src/flutter_ffi.rs | 4 ++ src/ipc.rs | 12 +++++ src/lang/ca.rs | 5 ++ src/lang/cn.rs | 5 ++ src/lang/cs.rs | 5 ++ src/lang/da.rs | 5 ++ src/lang/de.rs | 6 ++- src/lang/el.rs | 5 ++ src/lang/eo.rs | 5 ++ src/lang/es.rs | 5 ++ src/lang/fa.rs | 5 ++ src/lang/fr.rs | 5 ++ src/lang/hu.rs | 5 ++ src/lang/id.rs | 5 ++ src/lang/it.rs | 3 ++ src/lang/ja.rs | 5 ++ src/lang/ko.rs | 5 ++ src/lang/kz.rs | 5 ++ src/lang/lt.rs | 5 ++ src/lang/nl.rs | 5 ++ src/lang/pl.rs | 3 ++ src/lang/pt_PT.rs | 5 ++ src/lang/ptbr.rs | 5 ++ src/lang/ro.rs | 5 ++ src/lang/ru.rs | 5 ++ src/lang/sk.rs | 5 ++ src/lang/sl.rs | 5 ++ src/lang/sq.rs | 5 ++ src/lang/sr.rs | 5 ++ src/lang/sv.rs | 5 ++ src/lang/template.rs | 3 ++ src/lang/th.rs | 5 ++ src/lang/tr.rs | 5 ++ src/lang/tw.rs | 5 ++ src/lang/ua.rs | 5 ++ src/lang/vn.rs | 5 ++ src/port_forward.rs | 3 +- src/ui.rs | 5 ++ src/ui/index.tis | 1 + src/ui/remote.rs | 2 + src/ui_interface.rs | 17 +++++-- src/ui_session_interface.rs | 1 + 53 files changed, 350 insertions(+), 28 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index e91b8fcd2..d64f2cde5 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -797,6 +797,7 @@ void showToast(String text, {Duration timeout = const Duration(seconds: 2)}) { padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), child: Text( text, + textAlign: TextAlign.center, style: const TextStyle( decoration: TextDecoration.none, fontWeight: FontWeight.w300, @@ -1601,7 +1602,8 @@ bool callUniLinksUriHandler(Uri uri) { String? switch_uuid = param["switch_uuid"]; String? password = param["password"]; Future.delayed(Duration.zero, () { - rustDeskWinManager.newRemoteDesktop(peerId, password: password, switch_uuid: switch_uuid); + rustDeskWinManager.newRemoteDesktop(peerId, + password: password, switch_uuid: switch_uuid); }); return true; } @@ -2033,3 +2035,12 @@ Widget futureBuilder( } }); } + +void onCopyFingerprint(String value) { + if (value.isNotEmpty) { + Clipboard.setData(ClipboardData(text: value)); + showToast('$value\n${translate("Copied")}'); + } else { + showToast(translate("no fingerprints")); + } +} diff --git a/flutter/lib/common/shared_state.dart b/flutter/lib/common/shared_state.dart index 4659a01ce..e4711ddf8 100644 --- a/flutter/lib/common/shared_state.dart +++ b/flutter/lib/common/shared_state.dart @@ -121,6 +121,29 @@ class ConnectionTypeState { Get.find(tag: tag(id)); } +class FingerprintState { + static String tag(String id) => 'fingerprint_$id'; + + static void init(String id) { + final key = tag(id); + if (!Get.isRegistered(tag: key)) { + final RxString state = ''.obs; + Get.put(state, tag: key); + } else { + Get.find(tag: key).value = ''; + } + } + + static void delete(String id) { + final key = tag(id); + if (Get.isRegistered(tag: key)) { + Get.delete(tag: key); + } + } + + static RxString find(String id) => Get.find(tag: tag(id)); +} + class ShowRemoteCursorState { static String tag(String id) => 'show_remote_cursor_$id'; @@ -269,6 +292,7 @@ initSharedStates(String id) { KeyboardEnabledState.init(id); ShowRemoteCursorState.init(id); RemoteCursorMovedState.init(id); + FingerprintState.init(id); PeerBoolOption.init(id, 'zoom-cursor', () => false); } @@ -279,5 +303,6 @@ removeSharedStates(String id) { ShowRemoteCursorState.delete(id); KeyboardEnabledState.delete(id); RemoteCursorMovedState.delete(id); + FingerprintState.delete(id); PeerBoolOption.delete(id, 'zoom-cursor'); } diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index abf12de30..451c7d1a5 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -201,6 +201,13 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { ), onPressed: () => ffi.recordingModel.toggle())); } + // fingerprint + if (!isDesktop) { + v.add(TTextMenu( + child: Text(translate('Copy Fingerprint')), + onPressed: () => onCopyFingerprint(FingerprintState.find(id).value), + )); + } return v; } diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 93fffe3da..c083421fd 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -1390,11 +1390,18 @@ class _AboutState extends State<_About> { final license = await bind.mainGetLicense(); final version = await bind.mainGetVersion(); final buildDate = await bind.mainGetBuildDate(); - return {'license': license, 'version': version, 'buildDate': buildDate}; + final fingerprint = await bind.mainGetFingerprint(); + return { + 'license': license, + 'version': version, + 'buildDate': buildDate, + 'fingerprint': fingerprint + }; }(), hasData: (data) { final license = data['license'].toString(); final version = data['version'].toString(); final buildDate = data['buildDate'].toString(); + final fingerprint = data['fingerprint'].toString(); const linkStyle = TextStyle(decoration: TextDecoration.underline); final scrollController = ScrollController(); return DesktopScrollWrapper( @@ -1415,6 +1422,9 @@ class _AboutState extends State<_About> { SelectionArea( child: Text('${translate('Build Date')}: $buildDate') .marginSymmetric(vertical: 4.0)), + SelectionArea( + child: Text('${translate('Fingerprint')}: $fingerprint') + .marginSymmetric(vertical: 4.0)), InkWell( onTap: () { launchUrlString('https://rustdesk.com/privacy'); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 53774e36d..005c74522 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -4,6 +4,7 @@ import 'dart:ui' as ui; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/consts.dart'; @@ -158,20 +159,36 @@ class _ConnectionTabPageState extends State { ], ); } else { - final msgDirect = translate( - connectionType.direct.value == ConnectionType.strDirect - ? 'Direct Connection' - : 'Relay Connection'); - final msgSecure = translate( - connectionType.secure.value == ConnectionType.strSecure - ? 'Secure Connection' - : 'Insecure Connection'); + bool secure = + connectionType.secure.value == ConnectionType.strSecure; + bool direct = + connectionType.direct.value == ConnectionType.strDirect; + var msgConn; + if (secure && direct) { + msgConn = translate("Direct and encrypted connection"); + } else if (secure && !direct) { + msgConn = translate("Relayed and encrypted connection"); + } else if (!secure && direct) { + msgConn = translate("Direct and unencrypted connection"); + } else { + msgConn = translate("Relayed and unencrypted connection"); + } + var msgFingerprint = '${translate('Fingerprint')}:\n'; + var fingerprint = FingerprintState.find(key).value; + if (fingerprint.length > 5 * 8) { + var first = fingerprint.substring(0, 39); + var second = fingerprint.substring(40); + msgFingerprint += '$first\n$second'; + } else { + msgFingerprint += fingerprint; + } + final tab = Row( mainAxisAlignment: MainAxisAlignment.center, children: [ icon, Tooltip( - message: '$msgDirect\n$msgSecure', + message: '$msgConn\n$msgFingerprint', child: SvgPicture.asset( 'assets/${connectionType.secure.value}${connectionType.direct.value}.svg', width: themeConf.iconSize, @@ -285,6 +302,17 @@ class _ConnectionTabPageState extends State { } } + menu.add(MenuEntryButton( + childBuilder: (TextStyle? style) => Text( + translate('Copy Fingerprint'), + style: style, + ), + proc: () => onCopyFingerprint(FingerprintState.find(key).value), + padding: padding, + dismissOnClicked: true, + dismissCallback: cancelFunc, + )); + return mod_menu.PopupMenu( items: menu .map((entry) => entry.build( diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index ff7c9e240..103c3e9fd 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:settings_ui/settings_ui.dart'; @@ -45,6 +46,7 @@ class _SettingsState extends State with WidgetsBindingObserver { var _autoRecordIncomingSession = false; var _localIP = ""; var _directAccessPort = ""; + var _fingerprint = ""; @override void initState() { @@ -135,6 +137,12 @@ class _SettingsState extends State with WidgetsBindingObserver { _directAccessPort = directAccessPort; } + final fingerprint = await bind.mainGetFingerprint(); + if (_fingerprint != fingerprint) { + update = true; + _fingerprint = fingerprint; + } + if (update) { setState(() {}); } @@ -462,6 +470,14 @@ class _SettingsState extends State with WidgetsBindingObserver { )), ), leading: Icon(Icons.info)), + SettingsTile.navigation( + onPressed: (context) => onCopyFingerprint(_fingerprint), + title: Text(translate("Fingerprint")), + value: Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: Text(_fingerprint), + ), + leading: Icon(Icons.fingerprint)), ], ), ], diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 77e1b8fbf..388f5bd59 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -224,6 +224,8 @@ class FfiModel with ChangeNotifier { parent.target?.chatModel.onVoiceCallIncoming(); } else if (name == "update_voice_call_state") { parent.target?.serverModel.updateVoiceCallState(evt); + } else if (name == "fingerprint") { + FingerprintState.find(peerId).value = evt['fingerprint'] ?? ''; } else { debugPrint("Unknown event name: $name"); } diff --git a/src/client.rs b/src/client.rs index 1c6f12108..d6d58136e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -200,7 +200,7 @@ impl Client { token: &str, conn_type: ConnType, interface: impl Interface, - ) -> ResultType<(Stream, bool)> { + ) -> ResultType<(Stream, bool, Option>)> { match Self::_start(peer, key, token, conn_type, interface).await { Err(err) => { let err_str = err.to_string(); @@ -221,7 +221,7 @@ impl Client { token: &str, conn_type: ConnType, interface: impl Interface, - ) -> ResultType<(Stream, bool)> { + ) -> ResultType<(Stream, bool, Option>)> { // to-do: remember the port for each peer, so that we can retry easier if hbb_common::is_ip_str(peer) { return Ok(( @@ -231,6 +231,7 @@ impl Client { ) .await?, true, + None, )); } // Allow connect to {domain}:{port} @@ -238,6 +239,7 @@ impl Client { return Ok(( socket_client::connect_tcp(peer, RENDEZVOUS_TIMEOUT).await?, true, + None, )); } let (mut rendezvous_server, servers, contained) = crate::get_rendezvous_server(1_000).await; @@ -333,7 +335,7 @@ impl Client { my_addr.is_ipv4(), ) .await?; - Self::secure_connection( + let pk = Self::secure_connection( peer, signed_id_pk, key, @@ -342,7 +344,7 @@ impl Client { interface, ) .await?; - return Ok((conn, false)); + return Ok((conn, false, pk)); } _ => { log::error!("Unexpected protobuf msg received: {:?}", msg_in); @@ -403,7 +405,7 @@ impl Client { token: &str, conn_type: ConnType, interface: impl Interface, - ) -> ResultType<(Stream, bool)> { + ) -> ResultType<(Stream, bool, Option>)> { let direct_failures = PeerConfig::load(peer_id).direct_failures; let mut connect_timeout = 0; const MIN: u64 = 1000; @@ -473,8 +475,9 @@ impl Client { } let mut conn = conn?; log::info!("{:?} used to establish connection", start.elapsed()); - Self::secure_connection(peer_id, signed_id_pk, key, &mut conn, direct, interface).await?; - Ok((conn, direct)) + let pk = Self::secure_connection(peer_id, signed_id_pk, key, &mut conn, direct, interface) + .await?; + Ok((conn, direct, pk)) } /// Establish secure connection with the server. @@ -485,17 +488,19 @@ impl Client { conn: &mut Stream, direct: bool, interface: impl Interface, - ) -> ResultType<()> { + ) -> ResultType>> { let rs_pk = get_rs_pk(if key.is_empty() { hbb_common::config::RS_PUB_KEY } else { key }); let mut sign_pk = None; + let mut option_pk = None; if !signed_id_pk.is_empty() && rs_pk.is_some() { if let Ok((id, pk)) = decode_id_pk(&signed_id_pk, &rs_pk.unwrap()) { if id == peer_id { sign_pk = Some(sign::PublicKey(pk)); + option_pk = Some(pk.to_vec()); } } if sign_pk.is_none() { @@ -507,7 +512,7 @@ impl Client { None => { // send an empty message out in case server is setting up secure and waiting for first message conn.send(&Message::new()).await?; - return Ok(()); + return Ok(option_pk); } }; match timeout(READ_TIMEOUT, conn.next()).await? { @@ -560,7 +565,7 @@ impl Client { bail!("Reset by the peer"); } } - Ok(()) + Ok(option_pk) } /// Request a relay connection to the server. diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index eccc837f0..59c35006c 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -121,9 +121,11 @@ impl Remote { ) .await { - Ok((mut peer, direct)) => { + Ok((mut peer, direct, pk)) => { self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready self.handler.set_connection_info(direct, false); + self.handler + .set_fingerprint(crate::common::pk_to_fingerprint(pk.unwrap_or_default())); // just build for now #[cfg(not(windows))] diff --git a/src/common.rs b/src/common.rs index c96e42b37..d66938cf7 100644 --- a/src/common.rs +++ b/src/common.rs @@ -840,3 +840,17 @@ pub fn is_peer_version_ge(v: &str) -> bool { false } + +pub fn pk_to_fingerprint(pk: Vec) -> String { + let s: String = pk.iter().map(|u| format!("{:02x}", u)).collect(); + s.chars() + .enumerate() + .map(|(i, c)| { + if i > 0 && i % 4 == 0 { + format!(" {}", c) + } else { + format!("{}", c) + } + }) + .collect() +} diff --git a/src/flutter.rs b/src/flutter.rs index d2cba9dd7..6b8a9f128 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -395,6 +395,10 @@ impl InvokeUiSession for FlutterHandler { ); } + fn set_fingerprint(&self, fingerprint: String) { + self.push_event("fingerprint", vec![("fingerprint", &fingerprint)]); + } + fn job_error(&self, id: i32, err: String, file_num: i32) { self.push_event( "job_error", diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 0386c5ad3..29df39b8a 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -921,6 +921,10 @@ pub fn main_get_permanent_password() -> String { ui_interface::permanent_password() } +pub fn main_get_fingerprint() -> String { + get_fingerprint() +} + pub fn main_get_online_statue() -> i64 { ONLINE.lock().unwrap().values().max().unwrap_or(&0).clone() } diff --git a/src/ipc.rs b/src/ipc.rs index 5f415c6e8..a3222dd02 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -383,6 +383,12 @@ async fn handle(data: Data, stream: &mut Connection) { )); } else if name == "rendezvous_servers" { value = Some(Config::get_rendezvous_servers().join(",")); + } else if name == "fingerprint" { + value = if Config::get_key_confirmed() { + Some(crate::common::pk_to_fingerprint(Config::get_key_pair().1)) + } else { + None + }; } else { value = None; } @@ -690,6 +696,12 @@ pub fn get_permanent_password() -> String { } } +pub fn get_fingerprint() -> String { + get_config("fingerprint") + .unwrap_or_default() + .unwrap_or_default() +} + pub fn set_permanent_password(v: String) -> ResultType<()> { Config::set_permanent_password(&v); set_config("permanent-password", v) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 995b56f1b..49fd2fb89 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 7f94173f0..2daa44c64 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "desktop 未安装"), ("no_desktop_text_tip", "请安装 desktop"), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", "指纹"), + ("Copy Fingerprint", "复制指纹"), + ("no fingerprints", "没有指纹"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 94cbc0c24..3bbd3e9eb 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index d567799c7..d22cac717 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index b703880fd..11860601e 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -492,7 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Es ist kein Desktop verfügbar."), ("no_desktop_text_tip", "Bitte installieren Sie den GNOME-Desktop."), ("No need to elevate", "Erhöhung der Rechte nicht erforderlich"), + ("System Sound", ""), + ("Default", ""), ("New RDP", "Neue RDP-Verbindung"), - ("Failed to listen on {}: {}", "Lauschen fehlgeschlagen auf Port {}: {}"), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 8929ca5e7..b071de54d 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 5762ca118..3916afc01 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 772ae03a4..4f6b6e6b1 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "No hay escritorio disponible"), ("no_desktop_text_tip", "Por favor, instala GNOME Desktop"), ("No need to elevate", "No es necesario elevar privilegios"), + ("System Sound", ""), + ("Default", ""), ("New RDP", "Nuevo RDP"), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 9daeec94b..b00046807 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "هیچ دسکتاپی در دسترس نیست"), ("no_desktop_text_tip", "لطفا دسکتاپ گنوم را نصب کنید"), ("No need to elevate", "نیازی به ارتقاء نیست"), + ("System Sound", ""), + ("Default", ""), ("New RDP", "ریموت جدید"), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index dcee91876..1850ff2f6 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index b82469c62..513c07e69 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 0316965af..76acc5a99 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index f18df3dec..2720934ed 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -495,5 +495,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("System Sound", "Dispositivo audio sistema"), ("Default", "Predefinita"), ("New RDP", "Nuovo RDP"), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index f980138b0..835a8e1ce 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index beded1c1e..1bd6bb4a4 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index ead19ea1f..3006abaa9 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 8bc81a01e..7e8ae803d 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Nėra pasiekiamų nuotolinių darbalaukių"), ("no_desktop_text_tip", "Prašom įdiegti GNOME Desktop"), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index fc6ff1e45..7c62a4d28 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 9e7c7407e..b350f1f45 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -495,5 +495,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("System Sound", "Dźwięk Systemowy"), ("Default", "Domyślne"), ("New RDP", "Nowe RDP"), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 04e5c79bd..6814d8dcb 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 02c92875e..4a4a6401d 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 73e7161a3..d2a4cb265 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index a42ef5009..a08bb8113 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Нет доступных рабочих столов"), ("no_desktop_text_tip", "Установите GNOME Desktop"), ("No need to elevate", "Повышение прав не требуется"), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 18a77b790..51f12c102 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index a6b3f76f6..52f1381c8 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 317b8c3be..86aae1150 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index adf5c539c..554bd7589 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 702cbab16..69fb3abb8 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index e7a85bd5b..9d567c9b4 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -495,5 +495,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("System Sound", ""), ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 1255c888b..172677cc3 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index d1389a9fa..9907141a3 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 7bb5d04e4..f9337827a 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "沒有可用的桌面"), ("no_desktop_text_tip", "請安裝 GNOME 桌面"), ("No need to elevate", "不需要提升權限"), + ("System Sound", ""), + ("Default", ""), ("New RDP", "新的 RDP"), + ("Fingerprint", "指紋"), + ("Copy Fingerprint", "複製指紋"), + ("no fingerprints", "沒有指紋"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index bcda47591..6f460c1df 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 543155418..75d78fe72 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -492,6 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", ""), ("no_desktop_text_tip", ""), ("No need to elevate", ""), + ("System Sound", ""), + ("Default", ""), ("New RDP", ""), + ("Fingerprint", ""), + ("Copy Fingerprint", ""), + ("no fingerprints", ""), ].iter().cloned().collect(); } diff --git a/src/port_forward.rs b/src/port_forward.rs index 4e05ad92f..028875fa3 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -154,7 +154,8 @@ async fn connect_and_login_2( } else { ConnType::PORT_FORWARD }; - let (mut stream, direct) = Client::start(id, key, token, conn_type, interface.clone()).await?; + let (mut stream, direct, _pk) = + Client::start(id, key, token, conn_type, interface.clone()).await?; let mut interface = interface; let mut buffer = Vec::new(); let mut received = false; diff --git a/src/ui.rs b/src/ui.rs index ca50fbf9d..d08e82a36 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -470,6 +470,10 @@ impl UI { get_version() } + fn get_fingerprint(&self) -> String { + get_fingerprint() + } + fn get_app_name(&self) -> String { get_app_name() } @@ -649,6 +653,7 @@ impl sciter::EventHandler for UI { fn get_software_update_url(); fn get_new_version(); fn get_version(); + fn get_fingerprint(); fn update_me(String); fn show_run_without_install(); fn run_without_install(); diff --git a/src/ui/index.tis b/src/ui/index.tis index b2f71dafb..011b895d2 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -364,6 +364,7 @@ class MyIdMenu: Reactor.Component { var name = handler.get_app_name(); msgbox("custom-nocancel-nook-hasclose", translate("About") + " " + name, "

    \
    Version: " + handler.get_version() + " \ +
    Fingerprint: " + handler.get_fingerprint() + " \
    Privacy Statement
    \
    Website
    \
    Copyright © 2022 Purslane Ltd.\ diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 6d7dc7b42..c161cddf3 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -144,6 +144,8 @@ impl InvokeUiSession for SciterHandler { self.call("setConnectionType", &make_args!(is_secured, direct)); } + fn set_fingerprint(&self, _fingerprint: String) {} + fn job_error(&self, id: i32, err: String, file_num: i32) { self.call("jobError", &make_args!(id, err, file_num)); } diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 30c6b9ab4..10f2982f2 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -24,13 +24,12 @@ use hbb_common::{ rendezvous_proto::*, }; +use crate::common::SOFTWARE_UPDATE_URL; #[cfg(feature = "flutter")] use crate::hbbs_http::account; -use crate::{common::SOFTWARE_UPDATE_URL}; #[cfg(not(any(target_os = "ios")))] use crate::ipc; - type Message = RendezvousMessage; pub type Children = Arc)>>; @@ -515,8 +514,7 @@ pub fn get_error() -> String { #[cfg(target_os = "linux")] { let dtype = crate::platform::linux::get_display_server(); - if crate::platform::linux::DISPLAY_SERVER_WAYLAND == dtype - { + if crate::platform::linux::DISPLAY_SERVER_WAYLAND == dtype { return crate::server::wayland::common_get_error(); } if dtype != crate::platform::linux::DISPLAY_SERVER_X11 { @@ -852,6 +850,17 @@ pub fn get_user_default_option(key: String) -> String { UserDefaultConfig::load().get(&key) } +pub fn get_fingerprint() -> String { + #[cfg(any(target_os = "android", target_os = "ios"))] + if Config::get_key_confirmed() { + return crate::common::pk_to_fingerprint(Config::get_key_pair().1); + } else { + return "".to_owned(); + } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + return ipc::get_fingerprint(); +} + // notice: avoiding create ipc connection repeatedly, // because windows named pipe has serious memory leak issue. #[cfg(not(any(target_os = "android", target_os = "ios")))] diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 779119221..191d71448 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -889,6 +889,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { fn close_success(&self); fn update_quality_status(&self, qs: QualityStatus); fn set_connection_type(&self, is_secured: bool, direct: bool); + fn set_fingerprint(&self, fingerprint: String); fn job_error(&self, id: i32, err: String, file_num: i32); fn job_done(&self, id: i32, file_num: i32); fn clear_all_jobs(&self); From f7e2938e6b76f87402754fb5635cbdbc05092102 Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 19 Apr 2023 17:06:59 +0800 Subject: [PATCH 346/366] handle peer event Signed-off-by: fufesou --- libs/hbb_common/protos/message.proto | 11 +- src/client/io_loop.rs | 19 +++- src/flutter.rs | 4 + src/flutter_ffi.rs | 11 ++ src/plugin/callback_msg.rs | 4 +- src/plugin/errno.rs | 26 +++++ src/plugin/mod.rs | 17 ++- src/plugin/plugins.rs | 149 +++++++++++++++++++++++---- src/server/connection.rs | 35 +++++-- 9 files changed, 242 insertions(+), 34 deletions(-) create mode 100644 src/plugin/errno.rs diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index b4eb9bf4b..1f323b7a9 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -600,11 +600,17 @@ message SwitchSidesResponse { message SwitchBack {} -message Plugin { +message PluginRequest { string id = 1; bytes content = 2; } +message PluginResponse { + string id = 1; + string name = 2; + string msg = 3; +} + message Misc { oneof union { ChatMessage chat_message = 4; @@ -626,7 +632,8 @@ message Misc { SwitchSidesRequest switch_sides_request = 21; SwitchBack switch_back = 22; Resolution change_resolution = 24; - Plugin plugin = 25; + PluginRequest plugin_request = 25; + PluginResponse plugin_response = 26; } } diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 59c35006c..bbcd74477 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -311,7 +311,8 @@ impl Remote { { // Create a channel to receive error or closed message let (tx, rx) = std::sync::mpsc::channel(); - let (tx_audio_data, mut rx_audio_data) = hbb_common::tokio::sync::mpsc::unbounded_channel(); + let (tx_audio_data, mut rx_audio_data) = + hbb_common::tokio::sync::mpsc::unbounded_channel(); // Create a stand-alone inner, add subscribe to audio service let conn_id = CLIENT_SERVER.write().unwrap().get_new_id(); let client_conn_inner = ConnInner::new(conn_id.clone(), Some(tx_audio_data), None); @@ -1297,6 +1298,22 @@ impl Remote { #[cfg(feature = "flutter")] self.handler.switch_back(&self.handler.id); } + #[cfg(all(feature = "flutter", feature = "plugin_framework"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + Some(misc::Union::PluginRequest(p)) => { + allow_err!(crate::plugin::handle_server_event(&p.id, &p.content)); + // to-do: show message box on UI when error occurs? + } + #[cfg(all(feature = "flutter", feature = "plugin_framework"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + Some(misc::Union::PluginResponse(p)) => { + let name = if p.name.is_empty() { + "plugin".to_string() + } else { + p.name + }; + self.handler.msgbox("custom-nocancel", &name, &p.msg, ""); + } _ => {} }, Some(message::Union::TestDelay(t)) => { diff --git a/src/flutter.rs b/src/flutter.rs index 6b8a9f128..02c6b16b1 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1001,6 +1001,7 @@ pub fn session_next_rgba(id: *const char) { } } +#[inline] #[no_mangle] #[cfg(feature = "flutter_texture_render")] pub fn session_register_texture(id: *const char, ptr: usize) { @@ -1012,14 +1013,17 @@ pub fn session_register_texture(id: *const char, ptr: usize) { } } +#[inline] #[no_mangle] #[cfg(not(feature = "flutter_texture_render"))] pub fn session_register_texture(_id: *const char, _ptr: usize) {} +#[inline] pub fn push_session_event(peer: &str, name: &str, event: Vec<(&str, &str)>) -> Option { SESSIONS.read().unwrap().get(peer)?.push_event(name, event) } +#[inline] pub fn push_global_event(channel: &str, event: String) -> Option { Some(GLOBAL_EVENT_STREAM.read().unwrap().get(channel)?.add(event)) } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 29df39b8a..d18971332 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -48,10 +48,12 @@ fn initialize(app_dir: &str) { } } +#[inline] pub fn start_global_event_stream(s: StreamSink, app_type: String) -> ResultType<()> { super::flutter::start_global_event_stream(s, app_type) } +#[inline] pub fn stop_global_event_stream(app_type: String) { super::flutter::stop_global_event_stream(app_type) } @@ -1391,6 +1393,15 @@ pub fn send_url_scheme(_url: String) { std::thread::spawn(move || crate::handle_url_scheme(_url)); } +#[inline] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +pub fn plugin_event(id: String, event: Vec) { + #[cfg(feature = "plugin_framework")] + { + crate::plugin::handle_ui_event(&id, &event); + } +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log}; diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index c3a5a24d6..42d3cf283 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -1,6 +1,6 @@ use super::cstr_to_string; use crate::flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}; -use hbb_common::{lazy_static, log, message_proto::Plugin}; +use hbb_common::{lazy_static, log, message_proto::PluginRequest}; use serde_json; use std::{collections::HashMap, ffi::c_char, sync::Arc}; @@ -66,7 +66,7 @@ pub fn callback_msg( let content_slice = unsafe { std::slice::from_raw_parts(content as *const u8, len) }; let content_vec = Vec::from(content_slice); - let plugin = Plugin { + let plugin = PluginRequest { id, content: bytes::Bytes::from(content_vec), ..Default::default() diff --git a/src/plugin/errno.rs b/src/plugin/errno.rs new file mode 100644 index 000000000..e7419608c --- /dev/null +++ b/src/plugin/errno.rs @@ -0,0 +1,26 @@ +pub const ERR_SUCCESS: i32 = 0; + +// ====================================================== +// errors that will be handled by RustDesk + +pub const ERR_RUSTDESK_HANDLE_BASE: i32 = 10000; + +// not loaded +pub const ERR_PLUGIN_LOAD: i32 = 10001; +// not initialized +pub const ERR_PLUGIN_MSG_CB: i32 = 10101; +// invalid +pub const ERR_CALL_INVALID_METHOD: i32 = 10201; +pub const ERR_CALL_NOT_SUPPORTED_METHOD: i32 = 10202; +// failed on calling +pub const ERR_CALL_INVALID_ARGS: i32 = 10301; +pub const ERR_PEER_ID_MISMATCH: i32 = 10302; + +// ====================================================== +// errors that must be handled by plugin + +pub const ERR_PLUGIN_HANDLE_BASE: i32 = 20000; + +pub const EER_CALL_FAILED: i32 = 200021; +pub const ERR_PEER_ON_FAILED: i32 = 30012; +pub const ERR_PEER_OFF_FAILED: i32 = 30012; diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 9e3e5993d..3d1998b63 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -5,8 +5,11 @@ mod callback_msg; mod config; pub mod desc; mod plugins; +mod errno; -pub use plugins::load_plugins; +pub use plugins::{ + handle_client_event, handle_ui_event, load_plugin, load_plugins, reload_plugin, unload_plugin, +}; #[inline] fn cstr_to_string(cstr: *const c_char) -> ResultType { @@ -14,3 +17,15 @@ fn cstr_to_string(cstr: *const c_char) -> ResultType { CStr::from_ptr(cstr).to_bytes().to_vec() })?) } + +#[inline] +fn get_code_msg_from_ret(ret: *const c_void) -> (i32, String) { + assert!(ret.is_null() == false); + let code_bytes = unsafe { std::slice::from_raw_parts(ret as *const u8, 4) }; + let code = i32::from_le_bytes([code_bytes[0], code_bytes[1], code_bytes[2], code_bytes[3]]); + let msg = unsafe { CStr::from_ptr((ret as *const u8).add(4) as _) } + .to_str() + .unwrap_or("") + .to_string(); + Ok((code, msg)) +} diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs index a26f7a77c..39bda5cb7 100644 --- a/src/plugin/plugins.rs +++ b/src/plugin/plugins.rs @@ -5,23 +5,41 @@ use std::{ sync::{Arc, RwLock}, }; -use super::{callback_msg, desc::Desc}; +use super::{callback_msg, desc::Desc, errno::*, get_code_msg_from_ret}; use crate::flutter; -use hbb_common::{bail, dlopen::symbor::Library, lazy_static, libc, log, ResultType}; +use hbb_common::{ + bail, + dlopen::symbor::Library, + lazy_static, libc, log, + message_proto::{Message, Misc, PluginResponse}, + ResultType, +}; + +const METHOD_HANDLE_UI: &[u8; 10] = b"handle_ui\0"; +const METHOD_HANDLE_PEER: &[u8; 12] = b"handle_peer\0"; lazy_static::lazy_static! { pub static ref PLUGINS: Arc>> = Default::default(); } /// Initialize the plugins. -/// Return 0 if success. -pub type PluginFuncInit = fn() -> i32; +/// +/// Return null ptr if success. +/// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. +/// The plugin allocate memory with `libc::malloc` and return the pointer. +pub type PluginFuncInit = fn() -> *const c_void; /// Reset the plugin. -/// Return 0 if success. -pub type PluginFuncReset = fn() -> i32; +/// +/// Return null ptr if success. +/// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. +/// The plugin allocate memory with `libc::malloc` and return the pointer. +pub type PluginFuncReset = fn() -> *const c_void; /// Clear the plugin. -/// Return 0 if success. -pub type PluginFuncClear = fn() -> i32; +/// +/// Return null ptr if success. +/// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. +/// The plugin allocate memory with `libc::malloc` and return the pointer. +pub type PluginFuncClear = fn() -> *const c_void; /// Get the description of the plugin. /// Return the description. The plugin allocate memory with `libc::malloc` and return the pointer. pub type PluginFuncDesc = fn() -> *const c_char; @@ -37,22 +55,19 @@ type PluginFuncCallbackMsg = fn( peer: *const c_char, target: *const c_char, id: *const c_char, - content: *const c_char, + content: *const c_void, len: usize, ); pub type PluginFuncSetCallbackMsg = fn(PluginFuncCallbackMsg); /// The main function of the plugin. /// method: The method. "handle_ui" or "handle_peer" /// args: The arguments. -/// out: The output. The plugin allocate memory with `libc::malloc` and set the pointer to `out`. -/// out_len: The length of the output. -/// Return 0 if success. -pub type PluginFuncCall = fn( - method: *const c_char, - args: *const c_char, - out: *mut *mut c_char, - out_len: *mut usize, -) -> i32; +/// +/// Return null ptr if success. +/// Return the error message if failed. `i32-String` without dash, i32 is a signed little-endian number, the String is utf8 string. +/// The plugin allocate memory with `libc::malloc` and return the pointer. +pub type PluginFuncCall = + fn(method: *const c_char, args: *const c_void, len: usize) -> *const c_void; macro_rules! make_plugin { ($($field:ident : $tp:ty),+) => { @@ -140,7 +155,7 @@ pub fn reload_plugin(id: &str) -> ResultType<()> { load_plugin(&path) } -fn load_plugin(path: &str) -> ResultType<()> { +pub fn load_plugin(path: &str) -> ResultType<()> { let mut plugin = Plugin::new(path)?; let desc = (plugin.fn_desc)(); let desc_res = Desc::from_cstr(desc); @@ -149,6 +164,7 @@ fn load_plugin(path: &str) -> ResultType<()> { } let desc = desc_res?; let id = desc.id().to_string(); + // to-do validate plugin (plugin.fn_set_cb_msg)(callback_msg::callback_msg); update_config(&desc); reload_ui(&desc); @@ -157,6 +173,101 @@ fn load_plugin(path: &str) -> ResultType<()> { Ok(()) } +fn handle_event(method: &[u8], id: &str, event: &[u8]) -> ResultType<()> { + match PLUGINS.read().unwrap().get(id) { + Some(plugin) => { + let ret = (plugin.fn_call)(method.as_ptr() as _, event.as_ptr(), event.len()); + if ret.is_null() { + Ok(()) + } else { + let (code, msg) = get_code_msg_from_ret(ret); + unsafe { + libc::free(ret); + } + bail!( + "Failed to handle plugin event, id: {}, method: {}, code: {}, msg: {}", + id, + std::string::String::from_utf8(method.to_vec()).unwrap_or_default(), + code, + msg + ); + } + } + None => bail!("Plugin {} not found", id), + } +} + +#[inline] +pub fn handle_ui_event(id: &str, event: &[u8]) -> ResultType<()> { + handle_event(METHOD_HANDLE_UI, id, event) +} + +#[inline] +pub fn handle_server_event(id: &str, event: &[u8]) -> ResultType<()> { + handle_event(METHOD_HANDLE_PEER, id, event) +} + +#[inline] +pub fn handle_client_event(id: &str, event: &[u8]) -> Option { + match PLUGINS.read().unwrap().get(id) { + Some(plugin) => { + let ret = (plugin.fn_call)( + METHOD_HANDLE_PEER.as_ptr() as _, + event.as_ptr(), + event.len(), + ); + if ret.is_null() { + None + } else { + let (code, msg) = get_code_msg_from_ret(ret); + unsafe { + libc::free(ret); + } + if code > ERR_RUSTDESK_HANDLE_BASE && code < ERR_PLUGIN_HANDLE_BASE { + let name = match PLUGINS.read().unwrap().get(id) { + Some(plugin) => plugin.desc.as_ref().unwrap().name(), + None => "", + }; + match code { + ERR_CALL_NOT_SUPPORTED_METHOD => Some(make_plugin_response( + id, + name, + "plugin method is not supported", + )), + ERR_CALL_INVALID_ARGS => Some(make_plugin_response( + id, + name, + "plugin arguments is invalid", + )), + _ => Some(make_plugin_response(id, name, &msg)), + } + } else { + log::error!( + "Failed to handle client event, code: {}, msg: {}", + code, + msg + ); + None + } + } + } + None => Some(make_plugin_response(id, "", "plugin not found")), + } +} + +fn make_plugin_response(id: &str, name: &str, msg: &str) -> Message { + let mut misc = Misc::new(); + misc.set_plugin_response(PluginResponse { + id: id.to_owned(), + name: name.to_owned(), + msg: msg.to_owned(), + ..Default::default() + }); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + msg_out +} + fn update_config(desc: &Desc) { super::config::set_local_items(desc.id(), &desc.config().local); super::config::set_peer_items(desc.id(), &desc.config().peer); diff --git a/src/server/connection.rs b/src/server/connection.rs index f3768aacf..f2550d5e1 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1270,13 +1270,16 @@ impl Connection { // If err is LOGIN_MSG_DESKTOP_SESSION_NOT_READY, just keep this msg and go on checking password. #[cfg(all(target_os = "linux", feature = "linux_headless"))] #[cfg(not(any(feature = "flatpak", feature = "appimage")))] - if !desktop_err.is_empty() && desktop_err != crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY { + if !desktop_err.is_empty() + && desktop_err != crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY + { self.send_login_error(desktop_err).await; return true; } if !hbb_common::is_ipv4_str(&lr.username) && lr.username != Config::get_id() { - self.send_login_error(crate::client::LOGIN_MSG_OFFLINE).await; + self.send_login_error(crate::client::LOGIN_MSG_OFFLINE) + .await; } else if password::approve_mode() == ApproveMode::Click || password::approve_mode() == ApproveMode::Both && !password::has_valid_password() { @@ -1284,7 +1287,8 @@ impl Connection { if hbb_common::get_version_number(&lr.version) >= hbb_common::get_version_number("1.2.0") { - self.send_login_error(crate::client::LOGIN_MSG_NO_PASSWORD_ACCESS).await; + self.send_login_error(crate::client::LOGIN_MSG_NO_PASSWORD_ACCESS) + .await; } return true; } else if password::approve_mode() == ApproveMode::Password @@ -1323,8 +1327,10 @@ impl Connection { if desktop_err.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { - self.send_login_error(crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY) - .await; + self.send_login_error( + crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_EMPTY, + ) + .await; } #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] self.try_start_cm(lr.my_id, lr.my_name, false); @@ -1371,15 +1377,19 @@ impl Connection { #[cfg(all(target_os = "linux", feature = "linux_headless"))] #[cfg(not(any(feature = "flatpak", feature = "appimage")))] if desktop_err.is_empty() { - self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG).await; + self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG) + .await; self.try_start_cm(lr.my_id, lr.my_name, false); } else { - self.send_login_error(crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG) - .await; + self.send_login_error( + crate::client::LOGIN_MSG_DESKTOP_SESSION_NOT_READY_PASSWORD_WRONG, + ) + .await; } #[cfg(not(all(target_os = "linux", feature = "linux_headless")))] { - self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG).await; + self.send_login_error(crate::client::LOGIN_MSG_PASSWORD_WRONG) + .await; self.try_start_cm(lr.my_id, lr.my_name, false); } } else { @@ -1800,6 +1810,13 @@ impl Connection { } } } + #[cfg(all(feature = "flutter", feature = "plugin_framework"))] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + Some(misc::Union::PluginRequest(p)) => { + if let Some(msg) = create::plugin::handle_client_event(&p.id, &p.content) { + self.send(msg).await; + } + } _ => {} }, Some(message::Union::AudioFrame(frame)) => { From 5c42b4a1e89aa7dcfc65e8e3e274fcfac95312ea Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 19 Apr 2023 17:26:36 +0800 Subject: [PATCH 347/366] fix build Signed-off-by: fufesou --- src/flutter_ffi.rs | 2 +- src/plugin/callback_msg.rs | 12 ++++++++---- src/plugin/errno.rs | 2 ++ src/plugin/mod.rs | 9 +++++---- src/plugin/plugins.rs | 15 ++++++--------- src/server/connection.rs | 2 +- src/ui_session_interface.rs | 4 ++-- 7 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index d18971332..9da6d1552 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1398,7 +1398,7 @@ pub fn send_url_scheme(_url: String) { pub fn plugin_event(id: String, event: Vec) { #[cfg(feature = "plugin_framework")] { - crate::plugin::handle_ui_event(&id, &event); + allow_err!(crate::plugin::handle_ui_event(&id, &event)); } } diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 42d3cf283..2ceffca9e 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -2,7 +2,11 @@ use super::cstr_to_string; use crate::flutter::{self, APP_TYPE_CM, APP_TYPE_MAIN, SESSIONS}; use hbb_common::{lazy_static, log, message_proto::PluginRequest}; use serde_json; -use std::{collections::HashMap, ffi::c_char, sync::Arc}; +use std::{ + collections::HashMap, + ffi::{c_char, c_void}, + sync::Arc, +}; const MSG_TO_PEER_TARGET: &str = "peer"; const MSG_TO_UI_TARGET: &str = "ui"; @@ -42,7 +46,7 @@ pub fn callback_msg( peer: *const c_char, target: *const c_char, id: *const c_char, - content: *const c_char, + content: *const c_void, len: usize, ) { macro_rules! callback_msg_field { @@ -66,12 +70,12 @@ pub fn callback_msg( let content_slice = unsafe { std::slice::from_raw_parts(content as *const u8, len) }; let content_vec = Vec::from(content_slice); - let plugin = PluginRequest { + let request = PluginRequest { id, content: bytes::Bytes::from(content_vec), ..Default::default() }; - session.send_plugin(plugin); + session.send_plugin_request(request); } } MSG_TO_UI_TARGET => { diff --git a/src/plugin/errno.rs b/src/plugin/errno.rs index e7419608c..d48e97c49 100644 --- a/src/plugin/errno.rs +++ b/src/plugin/errno.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + pub const ERR_SUCCESS: i32 = 0; // ====================================================== diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 3d1998b63..2f16f0325 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -1,14 +1,15 @@ use hbb_common::ResultType; -use std::ffi::{c_char, CStr}; +use std::ffi::{c_char, c_void, CStr}; mod callback_msg; mod config; pub mod desc; -mod plugins; mod errno; +mod plugins; pub use plugins::{ - handle_client_event, handle_ui_event, load_plugin, load_plugins, reload_plugin, unload_plugin, + handle_client_event, handle_server_event, handle_ui_event, load_plugin, load_plugins, + reload_plugin, unload_plugin, }; #[inline] @@ -27,5 +28,5 @@ fn get_code_msg_from_ret(ret: *const c_void) -> (i32, String) { .to_str() .unwrap_or("") .to_string(); - Ok((code, msg)) + (code, msg) } diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs index 39bda5cb7..b7de6e0be 100644 --- a/src/plugin/plugins.rs +++ b/src/plugin/plugins.rs @@ -1,6 +1,6 @@ use std::{ collections::HashMap, - ffi::c_char, + ffi::{c_char, c_void}, path::Path, sync::{Arc, RwLock}, }; @@ -176,13 +176,13 @@ pub fn load_plugin(path: &str) -> ResultType<()> { fn handle_event(method: &[u8], id: &str, event: &[u8]) -> ResultType<()> { match PLUGINS.read().unwrap().get(id) { Some(plugin) => { - let ret = (plugin.fn_call)(method.as_ptr() as _, event.as_ptr(), event.len()); + let ret = (plugin.fn_call)(method.as_ptr() as _, event.as_ptr() as _, event.len()); if ret.is_null() { Ok(()) } else { let (code, msg) = get_code_msg_from_ret(ret); unsafe { - libc::free(ret); + libc::free(ret as _); } bail!( "Failed to handle plugin event, id: {}, method: {}, code: {}, msg: {}", @@ -213,7 +213,7 @@ pub fn handle_client_event(id: &str, event: &[u8]) -> Option { Some(plugin) => { let ret = (plugin.fn_call)( METHOD_HANDLE_PEER.as_ptr() as _, - event.as_ptr(), + event.as_ptr() as _, event.len(), ); if ret.is_null() { @@ -221,13 +221,10 @@ pub fn handle_client_event(id: &str, event: &[u8]) -> Option { } else { let (code, msg) = get_code_msg_from_ret(ret); unsafe { - libc::free(ret); + libc::free(ret as _); } if code > ERR_RUSTDESK_HANDLE_BASE && code < ERR_PLUGIN_HANDLE_BASE { - let name = match PLUGINS.read().unwrap().get(id) { - Some(plugin) => plugin.desc.as_ref().unwrap().name(), - None => "", - }; + let name = plugin.desc.as_ref().unwrap().name(); match code { ERR_CALL_NOT_SUPPORTED_METHOD => Some(make_plugin_response( id, diff --git a/src/server/connection.rs b/src/server/connection.rs index f2550d5e1..44e995a3a 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1813,7 +1813,7 @@ impl Connection { #[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] Some(misc::Union::PluginRequest(p)) => { - if let Some(msg) = create::plugin::handle_client_event(&p.id, &p.content) { + if let Some(msg) = crate::plugin::handle_client_event(&p.id, &p.content) { self.send(msg).await; } } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 191d71448..b28f29c4c 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -246,9 +246,9 @@ impl Session { #[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] - pub fn send_plugin(&self, plugin: Plugin) { + pub fn send_plugin_request(&self, request: PluginRequest) { let mut misc = Misc::new(); - misc.set_plugin(plugin); + misc.set_plugin_request(request); let mut msg_out = Message::new(); msg_out.set_misc(misc); self.send(Data::Message(msg_out)); From 560c6f3d53174019e1c11c3a41e0788d9a810cfd Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 19 Apr 2023 17:43:38 +0800 Subject: [PATCH 348/366] trivial changes Signed-off-by: fufesou --- src/flutter_ffi.rs | 1 + src/plugin/errno.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 9da6d1552..2b522d470 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -10,6 +10,7 @@ use crate::{ }; use flutter_rust_bridge::{StreamSink, SyncReturn}; use hbb_common::{ + allow_err, config::{self, LocalConfig, PeerConfig, PeerInfoSerde, ONLINE}, fs, log, message_proto::KeyboardMode, diff --git a/src/plugin/errno.rs b/src/plugin/errno.rs index d48e97c49..db580c0bd 100644 --- a/src/plugin/errno.rs +++ b/src/plugin/errno.rs @@ -19,7 +19,7 @@ pub const ERR_CALL_INVALID_ARGS: i32 = 10301; pub const ERR_PEER_ID_MISMATCH: i32 = 10302; // ====================================================== -// errors that must be handled by plugin +// errors that should be handled by the plugin pub const ERR_PLUGIN_HANDLE_BASE: i32 = 20000; From 50ed2b9337ee99122424e373f4c3e128400aab32 Mon Sep 17 00:00:00 2001 From: solokot Date: Wed, 19 Apr 2023 14:08:14 +0300 Subject: [PATCH 349/366] Update ru.rs --- src/lang/ru.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index a08bb8113..f9a375ff5 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -492,11 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Нет доступных рабочих столов"), ("no_desktop_text_tip", "Установите GNOME Desktop"), ("No need to elevate", "Повышение прав не требуется"), - ("System Sound", ""), - ("Default", ""), - ("New RDP", ""), - ("Fingerprint", ""), - ("Copy Fingerprint", ""), - ("no fingerprints", ""), + ("System Sound", "Системный звук"), + ("Default", "По умолчанию"), + ("New RDP", "Новый RDP"), + ("Fingerprint", "Отпечаток"), + ("Copy Fingerprint", "Копировать отпечаток"), + ("no fingerprints", "отпечатки отсутствуют"), ].iter().cloned().collect(); } From ba857b23bbdb5f0018b90365efc9d10d11446a34 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Wed, 19 Apr 2023 07:03:47 -0700 Subject: [PATCH 350/366] Update Italian language --- src/lang/it.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/it.rs b/src/lang/it.rs index 2720934ed..7612bac00 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -288,8 +288,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "L'attivazione di Cattura schermo avvierà automaticamente il servizio, consentendo ad altri dispositivi di richiedere una connessione da questo dispositivo."), ("android_stop_service_tip", "La chiusura del servizio chiuderà automaticamente tutte le connessioni stabilite."), ("android_version_audio_tip", "L'attuale versione di Android non supporta l'acquisizione audio, esegui l'aggiornamento ad Android 10 o versioni successive."), - ("android_start_service_tip", ""), - ("android_permission_may_not_change_tip", ""), + ("android_start_service_tip", "Per avviare il servizio di condivisione dello schermo seleziona [Avvia servizio] o abilita l'autorizzazione [Cattura schermo]."), + ("android_permission_may_not_change_tip", "Le autorizzazioni per le connessioni stabilite non possono essere modificate istantaneamente fino alla riconnessione."), ("Account", "Account"), ("Overwrite", "Sovrascrivi"), ("This file exists, skip or overwrite this file?", "Questo file esiste, vuoi ignorarlo o sovrascrivere questo file?"), @@ -495,8 +495,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("System Sound", "Dispositivo audio sistema"), ("Default", "Predefinita"), ("New RDP", "Nuovo RDP"), - ("Fingerprint", ""), - ("Copy Fingerprint", ""), - ("no fingerprints", ""), + ("Fingerprint", "Firma digitale"), + ("Copy Fingerprint", "Copia firma digitale"), + ("no fingerprints", "Nessuna firma digitale"), ].iter().cloned().collect(); } From c3b835c8d4d511918ec01de538e010bc942514c8 Mon Sep 17 00:00:00 2001 From: deep-soft Date: Wed, 19 Apr 2023 18:52:49 +0300 Subject: [PATCH 351/366] Update bridge.yml Fix bridge.yml: Update ca-certificates before sudo apt update. Error before update: Certificate verification failed: The certificate is NOT trusted. The certificate chain uses expired certificate. Could not handshake: Error in the certificate verification. --- .github/workflows/bridge.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index 2a69ca871..21d8fbce5 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -1,5 +1,6 @@ # This yaml shares the build bridge steps with ci and nightly. name: Build flutter-rust-bridge +# 2023-04-19 15:48:00+00:00 on: workflow_call: @@ -22,6 +23,7 @@ jobs: - name: Install prerequisites run: | + sudo apt install ca-certificates -y sudo apt update -y sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang cmake libclang-dev ninja-build llvm-dev libclang-10-dev llvm-10-dev pkg-config @@ -70,4 +72,4 @@ jobs: ./src/bridge_generated.rs ./src/bridge_generated.io.rs ./flutter/lib/generated_bridge.dart - ./flutter/lib/generated_bridge.freezed.dart \ No newline at end of file + ./flutter/lib/generated_bridge.freezed.dart From 26722eac99913ec8f31c1302d61cc11d6fff31ee Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Wed, 19 Apr 2023 20:59:53 +0200 Subject: [PATCH 352/366] Update de.rs --- src/lang/de.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index 11860601e..ff6f84378 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -123,8 +123,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Scrollbar", "Scroll-Leiste"), ("ScrollAuto", "Automatisch scrollen"), ("Good image quality", "Hohe Bildqualität"), - ("Balanced", "Ausgeglichen"), - ("Optimize reaction time", "Geschwindigkeit"), + ("Balanced", "Ausgeglichene Bildqualität"), + ("Optimize reaction time", "Reaktionszeit optimieren"), ("Custom", "Benutzerdefiniert"), ("Show remote cursor", "Entfernten Cursor anzeigen"), ("Show quality monitor", "Qualitätsüberwachung anzeigen"), @@ -181,7 +181,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Disconnect", "Verbindung trennen"), ("Allow using keyboard and mouse", "Verwendung von Maus und Tastatur zulassen"), ("Allow using clipboard", "Verwendung der Zwischenablage zulassen"), - ("Allow hearing sound", "System-Audio übertragen"), + ("Allow hearing sound", "Soundübertragung zulassen"), ("Allow file copy and paste", "Kopieren und Einfügen von Dateien zulassen"), ("Connected", "Verbunden"), ("Direct and encrypted connection", "Direkte und verschlüsselte Verbindung"), @@ -420,7 +420,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Select local keyboard type", "Lokalen Tastaturtyp auswählen"), ("software_render_tip", "Wenn Sie eine Nvidia-Grafikkarte haben und sich das entfernte Fenster sofort nach dem Herstellen der Verbindung schließt, kann es helfen, den Nouveau-Treiber zu installieren und Software-Rendering zu verwenden. Ein Neustart der Software ist erforderlich."), ("Always use software rendering", "Software-Rendering immer verwenden"), - ("config_input", "Um den entfernten Desktop mit der Tastatur steuern zu können, müssen Sie RustDesk die Berechtigung \"Input Monitoring\" erteilen."), + ("config_input", "Um den entfernten Desktop mit der Tastatur steuern zu können, müssen Sie RustDesk die Berechtigung \"Eingabeüberwachung\" erteilen."), ("config_microphone", "Um aus der Ferne sprechen zu können, müssen Sie RustDesk die Berechtigung \"Audio aufzeichnen\" erteilen."), ("request_elevation_tip", "Sie können auch erhöhte Rechte anfordern, wenn sich jemand auf der Gegenseite befindet."), ("Wait", "Warten"), @@ -492,11 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "Es ist kein Desktop verfügbar."), ("no_desktop_text_tip", "Bitte installieren Sie den GNOME-Desktop."), ("No need to elevate", "Erhöhung der Rechte nicht erforderlich"), - ("System Sound", ""), - ("Default", ""), + ("System Sound", "Systemsound"), + ("Default", "Systemstandard"), ("New RDP", "Neue RDP-Verbindung"), - ("Fingerprint", ""), - ("Copy Fingerprint", ""), - ("no fingerprints", ""), + ("Fingerprint", "Fingerabdruck"), + ("Copy Fingerprint", "Fingerabdruck kopieren"), + ("no fingerprints", "Keine Fingerabdrücke"), ].iter().cloned().collect(); } From 4200734593616eb5b35102a867072efd7b9ba971 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 10:29:24 +0800 Subject: [PATCH 353/366] add events to ui Signed-off-by: fufesou --- flutter/lib/models/model.dart | 9 +++ flutter/lib/plugin/desc.dart | 135 +++++++++++++++++++++++++++++++ flutter/lib/plugin/event.dart | 60 ++++++++++++++ flutter/lib/plugin/reloader.dart | 29 +++++++ flutter/lib/plugin/widget.dart | 17 ++++ src/plugin/callback_msg.rs | 2 +- src/plugin/desc.rs | 8 +- src/plugin/plugins.rs | 19 +++++ 8 files changed, 274 insertions(+), 5 deletions(-) create mode 100644 flutter/lib/plugin/desc.dart create mode 100644 flutter/lib/plugin/event.dart create mode 100644 flutter/lib/plugin/reloader.dart create mode 100644 flutter/lib/plugin/widget.dart diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 388f5bd59..b9a2a7598 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -16,6 +16,8 @@ import 'package:flutter_hbb/models/peer_tab_model.dart'; import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/models/user_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; +import 'package:flutter_hbb/plugin/event.dart'; +import 'package:flutter_hbb/plugin/reloader.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:tuple/tuple.dart'; import 'package:image/image.dart' as img2; @@ -226,6 +228,13 @@ class FfiModel with ChangeNotifier { parent.target?.serverModel.updateVoiceCallState(evt); } else if (name == "fingerprint") { FingerprintState.find(peerId).value = evt['fingerprint'] ?? ''; + } else if (name == "plugin_desc") { + handleReloading(evt, peerId); + } else if (name == "plugin_event") { + handlePluginEvent( + evt, peerId, (Map e) => handleMsgBox(e, peerId)); + } else if (name == "plugin_reload") { + handleReloading(evt, peerId); } else { debugPrint("Unknown event name: $name"); } diff --git a/flutter/lib/plugin/desc.dart b/flutter/lib/plugin/desc.dart new file mode 100644 index 000000000..c6b04d11c --- /dev/null +++ b/flutter/lib/plugin/desc.dart @@ -0,0 +1,135 @@ +import 'dart:collection'; + +class UiButton { + String key; + String text; + String icon; + String tooltip; + String action; + + UiButton(this.key, this.text, this.icon, this.tooltip, this.action); + UiButton.fromJson(Map json) + : key = json['key'] ?? '', + text = json['text'] ?? '', + icon = json['icon'] ?? '', + tooltip = json['tooltip'] ?? '', + action = json['action'] ?? ''; +} + +class UiCheckbox { + String key; + String text; + String tooltip; + String action; + + UiCheckbox(this.key, this.text, this.tooltip, this.action); + UiCheckbox.fromJson(Map json) + : key = json['key'] ?? '', + text = json['text'] ?? '', + tooltip = json['tooltip'] ?? '', + action = json['action'] ?? ''; +} + +class UiType { + UiButton? button; + UiCheckbox? checkbox; + + UiType.fromJson(Map json) + : button = json['t'] == 'Button' ? UiButton.fromJson(json['c']) : null, + checkbox = + json['t'] != 'Checkbox' ? UiCheckbox.fromJson(json['c']) : null; +} + +class Location { + HashMap ui; + + Location(this.ui); +} + +class ConfigItem { + String key; + String value; + String description; + String defaultValue; + + ConfigItem(this.key, this.value, this.defaultValue, this.description); + ConfigItem.fromJson(Map json) + : key = json['key'] ?? '', + value = json['value'] ?? '', + description = json['description'] ?? '', + defaultValue = json['default'] ?? ''; +} + +class Config { + List local; + List peer; + + Config(this.local, this.peer); + Config.fromJson(Map json) + : local = (json['local'] as List) + .map((e) => ConfigItem.fromJson(e)) + .toList(), + peer = (json['peer'] as List) + .map((e) => ConfigItem.fromJson(e)) + .toList(); +} + +class Desc { + String id; + String name; + String version; + String description; + String author; + String home; + String license; + String published; + String released; + String github; + Location location; + Config config; + + Desc( + this.id, + this.name, + this.version, + this.description, + this.author, + this.home, + this.license, + this.published, + this.released, + this.github, + this.location, + this.config); + + Desc.fromJson(Map json) + : id = json['id'] ?? '', + name = json['name'] ?? '', + version = json['version'] ?? '', + description = json['description'] ?? '', + author = json['author'] ?? '', + home = json['home'] ?? '', + license = json['license'] ?? '', + published = json['published'] ?? '', + released = json['released'] ?? '', + github = json['github'] ?? '', + location = Location(HashMap.from(json['location'])), + config = Config( + (json['config'] as List) + .map((e) => ConfigItem.fromJson(e)) + .toList(), + (json['config'] as List) + .map((e) => ConfigItem.fromJson(e)) + .toList()); +} + +final mapPluginDesc = {}; + +void updateDesc(Map desc) { + Desc d = Desc.fromJson(desc); + mapPluginDesc[d.id] = d; +} + +Desc? getDesc(String id) { + return mapPluginDesc[id]; +} diff --git a/flutter/lib/plugin/event.dart b/flutter/lib/plugin/event.dart new file mode 100644 index 000000000..04d0ddf45 --- /dev/null +++ b/flutter/lib/plugin/event.dart @@ -0,0 +1,60 @@ +void handlePluginEvent( + Map evt, + String peer, + Function(Map e) handleMsgBox, +) { + // content + // + // { + // "t": "Option", + // "c": { + // "id": "id from RustDesk platform", + // "name": "Privacy Mode", + // "version": "v0.1.0", + // "location": "client|remote|toolbar|display", + // "key": "privacy-mode", + // "value": "1" + // } + // } + // + // { + // "t": "MsgBox", + // "c": { + // "type": "custom-nocancel", + // "title": "Privacy Mode", + // "text": "Failed unknown", + // "link": "" + // } + // } + // + if (evt['content']?['c'] == null) return; + final t = evt['content']?['t']; + if (t == 'Option') { + handleOptionEvent(evt['content']?['c'], peer); + } else if (t == 'MsgBox') { + handleMsgBox(evt['content']?['c']); + } +} + +void handleOptionEvent(Map evt, String peer) { + // content + // + // { + // "id": "id from RustDesk platform", + // "name": "Privacy Mode", + // "version": "v0.1.0", + // "location": "client|remote|toolbar|display", + // "key": "privacy-mode", + // "value": "1" + // } + // + final key = evt['key']; + final value = evt['value']; + if (key == 'privacy-mode') { + if (value == '1') { + // enable privacy mode + } else { + // disable privacy mode + } + } +} diff --git a/flutter/lib/plugin/reloader.dart b/flutter/lib/plugin/reloader.dart new file mode 100644 index 000000000..1b1641f87 --- /dev/null +++ b/flutter/lib/plugin/reloader.dart @@ -0,0 +1,29 @@ +void handleReloading(Map evt, String peer) { + // location + // host|main|settings|display|others + // client|remote|toolbar|display + // + // ui + // { + // "t": "Button", + // "c": { + // "key": "key", + // "text": "text", + // "icon": "icon", + // "tooltip": "tooltip", + // "action": "action" + // } + // } + // + // { + // "t": "Checkbox", + // "c": { + // "key": "key", + // "text": "text", + // "tooltip": "tooltip", + // "action": "action" + // } + // } + // + +} diff --git a/flutter/lib/plugin/widget.dart b/flutter/lib/plugin/widget.dart new file mode 100644 index 000000000..dbdfbbadd --- /dev/null +++ b/flutter/lib/plugin/widget.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +final Map pluginWidgets = {}; + +class PluginWidget { + final String id; + final String name; + final String location; + final Widget widget; + + PluginWidget({ + required this.id, + required this.name, + required this.location, + required this.widget, + }); +} diff --git a/src/plugin/callback_msg.rs b/src/plugin/callback_msg.rs index 2ceffca9e..4d02db287 100644 --- a/src/plugin/callback_msg.rs +++ b/src/plugin/callback_msg.rs @@ -99,7 +99,7 @@ pub fn callback_msg( { let _res = flutter::push_session_event( &peer, - "plugin", + "plugin_event", vec![("peer", &peer), ("content", &content)], ); } diff --git a/src/plugin/desc.rs b/src/plugin/desc.rs index 26eda187a..bc094abf9 100644 --- a/src/plugin/desc.rs +++ b/src/plugin/desc.rs @@ -28,12 +28,12 @@ pub enum UiType { Checkbox(UiCheckbox), } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Location { pub ui: HashMap, } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ConfigItem { pub key: String, pub value: String, @@ -41,13 +41,13 @@ pub struct ConfigItem { pub description: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Config { pub local: Vec, pub peer: Vec, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct Desc { id: String, name: String, diff --git a/src/plugin/plugins.rs b/src/plugin/plugins.rs index b7de6e0be..e05940cd1 100644 --- a/src/plugin/plugins.rs +++ b/src/plugin/plugins.rs @@ -165,7 +165,9 @@ pub fn load_plugin(path: &str) -> ResultType<()> { let desc = desc_res?; let id = desc.id().to_string(); // to-do validate plugin + // to-do check the plugin id (make sure it does not use another plugin's id) (plugin.fn_set_cb_msg)(callback_msg::callback_msg); + update_ui_plugin_desc(&desc); update_config(&desc); reload_ui(&desc); plugin.desc = Some(desc); @@ -287,6 +289,8 @@ fn reload_ui(desc: &Desc) { if let Ok(ui) = serde_json::to_string(&ui) { let mut m = HashMap::new(); m.insert("name", "plugin_reload"); + m.insert("id", desc.id()); + m.insert("location", &location); m.insert("ui", &ui); flutter::push_global_event(v[1], serde_json::to_string(&m).unwrap()); } @@ -294,3 +298,18 @@ fn reload_ui(desc: &Desc) { } } } + +fn update_ui_plugin_desc(desc: &Desc) { + // This function is rarely used. There's no need to care about serialization efficiency here. + if let Ok(desc_str) = serde_json::to_string(desc) { + let mut m = HashMap::new(); + m.insert("name", "plugin_desc"); + m.insert("desc", &desc_str); + flutter::push_global_event(flutter::APP_TYPE_MAIN, serde_json::to_string(&m).unwrap()); + flutter::push_global_event( + flutter::APP_TYPE_DESKTOP_REMOTE, + serde_json::to_string(&m).unwrap(), + ); + flutter::push_global_event(flutter::APP_TYPE_CM, serde_json::to_string(&m).unwrap()); + } +} From d8dbf72ad2f0099e11bb765871eed6c9934f6f64 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 12:15:38 +0800 Subject: [PATCH 354/366] simulate scancode for translate mode Signed-off-by: fufesou --- Cargo.lock | 2 +- src/flutter_ffi.rs | 5 ++- src/server/input_service.rs | 72 ++----------------------------------- 3 files changed, 5 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 299d99c14..f1fb3595e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4840,7 +4840,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#aeea78dec3e731dd616da09668bbbd835a773d6c" +source = "git+https://github.com/fufesou/rdev#b866494215329f3e8e4863c3c5fb98a10297610c" dependencies = [ "cocoa", "core-foundation 0.9.3", diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 2b522d470..aaee8eaff 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -10,7 +10,6 @@ use crate::{ }; use flutter_rust_bridge::{StreamSink, SyncReturn}; use hbb_common::{ - allow_err, config::{self, LocalConfig, PeerConfig, PeerInfoSerde, ONLINE}, fs, log, message_proto::KeyboardMode, @@ -1396,10 +1395,10 @@ pub fn send_url_scheme(_url: String) { #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn plugin_event(id: String, event: Vec) { +pub fn plugin_event(_id: String, _event: Vec) { #[cfg(feature = "plugin_framework")] { - allow_err!(crate::plugin::handle_ui_event(&id, &event)); + allow_err!(crate::plugin::handle_ui_event(&_id, &_event)); } } diff --git a/src/server/input_service.rs b/src/server/input_service.rs index ab5f7e287..8096237f6 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -15,19 +15,9 @@ use std::{ thread, time::{self, Duration, Instant}, }; -#[cfg(target_os = "windows")] -use winapi::um::winuser::{ - ActivateKeyboardLayout, GetForegroundWindow, GetKeyboardLayout, GetWindowThreadProcessId, - VkKeyScanW, -}; const INVALID_CURSOR_POS: i32 = i32::MIN; -#[cfg(target_os = "windows")] -lazy_static::lazy_static! { - static ref LAST_HKL: Arc> = Arc::new(Mutex::new(0)); -} - #[derive(Default)] struct StateCursor { hcursor: u64, @@ -1244,45 +1234,7 @@ fn translate_process_code(code: u32, down: bool) { }; } -#[cfg(target_os = "windows")] -fn check_update_input_layout() { - unsafe { - let foreground_thread_id = - GetWindowThreadProcessId(GetForegroundWindow(), std::ptr::null_mut()); - let layout = GetKeyboardLayout(foreground_thread_id); - let layout_u32 = layout as u32; - let mut last_layout_lock = LAST_HKL.lock().unwrap(); - if *last_layout_lock == 0 || *last_layout_lock != layout_u32 { - let res = ActivateKeyboardLayout(layout, 0); - if res == layout { - *last_layout_lock = layout_u32; - } else { - log::error!("Failed to call ActivateKeyboardLayout, {}", layout_u32); - } - } - } -} - fn translate_keyboard_mode(evt: &KeyEvent) { - // --server could not detect the input layout change. - // This is a temporary workaround. - // - // There may be a better way to detect and handle the input layout change. - // while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) - // { - // ... - // if (msg.message == WM_INPUTLANGCHANGE) - // { - // // handle WM_INPUTLANGCHANGE message here - // check_update_input_layout(); - // } - // TranslateMessage(&msg); - // DispatchMessage(&msg); - // ... - // } - #[cfg(target_os = "windows")] - check_update_input_layout(); - match &evt.union { Some(key_event::Union::Seq(seq)) => { // Fr -> US @@ -1305,7 +1257,7 @@ fn translate_keyboard_mode(evt: &KeyEvent) { } for chr in seq.chars() { #[cfg(target_os = "windows")] - rdev::simulate_char(chr).ok(); + rdev::simulate_char(chr, true).ok(); #[cfg(target_os = "linux")] en.key_click(Key::Layout(chr)); } @@ -1334,27 +1286,7 @@ fn translate_keyboard_mode(evt: &KeyEvent) { fn simulate_win2win_hotkey(code: u32, down: bool) { let unicode: u16 = (code & 0x0000FFFF) as u16; if down { - // Try convert unicode to virtual keycode first. - // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscanw - let res = unsafe { VkKeyScanW(unicode) }; - if res as u16 != 0xFFFF { - let vk = res & 0x00FF; - let flag = res >> 8; - let modifiers = [rdev::Key::ShiftLeft, rdev::Key::ControlLeft, rdev::Key::Alt]; - let mod_len = modifiers.len(); - for pos in 0..mod_len { - if flag & (0x0001 << pos) != 0 { - allow_err!(rdev::simulate(&EventType::KeyPress(modifiers[pos]))); - } - } - allow_err!(rdev::simulate_code(Some(vk as _), None, true)); - allow_err!(rdev::simulate_code(Some(vk as _), None, false)); - for pos in 0..mod_len { - let rpos = mod_len - 1 - pos; - if flag & (0x0001 << rpos) != 0 { - allow_err!(rdev::simulate(&EventType::KeyRelease(modifiers[rpos]))); - } - } + if rdev::simulate_key_unicode(unicode, false).is_ok() { return; } } From 9d086077348ed59cd509b72ea0d1b343c3e75a9c Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 12:56:19 +0800 Subject: [PATCH 355/366] update rdev Signed-off-by: fufesou --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f1fb3595e..0d016c052 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4840,7 +4840,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#b866494215329f3e8e4863c3c5fb98a10297610c" +source = "git+https://github.com/fufesou/rdev#2b9d473dfd9a49649627f0684f49e920494837e2" dependencies = [ "cocoa", "core-foundation 0.9.3", From 3afd9e1f46219876bebfe2841791feadfd0d879a Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 13:02:12 +0800 Subject: [PATCH 356/366] fix build Signed-off-by: fufesou --- src/flutter_ffi.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index aaee8eaff..11485c224 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -9,6 +9,9 @@ use crate::{ ui_interface::{self, *}, }; use flutter_rust_bridge::{StreamSink, SyncReturn}; +#[cfg(feature = "plugin_framework")] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::allow_err; use hbb_common::{ config::{self, LocalConfig, PeerConfig, PeerInfoSerde, ONLINE}, fs, log, From 64689c8fd7a3fca8ff3cd5cf9f227bb2d81a2c73 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 13:18:56 +0800 Subject: [PATCH 357/366] fix build Signed-off-by: fufesou --- src/flutter_ffi.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 2b522d470..5f72431ef 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -9,8 +9,10 @@ use crate::{ ui_interface::{self, *}, }; use flutter_rust_bridge::{StreamSink, SyncReturn}; +#[cfg(feature = "plugin_framework")] +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use hbb_common::allow_err; use hbb_common::{ - allow_err, config::{self, LocalConfig, PeerConfig, PeerInfoSerde, ONLINE}, fs, log, message_proto::KeyboardMode, @@ -1395,11 +1397,11 @@ pub fn send_url_scheme(_url: String) { } #[inline] -#[cfg(not(any(target_os = "android", target_os = "ios")))] -pub fn plugin_event(id: String, event: Vec) { +pub fn plugin_event(_id: String, _event: Vec) { #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] { - allow_err!(crate::plugin::handle_ui_event(&id, &event)); + allow_err!(crate::plugin::handle_ui_event(&_id, &_event)); } } From a76fd86f19aa023f173079993a89909b4959ff3e Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 13:36:44 +0800 Subject: [PATCH 358/366] remove warns Signed-off-by: fufesou --- src/client.rs | 1 + src/keyboard.rs | 11 +++++++---- src/ui_session_interface.rs | 24 +++++++++++++++++------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/client.rs b/src/client.rs index d6d58136e..f37719641 100644 --- a/src/client.rs +++ b/src/client.rs @@ -165,6 +165,7 @@ impl OboePlayer { } } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn is_null(&self) -> bool { self.raw.is_null() } diff --git a/src/keyboard.rs b/src/keyboard.rs index c6da18097..e6e9da35a 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,6 +1,3 @@ -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::client::get_key_state; -use crate::common::GrabState; #[cfg(feature = "flutter")] use crate::flutter::{CUR_SESSION_ID, SESSIONS}; #[cfg(target_os = "windows")] @@ -8,6 +5,8 @@ use crate::platform::windows::{get_char_from_vk, get_unicode_from_vk}; #[cfg(not(any(feature = "flutter", feature = "cli")))] use crate::ui::CUR_SESSION; #[cfg(not(any(target_os = "android", target_os = "ios")))] +use crate::{client::get_key_state, common::GrabState}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::log; use hbb_common::message_proto::*; #[cfg(any(target_os = "windows", target_os = "macos"))] @@ -15,10 +14,11 @@ use rdev::KeyCode; use rdev::{Event, EventType, Key}; #[cfg(any(target_os = "windows", target_os = "macos"))] use std::sync::atomic::{AtomicBool, Ordering}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] +use std::time::SystemTime; use std::{ collections::{HashMap, HashSet}, sync::{Arc, Mutex}, - time::SystemTime, }; #[cfg(windows)] @@ -73,6 +73,7 @@ pub mod client { super::start_grab_loop(); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn change_grab_status(state: GrabState) { match state { GrabState::Ready => {} @@ -320,6 +321,7 @@ pub fn is_long_press(event: &Event) -> bool { return false; } +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn release_remote_keys() { // todo!: client quit suddenly, how to release keys? let to_release = TO_RELEASE.lock().unwrap().clone(); @@ -565,6 +567,7 @@ pub fn event_to_key_events( key_events } +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn event_type_to_event(event_type: EventType) -> Event { Event { event_type, diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index b28f29c4c..e71e13919 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1,12 +1,14 @@ #[cfg(not(any(target_os = "android", target_os = "ios")))] -use std::collections::HashMap; -use std::ops::{Deref, DerefMut}; -use std::str::FromStr; -use std::sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Mutex, RwLock, +use std::{collections::HashMap, sync::atomic::AtomicBool}; +use std::{ + ops::{Deref, DerefMut}, + str::FromStr, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, Mutex, RwLock, + }, + time::{Duration, SystemTime}, }; -use std::time::{Duration, SystemTime}; use async_trait::async_trait; use bytes::Bytes; @@ -27,10 +29,12 @@ use crate::client::{ input_os_password, load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, }; +#[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::GrabState; use crate::keyboard; use crate::{client::Data, client::Interface}; +#[cfg(not(any(target_os = "android", target_os = "ios")))] pub static IS_IN: AtomicBool = AtomicBool::new(false); #[derive(Clone, Default)] @@ -55,6 +59,7 @@ pub struct SessionPermissionConfig { pub server_clipboard_enabled: Arc>, } +#[cfg(not(any(target_os = "android", target_os = "ios")))] impl SessionPermissionConfig { pub fn is_text_clipboard_required(&self) -> bool { *self.server_clipboard_enabled.read().unwrap() @@ -64,6 +69,7 @@ impl SessionPermissionConfig { } impl Session { + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_permission_config(&self) -> SessionPermissionConfig { SessionPermissionConfig { lc: self.lc.clone(), @@ -89,6 +95,7 @@ impl Session { .eq(&ConnType::PORT_FORWARD) } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn is_rdp(&self) -> bool { self.lc.read().unwrap().conn_type.eq(&ConnType::RDP) } @@ -162,6 +169,7 @@ impl Session { self.lc.read().unwrap().is_privacy_mode_supported() } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn is_text_clipboard_required(&self) -> bool { *self.server_clipboard_enabled.read().unwrap() && *self.server_keyboard_enabled.read().unwrap() @@ -488,6 +496,7 @@ impl Session { self.send(Data::Message(msg_out)); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn enter(&self) { #[cfg(target_os = "windows")] { @@ -502,6 +511,7 @@ impl Session { keyboard::client::change_grab_status(GrabState::Run); } + #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn leave(&self) { #[cfg(target_os = "windows")] { From 55633bd26b4256bb29607405a5a576cd78b7f83c Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 13:40:03 +0800 Subject: [PATCH 359/366] fix build Signed-off-by: fufesou --- src/flutter_ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 11485c224..5f72431ef 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1397,9 +1397,9 @@ pub fn send_url_scheme(_url: String) { } #[inline] -#[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn plugin_event(_id: String, _event: Vec) { #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] { allow_err!(crate::plugin::handle_ui_event(&_id, &_event)); } From 746be72d0b5d140577a4cc6477bfad183e33f830 Mon Sep 17 00:00:00 2001 From: mehdi-song Date: Thu, 20 Apr 2023 09:09:20 +0000 Subject: [PATCH 360/366] Update fa.rs --- src/lang/fa.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lang/fa.rs b/src/lang/fa.rs index b00046807..0bc9c3bc8 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -492,11 +492,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("no_desktop_title_tip", "هیچ دسکتاپی در دسترس نیست"), ("no_desktop_text_tip", "لطفا دسکتاپ گنوم را نصب کنید"), ("No need to elevate", "نیازی به ارتقاء نیست"), - ("System Sound", ""), - ("Default", ""), + ("System Sound", "صدای سیستم"), + ("Default", "پیش فرض"), ("New RDP", "ریموت جدید"), - ("Fingerprint", ""), - ("Copy Fingerprint", ""), - ("no fingerprints", ""), + ("Fingerprint", "اثر انگشت"), + ("Copy Fingerprint", "کپی کردن اثر انگشت"), + ("no fingerprints", "بدون اثر انگشت"), ].iter().cloned().collect(); } From 286715d33654b732ca44c36cd1f1c848467737b3 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 18:38:52 +0800 Subject: [PATCH 361/366] win, translate mode, release scancode Signed-off-by: fufesou --- Cargo.lock | 2 +- src/server/input_service.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d016c052..1133105ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4840,7 +4840,7 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#2b9d473dfd9a49649627f0684f49e920494837e2" +source = "git+https://github.com/fufesou/rdev#f43a42fbedf1234a4bc132581790d63c9a2c8f92" dependencies = [ "cocoa", "core-foundation 0.9.3", diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 8096237f6..41423d436 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -1292,7 +1292,8 @@ fn simulate_win2win_hotkey(code: u32, down: bool) { } let keycode: u16 = ((code >> 16) & 0x0000FFFF) as u16; - allow_err!(rdev::simulate_code(Some(keycode), None, down)); + let scan = rdev::vk_to_scancode(keycode as _); + allow_err!(rdev::simulate_code(None, Some(scan), down)); } #[cfg(not(any(target_os = "windows", target_os = "linux")))] From bdb590834eab7256f1603002838f705039b07176 Mon Sep 17 00:00:00 2001 From: Guido Falsi Date: Wed, 19 Apr 2023 11:46:08 +0200 Subject: [PATCH 362/366] Skip anything after an # symbol in the filename. Windows renames files adding (1), (2) etc. before the .exe in case of duplicates, which causes the host or key values to be garbled. This allows to protect from this adding a final "#" symbol to the filename. --- src/license.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/license.rs b/src/license.rs index 8875d2b64..7d4270052 100644 --- a/src/license.rs +++ b/src/license.rs @@ -37,6 +37,11 @@ pub fn get_license_from_string(s: &str) -> ResultType { s }; if s.contains("host=") { + let s = if s.contains("#") { + &s[0..s.find("#").unwrap_or(s.len())] + } else { + s + }; let strs: Vec<&str> = s.split("host=").collect(); if strs.len() == 2 { let strs2: Vec<&str> = strs[1].split(",key=").collect(); From d3766b49d985b827afc790e5d60f522c0f5ca284 Mon Sep 17 00:00:00 2001 From: Pavel Krivanek Date: Thu, 20 Apr 2023 15:34:18 +0200 Subject: [PATCH 363/366] Change translation of Map mode to Zuordnungsmodus --- src/lang/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index ff6f84378..5112a8d35 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -315,7 +315,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Start the screen sharing service on boot, requires special permissions", "Bildschirmfreigabedienst beim Booten starten, erfordert zusätzliche Berechtigungen"), ("Connection not allowed", "Verbindung abgelehnt"), ("Legacy mode", "Kompatibilitätsmodus"), - ("Map mode", "Kartenmodus"), + ("Map mode", "Zuordnungsmodus"), ("Translate mode", "Übersetzungsmodus"), ("Use permanent password", "Permanentes Passwort verwenden"), ("Use both passwords", "Beide Passwörter verwenden"), From d9755abbc2cd9df14580535967732211726ad145 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 18:10:06 +0800 Subject: [PATCH 364/366] tmp commit Signed-off-by: fufesou --- flutter/lib/models/model.dart | 5 ++-- flutter/lib/plugin/common.dart | 1 + flutter/lib/plugin/desc.dart | 5 ++++ flutter/lib/plugin/model.dart | 28 +++++++++++++++++ flutter/lib/plugin/reloader.dart | 29 ------------------ flutter/lib/plugin/widget.dart | 30 +++++++++++++++++++ .../widgets/remote/toolbar/display.dart | 27 +++++++++++++++++ 7 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 flutter/lib/plugin/common.dart create mode 100644 flutter/lib/plugin/model.dart delete mode 100644 flutter/lib/plugin/reloader.dart create mode 100644 flutter/lib/plugin/widgets/remote/toolbar/display.dart diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index b9a2a7598..44ca71912 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -17,7 +17,8 @@ import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/models/user_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/plugin/event.dart'; -import 'package:flutter_hbb/plugin/reloader.dart'; +import 'package:flutter_hbb/plugin/desc.dart'; +import 'package:flutter_hbb/plugin/widget.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:tuple/tuple.dart'; import 'package:image/image.dart' as img2; @@ -229,7 +230,7 @@ class FfiModel with ChangeNotifier { } else if (name == "fingerprint") { FingerprintState.find(peerId).value = evt['fingerprint'] ?? ''; } else if (name == "plugin_desc") { - handleReloading(evt, peerId); + updateDesc(evt); } else if (name == "plugin_event") { handlePluginEvent( evt, peerId, (Map e) => handleMsgBox(e, peerId)); diff --git a/flutter/lib/plugin/common.dart b/flutter/lib/plugin/common.dart new file mode 100644 index 000000000..5cbc0f871 --- /dev/null +++ b/flutter/lib/plugin/common.dart @@ -0,0 +1 @@ +typedef PluginId = String; diff --git a/flutter/lib/plugin/desc.dart b/flutter/lib/plugin/desc.dart index c6b04d11c..6be712697 100644 --- a/flutter/lib/plugin/desc.dart +++ b/flutter/lib/plugin/desc.dart @@ -38,9 +38,14 @@ class UiType { : button = json['t'] == 'Button' ? UiButton.fromJson(json['c']) : null, checkbox = json['t'] != 'Checkbox' ? UiCheckbox.fromJson(json['c']) : null; + + bool get isValid => button != null || checkbox != null; } class Location { + // location key: + // host|main|settings|display|others + // client|remote|toolbar|display HashMap ui; Location(this.ui); diff --git a/flutter/lib/plugin/model.dart b/flutter/lib/plugin/model.dart new file mode 100644 index 000000000..824992e20 --- /dev/null +++ b/flutter/lib/plugin/model.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import './common.dart'; +import './desc.dart'; + +// ui location +// host|main|settings|display|others +// client|remote|toolbar|display + +final Map> locationModels = {}; + +class LocationModel with ChangeNotifier { + final List uiList = []; + + void add(UiType ui) { + uiList.add(ui); + notifyListeners(); + } +} + +void addLocation(PluginId id, String location, UiType ui) { + if (!locationModels.containsKey(id)) { + locationModels[id] = {}; + } + if (!locationModels[id]!.containsKey(location)) { + locationModels[id]![location] = LocationModel(); + } + locationModels[id]![location]!.add(ui); +} diff --git a/flutter/lib/plugin/reloader.dart b/flutter/lib/plugin/reloader.dart deleted file mode 100644 index 1b1641f87..000000000 --- a/flutter/lib/plugin/reloader.dart +++ /dev/null @@ -1,29 +0,0 @@ -void handleReloading(Map evt, String peer) { - // location - // host|main|settings|display|others - // client|remote|toolbar|display - // - // ui - // { - // "t": "Button", - // "c": { - // "key": "key", - // "text": "text", - // "icon": "icon", - // "tooltip": "tooltip", - // "action": "action" - // } - // } - // - // { - // "t": "Checkbox", - // "c": { - // "key": "key", - // "text": "text", - // "tooltip": "tooltip", - // "action": "action" - // } - // } - // - -} diff --git a/flutter/lib/plugin/widget.dart b/flutter/lib/plugin/widget.dart index dbdfbbadd..a99e25e4e 100644 --- a/flutter/lib/plugin/widget.dart +++ b/flutter/lib/plugin/widget.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import './desc.dart'; +import './model.dart'; final Map pluginWidgets = {}; @@ -14,4 +16,32 @@ class PluginWidget { required this.location, required this.widget, }); + + // static Widget createButton(UiButton btn) {} + + // static Widget createCheckbox(UiCheckbox chk) {} + + // // ui location + // // host|main|settings|display|others + // // client|remote|toolbar|display + // static Widget? create(String id, String locatin, UiType ui) { + // if (ui.button != null) { + // return createButton(ui.button!); + // } else if (ui.checkbox != null) { + // return createCheckbox(ui.checkbox!); + // } else { + // return null; + // } + // } +} + +void handleReloading(Map evt, String peer) { + if (evt['id'] == null || evt['location'] == null) { + return; + } + final ui = UiType.fromJson(evt); + if (!ui.isValid) { + return; + } + addLocation(evt['id']!, evt['location']!, ui); } diff --git a/flutter/lib/plugin/widgets/remote/toolbar/display.dart b/flutter/lib/plugin/widgets/remote/toolbar/display.dart new file mode 100644 index 000000000..1f7cc359e --- /dev/null +++ b/flutter/lib/plugin/widgets/remote/toolbar/display.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../../../model.dart'; + +class Display extends StatelessWidget { + final String peerId; + final LocationModel locationModel; + + Display({ + Key? key, + required this.peerId, + required this.locationModel, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: locationModel, + child: Consumer(builder: (context, model, child) { + return Column( + children: [], + ); + }), + ); + } +} From 9a08e0bed4ccc9f40afc802567b4c6f01b981be2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 20:57:47 +0800 Subject: [PATCH 365/366] add ui event Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 43 ++++----- flutter/lib/plugin/common.dart | 42 ++++++++ flutter/lib/plugin/desc.dart | 96 ++++++++++++------- flutter/lib/plugin/model.dart | 2 + flutter/lib/plugin/widget.dart | 3 - .../widgets/remote/toolbar/display.dart | 85 +++++++++++++++- src/flutter_ffi.rs | 48 ++++++++++ src/plugin/config.rs | 4 +- src/plugin/mod.rs | 2 + 9 files changed, 262 insertions(+), 63 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index f8a38c830..f44f62514 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:io'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; @@ -641,7 +640,7 @@ class _ControlMenu extends StatelessWidget { if (e.divider) { return Divider(); } else { - return _MenuItemButton( + return MenuButton( child: e.child, onPressed: e.onPressed, ffi: ffi, @@ -711,7 +710,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { if (!visible) return Offstage(); return Column( children: [ - _MenuItemButton( + MenuButton( child: Text(translate('Adjust Window')), onPressed: _doAdjustWindow, ffi: widget.ffi), @@ -828,7 +827,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { final v = data as List>; return Column(children: [ ...v - .map((e) => _RadioMenuButton( + .map((e) => RdoMenuButton( value: e.value, groupValue: e.groupValue, onChanged: e.onChanged, @@ -858,14 +857,14 @@ class _DisplayMenuState extends State<_DisplayMenu> { final enabled = widget.ffi.canvasModel.imageOverflow.value; return Column(children: [ - _RadioMenuButton( + RdoMenuButton( child: Text(translate('ScrollAuto')), value: kRemoteScrollStyleAuto, groupValue: groupValue, onChanged: enabled ? (value) => onChange(value) : null, ffi: widget.ffi, ), - _RadioMenuButton( + RdoMenuButton( child: Text(translate('Scrollbar')), value: kRemoteScrollStyleBar, groupValue: groupValue, @@ -886,7 +885,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { ffi: widget.ffi, child: Text(translate('Image Quality')), menuChildren: v - .map((e) => _RadioMenuButton( + .map((e) => RdoMenuButton( value: e.value, groupValue: e.groupValue, onChanged: e.onChanged, @@ -908,7 +907,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { ffi: widget.ffi, child: Text(translate('Codec')), menuChildren: v - .map((e) => _RadioMenuButton( + .map((e) => RdoMenuButton( value: e.value, groupValue: e.groupValue, onChanged: e.onChanged, @@ -948,7 +947,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { return _SubmenuButton( ffi: widget.ffi, menuChildren: resolutions - .map((e) => _RadioMenuButton( + .map((e) => RdoMenuButton( value: '${e.width}x${e.height}', groupValue: groupValue, onChanged: onChanged, @@ -966,7 +965,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { if (v.isEmpty) return Offstage(); return Column( children: v - .map((e) => _CheckboxMenuButton( + .map((e) => CkbMenuButton( value: e.value, onChanged: e.onChanged, child: e.child, @@ -1026,7 +1025,7 @@ class _KeyboardMenu extends StatelessWidget { KeyboardModeMenu(key: _kKeyMapMode, menu: 'Map mode'), KeyboardModeMenu(key: _kKeyTranslateMode, menu: 'Translate mode'), ]; - List<_RadioMenuButton> list = []; + List list = []; final enabled = !ffi.ffiModel.viewOnly; onChanged(String? value) async { if (value == null) return; @@ -1049,7 +1048,7 @@ class _KeyboardMenu extends StatelessWidget { if (mode.key == _kKeyTranslateMode) { text = '$text beta'; } - list.add(_RadioMenuButton( + list.add(RdoMenuButton( child: Text(text), value: mode.key, groupValue: groupValue, @@ -1069,7 +1068,7 @@ class _KeyboardMenu extends StatelessWidget { return Column( children: [ Divider(), - _MenuItemButton( + MenuButton( child: Text( '${translate('Local keyboard type')}: ${KBLayoutType.value}'), trailingIcon: const Icon(Icons.settings), @@ -1085,7 +1084,7 @@ class _KeyboardMenu extends StatelessWidget { view_mode() { final ffiModel = ffi.ffiModel; final enabled = version_cmp(pi.version, '1.2.0') >= 0 && ffiModel.keyboard; - return _CheckboxMenuButton( + return CkbMenuButton( value: ffiModel.viewOnly, onChanged: enabled ? (value) async { @@ -1129,7 +1128,7 @@ class _ChatMenuState extends State<_ChatMenu> { } textChat() { - return _MenuItemButton( + return MenuButton( child: Text(translate('Text chat')), ffi: widget.ffi, onPressed: () { @@ -1148,7 +1147,7 @@ class _ChatMenuState extends State<_ChatMenu> { } voiceCall() { - return _MenuItemButton( + return MenuButton( child: Text(translate('Voice call')), ffi: widget.ffi, onPressed: () => bind.sessionRequestVoiceCall(id: widget.id), @@ -1403,12 +1402,12 @@ class _SubmenuButton extends StatelessWidget { } } -class _MenuItemButton extends StatelessWidget { +class MenuButton extends StatelessWidget { final VoidCallback? onPressed; final Widget? trailingIcon; final Widget? child; final FFI ffi; - _MenuItemButton( + MenuButton( {Key? key, this.onPressed, this.trailingIcon, @@ -1431,12 +1430,12 @@ class _MenuItemButton extends StatelessWidget { } } -class _CheckboxMenuButton extends StatelessWidget { +class CkbMenuButton extends StatelessWidget { final bool? value; final ValueChanged? onChanged; final Widget? child; final FFI ffi; - const _CheckboxMenuButton( + const CkbMenuButton( {Key? key, required this.value, required this.onChanged, @@ -1460,13 +1459,13 @@ class _CheckboxMenuButton extends StatelessWidget { } } -class _RadioMenuButton extends StatelessWidget { +class RdoMenuButton extends StatelessWidget { final T value; final T? groupValue; final ValueChanged? onChanged; final Widget? child; final FFI ffi; - const _RadioMenuButton( + const RdoMenuButton( {Key? key, required this.value, required this.groupValue, diff --git a/flutter/lib/plugin/common.dart b/flutter/lib/plugin/common.dart index 5cbc0f871..8395e2a81 100644 --- a/flutter/lib/plugin/common.dart +++ b/flutter/lib/plugin/common.dart @@ -1 +1,43 @@ +import 'dart:convert'; + typedef PluginId = String; + +class MsgFromUi { + String remotePeerId; + String localPeerId; + String id; + String name; + String location; + String key; + String value; + String action; + + MsgFromUi({ + required this.remotePeerId, + required this.localPeerId, + required this.id, + required this.name, + required this.location, + required this.key, + required this.value, + required this.action, + }); + + Map toJson() { + return { + 'remote_peer_id': remotePeerId, + 'local_peer_id': localPeerId, + 'id': id, + 'name': name, + 'location': location, + 'key': key, + 'value': value, + 'action': action, + }; + } + + @override + String toString() { + return jsonEncode(toJson()); + } +} diff --git a/flutter/lib/plugin/desc.dart b/flutter/lib/plugin/desc.dart index 6be712697..8c0de4625 100644 --- a/flutter/lib/plugin/desc.dart +++ b/flutter/lib/plugin/desc.dart @@ -1,45 +1,58 @@ import 'dart:collection'; -class UiButton { - String key; - String text; - String icon; - String tooltip; - String action; - - UiButton(this.key, this.text, this.icon, this.tooltip, this.action); - UiButton.fromJson(Map json) - : key = json['key'] ?? '', - text = json['text'] ?? '', - icon = json['icon'] ?? '', - tooltip = json['tooltip'] ?? '', - action = json['action'] ?? ''; -} - -class UiCheckbox { - String key; - String text; - String tooltip; - String action; - - UiCheckbox(this.key, this.text, this.tooltip, this.action); - UiCheckbox.fromJson(Map json) - : key = json['key'] ?? '', - text = json['text'] ?? '', - tooltip = json['tooltip'] ?? '', - action = json['action'] ?? ''; -} +const String kValueTrue = '1'; +const String kValueFalse = '0'; class UiType { - UiButton? button; - UiCheckbox? checkbox; + String key; + String text; + String tooltip; + String action; + + UiType(this.key, this.text, this.tooltip, this.action); UiType.fromJson(Map json) - : button = json['t'] == 'Button' ? UiButton.fromJson(json['c']) : null, - checkbox = - json['t'] != 'Checkbox' ? UiCheckbox.fromJson(json['c']) : null; + : key = json['key'] ?? '', + text = json['text'] ?? '', + tooltip = json['tooltip'] ?? '', + action = json['action'] ?? ''; - bool get isValid => button != null || checkbox != null; + static UiType? create(Map json) { + if (json['t'] == 'Button') { + return UiButton.fromJson(json['c']); + } else if (json['t'] == 'Checkbox') { + return UiCheckbox.fromJson(json['c']); + } else { + return null; + } + } +} + +class UiButton extends UiType { + String icon; + + UiButton( + {required String key, + required String text, + required this.icon, + required String tooltip, + required String action}) + : super(key, text, tooltip, action); + + UiButton.fromJson(Map json) + : icon = json['icon'] ?? '', + super.fromJson(json); +} + +class UiCheckbox extends UiType { + UiCheckbox( + {required String key, + required String text, + required String tooltip, + required String action}) + : super(key, text, tooltip, action); + + UiCheckbox.fromJson(Map json) : super.fromJson(json); } class Location { @@ -49,6 +62,14 @@ class Location { HashMap ui; Location(this.ui); + Location.fromJson(Map json) : ui = HashMap() { + json.forEach((key, value) { + var ui = UiType.create(value); + if (ui != null) { + this.ui[ui.key] = ui; + } + }); + } } class ConfigItem { @@ -63,6 +84,11 @@ class ConfigItem { value = json['value'] ?? '', description = json['description'] ?? '', defaultValue = json['default'] ?? ''; + + static String get trueValue => kValueTrue; + static String get falseValue => kValueFalse; + static bool isTrue(String value) => value == kValueTrue; + static bool isFalse(String value) => value == kValueFalse; } class Config { diff --git a/flutter/lib/plugin/model.dart b/flutter/lib/plugin/model.dart index 824992e20..ac1b74828 100644 --- a/flutter/lib/plugin/model.dart +++ b/flutter/lib/plugin/model.dart @@ -15,6 +15,8 @@ class LocationModel with ChangeNotifier { uiList.add(ui); notifyListeners(); } + + bool get isEmpty => uiList.isEmpty; } void addLocation(PluginId id, String location, UiType ui) { diff --git a/flutter/lib/plugin/widget.dart b/flutter/lib/plugin/widget.dart index a99e25e4e..fc110077c 100644 --- a/flutter/lib/plugin/widget.dart +++ b/flutter/lib/plugin/widget.dart @@ -40,8 +40,5 @@ void handleReloading(Map evt, String peer) { return; } final ui = UiType.fromJson(evt); - if (!ui.isValid) { - return; - } addLocation(evt['id']!, evt['location']!, ui); } diff --git a/flutter/lib/plugin/widgets/remote/toolbar/display.dart b/flutter/lib/plugin/widgets/remote/toolbar/display.dart index 1f7cc359e..c5fa13e67 100644 --- a/flutter/lib/plugin/widgets/remote/toolbar/display.dart +++ b/flutter/lib/plugin/widgets/remote/toolbar/display.dart @@ -1,27 +1,110 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hbb/models/model.dart'; import 'package:provider/provider.dart'; +import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart'; +import 'package:flutter_hbb/models/platform_model.dart'; +import '../../../desc.dart'; import '../../../model.dart'; +import '../../../common.dart'; class Display extends StatelessWidget { + final PluginId pluginId; final String peerId; + final FFI ffi; + final String location; final LocationModel locationModel; Display({ Key? key, + required this.pluginId, required this.peerId, + required this.ffi, + required this.location, required this.locationModel, }) : super(key: key); + bool get isEmpty => locationModel.isEmpty; + @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: locationModel, child: Consumer(builder: (context, model, child) { return Column( - children: [], + children: locationModel.uiList.map((ui) => _buildItem(ui)).toList(), ); }), ); } + + Widget _buildItem(UiType ui) { + switch (ui.runtimeType) { + case UiButton: + return _buildMenuButton(ui as UiButton); + case UiCheckbox: + return _buildCheckboxMenuButton(ui as UiCheckbox); + default: + return Container(); + } + } + + Uint8List _makeEvent( + String localPeerId, + String key, { + bool? v, + }) { + final event = MsgFromUi( + remotePeerId: peerId, + localPeerId: localPeerId, + id: pluginId, + name: getDesc(pluginId)?.name ?? '', + location: location, + key: key, + value: + v != null ? (v ? ConfigItem.trueValue : ConfigItem.falseValue) : '', + action: '', + ); + return Uint8List.fromList(event.toString().codeUnits); + } + + Widget _buildMenuButton(UiButton ui) { + return MenuButton( + onPressed: () { + () async { + final localPeerId = await bind.mainGetMyId(); + bind.pluginEvent( + id: pluginId, + event: _makeEvent(localPeerId, ui.key), + ); + }(); + }, + // to-do: rustdesk translate or plugin translate ? + child: Text(ui.text), + ffi: ffi, + ); + } + + Widget _buildCheckboxMenuButton(UiCheckbox ui) { + final v = + bind.pluginGetSessionOption(id: pluginId, peer: peerId, key: ui.key); + return CkbMenuButton( + value: ConfigItem.isTrue(v), + onChanged: (v) { + if (v != null) { + () async { + final localPeerId = await bind.mainGetMyId(); + bind.pluginEvent( + id: pluginId, + event: _makeEvent(localPeerId, ui.key, v: v), + ); + }(); + } + }, + // to-do: rustdesk translate or plugin translate ? + child: Text(ui.text), + ffi: ffi, + ); + } } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 5f72431ef..62db17151 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1405,6 +1405,54 @@ pub fn plugin_event(_id: String, _event: Vec) { } } +#[inline] +pub fn plugin_get_session_option(_id: String, _peer: String, _key: String) -> SyncReturn { + #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + return SyncReturn(crate::plugin::PeerConfig::get(&_id, &_peer, &_key)); + } + #[cfg(any( + not(feature = "plugin_framework"), + target_os = "android", + target_os = "ios" + ))] + return SyncReturn("".to_owned()); +} + +#[inline] +pub fn plugin_set_session_option(_id: String, _peer: String, _key: String, _value: String) { + #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + crate::plugin::PeerConfig::set(&_id, &_peer, &_key, &_value); + } +} + +#[inline] +pub fn plugin_get_local_option(_id: String, _key: String) -> SyncReturn { + #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + allow_err!(crate::plugin::LocalConfig::get(&_id, &key)); + } + #[cfg(any( + not(feature = "plugin_framework"), + target_os = "android", + target_os = "ios" + ))] + return SyncReturn("".to_owned()); +} + +#[inline] +pub fn plugin_set_local_option(_id: String, _key: String, _value: String) { + #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + crate::plugin::LocalConfig::set(&_id, &_key, &_value); + } +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log}; diff --git a/src/plugin/config.rs b/src/plugin/config.rs index 7a9228425..d86053649 100644 --- a/src/plugin/config.rs +++ b/src/plugin/config.rs @@ -15,9 +15,9 @@ lazy_static::lazy_static! { } #[derive(Debug, Default, Serialize, Deserialize)] -struct LocalConfig(HashMap); +pub struct LocalConfig(HashMap); #[derive(Debug, Default, Serialize, Deserialize)] -struct PeerConfig(HashMap); +pub struct PeerConfig(HashMap); type PeersConfig = HashMap; #[inline] diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 2f16f0325..c7ff2c4af 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -12,6 +12,8 @@ pub use plugins::{ reload_plugin, unload_plugin, }; +pub use config::{LocalConfig, PeerConfig}; + #[inline] fn cstr_to_string(cstr: *const c_char) -> ResultType { Ok(String::from_utf8(unsafe { From 1b303b7b27f38e89185f4a8428c1aac73e7a4a96 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 22:53:43 +0800 Subject: [PATCH 366/366] plugin_framework, ui tmp Signed-off-by: fufesou --- flutter/lib/{ => desktop}/plugin/common.dart | 6 ++ flutter/lib/{ => desktop}/plugin/desc.dart | 0 flutter/lib/{ => desktop}/plugin/event.dart | 0 flutter/lib/desktop/plugin/model.dart | 44 ++++++++++ .../plugin/widget.dart} | 83 +++++++++++++++++-- .../lib/desktop/widgets/remote_toolbar.dart | 11 ++- flutter/lib/models/model.dart | 6 +- flutter/lib/plugin/model.dart | 30 ------- flutter/lib/plugin/widget.dart | 44 ---------- src/flutter_ffi.rs | 14 ++-- src/plugin/desc.rs | 2 +- 11 files changed, 147 insertions(+), 93 deletions(-) rename flutter/lib/{ => desktop}/plugin/common.dart (80%) rename flutter/lib/{ => desktop}/plugin/desc.dart (100%) rename flutter/lib/{ => desktop}/plugin/event.dart (100%) create mode 100644 flutter/lib/desktop/plugin/model.dart rename flutter/lib/{plugin/widgets/remote/toolbar/display.dart => desktop/plugin/widget.dart} (57%) delete mode 100644 flutter/lib/plugin/model.dart delete mode 100644 flutter/lib/plugin/widget.dart diff --git a/flutter/lib/plugin/common.dart b/flutter/lib/desktop/plugin/common.dart similarity index 80% rename from flutter/lib/plugin/common.dart rename to flutter/lib/desktop/plugin/common.dart index 8395e2a81..b1b3dbfa0 100644 --- a/flutter/lib/plugin/common.dart +++ b/flutter/lib/desktop/plugin/common.dart @@ -2,6 +2,12 @@ import 'dart:convert'; typedef PluginId = String; +// ui location +const String kLocationHostMainDisplayOthers = + 'host|main|settings|display|others'; +const String kLocationClientRemoteToolbarDisplay = + 'client|remote|toolbar|display'; + class MsgFromUi { String remotePeerId; String localPeerId; diff --git a/flutter/lib/plugin/desc.dart b/flutter/lib/desktop/plugin/desc.dart similarity index 100% rename from flutter/lib/plugin/desc.dart rename to flutter/lib/desktop/plugin/desc.dart diff --git a/flutter/lib/plugin/event.dart b/flutter/lib/desktop/plugin/event.dart similarity index 100% rename from flutter/lib/plugin/event.dart rename to flutter/lib/desktop/plugin/event.dart diff --git a/flutter/lib/desktop/plugin/model.dart b/flutter/lib/desktop/plugin/model.dart new file mode 100644 index 000000000..a823844aa --- /dev/null +++ b/flutter/lib/desktop/plugin/model.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import './common.dart'; +import './desc.dart'; + +final Map locationModels = {}; + +class PluginModel with ChangeNotifier { + final List uiList = []; + + void add(UiType ui) { + uiList.add(ui); + notifyListeners(); + } + + bool get isEmpty => uiList.isEmpty; +} + +class LocationModel with ChangeNotifier { + final Map pluginModels = {}; + + void add(PluginId id, UiType ui) { + if (pluginModels[id] != null) { + pluginModels[id]!.add(ui); + } else { + var model = PluginModel(); + model.add(ui); + pluginModels[id] = model; + notifyListeners(); + } + } + + bool get isEmpty => pluginModels.isEmpty; +} + +void addLocationUi(String location, PluginId id, UiType ui) { + locationModels[location]?.add(id, ui); +} + +LocationModel addLocation(String location) { + if (locationModels[location] == null) { + locationModels[location] = LocationModel(); + } + return locationModels[location]!; +} diff --git a/flutter/lib/plugin/widgets/remote/toolbar/display.dart b/flutter/lib/desktop/plugin/widget.dart similarity index 57% rename from flutter/lib/plugin/widgets/remote/toolbar/display.dart rename to flutter/lib/desktop/plugin/widget.dart index c5fa13e67..3f7b413ea 100644 --- a/flutter/lib/plugin/widgets/remote/toolbar/display.dart +++ b/flutter/lib/desktop/plugin/widget.dart @@ -5,20 +5,18 @@ import 'package:provider/provider.dart'; import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart'; import 'package:flutter_hbb/models/platform_model.dart'; -import '../../../desc.dart'; -import '../../../model.dart'; -import '../../../common.dart'; +import './desc.dart'; +import './model.dart'; +import './common.dart'; -class Display extends StatelessWidget { - final PluginId pluginId; +class LocationItem extends StatelessWidget { final String peerId; final FFI ffi; final String location; final LocationModel locationModel; - Display({ + LocationItem({ Key? key, - required this.pluginId, required this.peerId, required this.ffi, required this.location, @@ -27,18 +25,71 @@ class Display extends StatelessWidget { bool get isEmpty => locationModel.isEmpty; + static LocationItem createLocationItem( + String peerId, FFI ffi, String location) { + final model = addLocation(location); + return LocationItem( + peerId: peerId, + ffi: ffi, + location: location, + locationModel: model, + ); + } + @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: locationModel, child: Consumer(builder: (context, model, child) { return Column( - children: locationModel.uiList.map((ui) => _buildItem(ui)).toList(), + children: model.pluginModels.entries + .map((entry) => _buildPluginItem(entry.key, entry.value)) + .toList(), ); }), ); } + Widget _buildPluginItem(PluginId id, PluginModel model) => PluginItem( + pluginId: id, + peerId: peerId, + ffi: ffi, + location: location, + pluginModel: model, + ); +} + +class PluginItem extends StatelessWidget { + final PluginId pluginId; + final String peerId; + final FFI ffi; + final String location; + final PluginModel pluginModel; + + PluginItem({ + Key? key, + required this.pluginId, + required this.peerId, + required this.ffi, + required this.location, + required this.pluginModel, + }) : super(key: key); + + bool get isEmpty => pluginModel.isEmpty; + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: pluginModel, + child: Consumer(builder: (context, model, child) { + return Column( + children: model.uiList.map((ui) => _buildItem(ui)).toList(), + ); + }), + ); + } + + // to-do: add plugin icon and tooltip Widget _buildItem(UiType ui) { switch (ui.runtimeType) { case UiButton: @@ -80,7 +131,9 @@ class Display extends StatelessWidget { ); }(); }, - // to-do: rustdesk translate or plugin translate ? + trailingIcon: Icon( + IconData(int.parse(ui.icon, radix: 16), fontFamily: 'MaterialIcons')), + // to-do: RustDesk translate or plugin translate ? child: Text(ui.text), ffi: ffi, ); @@ -89,6 +142,10 @@ class Display extends StatelessWidget { Widget _buildCheckboxMenuButton(UiCheckbox ui) { final v = bind.pluginGetSessionOption(id: pluginId, peer: peerId, key: ui.key); + if (v == null) { + // session or plugin not found + return Container(); + } return CkbMenuButton( value: ConfigItem.isTrue(v), onChanged: (v) { @@ -108,3 +165,11 @@ class Display extends StatelessWidget { ); } } + +void handleReloading(Map evt, String peer) { + if (evt['id'] == null || evt['location'] == null) { + return; + } + final ui = UiType.fromJson(evt); + addLocationUi(evt['location']!, evt['id']!, ui); +} diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index f44f62514..f4895c785 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -8,6 +8,8 @@ import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; +import 'package:flutter_hbb/desktop/plugin/widget.dart'; +import 'package:flutter_hbb/desktop/plugin/common.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; @@ -655,13 +657,19 @@ class _DisplayMenu extends StatefulWidget { final FFI ffi; final MenubarState state; final Function(bool) setFullscreen; + final LocationItem pluginItem; _DisplayMenu( {Key? key, required this.id, required this.ffi, required this.state, required this.setFullscreen}) - : super(key: key); + : pluginItem = LocationItem.createLocationItem( + id, + ffi, + kLocationClientRemoteToolbarDisplay, + ), + super(key: key); @override State<_DisplayMenu> createState() => _DisplayMenuState(); @@ -699,6 +707,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { resolutions(), Divider(), toggles(), + widget.pluginItem, ]); } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 44ca71912..406783c2e 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -16,9 +16,9 @@ import 'package:flutter_hbb/models/peer_tab_model.dart'; import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/models/user_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; -import 'package:flutter_hbb/plugin/event.dart'; -import 'package:flutter_hbb/plugin/desc.dart'; -import 'package:flutter_hbb/plugin/widget.dart'; +import 'package:flutter_hbb/desktop/plugin/event.dart'; +import 'package:flutter_hbb/desktop/plugin/desc.dart'; +import 'package:flutter_hbb/desktop/plugin/widget.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:tuple/tuple.dart'; import 'package:image/image.dart' as img2; diff --git a/flutter/lib/plugin/model.dart b/flutter/lib/plugin/model.dart deleted file mode 100644 index ac1b74828..000000000 --- a/flutter/lib/plugin/model.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import './common.dart'; -import './desc.dart'; - -// ui location -// host|main|settings|display|others -// client|remote|toolbar|display - -final Map> locationModels = {}; - -class LocationModel with ChangeNotifier { - final List uiList = []; - - void add(UiType ui) { - uiList.add(ui); - notifyListeners(); - } - - bool get isEmpty => uiList.isEmpty; -} - -void addLocation(PluginId id, String location, UiType ui) { - if (!locationModels.containsKey(id)) { - locationModels[id] = {}; - } - if (!locationModels[id]!.containsKey(location)) { - locationModels[id]![location] = LocationModel(); - } - locationModels[id]![location]!.add(ui); -} diff --git a/flutter/lib/plugin/widget.dart b/flutter/lib/plugin/widget.dart deleted file mode 100644 index fc110077c..000000000 --- a/flutter/lib/plugin/widget.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; -import './desc.dart'; -import './model.dart'; - -final Map pluginWidgets = {}; - -class PluginWidget { - final String id; - final String name; - final String location; - final Widget widget; - - PluginWidget({ - required this.id, - required this.name, - required this.location, - required this.widget, - }); - - // static Widget createButton(UiButton btn) {} - - // static Widget createCheckbox(UiCheckbox chk) {} - - // // ui location - // // host|main|settings|display|others - // // client|remote|toolbar|display - // static Widget? create(String id, String locatin, UiType ui) { - // if (ui.button != null) { - // return createButton(ui.button!); - // } else if (ui.checkbox != null) { - // return createCheckbox(ui.checkbox!); - // } else { - // return null; - // } - // } -} - -void handleReloading(Map evt, String peer) { - if (evt['id'] == null || evt['location'] == null) { - return; - } - final ui = UiType.fromJson(evt); - addLocation(evt['id']!, evt['location']!, ui); -} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 62db17151..b5cc669ab 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1406,7 +1406,7 @@ pub fn plugin_event(_id: String, _event: Vec) { } #[inline] -pub fn plugin_get_session_option(_id: String, _peer: String, _key: String) -> SyncReturn { +pub fn plugin_get_session_option(_id: String, _peer: String, _key: String) -> SyncReturn> { #[cfg(feature = "plugin_framework")] #[cfg(not(any(target_os = "android", target_os = "ios")))] { @@ -1417,7 +1417,9 @@ pub fn plugin_get_session_option(_id: String, _peer: String, _key: String) -> Sy target_os = "android", target_os = "ios" ))] - return SyncReturn("".to_owned()); + { + return SyncReturn(None); + } } #[inline] @@ -1430,18 +1432,20 @@ pub fn plugin_set_session_option(_id: String, _peer: String, _key: String, _valu } #[inline] -pub fn plugin_get_local_option(_id: String, _key: String) -> SyncReturn { +pub fn plugin_get_local_option(_id: String, _key: String) -> SyncReturn> { #[cfg(feature = "plugin_framework")] #[cfg(not(any(target_os = "android", target_os = "ios")))] { - allow_err!(crate::plugin::LocalConfig::get(&_id, &key)); + return SyncReturn(crate::plugin::LocalConfig::get(&_id, &_key)); } #[cfg(any( not(feature = "plugin_framework"), target_os = "android", target_os = "ios" ))] - return SyncReturn("".to_owned()); + { + return SyncReturn(None); + } } #[inline] diff --git a/src/plugin/desc.rs b/src/plugin/desc.rs index bc094abf9..94a137570 100644 --- a/src/plugin/desc.rs +++ b/src/plugin/desc.rs @@ -8,7 +8,7 @@ use std::ffi::{c_char, CStr}; pub struct UiButton { key: String, text: String, - icon: String, + icon: String, // icon can be int in flutter, but string in other ui framework. And it is flexible to use string. tooltip: String, action: String, // The action to be triggered when the button is clicked. }