Compare commits

...

29 Commits
1.3.3 ... 1.3.4

Author SHA1 Message Date
fufesou
a23822074e feat: Android, opt, check update on startup (#10165)
* feat: Android, opt, check update on startup

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: check update only on startup

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: Android, "Download new version"

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-12-04 17:10:32 +08:00
21pages
3d17bf4990 linux dynamic load libva (#10171)
1. Linux dynamic load libva, which can fix lack of libva dependency for
   appimage or flatpak, also fix libva version mismatch between build
and run.
2. Remove libvdpau, it's not used, and add libva2 explicitly for deb and
   appimage
3. Print FFmpeg configure log to know the actual codecs.

Test
*  ubuntu 22.04 x64
  - [x] deb
  - [x] flatpak
  - [x] appimage
* ubuntu 18.04
  * deb: fcntl64 not found
  - [x]:appimage
  - [ ]: platpak
hwcodec example:
  - [x]: combination of lacking any of libva2, libva-x11-2, libva-drm2,
    intel-media-va-driver
- [ ] federa
- [ ] arch
- [ ] arm64: my ci can't finish arm64 building

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-12-04 17:10:10 +08:00
Yevhen Popok
fd67be4a16 Update uk.rs (#10162) 2024-12-04 10:31:13 +08:00
fufesou
e6edf39305 fix: support emptry folder transfer for web (#10151)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-12-03 14:22:20 +08:00
21pages
34d2c62781 set id/relay server with a dialog (#10150)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-12-03 14:14:29 +08:00
fufesou
bd0a33e467 fix: linux, window, workaround, mint, mate (#10146)
* refact: linux, window, workaround, mint, mate

Signed-off-by: fufesou <linlong1266@gmail.com>

* refact: case insensitive

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-12-03 01:02:41 +08:00
rustdesk
b8d36b6558 revert multi-window plugin 2024-12-02 22:29:37 +08:00
solokot
f38d89aaee Update ru.rs (#10143) 2024-12-02 18:30:24 +08:00
21pages
773b9d6645 win7 uses soft rendering by default (#10139)
win7 vm got black screen on remote window with texture rendering

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-12-02 17:10:34 +08:00
rustdesk
dea99ffb3a fix rustdesk exit crash 2024-12-02 16:11:12 +08:00
jkh0kr
3251045e22 Update ko.rs (#10138)
Update ko.rs
2024-12-02 15:47:23 +08:00
21pages
dc58c85e30 try fix mac textedit of server config (#10135)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-12-02 12:35:44 +08:00
fufesou
5a2a94d2cc fix: macos, input (#10133)
1. Workaround sticky `Fn` for more keys.
2. Workaround stikey `Help`.

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-12-02 00:09:03 +08:00
rustdesk
f330953f4f bump to 1.3.4 2024-12-01 18:49:24 +08:00
fufesou
8d4c86fe7f fix: workaround, linux window, transparent rounded corner (#10128)
* fix: linux window, rounded corner

Signed-off-by: fufesou <linlong1266@gmail.com>

* Update my_application.cc

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
2024-12-01 17:19:21 +08:00
Kleofass
f8c2713c5b Update lv.rs (#10124) 2024-12-01 00:27:39 +08:00
fufesou
082a66b282 refact: remove flutter_improved_scrolling (#10120)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-11-30 15:01:44 +08:00
21pages
743b0ce8ce fix mediacodec patch (#10119)
ensure set_parameters_id is not null

Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-11-30 13:29:03 +08:00
21pages
9d9b67aca5 update flutter texture rgba renderer plugin, remove switch rgba (#10070)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-11-30 12:19:42 +08:00
21pages
d60b5a6ca0 videotoolbox/mediacodec support changing bitrate dynamically (#10117)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-11-30 11:44:51 +08:00
Vasyl Gello
e0ed6ee986 Fix F-Droid build and bump Android NDK to r27c (#10105)
* Fix fdroid build

* Refactor recent @fufesou edits to reflect the fact that
  .gclient file is needed only on x86 to build jit-release
  version of flutter-engine and `flutter-sdk` directory is
  not affected by flutter version checkouts

* Install cargo-ndk and flutter-rust-codegen with `--locked`
  argument to avoid bumping `cargo-platform` to require
  newer Rust toolchain

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

* Bump Android NDK to r27c

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>

---------

Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
2024-11-30 09:24:45 +08:00
21pages
d3f0c80e94 "Untagged" tag uses the theme accent color (#10111)
Signed-off-by: 21pages <sunboeasy@gmail.com>
2024-11-30 09:24:05 +08:00
fufesou
b32ff87c6e fix: android, pan, canvas, remove toInt() (#10103)
Signed-off-by: fufesou <linlong1266@gmail.com>
2024-11-29 22:39:07 +08:00
rustdesk
b91b49229a enable our engine to fix dart supporting win7, https://github.com/rustdesk/rustdesk/issues/10085#issuecomment-2506485955 2024-11-29 19:10:57 +08:00
fufesou
afc8bb71dc feat: mobile, key help tool, more keys (#10068)
* feat: mobile, key help tool, vk_enter

Signed-off-by: fufesou <linlong1266@gmail.com>

* Mobile, add more function keys

Signed-off-by: fufesou <linlong1266@gmail.com>

* Mobile, more virtual function keys

Signed-off-by: fufesou <linlong1266@gmail.com>

* uinput, menu maps key_compose

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
2024-11-29 00:56:38 +08:00
XLion
4f86169f7f Update tw.rs (#10076) 2024-11-28 00:10:47 +08:00
Leo Mozoloa
2cf43042e6 Update fr.rs (#10075) 2024-11-27 23:00:41 +08:00
BoyChai
734fb8d6f7 Update README-ZH.md (#10069)
Modify Alibaba Cloud apt source
2024-11-27 10:47:09 +08:00
bovirus
3c7f6d3127 Italian language update (#10067) 2024-11-27 07:37:49 +08:00
59 changed files with 3313 additions and 709 deletions

View File

@@ -73,7 +73,7 @@ jobs:
- name: Install flutter rust bridge deps
shell: bash
run: |
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid"
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked
pushd flutter && sed -i -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' pubspec.yaml && flutter pub get && popd
- name: Run flutter rust bridge

View File

@@ -33,8 +33,8 @@ env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "1de2026f28ead93ff1773e6e680387643e914ea1"
VERSION: "1.3.3"
NDK_VERSION: "r27b"
VERSION: "1.3.4"
NDK_VERSION: "r27c"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
@@ -107,7 +107,6 @@ jobs:
# https://github.com/flutter/flutter/issues/155685
- name: Replace engine with rustdesk custom flutter engine
if: false
run: |
flutter doctor -v
flutter precache --windows
@@ -157,6 +156,7 @@ jobs:
done
exit 1
fi
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
shell: bash
- name: Build rustdesk
@@ -317,6 +317,7 @@ jobs:
done
exit 1
fi
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
shell: bash
- name: Build rustdesk
@@ -520,6 +521,7 @@ jobs:
done
exit 1
fi
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
shell: bash
- name: Install Rust toolchain
@@ -638,6 +640,7 @@ jobs:
os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel
extra-build-args: "",
arch: x86_64,
vcpkg-triplet: x64-osx,
}
- {
target: aarch64-apple-darwin,
@@ -645,6 +648,7 @@ jobs:
# extra-build-args: "--disable-flutter-texture-render", # disable this for mac, because we see a lot of users reporting flickering both on arm and x64, and we can not confirm if texture rendering has better performance if htere is no vram, https://github.com/rustdesk/rustdesk/issues/6296
extra-build-args: "--screencapturekit",
arch: aarch64,
vcpkg-triplet: arm64-osx,
}
steps:
- name: Export GitHub Actions cache environment variables
@@ -715,7 +719,7 @@ jobs:
shell: bash
run: |
cd "$(dirname "$(which flutter)")"
# https://github.com/flutter/flutter/issues/133533
# https://github.com/flutter/flutter/issues/1.3.43
sed -i -e 's/_setFramesEnabledState(false);/\/\/_setFramesEnabledState(false);/g' ../packages/flutter/lib/src/scheduler/binding.dart
grep -n '_setFramesEnabledState(false);' ../packages/flutter/lib/src/scheduler/binding.dart
@@ -756,6 +760,7 @@ jobs:
done
exit 1
fi
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
- name: Show version information (Rust, cargo, Clang)
shell: bash
@@ -932,7 +937,6 @@ jobs:
libpam0g-dev \
libpulse-dev \
libva-dev \
libvdpau-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
@@ -1033,7 +1037,7 @@ jobs:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
run: |
rustup target add ${{ matrix.job.target }}
cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }}
cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} --locked
case ${{ matrix.job.target }} in
aarch64-linux-android)
./flutter/ndk_arm64.sh
@@ -1209,7 +1213,6 @@ jobs:
libpam0g-dev \
libpulse-dev \
libva-dev \
libvdpau-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
@@ -1441,7 +1444,7 @@ jobs:
- name: Install vcpkg dependencies
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
run: |
sudo apt install -y libva-dev libvdpau-dev
sudo apt install -y libva-dev && apt show libva-dev
if ! $VCPKG_ROOT/vcpkg \
install \
--triplet ${{ matrix.job.vcpkg-triplet }} \
@@ -1455,6 +1458,7 @@ jobs:
done
exit 1
fi
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
shell: bash
- name: Restore bridge files
@@ -1499,7 +1503,6 @@ jobs:
libpam0g-dev \
libpulse-dev \
libva-dev \
libvdpau-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
@@ -1772,7 +1775,6 @@ jobs:
libpam0g-dev \
libpulse-dev \
libva-dev \
libvdpau-dev \
libxcb-randr0-dev \
libxcb-shape0-dev \
libxcb-xfixes0-dev \
@@ -1860,6 +1862,7 @@ jobs:
done
exit 1
fi
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
# build rustdesk
python3 ./res/inline-sciter.py
export CARGO_INCREMENTAL=0

View File

@@ -18,7 +18,7 @@ env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.06.15
VCPKG_COMMIT_ID: "f7423ee180c4b7f40d43402c2feb3859161ef625"
VERSION: "1.3.3"
VERSION: "1.3.4"
NDK_VERSION: "r26d"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
@@ -149,7 +149,7 @@ jobs:
shell: bash
run: |
sed -i '' 's/3.1.0/2.17.0/g' flutter/pubspec.yaml;
cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid"
cargo install flutter_rust_bridge_codegen --version ${{ matrix.job.bridge }} --features "uuid" --locked
# below works for mac to make buildable on 3.13.9
# pushd flutter/lib; find . -name "*.dart" | xargs -I{} sed -i '' 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g' {}; popd;
pushd flutter && flutter pub get && popd
@@ -302,7 +302,7 @@ jobs:
- name: Install flutter rust bridge deps
run: |
git config --global core.longpaths true
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid"
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked
sed -i 's/uni_links_desktop/#uni_links_desktop/g' flutter/pubspec.yaml
pushd flutter/lib; find . | grep dart | xargs sed -i 's/textScaler: TextScaler.linear(\(.*\)),/textScaleFactor: \1,/g'; popd;
pushd flutter ; flutter pub get ; popd
@@ -347,7 +347,7 @@ jobs:
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
run: |
rustup target add ${{ matrix.job.target }}
cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }}
cargo install cargo-ndk --version ${{ env.CARGO_NDK_VERSION }} --locked
case ${{ matrix.job.target }} in
aarch64-linux-android)
./flutter/ndk_arm64.sh

12
Cargo.lock generated
View File

@@ -3064,8 +3064,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hwcodec"
version = "0.7.0"
source = "git+https://github.com/rustdesk-org/hwcodec#da7dab48df19edb5a7138ff9e01bf9f148b523da"
version = "0.7.1"
source = "git+https://github.com/rustdesk-org/hwcodec#835e599ed229e4e01b6fa3566e02ea45c73e2e9c"
dependencies = [
"bindgen 0.59.2",
"cc",
@@ -3519,7 +3519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [
"cfg-if 1.0.0",
"windows-targets 0.48.5",
"windows-targets 0.52.5",
]
[[package]]
@@ -5219,7 +5219,7 @@ dependencies = [
[[package]]
name = "rdev"
version = "0.5.0-2"
source = "git+https://github.com/rustdesk-org/rdev#961d25cc00c6b3ef80f444e6a7bed9872e2c35ea"
source = "git+https://github.com/rustdesk-org/rdev#01ac3ec8009f04f7615842b9152338844b806184"
dependencies = [
"cocoa 0.24.1",
"core-foundation 0.9.4",
@@ -5494,7 +5494,7 @@ dependencies = [
[[package]]
name = "rustdesk"
version = "1.3.3"
version = "1.3.4"
dependencies = [
"android-wakelock",
"android_logger",
@@ -5594,7 +5594,7 @@ dependencies = [
[[package]]
name = "rustdesk-portable-packer"
version = "1.3.3"
version = "1.3.4"
dependencies = [
"brotli",
"dirs 5.0.1",

View File

@@ -1,6 +1,6 @@
[package]
name = "rustdesk"
version = "1.3.3"
version = "1.3.4"
authors = ["rustdesk <info@rustdesk.com>"]
edition = "2021"
build= "build.rs"

View File

@@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.3.3
version: 1.3.4
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:
@@ -47,9 +47,9 @@ AppDir:
- libasound2
- libsystemd0
- curl
- libva2
- libva-drm2
- libva-x11-2
- libvdpau1
- libgstreamer-plugins-base1.0-0
- gstreamer1.0-pipewire
- libwayland-client0

View File

@@ -18,7 +18,7 @@ AppDir:
id: rustdesk
name: rustdesk
icon: rustdesk
version: 1.3.3
version: 1.3.4
exec: usr/lib/rustdesk/rustdesk
exec_args: $@
apt:
@@ -50,9 +50,9 @@ AppDir:
- libasound2
- libsystemd0
- curl
- libva2
- libva-drm2
- libva-x11-2
- libvdpau1
- libgstreamer-plugins-base1.0-0
- gstreamer1.0-pipewire
- libwayland-client0

View File

@@ -111,7 +111,7 @@ def make_parser():
'--hwcodec',
action='store_true',
help='Enable feature hwcodec' + (
'' if windows or osx else ', need libva-dev, libvdpau-dev.')
'' if windows or osx else ', need libva-dev.')
)
parser.add_argument(
'--vram',
@@ -298,7 +298,7 @@ Version: %s
Architecture: %s
Maintainer: rustdesk <info@rustdesk.com>
Homepage: https://rustdesk.com
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva-drm2, libva-x11-2, libvdpau1, libgstreamer-plugins-base1.0-0, libpam0g, gstreamer1.0-pipewire%s
Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva2, libva-drm2, libva-x11-2, libgstreamer-plugins-base1.0-0, libpam0g, gstreamer1.0-pipewire%s
Recommends: libayatana-appindicator3-1
Description: A remote control software.

View File

@@ -135,8 +135,8 @@ docker build -t "rustdesk-builder" . # 构建容器
```
在Dockerfile的RUN apt update之前插入两行
RUN sed -i "s/deb.debian.org/mirrors.163.com/g" /etc/apt/sources.list
RUN sed -i "s/security.debian.org/mirrors.163.com/g" /etc/apt/sources.list
RUN sed -i "s|deb.debian.org|mirrors.aliyun.com|g" /etc/apt/sources.list && \
sed -i "s|security.debian.org|mirrors.aliyun.com|g" /etc/apt/sources.list
```
2. 修改容器系统中的 cargo 源,在`RUN ./rustup.sh -y`后插入下面代码:

View File

@@ -15,6 +15,13 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<queries>
<intent>
<!-- https://developer.android.com/training/package-visibility/use-cases#open-urls-custom-tabs -->
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
<application
android:icon="@mipmap/ic_launcher"
android:label="RustDesk"

View File

@@ -68,6 +68,7 @@ function build {
pushd "$SCRIPTDIR/.."
$VCPKG_ROOT/vcpkg install --triplet $VCPKG_TARGET --x-install-root="$VCPKG_ROOT/installed"
popd
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-$VCPKG_TARGET-rel-out.log" || true
echo "*** [$ANDROID_ABI][Finished] Build and install vcpkg dependencies"
if [ -d "$VCPKG_ROOT/installed/arm-neon-android" ]; then

View File

@@ -1,7 +1,5 @@
#!/bin/bash
set -x
#
# Script to build F-Droid release of RustDesk
#
@@ -23,6 +21,43 @@ set -x
# + build: perform actual build of APK file
#
# Start of functions
# Install Flutter of version `VERSION` from Github repository
# into directory `FLUTTER_DIR` and apply patches if needed
prepare_flutter() {
VERSION="${1}"
FLUTTER_DIR="${2}"
if [ ! -f "${FLUTTER_DIR}/bin/flutter" ]; then
git clone https://github.com/flutter/flutter "${FLUTTER_DIR}"
fi
pushd "${FLUTTER_DIR}"
git restore .
git checkout "${VERSION}"
# Patch flutter
if dpkg --compare-versions "${VERSION}" ge "3.24.4"; then
git apply "${ROOTDIR}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff"
fi
flutter config --no-analytics
popd # ${FLUTTER_DIR}
}
# Start of script
set -x
# Note current working directory as root dir for patches
ROOTDIR="${PWD}"
# Parse command-line arguments
VERNAME="${1}"
@@ -82,21 +117,6 @@ export PATH="${PATH}:${HOME}/flutter/bin:${HOME}/depot_tools"
export VCPKG_ROOT="${HOME}/vcpkg"
prepare_Flutter() {
version="${1}"
pushd "${HOME}"
if [ ! -f "${HOME}/flutter/bin/flutter" ]; then
git clone https://github.com/flutter/flutter
fi
pushd flutter
git restore .
git checkout "${version}"
flutter config --no-analytics
popd # flutter
popd # ${HOME}
}
# Now act depending on build step
# NOTE: F-Droid maintainers require explicit declaration of dependencies
@@ -116,15 +136,20 @@ prebuild)
.env.CARGO_NDK_VERSION \
.github/workflows/flutter-build.yml)"
# Flutter used to compile main Rustdesk library
FLUTTER_VERSION="$(yq -r \
.env.ANDROID_FLUTTER_VERSION \
.github/workflows/flutter-build.yml)"
if [ -z "${FLUTTER_VERSION}" ]; then
FLUTTER_VERSION="$(yq -r \
.env.FLUTTER_VERSION \
.github/workflows/flutter-build.yml)"
fi
# Flutter used to compile Flutter<->Rust bridge files
FLUTTER_BRIDGE_VERSION="$(yq -r \
.env.FLUTTER_VERSION \
.github/workflows/bridge.yml)"
@@ -207,14 +232,16 @@ prebuild)
cargo install \
cargo-ndk \
--version "${CARGO_NDK_VERSION}"
--version "${CARGO_NDK_VERSION}" \
--locked
# Install rust bridge generator
cargo install cargo-expand
cargo install flutter_rust_bridge_codegen \
--version "${FLUTTER_RUST_BRIDGE_VERSION}" \
--features "uuid"
--features "uuid" \
--locked
# Populate native vcpkg dependencies
@@ -277,46 +304,66 @@ prebuild)
git apply res/fdroid/patches/*.patch
# Backup .gclient file, for later restore
# If Flutter version used to generate bridge files differs from Flutter
# version used to compile Rustdesk library, generate bridge using the
# `FLUTTER_BRIDGE_VERSION` an restore the pubspec later
cp flutter-sdk/.gclient flutter-sdk/.gclient.bak
if [ "${FLUTTER_VERSION}" != "${FLUTTER_BRIDGE_VERSION}" ]; then
# Install Flutter bridge version
# For FLUTTER_BRIDGE_VERSION
sed \
-i \
-e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' \
flutter/pubspec.yaml
prepare_flutter "${FLUTTER_BRIDGE_VERSION}" "${HOME}/flutter"
# Install Flutter bridge version
prepare_Flutter "${FLUTTER_BRIDGE_VERSION}"
cp flutter-sdk/.gclient.bak flutter-sdk/.gclient
sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_BRIDGE_VERSION}/" flutter-sdk/.gclient
# Save changes
# Download Flutter dependencies
pushd flutter
flutter clean && flutter packages pub get
popd # flutter
git add .
# Generate FFI bindings
flutter_rust_bridge_codegen \
--rust-input ./src/flutter_ffi.rs \
--dart-output ./flutter/lib/generated_bridge.dart
# Edit pubspec to make flutter bridge version work
git restore flutter/pubspec.*
sed \
-i \
-e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' \
flutter/pubspec.yaml
# Install Flutter
prepare_Flutter "${FLUTTER_VERSION}"
cp flutter-sdk/.gclient.bak flutter-sdk/.gclient
sed -i "s/FLUTTER_VERSION_PLACEHOLDER/${FLUTTER_VERSION}/" flutter-sdk/.gclient
# Download Flutter dependencies
pushd flutter
flutter clean
flutter packages pub get
popd # flutter
# Generate FFI bindings
flutter_rust_bridge_codegen \
--rust-input ./src/flutter_ffi.rs \
--dart-output ./flutter/lib/generated_bridge.dart
# Add bridge files to save-list
git add -f ./flutter/lib/generated_bridge.* ./src/bridge_generated.*
# Restore everything
git checkout '*'
git clean -dffx
git reset
fi
# Install Flutter version for RustDesk library build
prepare_flutter "${FLUTTER_VERSION}" "${HOME}/flutter"
# gms is not in thoes files now, but we still keep the following line for future reference(maybe).
sed \
-i \
-e '/gms/d' \
flutter/android/build.gradle \
flutter/android/app/build.gradle
# `firebase_analytics`` is not in thoes files now, but we still keep the following lines.
# `firebase_analytics` is not in these files now, but we still keep the following lines.
sed \
-i \
-e '/firebase_analytics/d' \
@@ -343,9 +390,12 @@ build)
# '.github/workflows/flutter-build.yml'
#
# Flutter used to compile main Rustdesk library
FLUTTER_VERSION="$(yq -r \
.env.ANDROID_FLUTTER_VERSION \
.github/workflows/flutter-build.yml)"
if [ -z "${FLUTTER_VERSION}" ]; then
FLUTTER_VERSION="$(yq -r \
.env.FLUTTER_VERSION \
@@ -381,7 +431,8 @@ build)
pushd flutter
flutter clean && flutter packages pub get
flutter clean
flutter packages pub get
popd # flutter

View File

@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'dart:io';
import 'package:back_button_interceptor/back_button_interceptor.dart';
import 'package:desktop_multi_window/desktop_multi_window.dart';
@@ -2730,30 +2731,6 @@ Future<bool> osxRequestAudio() async {
return await kMacOSPermChannel.invokeMethod("requestRecordAudio");
}
class DraggableNeverScrollableScrollPhysics extends ScrollPhysics {
/// Creates scroll physics that does not let the user scroll.
const DraggableNeverScrollableScrollPhysics({super.parent});
@override
DraggableNeverScrollableScrollPhysics applyTo(ScrollPhysics? ancestor) {
return DraggableNeverScrollableScrollPhysics(parent: buildParent(ancestor));
}
@override
bool shouldAcceptUserOffset(ScrollMetrics position) {
// TODO: find a better solution to check if the offset change is caused by the scrollbar.
// Workaround: when dragging with the scrollbar, it always triggers an [IdleScrollActivity].
if (position is ScrollPositionWithSingleContext) {
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
return position.activity is IdleScrollActivity;
}
return false;
}
@override
bool get allowImplicitScrolling => false;
}
Widget futureBuilder(
{required Future? future, required Widget Function(dynamic data) hasData}) {
return FutureBuilder(
@@ -3483,6 +3460,35 @@ Widget buildPresetPasswordWarning() {
);
}
bool get isLinuxMateDesktop =>
isLinux &&
(Platform.environment['XDG_CURRENT_DESKTOP']?.toLowerCase() == 'mate' ||
Platform.environment['XDG_SESSION_DESKTOP']?.toLowerCase() == 'mate' ||
Platform.environment['DESKTOP_SESSION']?.toLowerCase() == 'mate');
Map<String, dynamic>? _linuxOsDistro;
String getLinuxOsDistroId() {
if (_linuxOsDistro == null) {
String osInfo = bind.getOsDistroInfo();
if (osInfo.isEmpty) {
_linuxOsDistro = {};
} else {
try {
_linuxOsDistro = jsonDecode(osInfo);
} catch (e) {
debugPrint('Failed to parse os info: $e');
// Don't call `bind.getOsDistroInfo()` again if failed to parse osInfo.
_linuxOsDistro = {};
}
}
}
return (_linuxOsDistro?['id'] ?? '') as String;
}
bool get isLinuxMint =>
getLinuxOsDistroId().toLowerCase().contains('linuxmint');
// https://github.com/leanflutter/window_manager/blob/87dd7a50b4cb47a375b9fc697f05e56eea0a2ab3/lib/src/widgets/virtual_window_frame.dart#L44
Widget buildVirtualWindowFrame(BuildContext context, Widget child) {
boxShadow() => isMainDesktopWindow
@@ -3627,3 +3633,20 @@ List<SubWindowResizeEdge>? get subWindowManagerEnableResizeEdges => isWindows
void earlyAssert() {
assert('\1' == '1');
}
void checkUpdate() {
if (isDesktop || isAndroid) {
if (!bind.isCustomClient()) {
platformFFI.registerEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
(Map<String, dynamic> evt) async {
if (evt['url'] is String) {
stateGlobal.updateUrl.value = evt['url'];
}
});
Timer(const Duration(seconds: 1), () async {
bind.mainGetSoftwareUpdateUrl();
});
}
}
}

View File

@@ -5,7 +5,6 @@ import 'package:dynamic_layouts/dynamic_layouts.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:flutter_hbb/models/ab_model.dart';
import 'package:flutter_hbb/models/peer_tab_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
@@ -271,33 +270,24 @@ class _PeersViewState extends State<_PeersView>
},
)
: peerCardUiType.value == PeerUiType.list
? DesktopScrollWrapper(
scrollController: _scrollController,
child: ListView.builder(
controller: _scrollController,
physics: DraggableNeverScrollableScrollPhysics(),
itemCount: peers.length,
itemBuilder: (BuildContext context, int index) {
return buildOnePeer(peers[index], false)
.marginOnly(
right: space,
top: index == 0 ? 0 : space / 2,
bottom: space / 2);
}),
? ListView.builder(
controller: _scrollController,
itemCount: peers.length,
itemBuilder: (BuildContext context, int index) {
return buildOnePeer(peers[index], false).marginOnly(
right: space,
top: index == 0 ? 0 : space / 2,
bottom: space / 2);
},
)
: DesktopScrollWrapper(
scrollController: _scrollController,
child: DynamicGridView.builder(
controller: _scrollController,
physics: DraggableNeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithWrapping(
mainAxisSpacing: space / 2,
crossAxisSpacing: space),
itemCount: peers.length,
itemBuilder: (BuildContext context, int index) {
return buildOnePeer(peers[index], false);
}),
));
: DynamicGridView.builder(
gridDelegate: SliverGridDelegateWithWrapping(
mainAxisSpacing: space / 2,
crossAxisSpacing: space),
itemCount: peers.length,
itemBuilder: (BuildContext context, int index) {
return buildOnePeer(peers[index], false);
}));
if (updateEvent == UpdateEvent.load) {
_curPeers.clear();

View File

@@ -244,10 +244,6 @@ const double kDesktopIconButtonSplashRadius = 20;
/// [kMinCursorSize] indicates min cursor (w, h)
const int kMinCursorSize = 12;
/// [kDefaultScrollAmountMultiplier] indicates how many rows can be scrolled after a minimum scroll action of mouse
const kDefaultScrollAmountMultiplier = 5.0;
const kDefaultScrollDuration = Duration(milliseconds: 50);
const kDefaultMouseWheelThrottleDuration = Duration(milliseconds: 50);
const kFullScreenEdgeSize = 0.0;
const kMaximizeEdgeSize = 0.0;
// Do not use kWindowResizeEdgeSize directly. Use `windowResizeEdgeSize` in `common.dart` instead.

View File

@@ -12,9 +12,9 @@ import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/pages/connection_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/server_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/plugin/ui_manager.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:get/get.dart';
@@ -40,7 +40,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
@override
bool get wantKeepAlive => true;
var updateUrl = '';
var systemError = '';
StreamSubscription? _uniLinksSubscription;
var svcStopped = false.obs;
@@ -87,7 +86,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
if (!isOutgoingOnly) buildIDBoard(context),
if (!isOutgoingOnly) buildPasswordBoard(context),
FutureBuilder<Widget>(
future: buildHelpCards(),
future: Future.value(
Obx(() => buildHelpCards(stateGlobal.updateUrl.value))),
builder: (_, data) {
if (data.hasData) {
if (isIncomingOnly) {
@@ -125,47 +125,43 @@ class _DesktopHomePageState extends State<DesktopHomePage>
child: Container(
width: isIncomingOnly ? 280.0 : 200.0,
color: Theme.of(context).colorScheme.background,
child: DesktopScrollWrapper(
scrollController: _leftPaneScrollController,
child: Stack(
children: [
SingleChildScrollView(
controller: _leftPaneScrollController,
physics: DraggableNeverScrollableScrollPhysics(),
child: Column(
key: _childKey,
children: children,
),
child: Stack(
children: [
SingleChildScrollView(
controller: _leftPaneScrollController,
child: Column(
key: _childKey,
children: children,
),
if (isOutgoingOnly)
Positioned(
bottom: 6,
left: 12,
child: Align(
alignment: Alignment.centerLeft,
child: InkWell(
child: Obx(
() => Icon(
Icons.settings,
color: _editHover.value
? textColor
: Colors.grey.withOpacity(0.5),
size: 22,
),
),
if (isOutgoingOnly)
Positioned(
bottom: 6,
left: 12,
child: Align(
alignment: Alignment.centerLeft,
child: InkWell(
child: Obx(
() => Icon(
Icons.settings,
color: _editHover.value
? textColor
: Colors.grey.withOpacity(0.5),
size: 22,
),
onTap: () => {
if (DesktopSettingPage.tabKeys.isNotEmpty)
{
DesktopSettingPage.switch2page(
DesktopSettingPage.tabKeys[0])
}
},
onHover: (value) => _editHover.value = value,
),
onTap: () => {
if (DesktopSettingPage.tabKeys.isNotEmpty)
{
DesktopSettingPage.switch2page(
DesktopSettingPage.tabKeys[0])
}
},
onHover: (value) => _editHover.value = value,
),
)
],
),
),
)
],
),
),
);
@@ -420,7 +416,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
);
}
Future<Widget> buildHelpCards() async {
Widget buildHelpCards(String updateUrl) {
if (!bind.isCustomClient() &&
updateUrl.isNotEmpty &&
!isCardClosed &&
@@ -674,20 +670,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
@override
void initState() {
super.initState();
if (!bind.isCustomClient()) {
platformFFI.registerEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
(Map<String, dynamic> evt) async {
if (evt['url'] is String) {
setState(() {
updateUrl = evt['url'];
});
}
});
Timer(const Duration(seconds: 1), () async {
bind.mainGetSoftwareUpdateUrl();
});
}
_updateTimer = periodic_immediate(const Duration(seconds: 1), () async {
await gFFI.serverModel.fetchID();
final error = await bind.mainGetError();

View File

@@ -11,15 +11,16 @@ import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/server_model.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:flutter_hbb/plugin/manager.dart';
import 'package:flutter_hbb/plugin/widgets/desktop_settings.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import '../../common/widgets/dialog.dart';
import '../../common/widgets/login.dart';
@@ -226,13 +227,11 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
Expanded(
child: Container(
color: Theme.of(context).scaffoldBackgroundColor,
child: DesktopScrollWrapper(
scrollController: controller,
child: PageView(
controller: controller,
physics: NeverScrollableScrollPhysics(),
children: _children(),
)),
child: PageView(
controller: controller,
physics: NeverScrollableScrollPhysics(),
children: _children(),
),
),
)
],
@@ -281,13 +280,10 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
Widget _listView({required List<_TabInfo> tabs}) {
final scrollController = ScrollController();
return DesktopScrollWrapper(
scrollController: scrollController,
child: ListView(
physics: DraggableNeverScrollableScrollPhysics(),
controller: scrollController,
children: tabs.map((tab) => _listItem(tab: tab)).toList(),
));
return ListView(
controller: scrollController,
children: tabs.map((tab) => _listItem(tab: tab)).toList(),
);
}
Widget _listItem({required _TabInfo tab}) {
@@ -349,22 +345,19 @@ class _GeneralState extends State<_General> {
@override
Widget build(BuildContext context) {
final scrollController = ScrollController();
return DesktopScrollWrapper(
scrollController: scrollController,
child: ListView(
physics: DraggableNeverScrollableScrollPhysics(),
controller: scrollController,
children: [
if (!isWeb) service(),
theme(),
_Card(title: 'Language', children: [language()]),
if (!isWeb) hwcodec(),
if (!isWeb) audio(context),
if (!isWeb) record(context),
if (!isWeb) WaylandCard(),
other()
],
).marginOnly(bottom: _kListViewBottomMargin));
return ListView(
controller: scrollController,
children: [
if (!isWeb) service(),
theme(),
_Card(title: 'Language', children: [language()]),
if (!isWeb) hwcodec(),
if (!isWeb) audio(context),
if (!isWeb) record(context),
if (!isWeb) WaylandCard(),
other()
],
).marginOnly(bottom: _kListViewBottomMargin);
}
Widget theme() {
@@ -705,29 +698,26 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return DesktopScrollWrapper(
scrollController: scrollController,
child: SingleChildScrollView(
physics: DraggableNeverScrollableScrollPhysics(),
controller: scrollController,
child: Column(
children: [
_lock(locked, 'Unlock Security Settings', () {
locked = false;
setState(() => {});
}),
AbsorbPointer(
absorbing: locked,
child: Column(children: [
permissions(context),
password(context),
_Card(title: '2FA', children: [tfa()]),
_Card(title: 'ID', children: [changeId()]),
more(context),
]),
),
],
)).marginOnly(bottom: _kListViewBottomMargin));
return SingleChildScrollView(
controller: scrollController,
child: Column(
children: [
_lock(locked, 'Unlock Security Settings', () {
locked = false;
setState(() => {});
}),
AbsorbPointer(
absorbing: locked,
child: Column(children: [
permissions(context),
password(context),
_Card(title: '2FA', children: [tfa()]),
_Card(title: 'ID', children: [changeId()]),
more(context),
]),
),
],
)).marginOnly(bottom: _kListViewBottomMargin);
}
Widget tfa() {
@@ -1374,112 +1364,82 @@ class _NetworkState extends State<_Network> with AutomaticKeepAliveClientMixin {
bool get wantKeepAlive => true;
bool locked = !isWeb && bind.mainIsInstalled();
final scrollController = ScrollController();
@override
Widget build(BuildContext context) {
super.build(context);
bool enabled = !locked;
final scrollController = ScrollController();
final hideServer =
bind.mainGetBuildinOption(key: kOptionHideServerSetting) == 'Y';
// TODO: support web proxy
final hideProxy =
isWeb || bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
return DesktopScrollWrapper(
scrollController: scrollController,
child: ListView(
controller: scrollController,
physics: DraggableNeverScrollableScrollPhysics(),
children: [
_lock(locked, 'Unlock Network Settings', () {
locked = false;
setState(() => {});
}),
AbsorbPointer(
absorbing: locked,
child: Column(children: [
if (!hideServer) server(enabled),
if (!hideProxy)
_Card(title: 'Proxy', children: [
_Button('Socks5/Http(s) Proxy', changeSocks5Proxy,
enabled: enabled),
]),
]),
),
]).marginOnly(bottom: _kListViewBottomMargin));
return ListView(controller: scrollController, children: [
_lock(locked, 'Unlock Network Settings', () {
locked = false;
setState(() => {});
}),
AbsorbPointer(
absorbing: locked,
child: Column(children: [
network(context),
]),
),
]).marginOnly(bottom: _kListViewBottomMargin);
}
server(bool enabled) {
// Simple temp wrapper for PR check
tmpWrapper() {
// Setting page is not modal, oldOptions should only be used when getting options, never when setting.
Map<String, dynamic> oldOptions = jsonDecode(bind.mainGetOptionsSync());
old(String key) {
return (oldOptions[key] ?? '').trim();
}
Widget network(BuildContext context) {
final hideServer =
bind.mainGetBuildinOption(key: kOptionHideServerSetting) == 'Y';
final hideProxy =
isWeb || bind.mainGetBuildinOption(key: kOptionHideProxySetting) == 'Y';
RxString idErrMsg = ''.obs;
RxString relayErrMsg = ''.obs;
RxString apiErrMsg = ''.obs;
var idController =
TextEditingController(text: old('custom-rendezvous-server'));
var relayController = TextEditingController(text: old('relay-server'));
var apiController = TextEditingController(text: old('api-server'));
var keyController = TextEditingController(text: old('key'));
final controllers = [
idController,
relayController,
apiController,
keyController,
];
final errMsgs = [
idErrMsg,
relayErrMsg,
apiErrMsg,
];
submit() async {
bool result = await setServerConfig(
null,
errMsgs,
ServerConfig(
idServer: idController.text,
relayServer: relayController.text,
apiServer: apiController.text,
key: keyController.text));
if (result) {
setState(() {});
showToast(translate('Successful'));
} else {
showToast(translate('Failed'));
}
}
bool secure = !enabled;
return _Card(
title: 'ID/Relay Server',
title_suffix: ServerConfigImportExportWidgets(controllers, errMsgs),
children: [
Column(
children: [
Obx(() => _LabeledTextField(context, 'ID Server', idController,
idErrMsg.value, enabled, secure)),
if (!isWeb)
Obx(() => _LabeledTextField(context, 'Relay Server',
relayController, relayErrMsg.value, enabled, secure)),
Obx(() => _LabeledTextField(context, 'API Server',
apiController, apiErrMsg.value, enabled, secure)),
_LabeledTextField(
context, 'Key', keyController, '', enabled, secure),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [_Button('Apply', submit, enabled: enabled)],
).marginOnly(top: 10),
],
)
]);
if (hideServer && hideProxy) {
return Offstage();
}
return tmpWrapper();
return _Card(
title: 'Network',
children: [
Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (!hideServer)
ListTile(
leading: Icon(Icons.dns_outlined, color: _accentColor),
title: Text(
translate('ID/Relay Server'),
style: TextStyle(fontSize: _kContentFontSize),
),
enabled: !locked,
onTap: () => showServerSettings(gFFI.dialogManager),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding: EdgeInsets.symmetric(horizontal: 16),
minLeadingWidth: 0,
horizontalTitleGap: 10,
),
if (!hideServer && !hideProxy)
Divider(height: 1, indent: 16, endIndent: 16),
if (!hideProxy)
ListTile(
leading:
Icon(Icons.network_ping_outlined, color: _accentColor),
title: Text(
translate('Socks5/Http(s) Proxy'),
style: TextStyle(fontSize: _kContentFontSize),
),
enabled: !locked,
onTap: changeSocks5Proxy,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding: EdgeInsets.symmetric(horizontal: 16),
minLeadingWidth: 0,
horizontalTitleGap: 10,
),
],
),
),
],
);
}
}
@@ -1494,19 +1454,14 @@ class _DisplayState extends State<_Display> {
@override
Widget build(BuildContext context) {
final scrollController = ScrollController();
return DesktopScrollWrapper(
scrollController: scrollController,
child: ListView(
controller: scrollController,
physics: DraggableNeverScrollableScrollPhysics(),
children: [
viewStyle(context),
scrollStyle(context),
imageQuality(context),
codec(context),
if (!isWeb) privacyModeImpl(context),
other(context),
]).marginOnly(bottom: _kListViewBottomMargin));
return ListView(controller: scrollController, children: [
viewStyle(context),
scrollStyle(context),
imageQuality(context),
codec(context),
if (!isWeb) privacyModeImpl(context),
other(context),
]).marginOnly(bottom: _kListViewBottomMargin);
}
Widget viewStyle(BuildContext context) {
@@ -1729,15 +1684,12 @@ class _AccountState extends State<_Account> {
@override
Widget build(BuildContext context) {
final scrollController = ScrollController();
return DesktopScrollWrapper(
scrollController: scrollController,
child: ListView(
physics: DraggableNeverScrollableScrollPhysics(),
controller: scrollController,
children: [
_Card(title: 'Account', children: [accountAction(), useInfo()]),
],
).marginOnly(bottom: _kListViewBottomMargin));
return ListView(
controller: scrollController,
children: [
_Card(title: 'Account', children: [accountAction(), useInfo()]),
],
).marginOnly(bottom: _kListViewBottomMargin);
}
Widget accountAction() {
@@ -1834,18 +1786,14 @@ class _PluginState extends State<_Plugin> {
Widget build(BuildContext context) {
bind.pluginListReload();
final scrollController = ScrollController();
return DesktopScrollWrapper(
scrollController: scrollController,
child: ChangeNotifierProvider.value(
value: pluginManager,
child: Consumer<PluginManager>(builder: (context, model, child) {
return ListView(
physics: DraggableNeverScrollableScrollPhysics(),
controller: scrollController,
children: model.plugins.map((entry) => pluginCard(entry)).toList(),
).marginOnly(bottom: _kListViewBottomMargin);
}),
),
return ChangeNotifierProvider.value(
value: pluginManager,
child: Consumer<PluginManager>(builder: (context, model, child) {
return ListView(
controller: scrollController,
children: model.plugins.map((entry) => pluginCard(entry)).toList(),
).marginOnly(bottom: _kListViewBottomMargin);
}),
);
}
@@ -1897,75 +1845,72 @@ class _AboutState extends State<_About> {
final fingerprint = data['fingerprint'].toString();
const linkStyle = TextStyle(decoration: TextDecoration.underline);
final scrollController = ScrollController();
return DesktopScrollWrapper(
scrollController: scrollController,
child: SingleChildScrollView(
controller: scrollController,
physics: DraggableNeverScrollableScrollPhysics(),
child: _Card(title: translate('About RustDesk'), children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 8.0,
),
SelectionArea(
child: Text('${translate('Version')}: $version')
.marginSymmetric(vertical: 4.0)),
SelectionArea(
child: Text('${translate('Build Date')}: $buildDate')
.marginSymmetric(vertical: 4.0)),
if (!isWeb)
SelectionArea(
child: Text('${translate('Fingerprint')}: $fingerprint')
.marginSymmetric(vertical: 4.0)),
InkWell(
onTap: () {
launchUrlString('https://rustdesk.com/privacy.html');
},
child: Text(
translate('Privacy Statement'),
style: linkStyle,
).marginSymmetric(vertical: 4.0)),
InkWell(
onTap: () {
launchUrlString('https://rustdesk.com');
},
child: Text(
translate('Website'),
style: linkStyle,
).marginSymmetric(vertical: 4.0)),
Container(
decoration: const BoxDecoration(color: Color(0xFF2c8cff)),
padding:
const EdgeInsets.symmetric(vertical: 24, horizontal: 8),
child: SelectionArea(
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Copyright © ${DateTime.now().toString().substring(0, 4)} Purslane Ltd.\n$license',
style: const TextStyle(color: Colors.white),
),
Text(
translate('Slogan_tip'),
style: TextStyle(
fontWeight: FontWeight.w800,
color: Colors.white),
)
],
return SingleChildScrollView(
controller: scrollController,
child: _Card(title: translate('About RustDesk'), children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 8.0,
),
SelectionArea(
child: Text('${translate('Version')}: $version')
.marginSymmetric(vertical: 4.0)),
SelectionArea(
child: Text('${translate('Build Date')}: $buildDate')
.marginSymmetric(vertical: 4.0)),
if (!isWeb)
SelectionArea(
child: Text('${translate('Fingerprint')}: $fingerprint')
.marginSymmetric(vertical: 4.0)),
InkWell(
onTap: () {
launchUrlString('https://rustdesk.com/privacy.html');
},
child: Text(
translate('Privacy Statement'),
style: linkStyle,
).marginSymmetric(vertical: 4.0)),
InkWell(
onTap: () {
launchUrlString('https://rustdesk.com');
},
child: Text(
translate('Website'),
style: linkStyle,
).marginSymmetric(vertical: 4.0)),
Container(
decoration: const BoxDecoration(color: Color(0xFF2c8cff)),
padding:
const EdgeInsets.symmetric(vertical: 24, horizontal: 8),
child: SelectionArea(
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Copyright © ${DateTime.now().toString().substring(0, 4)} Purslane Ltd.\n$license',
style: const TextStyle(color: Colors.white),
),
),
],
)),
).marginSymmetric(vertical: 4.0)
],
).marginOnly(left: _kContentHMargin)
]),
));
Text(
translate('Slogan_tip'),
style: TextStyle(
fontWeight: FontWeight.w800,
color: Colors.white),
)
],
),
),
],
)),
).marginSymmetric(vertical: 4.0)
],
).marginOnly(left: _kContentHMargin)
]),
);
});
}
}
@@ -2283,26 +2228,39 @@ _LabeledTextField(
String errorText,
bool enabled,
bool secure) {
return Row(
return Table(
columnWidths: const {
0: FixedColumnWidth(150),
1: FlexColumnWidth(),
},
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: [
ConstrainedBox(
constraints: const BoxConstraints(minWidth: 140),
child: Text(
'${translate(label)}:',
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16, color: disabledTextColor(context, enabled)),
).marginOnly(right: 10)),
Expanded(
child: TextField(
TableRow(
children: [
Padding(
padding: const EdgeInsets.only(right: 10),
child: Text(
'${translate(label)}:',
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16,
color: disabledTextColor(context, enabled),
),
),
),
TextField(
controller: controller,
enabled: enabled,
obscureText: secure,
autocorrect: false,
decoration: InputDecoration(
errorText: errorText.isNotEmpty ? errorText : null),
errorText: errorText.isNotEmpty ? errorText : null,
),
style: TextStyle(
color: disabledTextColor(context, enabled),
)),
),
),
],
),
],
).marginOnly(bottom: 8);

View File

@@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart';
import 'package:flutter_hbb/models/state_model.dart';
import '../../consts.dart';
@@ -742,12 +741,6 @@ class _ImagePaintState extends State<ImagePaint> {
ScrollController horizontal,
ScrollController vertical,
) {
final scrollConfig = CustomMouseWheelScrollConfig(
scrollDuration: kDefaultScrollDuration,
scrollCurve: Curves.linearToEaseOut,
mouseWheelTurnsThrottleTimeMs:
kDefaultMouseWheelThrottleDuration.inMilliseconds,
scrollAmountMultiplier: kDefaultScrollAmountMultiplier);
var widget = child;
if (layoutSize.width < size.width) {
widget = ScrollConfiguration(
@@ -793,36 +786,26 @@ class _ImagePaintState extends State<ImagePaint> {
);
}
if (layoutSize.width < size.width) {
widget = ImprovedScrolling(
scrollController: horizontal,
enableCustomMouseWheelScrolling: cursorOverImage.isFalse,
customMouseWheelScrollConfig: scrollConfig,
child: RawScrollbar(
thickness: kScrollbarThickness,
thumbColor: Colors.grey,
controller: horizontal,
thumbVisibility: false,
trackVisibility: false,
notificationPredicate: layoutSize.height < size.height
? (notification) => notification.depth == 1
: defaultScrollNotificationPredicate,
child: widget,
),
widget = RawScrollbar(
thickness: kScrollbarThickness,
thumbColor: Colors.grey,
controller: horizontal,
thumbVisibility: false,
trackVisibility: false,
notificationPredicate: layoutSize.height < size.height
? (notification) => notification.depth == 1
: defaultScrollNotificationPredicate,
child: widget,
);
}
if (layoutSize.height < size.height) {
widget = ImprovedScrolling(
scrollController: vertical,
enableCustomMouseWheelScrolling: cursorOverImage.isFalse,
customMouseWheelScrollConfig: scrollConfig,
child: RawScrollbar(
thickness: kScrollbarThickness,
thumbColor: Colors.grey,
controller: vertical,
thumbVisibility: false,
trackVisibility: false,
child: widget,
),
widget = RawScrollbar(
thickness: kScrollbarThickness,
thumbColor: Colors.grey,
controller: vertical,
thumbVisibility: false,
trackVisibility: false,
child: widget,
);
}

View File

@@ -1,27 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_hbb/consts.dart';
import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart';
class DesktopScrollWrapper extends StatelessWidget {
final ScrollController scrollController;
final Widget child;
const DesktopScrollWrapper(
{Key? key, required this.scrollController, required this.child})
: super(key: key);
@override
Widget build(BuildContext context) {
return ImprovedScrolling(
scrollController: scrollController,
enableCustomMouseWheelScrolling: true,
// enableKeyboardScrolling: true, // strange behavior on mac
customMouseWheelScrollConfig: CustomMouseWheelScrollConfig(
scrollDuration: kDefaultScrollDuration,
scrollCurve: Curves.linearToEaseOut,
mouseWheelTurnsThrottleTimeMs:
kDefaultMouseWheelThrottleDuration.inMilliseconds,
scrollAmountMultiplier: kDefaultScrollAmountMultiplier),
child: child,
);
}
}

View File

@@ -120,6 +120,7 @@ Future<void> initEnv(String appType) async {
void runMainApp(bool startService) async {
// register uni links
await initEnv(kAppTypeMain);
checkUpdate();
// trigger connection status updater
await bind.mainCheckConnectStatus();
if (startService) {
@@ -156,6 +157,7 @@ void runMainApp(bool startService) async {
void runMobileApp() async {
await initEnv(kAppTypeMain);
checkUpdate();
if (isAndroid) androidChannelInit();
if (isAndroid) platformFFI.syncAndroidServiceAppDirConfigPath();
draggablePositions.load();
@@ -483,7 +485,16 @@ class _AppState extends State<App> with WidgetsBindingObserver {
child = keyListenerBuilder(context, child);
}
if (isLinux) {
child = buildVirtualWindowFrame(context, child);
// `(!(isLinuxMateDesktop || isLinuxMint))` is not used here for clarity.
// `isLinuxMint` will call ffi function.
if (!isLinuxMateDesktop) {
if (!isLinuxMint) {
debugPrint(
'Linux distro is not linuxmint, and desktop is not mate, '
'so we build virtual window frame.');
child = buildVirtualWindowFrame(context, child);
}
}
}
return child;
},

View File

@@ -4,6 +4,7 @@ import 'package:auto_size_text_field/auto_size_text_field.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/formatter/id_formatter.dart';
import 'package:flutter_hbb/common/widgets/connection_page_title.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -40,8 +41,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
final _idController = IDTextEditingController();
final RxBool _idEmpty = true.obs;
/// Update url. If it's not null, means an update is available.
var _updateUrl = '';
List<Peer> peers = [];
bool isPeersLoading = false;
@@ -72,22 +71,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
}
});
}
if (isAndroid) {
if (!bind.isCustomClient()) {
platformFFI.registerEventHandler(
kCheckSoftwareUpdateFinish, kCheckSoftwareUpdateFinish,
(Map<String, dynamic> evt) async {
if (evt['url'] is String) {
setState(() {
_updateUrl = evt['url'];
});
}
});
Timer(const Duration(seconds: 1), () async {
bind.mainGetSoftwareUpdateUrl();
});
}
}
}
@override
@@ -97,7 +80,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
slivers: [
SliverList(
delegate: SliverChildListDelegate([
if (!bind.isCustomClient()) _buildUpdateUI(),
if (!bind.isCustomClient())
Obx(() => _buildUpdateUI(stateGlobal.updateUrl.value)),
_buildRemoteIDTextField(),
])),
SliverFillRemaining(
@@ -116,13 +100,21 @@ class _ConnectionPageState extends State<ConnectionPage> {
}
/// UI for software update.
/// If [_updateUrl] is not empty, shows a button to update the software.
Widget _buildUpdateUI() {
return _updateUrl.isEmpty
/// If _updateUrl] is not empty, shows a button to update the software.
Widget _buildUpdateUI(String updateUrl) {
return updateUrl.isEmpty
? const SizedBox(height: 0)
: InkWell(
onTap: () async {
final url = 'https://rustdesk.com/download';
// https://pub.dev/packages/url_launcher#configuration
// https://developer.android.com/training/package-visibility/use-cases#open-urls-custom-tabs
//
// `await launchUrl(Uri.parse(url))` can also run if skip
// 1. The following check
// 2. `<action android:name="android.support.customtabs.action.CustomTabsService" />` in AndroidManifest.xml
//
// But it is better to add the check.
if (await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url));
}

View File

@@ -872,6 +872,8 @@ class _KeyHelpToolsState extends State<KeyHelpTools> {
final pi = gFFI.ffiModel.pi;
final isMac = pi.platform == kPeerPlatformMacOS;
final isWin = pi.platform == kPeerPlatformWindows;
final isLinux = pi.platform == kPeerPlatformLinux;
final modifiers = <Widget>[
wrap('Ctrl ', () {
setState(() => inputModel.ctrl = !inputModel.ctrl);
@@ -952,6 +954,28 @@ class _KeyHelpToolsState extends State<KeyHelpTools> {
wrap('PgDn', () {
inputModel.inputKey('VK_NEXT');
}),
// to-do: support PrtScr on Mac
if (isWin || isLinux)
wrap('PrtScr', () {
inputModel.inputKey('VK_SNAPSHOT');
}),
if (isWin || isLinux)
wrap('ScrollLock', () {
inputModel.inputKey('VK_SCROLL');
}),
if (isWin || isLinux)
wrap('Pause', () {
inputModel.inputKey('VK_PAUSE');
}),
if (isWin || isLinux)
// Maybe it's better to call it "Menu"
// https://en.wikipedia.org/wiki/Menu_key
wrap('Menu', () {
inputModel.inputKey('Apps');
}),
wrap('Enter', () {
inputModel.inputKey('VK_ENTER');
}),
SizedBox(width: 9999),
wrap('', () {
inputModel.inputKey('VK_LEFT');

View File

@@ -5,6 +5,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
import 'package:flutter_hbb/models/state_model.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
import 'package:settings_ui/settings_ui.dart';
@@ -70,6 +71,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
false; //androidVersion >= 26; // remove because not work on every device
var _ignoreBatteryOpt = false;
var _enableStartOnBoot = false;
var _checkUpdateOnStartup = false;
var _floatingWindowDisabled = false;
var _keepScreenOn = KeepScreenOn.duringControlled; // relay on floating window
var _enableAbr = false;
@@ -154,6 +156,13 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_enableStartOnBoot = enableStartOnBoot;
}
var checkUpdateOnStartup =
mainGetLocalBoolOptionSync(kOptionEnableCheckUpdate);
if (checkUpdateOnStartup != _checkUpdateOnStartup) {
update = true;
_checkUpdateOnStartup = checkUpdateOnStartup;
}
var floatingWindowDisabled =
bind.mainGetLocalOption(key: kOptionDisableFloatingWindow) == "Y" ||
!await AndroidPermissionManager.check(kSystemAlertWindow);
@@ -552,6 +561,22 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, toValue);
}));
if (!bind.isCustomClient()) {
enhancementsTiles.add(
SettingsTile.switchTile(
initialValue: _checkUpdateOnStartup,
title:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(translate('Check for software update on startup')),
]),
onToggle: (bool toValue) async {
await mainSetLocalBoolOption(kOptionEnableCheckUpdate, toValue);
setState(() => _checkUpdateOnStartup = toValue);
},
),
);
}
onFloatingWindowChanged(bool toValue) async {
if (toValue) {
if (!await AndroidPermissionManager.check(kSystemAlertWindow)) {
@@ -828,11 +853,6 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
}
}
void showServerSettings(OverlayDialogManager dialogManager) async {
Map<String, dynamic> options = jsonDecode(await bind.mainGetOptions());
showServerSettingsWithValue(ServerConfig.fromOptions(options), dialogManager);
}
void showLanguageSettings(OverlayDialogManager dialogManager) async {
try {
final langs = json.decode(await bind.mainGetLangs()) as List<dynamic>;

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
import 'package:flutter_hbb/common/widgets/toolbar.dart';
@@ -146,6 +147,16 @@ void setTemporaryPasswordLengthDialog(
}, backDismiss: true, clickMaskDismiss: true);
}
void showServerSettings(OverlayDialogManager dialogManager) async {
Map<String, dynamic> options = {};
try {
options = jsonDecode(await bind.mainGetOptions());
} catch (e) {
print("Invalid server config: $e");
}
showServerSettingsWithValue(ServerConfig.fromOptions(options), dialogManager);
}
void showServerSettingsWithValue(
ServerConfig serverConfig, OverlayDialogManager dialogManager) async {
var isInProgress = false;
@@ -184,6 +195,43 @@ void showServerSettingsWithValue(
return ret;
}
Widget buildField(
String label, TextEditingController controller, String errorMsg,
{String? Function(String?)? validator, bool autofocus = false}) {
if (isDesktop || isWeb) {
return Row(
children: [
SizedBox(
width: 120,
child: Text(label),
),
SizedBox(width: 8),
Expanded(
child: TextFormField(
controller: controller,
decoration: InputDecoration(
errorText: errorMsg.isEmpty ? null : errorMsg,
contentPadding:
EdgeInsets.symmetric(horizontal: 8, vertical: 12),
),
validator: validator,
autofocus: autofocus,
),
),
],
);
}
return TextFormField(
controller: controller,
decoration: InputDecoration(
labelText: label,
errorText: errorMsg.isEmpty ? null : errorMsg,
),
validator: validator,
);
}
return CustomAlertDialog(
title: Row(
children: [
@@ -191,56 +239,45 @@ void showServerSettingsWithValue(
...ServerConfigImportExportWidgets(controllers, errMsgs),
],
),
content: Form(
content: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 500),
child: Form(
child: Obx(() => Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
controller: idCtrl,
decoration: InputDecoration(
labelText: translate('ID Server'),
errorText: idServerMsg.value.isEmpty
? null
: idServerMsg.value),
)
] +
[
if (isAndroid)
TextFormField(
controller: relayCtrl,
decoration: InputDecoration(
labelText: translate('Relay Server'),
errorText: relayServerMsg.value.isEmpty
? null
: relayServerMsg.value),
)
] +
[
TextFormField(
controller: apiCtrl,
decoration: InputDecoration(
labelText: translate('API Server'),
),
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (v) {
if (v != null && v.isNotEmpty) {
if (!(v.startsWith('http://') ||
v.startsWith("https://"))) {
return translate("invalid_http");
}
mainAxisSize: MainAxisSize.min,
children: [
buildField(translate('ID Server'), idCtrl, idServerMsg.value,
autofocus: true),
SizedBox(height: 8),
if (!isIOS && !isWeb) ...[
buildField(translate('Relay Server'), relayCtrl,
relayServerMsg.value),
SizedBox(height: 8),
],
buildField(
translate('API Server'),
apiCtrl,
apiServerMsg.value,
validator: (v) {
if (v != null && v.isNotEmpty) {
if (!(v.startsWith('http://') ||
v.startsWith("https://"))) {
return translate("invalid_http");
}
return null;
},
}
return null;
},
),
SizedBox(height: 8),
buildField('Key', keyCtrl, ''),
if (isInProgress)
Padding(
padding: EdgeInsets.only(top: 8),
child: LinearProgressIndicator(),
),
TextFormField(
controller: keyCtrl,
decoration: InputDecoration(
labelText: 'Key',
),
),
// NOT use Offstage to wrap LinearProgressIndicator
if (isInProgress) const LinearProgressIndicator(),
]))),
],
)),
),
),
actions: [
dialogButton('Cancel', onPressed: () {
close();

View File

@@ -648,6 +648,9 @@ class AbModel {
}
Color getCurrentAbTagColor(String tag) {
if (tag == kUntagged) {
return MyTheme.accent;
}
int? colorValue = current.tagColors[tag];
if (colorValue != null) {
return Color(colorValue);

View File

@@ -261,6 +261,27 @@ class FileModel {
debugPrint("Failed to decode onSelectedFiles: $e");
}
}
void sendEmptyDirs(dynamic obj) {
late final List<dynamic> emptyDirs;
try {
emptyDirs = jsonDecode(obj['dirs'] as String);
} catch (e) {
debugPrint("Failed to decode sendEmptyDirs: $e");
}
final otherSideData = remoteController.directoryData();
final toPath = otherSideData.directory.path;
final isPeerWindows = otherSideData.options.isWindows;
final isLocalWindows = isWindows || isWebOnWindows;
for (var dir in emptyDirs) {
if (isLocalWindows != isPeerWindows) {
dir = PathUtil.convert(dir, isLocalWindows, isPeerWindows);
}
var peerPath = PathUtil.join(toPath, dir, isPeerWindows);
remoteController.createDirWithRemote(peerPath, true);
}
}
}
class DirectoryData {
@@ -502,8 +523,9 @@ class FileController {
"path: ${from.path}, toPath: $toPath, to: ${PathUtil.join(toPath, from.name, isWindows)}");
}
if (!isLocal &&
versionCmp(rootState.target!.ffiModel.pi.version, '1.3.3') < 0) {
if (isWeb ||
(!isLocal &&
versionCmp(rootState.target!.ffiModel.pi.version, '1.3.3') < 0)) {
return;
}
@@ -1506,6 +1528,12 @@ class PathUtil {
return pathUtil.split(path);
}
static String convert(String path, bool isMainWindows, bool isOtherWindows) {
final mainPathUtil = isMainWindows ? windowsContext : posixContext;
final otherPathUtil = isOtherWindows ? windowsContext : posixContext;
return otherPathUtil.joinAll(mainPathUtil.split(path));
}
static String dirname(String path, bool isWindows) {
final pathUtil = isWindows ? windowsContext : posixContext;
return pathUtil.dirname(path);

View File

@@ -1080,7 +1080,7 @@ class InputModel {
onExit: true,
);
static int tryGetNearestRange(int v, int min, int max, int n) {
static double tryGetNearestRange(double v, double min, double max, double n) {
if (v < min && v >= min - n) {
v = min;
}
@@ -1138,8 +1138,8 @@ class InputModel {
return;
}
evtValue = {
'x': pos.x,
'y': pos.y,
'x': pos.x.toInt(),
'y': pos.y.toInt(),
};
}
@@ -1221,8 +1221,8 @@ class InputModel {
evt['x'] = '0';
evt['y'] = '0';
} else {
evt['x'] = '${pos.x}';
evt['y'] = '${pos.y}';
evt['x'] = '${pos.x.toInt()}';
evt['y'] = '${pos.y.toInt()}';
}
Map<int, String> mapButtons = {
@@ -1362,31 +1362,27 @@ class InputModel {
y = pos.dy;
}
var evtX = 0;
var evtY = 0;
try {
evtX = x.round();
evtY = y.round();
} catch (e) {
debugPrintStack(label: 'canvas.scale value ${canvas.scale}, $e');
return null;
}
return InputModel.getPointInRemoteRect(
true, peerPlatform, kind, evtType, evtX, evtY, rect,
true, peerPlatform, kind, evtType, x, y, rect,
buttons: buttons);
}
static Point? getPointInRemoteRect(bool isLocalDesktop, String? peerPlatform,
String kind, String evtType, int evtX, int evtY, Rect rect,
static Point<double>? getPointInRemoteRect(
bool isLocalDesktop,
String? peerPlatform,
String kind,
String evtType,
double evtX,
double evtY,
Rect rect,
{int buttons = kPrimaryMouseButton}) {
int minX = rect.left.toInt();
double minX = rect.left;
// https://github.com/rustdesk/rustdesk/issues/6678
// For Windows, [0,maxX], [0,maxY] should be set to enable window snapping.
int maxX = (rect.left + rect.width).toInt() -
double maxX = (rect.left + rect.width) -
(peerPlatform == kPeerPlatformWindows ? 0 : 1);
int minY = rect.top.toInt();
int maxY = (rect.top + rect.height).toInt() -
double minY = rect.top;
double maxY = (rect.top + rect.height) -
(peerPlatform == kPeerPlatformWindows ? 0 : 1);
evtX = InputModel.tryGetNearestRange(evtX, minX, maxX, 5);
evtY = InputModel.tryGetNearestRange(evtY, minY, maxY, 5);

View File

@@ -402,6 +402,10 @@ class FfiModel with ChangeNotifier {
if (isWeb) {
parent.target?.fileModel.onSelectedFiles(evt);
}
} else if (name == "send_emptry_dirs") {
if (isWeb) {
parent.target?.fileModel.sendEmptyDirs(evt);
}
} else if (name == "record_status") {
if (desktopType == DesktopType.remote || isMobile) {
parent.target?.recordingModel.updateStatus(evt['start'] == 'true');
@@ -1268,7 +1272,9 @@ class ImageModel with ChangeNotifier {
rgba,
rect?.width.toInt() ?? 0,
rect?.height.toInt() ?? 0,
isWeb ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888,
isWeb | isWindows | isLinux
? ui.PixelFormat.rgba8888
: ui.PixelFormat.bgra8888,
);
if (parent.target?.id != pid) return;
await update(image);
@@ -2184,7 +2190,7 @@ class CursorModel with ChangeNotifier {
if (dx == 0 && dy == 0) return;
Point? newPos;
Point<double>? newPos;
final rect = parent.target?.ffiModel.rect;
if (rect == null) {
// unreachable
@@ -2195,8 +2201,8 @@ class CursorModel with ChangeNotifier {
parent.target?.ffiModel.pi.platform,
kPointerEventKindMouse,
kMouseEventTypeDefault,
(_x + dx).toInt(),
(_y + dy).toInt(),
_x + dx,
_y + dy,
rect,
buttons: kPrimaryButton);
if (newPos == null) {
@@ -2204,8 +2210,8 @@ class CursorModel with ChangeNotifier {
}
dx = newPos.x - _x;
dy = newPos.y - _y;
_x = newPos.x.toDouble();
_y = newPos.y.toDouble();
_x = newPos.x;
_y = newPos.y;
if (tryMoveCanvasX && dx != 0) {
parent.target?.canvasModel.panX(-dx * scale);
}

View File

@@ -25,6 +25,8 @@ class StateGlobal {
final isPortrait = false.obs;
final updateUrl = ''.obs;
String _inputSource = '';
// Use for desktop -> remote toolbar -> resolution

View File

@@ -1801,6 +1801,26 @@ class RustdeskImpl {
throw UnimplementedError("mainMaxEncryptLen");
}
bool mainAudioSupportLoopback({dynamic hint}) {
return false;
}
Future<String> sessionReadLocalEmptyDirsRecursiveSync(
{required UuidValue sessionId,
required String path,
required bool includeHidden,
dynamic hint}) {
throw UnimplementedError("sessionReadLocalEmptyDirsRecursiveSync");
}
Future<void> sessionReadRemoteEmptyDirsRecursiveSync(
{required UuidValue sessionId,
required String path,
required bool includeHidden,
dynamic hint}) {
throw UnimplementedError("sessionReadRemoteEmptyDirsRecursiveSync");
}
Future<void> sessionRenameFile(
{required UuidValue sessionId,
required int actId,
@@ -1828,5 +1848,9 @@ class RustdeskImpl {
throw UnimplementedError("sessionGetConnToken");
}
String getOsDistroInfo({dynamic hint}) {
return '';
}
void dispose() {}
}

View File

@@ -16,6 +16,9 @@ G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
extern bool gIsConnectionManager;
GtkWidget *find_gl_area(GtkWidget *widget);
void try_set_transparent(GtkWindow* window, GdkScreen* screen, FlView* view);
// Implements GApplication::activate.
static void my_application_activate(GApplication* application) {
MyApplication* self = MY_APPLICATION(application);
@@ -39,9 +42,10 @@ static void my_application_activate(GApplication* application) {
// If running on Wayland assume the header bar will work (may need changing
// if future cases occur).
gboolean use_header_bar = TRUE;
GdkScreen* screen = NULL;
#ifdef GDK_WINDOWING_X11
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
screen = gtk_window_get_screen(window);
if (screen != NULL && GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
@@ -76,6 +80,8 @@ static void my_application_activate(GApplication* application) {
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
try_set_transparent(window, screen, view);
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
gtk_widget_grab_focus(GTK_WIDGET(view));
@@ -121,3 +127,106 @@ MyApplication* my_application_new() {
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
}
GtkWidget *find_gl_area(GtkWidget *widget)
{
if (GTK_IS_GL_AREA(widget)) {
return widget;
}
if (GTK_IS_CONTAINER(widget)) {
GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
for (GList *iter = children; iter != NULL; iter = g_list_next(iter)) {
GtkWidget *child = GTK_WIDGET(iter->data);
GtkWidget *gl_area = find_gl_area(child);
if (gl_area != NULL) {
g_list_free(children);
return gl_area;
}
}
g_list_free(children);
}
return NULL;
}
bool is_linux_mint()
{
bool is_mint = false;
char line[256];
FILE *fp = fopen("/etc/os-release", "r");
if (fp == NULL) {
return false;
}
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, "ID=linuxmint") != NULL) {
is_mint = true;
break;
}
}
fclose(fp);
return is_mint;
}
bool is_desktop_mate()
{
const char* desktop = NULL;
desktop = getenv("XDG_CURRENT_DESKTOP");
printf("Linux desktop, XDG_CURRENT_DESKTOP: %s\n", desktop == NULL ? "" : desktop);
if (desktop == NULL) {
desktop = getenv("XDG_SESSION_DESKTOP");
printf("Linux desktop, XDG_SESSION_DESKTOP: %s\n", desktop == NULL ? "" : desktop);
}
if (desktop == NULL) {
desktop = getenv("DESKTOP_SESSION");
printf("Linux desktop, DESKTOP_SESSION: %s\n", desktop == NULL ? "" : desktop);
}
if (desktop != NULL && strcasecmp(desktop, "mate") == 0) {
return true;
}
return false;
}
bool skip_setting_transparent()
{
if (is_desktop_mate()) {
printf("Linux desktop, MATE\n");
return true;
}
if (is_linux_mint()) {
printf("Linux desktop, Linux Mint\n");
return true;
}
return false;
}
// https://github.com/flutter/flutter/issues/152154
// Remove this workaround when flutter version is updated.
void try_set_transparent(GtkWindow* window, GdkScreen* screen, FlView* view)
{
GtkWidget *gl_area = NULL;
if (skip_setting_transparent()) {
printf("Skip setting transparent\n");
return;
}
printf("Try setting transparent\n");
gl_area = find_gl_area(GTK_WIDGET(view));
if (gl_area != NULL) {
gtk_gl_area_set_has_alpha(GTK_GL_AREA(gl_area), TRUE);
}
if (screen != NULL) {
GdkVisual *visual = NULL;
gtk_widget_set_app_paintable(GTK_WIDGET(window), TRUE);
visual = gdk_screen_get_rgba_visual(screen);
if (visual != NULL && gdk_screen_is_composited(screen)) {
gtk_widget_set_visual(GTK_WIDGET(window), visual);
}
}
}

View File

@@ -335,7 +335,7 @@ packages:
description:
path: "."
ref: HEAD
resolved-ref: "519350f1f40746798299e94786197d058353bac9"
resolved-ref: "4f562ab49d289cfa36bfda7cff12746ec0200033"
url: "https://github.com/rustdesk-org/rustdesk_desktop_multi_window"
source: git
version: "0.1.0"
@@ -530,15 +530,6 @@ packages:
url: "https://github.com/rustdesk-org/flutter_gpu_texture_renderer"
source: git
version: "0.0.1"
flutter_improved_scrolling:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: "62f09545149f320616467c306c8c5f71714a18e6"
url: "https://github.com/rustdesk-org/flutter_improved_scrolling"
source: git
version: "0.0.3"
flutter_keyboard_visibility:
dependency: "direct main"
description:
@@ -1277,10 +1268,11 @@ packages:
texture_rgba_renderer:
dependency: "direct main"
description:
name: texture_rgba_renderer
sha256: cb048abdd800468ca40749ca10d1db9d1e6a055d1cde6234c05191293f0c7d61
url: "https://pub.dev"
source: hosted
path: "."
ref: "42797e0f03141dc2b585f76c64a13974508058b4"
resolved-ref: "42797e0f03141dc2b585f76c64a13974508058b4"
url: "https://github.com/rustdesk-org/flutter_texture_rgba_renderer"
source: git
version: "0.0.16"
timing:
dependency: transitive

View File

@@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers
version: 1.3.3+52
version: 1.3.4+53
environment:
sdk: '^3.1.0'
@@ -71,13 +71,6 @@ dependencies:
debounce_throttle: ^2.0.0
file_picker: ^5.1.0
flutter_svg: ^2.0.5
flutter_improved_scrolling:
# currently, we use flutter 3.10.0+.
#
# for flutter 3.0.5, please use official version(just comment code below).
# if build rustdesk by flutter >=3.3, please use our custom pub below (uncomment code below).
git:
url: https://github.com/rustdesk-org/flutter_improved_scrolling
uni_links:
git:
url: https://github.com/rustdesk-org/uni_links
@@ -91,7 +84,10 @@ dependencies:
password_strength: ^0.2.0
flutter_launcher_icons: ^0.13.1
flutter_keyboard_visibility: ^5.4.0
texture_rgba_renderer: ^0.0.16
texture_rgba_renderer:
git:
url: https://github.com/rustdesk-org/flutter_texture_rgba_renderer
ref: 42797e0f03141dc2b585f76c64a13974508058b4
percent_indicator: ^4.2.2
dropdown_button2: ^2.0.0
flutter_gpu_texture_renderer:

View File

@@ -345,7 +345,7 @@ fn convert_to_tfc_key(key: Key) -> Option<TFC_Key> {
Key::Numpad9 => TFC_Key::N9,
Key::Decimal => TFC_Key::NumpadDecimal,
Key::Clear => TFC_Key::NumpadClear,
Key::Pause => TFC_Key::PlayPause,
Key::Pause => TFC_Key::Pause,
Key::Print => TFC_Key::Print,
Key::Snapshot => TFC_Key::PrintScreen,
Key::Insert => TFC_Key::Insert,

View File

@@ -13,22 +13,35 @@ pub const XDG_CURRENT_DESKTOP: &str = "XDG_CURRENT_DESKTOP";
pub struct Distro {
pub name: String,
pub id: String,
pub version_id: String,
}
impl Distro {
fn new() -> Self {
// to-do:
// 1. Remove `run_cmds`, read file once
// 2. Add more distro infos
let name = run_cmds("awk -F'=' '/^NAME=/ {print $2}' /etc/os-release")
.unwrap_or_default()
.trim()
.trim_matches('"')
.to_string();
let id = run_cmds("awk -F'=' '/^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")
.unwrap_or_default()
.trim()
.trim_matches('"')
.to_string();
Self { name, version_id }
Self {
name,
id,
version_id,
}
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "rustdesk-portable-packer"
version = "1.3.3"
version = "1.3.4"
edition = "2021"
description = "RustDesk Remote Desktop"

View File

@@ -193,15 +193,11 @@ impl EncoderApi for HwRamEncoder {
}
fn support_abr(&self) -> bool {
["qsv", "vaapi", "mediacodec", "videotoolbox"]
.iter()
.all(|&x| !self.config.name.contains(x))
["qsv", "vaapi"].iter().all(|&x| !self.config.name.contains(x))
}
fn support_changing_quality(&self) -> bool {
["vaapi", "mediacodec", "videotoolbox"]
.iter()
.all(|&x| !self.config.name.contains(x))
["vaapi"].iter().all(|&x| !self.config.name.contains(x))
}
fn latency_free(&self) -> bool {

View File

@@ -1,5 +1,5 @@
pkgname=rustdesk
pkgver=1.3.3
pkgver=1.3.4
pkgrel=0
epoch=
pkgdesc=""
@@ -7,7 +7,7 @@ arch=('x86_64')
url=""
license=('AGPL-3.0')
groups=()
depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'libva' 'libvdpau' 'libappindicator-gtk3' 'pam' 'gst-plugins-base' 'gst-plugin-pipewire')
depends=('gtk3' 'xdotool' 'libxcb' 'libxfixes' 'alsa-lib' 'libva' 'libappindicator-gtk3' 'pam' 'gst-plugins-base' 'gst-plugin-pipewire')
makedepends=()
checkdepends=()
optdepends=()

View File

@@ -1,11 +1,11 @@
Name: rustdesk
Version: 1.3.3
Version: 1.3.4
Release: 0
Summary: RPM package
License: GPL-3.0
URL: https://rustdesk.com
Vendor: rustdesk <info@rustdesk.com>
Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils libXtst6 libvdpau1 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
Recommends: libayatana-appindicator3-1
Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libfile_selector_linux_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)

View File

@@ -1,11 +1,11 @@
Name: rustdesk
Version: 1.3.3
Version: 1.3.4
Release: 0
Summary: RPM package
License: GPL-3.0
URL: https://rustdesk.com
Vendor: rustdesk <info@rustdesk.com>
Requires: gtk3 libxcb libxdo libXfixes alsa-lib libvdpau libva pam gstreamer1-plugins-base
Requires: gtk3 libxcb libxdo libXfixes alsa-lib libva pam gstreamer1-plugins-base
Recommends: libayatana-appindicator-gtk3
Provides: libdesktop_drop_plugin.so()(64bit), libdesktop_multi_window_plugin.so()(64bit), libfile_selector_linux_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)

View File

@@ -3,7 +3,7 @@ Version: 1.1.9
Release: 0
Summary: RPM package
License: GPL-3.0
Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils libXtst6 libvdpau1 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
Requires: gtk3 libxcb1 xdotool libXfixes3 alsa-utils libXtst6 libva2 pam gstreamer-plugins-base gstreamer-plugin-pipewire
Recommends: libayatana-appindicator3-1
%description

View File

@@ -1,11 +1,11 @@
Name: rustdesk
Version: 1.3.3
Version: 1.3.4
Release: 0
Summary: RPM package
License: GPL-3.0
URL: https://rustdesk.com
Vendor: rustdesk <info@rustdesk.com>
Requires: gtk3 libxcb libxdo libXfixes alsa-lib libvdpau1 libva2 pam gstreamer1-plugins-base
Requires: gtk3 libxcb libxdo libXfixes alsa-lib libva2 pam gstreamer1-plugins-base
Recommends: libayatana-appindicator-gtk3
%description

View File

@@ -0,0 +1,84 @@
From 7f12898fe8fd12c1042c98b34825ab2eda89e54d Mon Sep 17 00:00:00 2001
From: 21pages <sunboeasy@gmail.com>
Date: Sun, 24 Nov 2024 12:58:39 +0800
Subject: [PATCH 1/2] videotoolbox changing bitrate
Signed-off-by: 21pages <sunboeasy@gmail.com>
---
libavcodec/videotoolboxenc.c | 39 ++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c
index 5ea9afee22..89c927cdcc 100644
--- a/libavcodec/videotoolboxenc.c
+++ b/libavcodec/videotoolboxenc.c
@@ -278,6 +278,8 @@ typedef struct VTEncContext {
int max_slice_bytes;
int power_efficient;
int max_ref_frames;
+
+ int last_bit_rate;
} VTEncContext;
static int vt_dump_encoder(AVCodecContext *avctx)
@@ -1174,6 +1176,7 @@ static int vtenc_create_encoder(AVCodecContext *avctx,
int64_t one_second_value = 0;
void *nums[2];
+ vtctx->last_bit_rate = bit_rate;
int status = VTCompressionSessionCreate(kCFAllocatorDefault,
avctx->width,
avctx->height,
@@ -2618,6 +2621,41 @@ static int vtenc_send_frame(AVCodecContext *avctx,
return 0;
}
+static void update_config(AVCodecContext *avctx)
+{
+ VTEncContext *vtctx = avctx->priv_data;
+
+ if (avctx->codec_id != AV_CODEC_ID_PRORES) {
+ if (avctx->bit_rate != vtctx->last_bit_rate) {
+ av_log(avctx, AV_LOG_INFO, "Setting bit rate to %d\n", avctx->bit_rate);
+ vtctx->last_bit_rate = avctx->bit_rate;
+ SInt32 bit_rate = avctx->bit_rate;
+ CFNumberRef bit_rate_num = CFNumberCreate(kCFAllocatorDefault,
+ kCFNumberSInt32Type,
+ &bit_rate);
+ if (!bit_rate_num) return;
+
+ if (vtctx->constant_bit_rate) {
+ int status = VTSessionSetProperty(vtctx->session,
+ compat_keys.kVTCompressionPropertyKey_ConstantBitRate,
+ bit_rate_num);
+ if (status == kVTPropertyNotSupportedErr) {
+ av_log(avctx, AV_LOG_ERROR, "Error: -constant_bit_rate true is not supported by the encoder.\n");
+ }
+ } else {
+ int status = VTSessionSetProperty(vtctx->session,
+ kVTCompressionPropertyKey_AverageBitRate,
+ bit_rate_num);
+ if (!status) {
+ av_log(avctx, AV_LOG_ERROR, "Error: cannot set average bit rate: %d\n", status);
+ }
+ }
+
+ CFRelease(bit_rate_num);
+ }
+ }
+}
+
static av_cold int vtenc_frame(
AVCodecContext *avctx,
AVPacket *pkt,
@@ -2630,6 +2668,7 @@ static av_cold int vtenc_frame(
CMSampleBufferRef buf = NULL;
ExtraSEI *sei = NULL;
+ update_config(avctx);
if (frame) {
status = vtenc_send_frame(avctx, vtctx, frame);
--
2.43.0.windows.1

View File

@@ -0,0 +1,246 @@
From ed73f8f6494d74ae47218f9503c7e3de385d9253 Mon Sep 17 00:00:00 2001
From: 21pages <sunboeasy@gmail.com>
Date: Sun, 24 Nov 2024 14:17:39 +0800
Subject: [PATCH 1/2] mediacodec changing bitrate
Signed-off-by: 21pages <sunboeasy@gmail.com>
---
libavcodec/mediacodec_wrapper.c | 97 +++++++++++++++++++++++++++++++++
libavcodec/mediacodec_wrapper.h | 7 +++
libavcodec/mediacodecenc.c | 18 ++++++
3 files changed, 122 insertions(+)
diff --git a/libavcodec/mediacodec_wrapper.c b/libavcodec/mediacodec_wrapper.c
index 306359071e..7edb38a7d7 100644
--- a/libavcodec/mediacodec_wrapper.c
+++ b/libavcodec/mediacodec_wrapper.c
@@ -35,6 +35,8 @@
#include "ffjni.h"
#include "mediacodec_wrapper.h"
+#define PARAMETER_KEY_VIDEO_BITRATE "video-bitrate"
+
struct JNIAMediaCodecListFields {
jclass mediacodec_list_class;
@@ -195,6 +197,8 @@ struct JNIAMediaCodecFields {
jmethodID set_input_surface_id;
jmethodID signal_end_of_input_stream_id;
+ jmethodID set_parameters_id;
+
jclass mediainfo_class;
jmethodID init_id;
@@ -248,6 +252,8 @@ static const struct FFJniField jni_amediacodec_mapping[] = {
{ "android/media/MediaCodec", "setInputSurface", "(Landroid/view/Surface;)V", FF_JNI_METHOD, OFFSET(set_input_surface_id), 0 },
{ "android/media/MediaCodec", "signalEndOfInputStream", "()V", FF_JNI_METHOD, OFFSET(signal_end_of_input_stream_id), 0 },
+ { "android/media/MediaCodec", "setParameters", "(Landroid/os/Bundle;)V", FF_JNI_METHOD, OFFSET(set_parameters_id), 0 },
+
{ "android/media/MediaCodec$BufferInfo", NULL, NULL, FF_JNI_CLASS, OFFSET(mediainfo_class), 1 },
{ "android/media/MediaCodec.BufferInfo", "<init>", "()V", FF_JNI_METHOD, OFFSET(init_id), 1 },
@@ -292,6 +298,24 @@ typedef struct FFAMediaCodecJni {
static const FFAMediaCodec media_codec_jni;
+struct JNIABundleFields
+{
+ jclass bundle_class;
+ jmethodID init_id;
+ jmethodID put_int_id;
+};
+
+#define OFFSET(x) offsetof(struct JNIABundleFields, x)
+static const struct FFJniField jni_abundle_mapping[] = {
+ { "android/os/Bundle", NULL, NULL, FF_JNI_CLASS, OFFSET(bundle_class), 1 },
+
+ { "android/os/Bundle", "<init>", "()V", FF_JNI_METHOD, OFFSET(init_id), 1 },
+ { "android/os/Bundle", "putInt", "(Ljava/lang/String;I)V", FF_JNI_METHOD, OFFSET(put_int_id), 1 },
+
+ { NULL }
+};
+#undef OFFSET
+
#define JNI_GET_ENV_OR_RETURN(env, log_ctx, ret) do { \
(env) = ff_jni_get_env(log_ctx); \
if (!(env)) { \
@@ -1761,6 +1785,69 @@ static int mediacodec_jni_signalEndOfInputStream(FFAMediaCodec *ctx)
return 0;
}
+static int mediacodec_jni_setParameter(FFAMediaCodec *ctx, const char* name, int value)
+{
+ JNIEnv *env = NULL;
+ struct JNIABundleFields jfields = { 0 };
+ jobject object = NULL;
+ jstring key = NULL;
+ FFAMediaCodecJni *codec = (FFAMediaCodecJni *)ctx;
+ void *log_ctx = codec;
+ int ret = -1;
+
+ JNI_GET_ENV_OR_RETURN(env, codec, AVERROR_EXTERNAL);
+
+ if (ff_jni_init_jfields(env, &jfields, jni_abundle_mapping, 0, log_ctx) < 0) {
+ av_log(log_ctx, AV_LOG_ERROR, "Failed to init jfields\n");
+ goto fail;
+ }
+
+ object = (*env)->NewObject(env, jfields.bundle_class, jfields.init_id);
+ if (!object) {
+ av_log(log_ctx, AV_LOG_ERROR, "Failed to create bundle object\n");
+ goto fail;
+ }
+
+ key = ff_jni_utf_chars_to_jstring(env, name, log_ctx);
+ if (!key) {
+ av_log(log_ctx, AV_LOG_ERROR, "Failed to convert key to jstring\n");
+ goto fail;
+ }
+
+ (*env)->CallVoidMethod(env, object, jfields.put_int_id, key, value);
+ if (ff_jni_exception_check(env, 1, log_ctx) < 0) {
+ goto fail;
+ }
+
+ if (!codec->jfields.set_parameters_id) {
+ av_log(log_ctx, AV_LOG_ERROR, "System doesn't support setParameters\n");
+ goto fail;
+ }
+
+ (*env)->CallVoidMethod(env, codec->object, codec->jfields.set_parameters_id, object);
+ if (ff_jni_exception_check(env, 1, log_ctx) < 0) {
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ if (key) {
+ (*env)->DeleteLocalRef(env, key);
+ }
+ if (object) {
+ (*env)->DeleteLocalRef(env, object);
+ }
+ ff_jni_reset_jfields(env, &jfields, jni_abundle_mapping, 0, log_ctx);
+
+ return ret;
+}
+
+static int mediacodec_jni_setDynamicBitrate(FFAMediaCodec *ctx, int bitrate)
+{
+ return mediacodec_jni_setParameter(ctx, PARAMETER_KEY_VIDEO_BITRATE, bitrate);
+}
+
static const FFAMediaFormat media_format_jni = {
.class = &amediaformat_class,
@@ -1820,6 +1907,8 @@ static const FFAMediaCodec media_codec_jni = {
.getConfigureFlagEncode = mediacodec_jni_getConfigureFlagEncode,
.cleanOutputBuffers = mediacodec_jni_cleanOutputBuffers,
.signalEndOfInputStream = mediacodec_jni_signalEndOfInputStream,
+
+ .setDynamicBitrate = mediacodec_jni_setDynamicBitrate,
};
typedef struct FFAMediaFormatNdk {
@@ -2428,6 +2517,12 @@ static int mediacodec_ndk_signalEndOfInputStream(FFAMediaCodec *ctx)
return 0;
}
+static int mediacodec_ndk_setDynamicBitrate(FFAMediaCodec *ctx, int bitrate)
+{
+ av_log(ctx, AV_LOG_ERROR, "ndk setDynamicBitrate unavailable\n");
+ return -1;
+}
+
static const FFAMediaFormat media_format_ndk = {
.class = &amediaformat_ndk_class,
@@ -2489,6 +2584,8 @@ static const FFAMediaCodec media_codec_ndk = {
.getConfigureFlagEncode = mediacodec_ndk_getConfigureFlagEncode,
.cleanOutputBuffers = mediacodec_ndk_cleanOutputBuffers,
.signalEndOfInputStream = mediacodec_ndk_signalEndOfInputStream,
+
+ .setDynamicBitrate = mediacodec_ndk_setDynamicBitrate,
};
FFAMediaFormat *ff_AMediaFormat_new(int ndk)
diff --git a/libavcodec/mediacodec_wrapper.h b/libavcodec/mediacodec_wrapper.h
index 11a4260497..86c64556ad 100644
--- a/libavcodec/mediacodec_wrapper.h
+++ b/libavcodec/mediacodec_wrapper.h
@@ -219,6 +219,8 @@ struct FFAMediaCodec {
// For encoder with FFANativeWindow as input.
int (*signalEndOfInputStream)(FFAMediaCodec *);
+
+ int (*setDynamicBitrate)(FFAMediaCodec *codec, int bitrate);
};
static inline char *ff_AMediaCodec_getName(FFAMediaCodec *codec)
@@ -343,6 +345,11 @@ static inline int ff_AMediaCodec_signalEndOfInputStream(FFAMediaCodec *codec)
return codec->signalEndOfInputStream(codec);
}
+static inline int ff_AMediaCodec_setDynamicBitrate(FFAMediaCodec *codec, int bitrate)
+{
+ return codec->setDynamicBitrate(codec, bitrate);
+}
+
int ff_Build_SDK_INT(AVCodecContext *avctx);
enum FFAMediaFormatColorRange {
diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c
index d3bf27cb7f..621529d686 100644
--- a/libavcodec/mediacodecenc.c
+++ b/libavcodec/mediacodecenc.c
@@ -73,6 +73,8 @@ typedef struct MediaCodecEncContext {
int bitrate_mode;
int level;
int pts_as_dts;
+
+ int last_bit_rate;
} MediaCodecEncContext;
enum {
@@ -155,6 +157,8 @@ static av_cold int mediacodec_init(AVCodecContext *avctx)
int ret;
int gop;
+ s->last_bit_rate = avctx->bit_rate;
+
if (s->use_ndk_codec < 0)
s->use_ndk_codec = !av_jni_get_java_vm(avctx);
@@ -515,12 +519,26 @@ static int mediacodec_send(AVCodecContext *avctx,
return 0;
}
+static void update_config(AVCodecContext *avctx)
+{
+ MediaCodecEncContext *s = avctx->priv_data;
+ if (avctx->bit_rate != s->last_bit_rate) {
+ s->last_bit_rate = avctx->bit_rate;
+ if (0 != ff_AMediaCodec_setDynamicBitrate(s->codec, avctx->bit_rate)) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to set bitrate to %d\n", avctx->bit_rate);
+ } else {
+ av_log(avctx, AV_LOG_INFO, "Set bitrate to %d\n", avctx->bit_rate);
+ }
+ }
+}
+
static int mediacodec_encode(AVCodecContext *avctx, AVPacket *pkt)
{
MediaCodecEncContext *s = avctx->priv_data;
int ret;
int got_packet = 0;
+ update_config(avctx);
// Return on three case:
// 1. Serious error
// 2. Got a packet success
--
2.34.1

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,9 @@ vcpkg_from_github(
patch/0001-avcodec-amfenc-add-query_timeout-option-for-h264-hev.patch
patch/0002-libavcodec-amfenc-reconfig-when-bitrate-change.patch
patch/0003-amf-colorspace.patch
patch/0004-videotoolbox-changing-bitrate.patch
patch/0005-mediacodec-changing-bitrate.patch
patch/0006-dlopen-libva.patch
)
if(SOURCE_PATH MATCHES " ")
@@ -77,13 +80,15 @@ else()
endif()
if(VCPKG_TARGET_IS_LINUX)
string(APPEND OPTIONS "\
string(APPEND OPTIONS "\
--target-os=linux \
--enable-pthreads \
--disable-vdpau \
")
if(VCPKG_TARGET_ARCHITECTURE STREQUAL "arm")
else()
string(APPEND OPTIONS "\
string(APPEND OPTIONS "\
--enable-cuda \
--enable-ffnvcodec \
--enable-encoder=h264_nvenc \
@@ -98,8 +103,9 @@ if(VCPKG_TARGET_IS_LINUX)
--enable-encoder=h264_vaapi \
--enable-encoder=hevc_vaapi \
")
if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x64")
string(APPEND OPTIONS "\
string(APPEND OPTIONS "\
--enable-cuda_llvm \
")
endif()
@@ -127,7 +133,8 @@ elseif(VCPKG_TARGET_IS_WINDOWS)
--enable-libmfx \
--enable-encoder=h264_qsv \
--enable-encoder=hevc_qsv \
")
")
if(VCPKG_TARGET_ARCHITECTURE STREQUAL "x86")
set(LIB_MACHINE_ARG /machine:x86)
string(APPEND OPTIONS " --arch=i686 --enable-cross-compile")
@@ -189,6 +196,7 @@ endif()
string(APPEND VCPKG_COMBINED_C_FLAGS_DEBUG " -I \"${CURRENT_INSTALLED_DIR}/include\"")
string(APPEND VCPKG_COMBINED_C_FLAGS_RELEASE " -I \"${CURRENT_INSTALLED_DIR}/include\"")
if(VCPKG_TARGET_IS_WINDOWS)
string(APPEND VCPKG_COMBINED_C_FLAGS_DEBUG " -I \"${CURRENT_INSTALLED_DIR}/include/mfx\"")
string(APPEND VCPKG_COMBINED_C_FLAGS_RELEASE " -I \"${CURRENT_INSTALLED_DIR}/include/mfx\"")
@@ -202,9 +210,11 @@ if(VCPKG_DETECTED_CMAKE_C_COMPILER)
get_filename_component(CC_filename "${VCPKG_DETECTED_CMAKE_C_COMPILER}" NAME)
set(ENV{CC} "${CC_filename}")
string(APPEND OPTIONS " --cc=${CC_filename}")
if(VCPKG_HOST_IS_WINDOWS)
string(APPEND OPTIONS " --host_cc=${CC_filename}")
endif()
list(APPEND prog_env "${CC_path}")
endif()
@@ -282,6 +292,7 @@ if(VCPKG_HOST_IS_WINDOWS)
else()
# find_program(SHELL bash)
endif()
list(REMOVE_DUPLICATES prog_env)
vcpkg_add_to_path(PREPEND ${prog_env})

View File

@@ -1188,9 +1188,15 @@ impl VideoHandler {
pub fn new(format: CodecFormat, _display: usize) -> Self {
let luid = Self::get_adapter_luid();
log::info!("new video handler for display #{_display}, format: {format:?}, luid: {luid:?}");
let rgba_format =
if cfg!(feature = "flutter") && (cfg!(windows) || cfg!(target_os = "linux")) {
ImageFormat::ABGR
} else {
ImageFormat::ARGB
};
VideoHandler {
decoder: Decoder::new(format, luid),
rgb: ImageRgb::new(ImageFormat::ARGB, crate::get_dst_align_rgba()),
rgb: ImageRgb::new(rgba_format, crate::get_dst_align_rgba()),
texture: Default::default(),
recorder: Default::default(),
record: false,
@@ -3293,6 +3299,7 @@ lazy_static::lazy_static! {
("VK_PRINT", Key::ControlKey(ControlKey::Print)),
("VK_EXECUTE", Key::ControlKey(ControlKey::Execute)),
("VK_SNAPSHOT", Key::ControlKey(ControlKey::Snapshot)),
("VK_SCROLL", Key::ControlKey(ControlKey::Scroll)),
("VK_INSERT", Key::ControlKey(ControlKey::Insert)),
("VK_DELETE", Key::ControlKey(ControlKey::Delete)),
("VK_HELP", Key::ControlKey(ControlKey::Help)),

View File

@@ -19,6 +19,7 @@ use hbb_common::allow_err;
use hbb_common::{
config::{self, LocalConfig, PeerConfig, PeerInfoSerde},
fs, lazy_static, log,
message_proto::Hash,
rendezvous_proto::ConnType,
ResultType,
};
@@ -1417,7 +1418,8 @@ pub fn main_get_last_remote_id() -> String {
}
pub fn main_get_software_update_url() {
if get_local_option("enable-check-update".to_string()) != "N" {
let opt = get_local_option(config::keys::OPTION_ENABLE_CHECK_UPDATE.to_string());
if config::option2bool(config::keys::OPTION_ENABLE_CHECK_UPDATE, &opt) {
crate::common::check_software_update();
}
}
@@ -2341,6 +2343,25 @@ pub fn main_audio_support_loopback() -> SyncReturn<bool> {
SyncReturn(is_surpport)
}
pub fn get_os_distro_info() -> SyncReturn<String> {
#[cfg(target_os = "linux")]
{
let distro = &hbb_common::platform::linux::DISTRO;
SyncReturn(
serde_json::to_string(&HashMap::from([
("name", distro.name.clone()),
("id", distro.id.clone()),
("version_id", distro.version_id.clone()),
]))
.unwrap_or_default(),
)
}
#[cfg(not(target_os = "linux"))]
{
SyncReturn("".to_owned())
}
}
#[cfg(target_os = "android")]
pub mod server_side {
use hbb_common::{config, log};

View File

@@ -145,7 +145,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Failed to make direct connection to remote desktop", "Impossible d'établir une connexion directe"),
("Set Password", "Définir le mot de passe"),
("OS Password", "Mot de passe du système d'exploitation"),
("install_tip", "Vous utilisez une version non installée. En raison des restrictions UAC, en tant que terminal contrôlé, dans certains cas, il ne sera pas en mesure de contrôler la souris et le clavier ou d'enregistrer l'écran. Veuillez cliquer sur le bouton ci-dessous pour installer RustDesk au système pour éviter la question ci-dessus."),
("install_tip", "RustDesk n'est pas installé, ce qui peut limiter son utilisation à cause de l'UAC. Cliquez ci-dessous pour l'installer."),
("Click to upgrade", "Cliquer pour mettre à niveau"),
("Click to download", "Cliquer pour télécharger"),
("Click to update", "Cliquer pour mettre à jour"),

View File

@@ -654,6 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload files", "File upload"),
("Clipboard is synchronized", "Gli appunti sono sincronizzati"),
("Update client clipboard", "Aggiorna appunti client"),
("Untagged", ""),
("Untagged", "Senza tag"),
].iter().cloned().collect();
}

View File

@@ -653,7 +653,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "폴더 업로드"),
("Upload files", "파일 업로드"),
("Clipboard is synchronized", "클립보드가 동기화됨"),
("Update client clipboard", ""),
("Untagged", ""),
("Update client clipboard", "클라이언트 클립보드 업데이트"),
("Untagged", "태그 없음"),
].iter().cloned().collect();
}

View File

@@ -653,7 +653,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "Augšupielādēt mapi"),
("Upload files", "Augšupielādēt failus"),
("Clipboard is synchronized", "Starpliktuve ir sinhronizēta"),
("Update client clipboard", ""),
("Untagged", ""),
("Update client clipboard", "Atjaunināt klienta starpliktuvi"),
("Untagged", "Neatzīmēts"),
].iter().cloned().collect();
}

View File

@@ -654,6 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload files", "Загрузить файлы"),
("Clipboard is synchronized", "Буфер обмена синхронизирован"),
("Update client clipboard", "Обновить буфер обмена клиента"),
("Untagged", ""),
("Untagged", "Без метки"),
].iter().cloned().collect();
}

View File

@@ -653,7 +653,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload folder", "上傳資料夾"),
("Upload files", "上傳檔案"),
("Clipboard is synchronized", "剪貼簿已同步"),
("Update client clipboard", ""),
("Untagged", ""),
("Update client clipboard", "更新客戶端的剪貼簿"),
("Untagged", "無標籤"),
].iter().cloned().collect();
}

View File

@@ -120,9 +120,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Original", "Оригінал"),
("Shrink", "Зменшити"),
("Stretch", "Розтягнути"),
("Scrollbar", "Смуга прокрутки"),
("ScrollAuto", "Автоматична прокрутка"),
("Good image quality", "Хороша якість зображення"),
("Scrollbar", "Смужка гортання"),
("ScrollAuto", "Автоматичне гортання"),
("Good image quality", "Гарна якість зображення"),
("Balanced", "Збалансована"),
("Optimize reaction time", "Оптимізувати час реакції"),
("Custom", "Користувацька"),
@@ -199,10 +199,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Please enter the folder name", "Будь ласка, введіть назву для теки"),
("Fix it", "Виправити"),
("Warning", "Попередження"),
("Login screen using Wayland is not supported", "Вхід в систему з використанням Wayland не підтримується"),
("Login screen using Wayland is not supported", "Екран входу, який використовує Wayland, не підтримується"),
("Reboot required", "Потрібне перезавантаження"),
("Unsupported display server", "Графічний сервер не підтримується"),
("x11 expected", "Очікується X11"),
("x11 expected", "Потрібен X11"),
("Port", "Порт"),
("Settings", "Налаштування"),
("Username", "Імʼя користувача"),
@@ -220,21 +220,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Verification code", "Код підтвердження"),
("verification_tip", "Код підтвердження надіслано на зареєстровану email-адресу, введіть код підтвердження для продовження авторизації."),
("Logout", "Вийти"),
("Tags", "Теги"),
("Tags", "Мітки"),
("Search ID", "Пошук за ID"),
("whitelist_sep", "Відокремлення комою, крапкою з комою, пропуском або новим рядком"),
("Add ID", "Додати ID"),
("Add Tag", "Додати ключове слово"),
("Unselect all tags", "Скасувати вибір усіх тегів"),
("Add Tag", "Додати мітку"),
("Unselect all tags", "Скасувати вибір усіх міток"),
("Network error", "Помилка мережі"),
("Username missed", "Імʼя користувача відсутнє"),
("Password missed", "Пароль відсутній"),
("Wrong credentials", "Неправильні дані"),
("The verification code is incorrect or has expired", "Код підтвердження некоректний або протермінований"),
("Edit Tag", "Редагувати тег"),
("Edit Tag", "Редагувати мітку"),
("Forget Password", "Не зберігати пароль"),
("Favorites", "Вибране"),
("Add to Favorites", "Додати в обране"),
("Add to Favorites", "Додати до обраного"),
("Remove from Favorites", "Видалити з обраного"),
("Empty", "Пусто"),
("Invalid folder name", "Неприпустима назва теки"),
@@ -279,7 +279,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Screen Connection", "Підключення екрана"),
("Do you accept?", "Ви згодні?"),
("Open System Setting", "Відкрити налаштування системи"),
("How to get Android input permission?", "Як отримати дозвіл на введення Android?"),
("How to get Android input permission?", "Як отримати дозвіл на введення в Android?"),
("android_input_permission_tip1", "Для того, щоб віддалений пристрій міг керувати вашим Android-пристроєм за допомогою миші або дотику, вам необхідно дозволити RustDesk використовувати службу \"Спеціальні можливості\"."),
("android_input_permission_tip2", "Будь ласка, перейдіть на наступну сторінку системних налаштувань, знайдіть та увійдіть у [Встановлені служби], увімкніть службу [RustDesk Input]."),
("android_new_connection_tip", "Отримано новий запит на керування вашим поточним пристроєм."),
@@ -329,15 +329,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Display Settings", "Налаштування дисплею"),
("Ratio", "Співвідношення"),
("Image Quality", "Якість зображення"),
("Scroll Style", "Стиль прокрутки"),
("Scroll Style", "Стиль гортання"),
("Show Toolbar", "Показати панель інструментів"),
("Hide Toolbar", "Приховати панель інструментів"),
("Direct Connection", "Пряме підключення"),
("Relay Connection", "Ретрансльоване підключення"),
("Secure Connection", "Безпечне підключення"),
("Insecure Connection", "Небезпечне підключення"),
("Scale original", "Оригінал масштабу"),
("Scale adaptive", "Масштаб адаптивний"),
("Scale original", "Оригінальний масштаб"),
("Scale adaptive", "Адаптивний масштаб"),
("General", "Загальні"),
("Security", "Безпека"),
("Theme", "Тема"),
@@ -402,7 +402,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Hide connection management window", "Приховати вікно керування підключеннями"),
("hide_cm_tip", "Дозволено приховати лише якщо сеанс підтверджується постійним паролем"),
("wayland_experiment_tip", "Підтримка Wayland на експериментальній стадії, будь ласка, використовуйте X11, якщо необхідний автоматичний доступ."),
("Right click to select tabs", "Правий клік для вибору вкладки"),
("Right click to select tabs", "Вибір вкладок клацанням правою"),
("Skipped", "Пропущено"),
("Add to address book", "Додати IP до Адресної книги"),
("Group", "Група"),
@@ -513,7 +513,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Stop", "Зупинити"),
("exceed_max_devices", "У вас максимальна кількість керованих пристроїв."),
("Sync with recent sessions", "Синхронізація з нещодавніми сеансами"),
("Sort tags", "Сортувати теги"),
("Sort tags", "Сортувати мітки"),
("Open connection in new tab", "Відкрити підключення в новій вкладці"),
("Move tab to new window", "Перемістити вкладку до нового вікна"),
("Can not be empty", "Не може бути порожнім"),
@@ -524,7 +524,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Grid View", "Перегляд ґраткою"),
("List View", "Перегляд списком"),
("Select", "Вибрати"),
("Toggle Tags", "Видимість тегів"),
("Toggle Tags", "Видимість міток"),
("pull_ab_failed_tip", "Не вдалося оновити адресну книгу"),
("push_ab_failed_tip", "Не вдалося синхронізувати адресну книгу"),
("synced_peer_readded_tip", "Пристрої з нещодавніх сеансів будуть синхронізовані з адресною книгою."),
@@ -533,7 +533,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("HSV Color", "Колір HSV"),
("Installation Successful!", "Успішне встановлення!"),
("Installation failed!", "Невдале встановлення!"),
("Reverse mouse wheel", "Зворотній напрям прокрутки"),
("Reverse mouse wheel", "Зворотній напрям гортання"),
("{} sessions", "{} сеансів"),
("scam_title", "Вас можуть ОБМАНУТИ!"),
("scam_text1", "Якщо ви розмовляєте по телефону з кимось, кого НЕ ЗНАЄТЕ чи кому НЕ ДОВІРЯЄТЕ, і ця особа хоче, щоб ви використали RustDesk та запустили службу, не робіть цього та негайно завершіть дзвінок."),
@@ -654,6 +654,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Upload files", "Надіслати файли"),
("Clipboard is synchronized", "Буфер обміну синхронізовано"),
("Update client clipboard", "Оновити буфер обміну клієнта"),
("Untagged", ""),
("Untagged", "Без міток"),
].iter().cloned().collect();
}

View File

@@ -222,6 +222,11 @@ extern "C"
return IsWindowsServer();
}
bool is_windows_10_or_greater()
{
return IsWindows10OrGreater();
}
HANDLE LaunchProcessWin(LPCWSTR cmd, DWORD dwSessionId, BOOL as_user, DWORD *pDwTokenPid)
{
HANDLE hProcess = NULL;

View File

@@ -479,6 +479,7 @@ extern "C" {
fn selectInputDesktop() -> BOOL;
fn inputDesktopSelected() -> BOOL;
fn is_windows_server() -> BOOL;
fn is_windows_10_or_greater() -> BOOL;
fn handleMask(
out: *mut u8,
mask: *const u8,
@@ -1559,6 +1560,11 @@ pub fn is_win_server() -> bool {
unsafe { is_windows_server() > 0 }
}
#[inline]
pub fn is_win_10_or_greater() -> bool {
unsafe { is_windows_10_or_greater() > 0 }
}
pub fn bootstrap() {
if let Ok(lic) = get_license_from_exe_name() {
*config::EXE_RENDEZVOUS_SERVER.write().unwrap() = lic.host.clone();

View File

@@ -239,7 +239,7 @@ pub mod service {
(enigo::Key::Select, evdev::Key::KEY_SELECT),
(enigo::Key::Print, evdev::Key::KEY_PRINT),
// (enigo::Key::Execute, evdev::Key::KEY_EXECUTE),
// (enigo::Key::Snapshot, evdev::Key::KEY_SNAPSHOT),
(enigo::Key::Snapshot, evdev::Key::KEY_SYSRQ),
(enigo::Key::Insert, evdev::Key::KEY_INSERT),
(enigo::Key::Help, evdev::Key::KEY_HELP),
(enigo::Key::Sleep, evdev::Key::KEY_SLEEP),
@@ -247,7 +247,7 @@ pub mod service {
(enigo::Key::Scroll, evdev::Key::KEY_SCROLLLOCK),
(enigo::Key::NumLock, evdev::Key::KEY_NUMLOCK),
(enigo::Key::RWin, evdev::Key::KEY_RIGHTMETA),
(enigo::Key::Apps, evdev::Key::KEY_CONTEXT_MENU),
(enigo::Key::Apps, evdev::Key::KEY_COMPOSE), // it's a little strange that the key is mapped to KEY_COMPOSE, not KEY_MENU
(enigo::Key::Multiply, evdev::Key::KEY_KPASTERISK),
(enigo::Key::Add, evdev::Key::KEY_KPPLUS),
(enigo::Key::Subtract, evdev::Key::KEY_KPMINUS),

View File

@@ -173,21 +173,36 @@ pub fn get_option<T: AsRef<str>>(key: T) -> String {
}
#[inline]
#[cfg(target_os = "macos")]
pub fn use_texture_render() -> bool {
cfg!(feature = "flutter") && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) == "Y"
}
#[cfg(target_os = "android")]
return false;
#[cfg(target_os = "ios")]
return false;
#[inline]
#[cfg(any(target_os = "windows", target_os = "linux"))]
pub fn use_texture_render() -> bool {
cfg!(feature = "flutter") && LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) != "N"
}
#[cfg(target_os = "macos")]
return cfg!(feature = "flutter")
&& LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) == "Y";
#[inline]
#[cfg(any(target_os = "android", target_os = "ios"))]
pub fn use_texture_render() -> bool {
false
#[cfg(target_os = "linux")]
return cfg!(feature = "flutter")
&& LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) != "N";
#[cfg(target_os = "windows")]
{
if !cfg!(feature = "flutter") {
return false;
}
// https://learn.microsoft.com/en-us/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
#[cfg(debug_assertions)]
let default_texture = true;
#[cfg(not(debug_assertions))]
let default_texture = crate::platform::is_win_10_or_greater();
if default_texture {
LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) != "N"
} else {
return LocalConfig::get_option(config::keys::OPTION_TEXTURE_RENDER) == "Y";
}
}
}
#[inline]