Merge branch 'master' of https://github.com/FastAct/rustdesk
3
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -1,11 +1,12 @@
|
||||
name: 🐞 Bug report
|
||||
description: Thanks for taking the time to fill out this bug report! Please fill the form in **English**
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: desc
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: A clear and concise description of what the bug is
|
||||
description: A clear and concise description of what the bug is (if it's a keyboard issue, provide the keyboard mode you're using. e.g. legacy, map, translate)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
@ -1,5 +1,6 @@
|
||||
name: 🛠️ Feature request
|
||||
description: Suggest an idea for RustDesk
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: desc
|
||||
|
||||
45
.github/workflows/flutter-ci.yml
vendored
@ -18,11 +18,12 @@ on:
|
||||
|
||||
env:
|
||||
LLVM_VERSION: "15.0.6"
|
||||
FLUTTER_VERSION: "3.7.0"
|
||||
FLUTTER_VERSION: "3.7.5"
|
||||
# vcpkg version: 2022.05.10
|
||||
# for multiarch gcc compatibility
|
||||
VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44"
|
||||
VERSION: "1.2.0"
|
||||
NDK_VERSION: "r23"
|
||||
|
||||
jobs:
|
||||
build-for-windows:
|
||||
@ -62,7 +63,7 @@ jobs:
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: "1.62"
|
||||
toolchain: stable
|
||||
target: ${{ matrix.job.target }}
|
||||
override: true
|
||||
components: rustfmt
|
||||
@ -260,7 +261,7 @@ jobs:
|
||||
job:
|
||||
- {
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
os: ubuntu-18.04,
|
||||
os: ubuntu-20.04,
|
||||
extra-build-args: "",
|
||||
}
|
||||
steps:
|
||||
@ -319,7 +320,7 @@ jobs:
|
||||
./flutter/lib/generated_bridge.dart
|
||||
./flutter/lib/generated_bridge.freezed.dart
|
||||
|
||||
build-rustdesk-android-arm64:
|
||||
build-rustdesk-android:
|
||||
needs: [generate-bridge-linux]
|
||||
name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
@ -330,15 +331,17 @@ jobs:
|
||||
- {
|
||||
arch: x86_64,
|
||||
target: aarch64-linux-android,
|
||||
os: ubuntu-20.04,
|
||||
extra-build-features: "",
|
||||
openssl-arch: android-arm64
|
||||
}
|
||||
- {
|
||||
arch: x86_64,
|
||||
target: armv7-linux-androideabi,
|
||||
os: ubuntu-18.04,
|
||||
extra-build-features: "",
|
||||
openssl-arch: android-arm
|
||||
}
|
||||
# - {
|
||||
# arch: x86_64,
|
||||
# target: armv7-linux-androideabi,
|
||||
# os: ubuntu-18.04,
|
||||
# extra-build-features: "",
|
||||
# }
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@ -354,15 +357,14 @@ jobs:
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r22b
|
||||
ndk-version: ${{ env.NDK_VERSION }}
|
||||
add-to-path: true
|
||||
|
||||
- name: Download deps
|
||||
- name: Clone deps
|
||||
shell: bash
|
||||
run: |
|
||||
pushd /opt
|
||||
wget https://github.com/rustdesk/doc.rustdesk.com/releases/download/console/dep.tar.gz
|
||||
tar xzf dep.tar.gz
|
||||
git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
|
||||
|
||||
- name: Restore bridge files
|
||||
uses: actions/download-artifact@master
|
||||
@ -390,7 +392,7 @@ jobs:
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
VCPKG_ROOT: /opt/vcpkg
|
||||
VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg
|
||||
run: |
|
||||
rustup target add ${{ matrix.job.target }}
|
||||
cargo install cargo-ndk
|
||||
@ -413,16 +415,12 @@ jobs:
|
||||
JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64
|
||||
run: |
|
||||
export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH
|
||||
# download so
|
||||
pushd flutter
|
||||
wget -O so.tar.gz https://github.com/rustdesk/doc.rustdesk.com/releases/download/console/so.tar.gz
|
||||
tar xzvf so.tar.gz
|
||||
popd
|
||||
# temporary use debug sign config
|
||||
sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle
|
||||
case ${{ matrix.job.target }} in
|
||||
aarch64-linux-android)
|
||||
mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
|
||||
cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/arm64-v8a/*.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/
|
||||
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
|
||||
# build flutter
|
||||
pushd flutter
|
||||
@ -431,6 +429,7 @@ jobs:
|
||||
;;
|
||||
armv7-linux-androideabi)
|
||||
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
|
||||
cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/armeabi-v7a/*.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/
|
||||
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
|
||||
# build flutter
|
||||
pushd flutter
|
||||
@ -907,19 +906,19 @@ jobs:
|
||||
- {
|
||||
arch: x86_64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
os: ubuntu-18.04,
|
||||
os: ubuntu-20.04,
|
||||
extra-build-features: "",
|
||||
}
|
||||
- {
|
||||
arch: x86_64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
os: ubuntu-18.04,
|
||||
os: ubuntu-20.04,
|
||||
extra-build-features: "flatpak",
|
||||
}
|
||||
- {
|
||||
arch: x86_64,
|
||||
target: x86_64-unknown-linux-gnu,
|
||||
os: ubuntu-18.04,
|
||||
os: ubuntu-20.04,
|
||||
extra-build-features: "appimage",
|
||||
}
|
||||
# - { target: x86_64-unknown-linux-musl , os: ubuntu-20.04, use-cross: true }
|
||||
|
||||
43
.github/workflows/flutter-nightly.yml
vendored
@ -14,6 +14,7 @@ env:
|
||||
# for multiarch gcc compatibility
|
||||
VCPKG_COMMIT_ID: "14e7bb4ae24616ec54ff6b2f6ef4e8659434ea44"
|
||||
VERSION: "1.2.0"
|
||||
NDK_VERSION: "r23"
|
||||
#signing keys env variable checks
|
||||
ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}'
|
||||
MACOS_P12_BASE64: '${{ secrets.MACOS_P12_BASE64 }}'
|
||||
@ -86,7 +87,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Build rustdesk
|
||||
run: python3 .\build.py --portable --hwcodec --flutter
|
||||
run: python3 .\build.py --portable --hwcodec --flutter --feature IddDriver
|
||||
|
||||
- name: Sign rustdesk files
|
||||
uses: GermanBluefox/code-sign-action@v7
|
||||
@ -419,7 +420,7 @@ jobs:
|
||||
./flutter/lib/generated_bridge.dart
|
||||
./flutter/lib/generated_bridge.freezed.dart
|
||||
|
||||
build-rustdesk-android-arm64:
|
||||
build-rustdesk-android:
|
||||
needs: [generate-bridge-linux]
|
||||
name: build rustdesk android apk ${{ matrix.job.target }} (${{ matrix.job.os }}) [${{ matrix.job.extra-build-features }}]
|
||||
runs-on: ${{ matrix.job.os }}
|
||||
@ -430,15 +431,17 @@ jobs:
|
||||
- {
|
||||
arch: x86_64,
|
||||
target: aarch64-linux-android,
|
||||
os: ubuntu-20.04,
|
||||
extra-build-features: "",
|
||||
openssl-arch: android-arm64
|
||||
}
|
||||
- {
|
||||
arch: x86_64,
|
||||
target: armv7-linux-androideabi,
|
||||
os: ubuntu-18.04,
|
||||
extra-build-features: "",
|
||||
openssl-arch: android-arm
|
||||
}
|
||||
# - {
|
||||
# arch: x86_64,
|
||||
# target: armv7-linux-androideabi,
|
||||
# os: ubuntu-18.04,
|
||||
# extra-build-features: "",
|
||||
# }
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
@ -454,15 +457,14 @@ jobs:
|
||||
- uses: nttld/setup-ndk@v1
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r22b
|
||||
ndk-version: ${{ env.NDK_VERSION }}
|
||||
add-to-path: true
|
||||
|
||||
- name: Download deps
|
||||
- name: Clone deps
|
||||
shell: bash
|
||||
run: |
|
||||
pushd /opt
|
||||
wget https://github.com/rustdesk/doc.rustdesk.com/releases/download/console/dep.tar.gz
|
||||
tar xzf dep.tar.gz
|
||||
git clone https://github.com/Kingtous/rustdesk_thirdparty_lib.git --depth=1
|
||||
|
||||
- name: Restore bridge files
|
||||
uses: actions/download-artifact@master
|
||||
@ -490,7 +492,7 @@ jobs:
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
ANDROID_NDK_ROOT: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
VCPKG_ROOT: /opt/vcpkg
|
||||
VCPKG_ROOT: /opt/rustdesk_thirdparty_lib/vcpkg
|
||||
run: |
|
||||
rustup target add ${{ matrix.job.target }}
|
||||
cargo install cargo-ndk
|
||||
@ -513,16 +515,12 @@ jobs:
|
||||
JAVA_HOME: /usr/lib/jvm/java-11-openjdk-amd64
|
||||
run: |
|
||||
export PATH=/usr/lib/jvm/java-11-openjdk-amd64/bin:$PATH
|
||||
# download so
|
||||
pushd flutter
|
||||
wget -O so.tar.gz https://github.com/rustdesk/doc.rustdesk.com/releases/download/console/so.tar.gz
|
||||
tar xzvf so.tar.gz
|
||||
popd
|
||||
# temporary use debug sign config
|
||||
sed -i "s/signingConfigs.release/signingConfigs.debug/g" ./flutter/android/app/build.gradle
|
||||
case ${{ matrix.job.target }} in
|
||||
aarch64-linux-android)
|
||||
mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
|
||||
cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/arm64-v8a/*.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/
|
||||
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
|
||||
# build flutter
|
||||
pushd flutter
|
||||
@ -531,6 +529,7 @@ jobs:
|
||||
;;
|
||||
armv7-linux-androideabi)
|
||||
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
|
||||
cp /opt/rustdesk_thirdparty_lib/android/app/src/main/jniLibs/armeabi-v7a/*.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/
|
||||
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
|
||||
# build flutter
|
||||
pushd flutter
|
||||
@ -732,7 +731,7 @@ jobs:
|
||||
x86_64)
|
||||
# no need mock on x86_64
|
||||
export VCPKG_ROOT=/opt/artifacts/vcpkg
|
||||
cargo build --lib --features hwcodec,flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release
|
||||
cargo build --lib --features hwcodec,flutter,${{ matrix.job.extra-build-features }} --release
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -900,7 +899,7 @@ jobs:
|
||||
ln -s /usr/include /vcpkg/installed/arm64-linux/include
|
||||
export VCPKG_ROOT=/vcpkg
|
||||
# disable hwcodec for compilation
|
||||
cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release
|
||||
cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release
|
||||
;;
|
||||
armv7)
|
||||
cp -r /opt/artifacts/vcpkg/installed/lib/* /usr/lib/arm-linux-gnueabihf/
|
||||
@ -910,7 +909,7 @@ jobs:
|
||||
ln -s /usr/include /vcpkg/installed/arm-linux/include
|
||||
export VCPKG_ROOT=/vcpkg
|
||||
# disable hwcodec for compilation
|
||||
cargo build --lib --features flutter,flutter_texture_render,${{ matrix.job.extra-build-features }} --release
|
||||
cargo build --lib --features flutter,${{ matrix.job.extra-build-features }} --release
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -1511,7 +1510,7 @@ jobs:
|
||||
pushd flatpak
|
||||
git clone https://github.com/flathub/shared-modules.git --depth=1
|
||||
flatpak-builder --user --force-clean --repo=repo ./build ./rustdesk.json
|
||||
flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak org.rustdesk.rustdesk
|
||||
flatpak build-bundle ./repo rustdesk-${{ env.VERSION }}-${{ matrix.job.target }}.flatpak com.rustdesk.RustDesk
|
||||
|
||||
- name: Publish flatpak package
|
||||
uses: softprops/action-gh-release@v1
|
||||
|
||||
1944
Cargo.lock
generated
22
Cargo.toml
@ -45,33 +45,33 @@ lazy_static = "1.4"
|
||||
sha2 = "0.10"
|
||||
repng = "0.2"
|
||||
parity-tokio-ipc = { git = "https://github.com/open-trade/parity-tokio-ipc" }
|
||||
flexi_logger = { version = "0.22", features = ["async", "use_chrono_for_offset"] }
|
||||
runas = "0.2"
|
||||
runas = "1.0"
|
||||
magnum-opus = { git = "https://github.com/rustdesk/magnum-opus" }
|
||||
dasp = { version = "0.11", features = ["signal", "interpolate-linear", "interpolate"], optional = true }
|
||||
rubato = { version = "0.12", optional = true }
|
||||
samplerate = { version = "0.2", optional = true }
|
||||
async-trait = "0.1"
|
||||
uuid = { version = "1.0", features = ["v4"] }
|
||||
clap = "3.0"
|
||||
clap = "4.1"
|
||||
rpassword = "7.0"
|
||||
base64 = "0.13"
|
||||
base64 = "0.21"
|
||||
num_cpus = "1.13"
|
||||
bytes = { version = "1.2", features = ["serde"] }
|
||||
default-net = "0.12.0"
|
||||
wol-rs = "0.9.1"
|
||||
wol-rs = "1.0"
|
||||
flutter_rust_bridge = { version = "1.61.1", optional = true }
|
||||
errno = "0.2.8"
|
||||
errno = "0.3"
|
||||
rdev = { git = "https://github.com/fufesou/rdev" }
|
||||
url = { version = "2.1", features = ["serde"] }
|
||||
dlopen = "0.1"
|
||||
hex = "0.4.3"
|
||||
|
||||
reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false }
|
||||
chrono = "0.4.23"
|
||||
cidr-utils = "0.5.9"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "linux")))'.dependencies]
|
||||
cpal = "0.13.5"
|
||||
cpal = "0.14"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
machine-uid = "0.2"
|
||||
@ -81,14 +81,14 @@ sys-locale = "0.2"
|
||||
enigo = { path = "libs/enigo", features = [ "with_serde" ] }
|
||||
clipboard = { path = "libs/clipboard" }
|
||||
ctrlc = "3.2"
|
||||
arboard = "2.0"
|
||||
arboard = "3.2"
|
||||
#minreq = { version = "2.4", features = ["punycode", "https-native"] }
|
||||
system_shutdown = "3.0.0"
|
||||
system_shutdown = "4.0"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
trayicon = { git = "https://github.com/open-trade/trayicon-rs", features = ["winit"] }
|
||||
winit = "0.26"
|
||||
winapi = { version = "0.3", features = ["winuser"] }
|
||||
winapi = { version = "0.3", features = ["winuser", "wincrypt"] }
|
||||
winreg = "0.10"
|
||||
windows-service = "0.4"
|
||||
virtual_display = { path = "libs/virtual_display" }
|
||||
@ -132,6 +132,7 @@ flutter_rust_bridge = "1.61.1"
|
||||
|
||||
[workspace]
|
||||
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/simple_rc", "libs/portable"]
|
||||
exclude = ["vdi/host"]
|
||||
|
||||
[package.metadata.winres]
|
||||
LegalCopyright = "Copyright © 2022 Purslane, Inc."
|
||||
@ -147,6 +148,7 @@ cc = "1.0"
|
||||
hbb_common = { path = "libs/hbb_common" }
|
||||
simple_rc = { path = "libs/simple_rc", optional = true }
|
||||
flutter_rust_bridge_codegen = "1.61.1"
|
||||
os-version = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
hound = "3.5"
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="docs/README-UA.md">Українська</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>]<br>
|
||||
[<a href="docs/README-UA.md">Українська</a>] | [<a href="docs/README-CS.md">česky</a>] | [<a href="docs/README-ZH.md">中文</a>] | | [<a href="docs/README-HU.md">Magyar</a>] | [<a href="docs/README-ES.md">Español</a>] | [<a href="docs/README-FA.md">فارسی</a>] | [<a href="docs/README-FR.md">Français</a>] | [<a href="docs/README-DE.md">Deutsch</a>] | [<a href="docs/README-PL.md">Polski</a>] | [<a href="docs/README-ID.md">Indonesian</a>] | [<a href="docs/README-FI.md">Suomi</a>] | [<a href="docs/README-ML.md">മലയാളം</a>] | [<a href="docs/README-JP.md">日本語</a>] | [<a href="docs/README-NL.md">Nederlands</a>] | [<a href="docs/README-IT.md">Italiano</a>] | [<a href="docs/README-RU.md">Русский</a>] | [<a href="docs/README-PTBR.md">Português (Brasil)</a>] | [<a href="docs/README-EO.md">Esperanto</a>] | [<a href="docs/README-KR.md">한국어</a>] | [<a href="docs/README-AR.md">العربي</a>] | [<a href="docs/README-VN.md">Tiếng Việt</a>] | [<a href="docs/README-DA.md">Dansk</a>] | [<a href="docs/README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>We need your help to translate this README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> and <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> to your native language</b>
|
||||
</p>
|
||||
|
||||
@ -37,9 +37,9 @@ Below are the servers you are using for free, they may change over time. If you
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | dc.volia (2VM) | 2 vCPU / 4GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dev Container
|
||||
|
||||
|
||||
189
build.py
@ -18,14 +18,11 @@ exe_path = 'target/release/' + hbb_name
|
||||
flutter_win_target_dir = 'flutter/build/windows/runner/Release/'
|
||||
skip_cargo = False
|
||||
|
||||
def custom_os_system(cmd):
|
||||
err = os._system(cmd)
|
||||
def system2(cmd):
|
||||
err = os.system(cmd)
|
||||
if err != 0:
|
||||
print(f"Error occurred when executing: {cmd}. Exiting.")
|
||||
sys.exit(-1)
|
||||
# replace prebuilt os.system
|
||||
os._system = os.system
|
||||
os.system = custom_os_system
|
||||
|
||||
def get_version():
|
||||
with open("Cargo.toml", encoding="utf-8") as fh:
|
||||
@ -40,7 +37,7 @@ def parse_rc_features(feature):
|
||||
'IddDriver': {
|
||||
'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/RustDeskIddDriver_x64.zip',
|
||||
'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.1/checksum_md5',
|
||||
'exclude': ['README.md'],
|
||||
'exclude': ['README.md', 'certmgr.exe', 'install_cert_runas_admin.bat'],
|
||||
},
|
||||
'PrivacyMode': {
|
||||
'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.1'
|
||||
@ -144,8 +141,8 @@ def generate_build_script_for_docker():
|
||||
# build rustdesk
|
||||
./build.py --flutter --hwcodec
|
||||
''')
|
||||
os.system("chmod +x /tmp/build.sh")
|
||||
os.system("bash /tmp/build.sh")
|
||||
system2("chmod +x /tmp/build.sh")
|
||||
system2("bash /tmp/build.sh")
|
||||
|
||||
|
||||
def download_extract_features(features, res_dir):
|
||||
@ -250,7 +247,7 @@ def get_features(args):
|
||||
|
||||
def generate_control_file(version):
|
||||
control_file_path = "../res/DEBIAN/control"
|
||||
os.system('/bin/rm -rf %s' % control_file_path)
|
||||
system2('/bin/rm -rf %s' % control_file_path)
|
||||
|
||||
content = """Package: rustdesk
|
||||
Version: %s
|
||||
@ -268,45 +265,45 @@ Description: A remote control software.
|
||||
|
||||
def ffi_bindgen_function_refactor():
|
||||
# workaround ffigen
|
||||
os.system(
|
||||
system2(
|
||||
'sed -i "s/ffi.NativeFunction<ffi.Bool Function(DartPort/ffi.NativeFunction<ffi.Uint8 Function(DartPort/g" flutter/lib/generated_bridge.dart')
|
||||
|
||||
|
||||
def build_flutter_deb(version, features):
|
||||
if not skip_cargo:
|
||||
os.system(f'cargo build --features {features} --lib --release')
|
||||
system2(f'cargo build --features {features} --lib --release')
|
||||
ffi_bindgen_function_refactor()
|
||||
os.chdir('flutter')
|
||||
os.system('flutter build linux --release')
|
||||
os.system('mkdir -p tmpdeb/usr/bin/')
|
||||
os.system('mkdir -p tmpdeb/usr/lib/rustdesk')
|
||||
os.system('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||
os.system('mkdir -p tmpdeb/usr/share/applications/')
|
||||
os.system('mkdir -p tmpdeb/usr/share/polkit-1/actions')
|
||||
os.system('rm tmpdeb/usr/bin/rustdesk || true')
|
||||
os.system(
|
||||
system2('flutter build linux --release')
|
||||
system2('mkdir -p tmpdeb/usr/bin/')
|
||||
system2('mkdir -p tmpdeb/usr/lib/rustdesk')
|
||||
system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||
system2('mkdir -p tmpdeb/usr/share/applications/')
|
||||
system2('mkdir -p tmpdeb/usr/share/polkit-1/actions')
|
||||
system2('rm tmpdeb/usr/bin/rustdesk || true')
|
||||
system2(
|
||||
'cp -r build/linux/x64/release/bundle/* tmpdeb/usr/lib/rustdesk/')
|
||||
os.system(
|
||||
system2(
|
||||
'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||
os.system(
|
||||
system2(
|
||||
'cp ../res/128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png')
|
||||
os.system(
|
||||
system2(
|
||||
'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
|
||||
os.system(
|
||||
system2(
|
||||
'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop')
|
||||
os.system(
|
||||
system2(
|
||||
'cp ../res/com.rustdesk.RustDesk.policy tmpdeb/usr/share/polkit-1/actions/')
|
||||
os.system(
|
||||
system2(
|
||||
"echo \"#!/bin/sh\" >> tmpdeb/usr/share/rustdesk/files/polkit && chmod a+x tmpdeb/usr/share/rustdesk/files/polkit")
|
||||
|
||||
os.system('mkdir -p tmpdeb/DEBIAN')
|
||||
system2('mkdir -p tmpdeb/DEBIAN')
|
||||
generate_control_file(version)
|
||||
os.system('cp -a ../res/DEBIAN/* tmpdeb/DEBIAN/')
|
||||
system2('cp -a ../res/DEBIAN/* tmpdeb/DEBIAN/')
|
||||
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service')
|
||||
os.system('dpkg-deb -b tmpdeb rustdesk.deb;')
|
||||
system2('dpkg-deb -b tmpdeb rustdesk.deb;')
|
||||
|
||||
os.system('/bin/rm -rf tmpdeb/')
|
||||
os.system('/bin/rm -rf ../res/DEBIAN/control')
|
||||
system2('/bin/rm -rf tmpdeb/')
|
||||
system2('/bin/rm -rf ../res/DEBIAN/control')
|
||||
os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
|
||||
os.chdir("..")
|
||||
|
||||
@ -314,46 +311,43 @@ def build_flutter_deb(version, features):
|
||||
def build_flutter_dmg(version, features):
|
||||
if not skip_cargo:
|
||||
# set minimum osx build target, now is 10.14, which is the same as the flutter xcode project
|
||||
os.system(f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release')
|
||||
system2(f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release')
|
||||
# copy dylib
|
||||
os.system(
|
||||
system2(
|
||||
"cp target/release/liblibrustdesk.dylib target/release/librustdesk.dylib")
|
||||
# ffi_bindgen_function_refactor()
|
||||
# limitations from flutter rust bridge
|
||||
os.system('sed -i "" "s/char \*\*rustdesk_core_main(int \*args_len);//" flutter/macos/Runner/bridge_generated.h')
|
||||
os.chdir('flutter')
|
||||
os.system('flutter build macos --release')
|
||||
os.system(
|
||||
"create-dmg rustdesk.dmg ./build/macos/Build/Products/Release/RustDesk.app")
|
||||
system2('flutter build macos --release')
|
||||
system2(
|
||||
"create-dmg --volname \"RustDesk Installer\" --window-pos 200 120 --window-size 800 400 --icon-size 100 --app-drop-link 600 185 --icon RustDesk.app 200 190 --hide-extension RustDesk.app rustdesk.dmg ./build/macos/Build/Products/Release/RustDesk.app")
|
||||
os.rename("rustdesk.dmg", f"../rustdesk-{version}.dmg")
|
||||
os.chdir("..")
|
||||
|
||||
|
||||
def build_flutter_arch_manjaro(version, features):
|
||||
if not skip_cargo:
|
||||
os.system(f'cargo build --features {features} --lib --release')
|
||||
system2(f'cargo build --features {features} --lib --release')
|
||||
ffi_bindgen_function_refactor()
|
||||
os.chdir('flutter')
|
||||
os.system('flutter build linux --release')
|
||||
os.system('strip build/linux/x64/release/bundle/lib/librustdesk.so')
|
||||
system2('flutter build linux --release')
|
||||
system2('strip build/linux/x64/release/bundle/lib/librustdesk.so')
|
||||
os.chdir('../res')
|
||||
os.system('HBB=`pwd`/.. FLUTTER=1 makepkg -f')
|
||||
system2('HBB=`pwd`/.. FLUTTER=1 makepkg -f')
|
||||
|
||||
|
||||
def build_flutter_windows(version, features):
|
||||
if not skip_cargo:
|
||||
os.system(f'cargo build --features {features} --lib --release')
|
||||
system2(f'cargo build --features {features} --lib --release')
|
||||
if not os.path.exists("target/release/librustdesk.dll"):
|
||||
print("cargo build failed, please check rust source code.")
|
||||
exit(-1)
|
||||
os.chdir('flutter')
|
||||
os.system('flutter build windows --release')
|
||||
system2('flutter build windows --release')
|
||||
os.chdir('..')
|
||||
shutil.copy2('target/release/deps/dylib_virtual_display.dll',
|
||||
flutter_win_target_dir)
|
||||
os.chdir('libs/portable')
|
||||
os.system('pip3 install -r requirements.txt')
|
||||
os.system(
|
||||
system2('pip3 install -r requirements.txt')
|
||||
system2(
|
||||
f'python3 ./generate.py -f ../../{flutter_win_target_dir} -o . -e ../../{flutter_win_target_dir}/rustdesk.exe')
|
||||
os.chdir('../..')
|
||||
if os.path.exists('./rustdesk_portable.exe'):
|
||||
@ -374,22 +368,15 @@ def main():
|
||||
parser = make_parser()
|
||||
args = parser.parse_args()
|
||||
|
||||
shutil.copy2('Cargo.toml', 'Cargo.toml.bk')
|
||||
shutil.copy2('src/main.rs', 'src/main.rs.bk')
|
||||
if windows:
|
||||
txt = open('src/main.rs', encoding='utf8').read()
|
||||
with open('src/main.rs', 'wt', encoding='utf8') as fh:
|
||||
fh.write(txt.replace(
|
||||
'//#![windows_subsystem', '#![windows_subsystem'))
|
||||
if os.path.exists(exe_path):
|
||||
os.unlink(exe_path)
|
||||
if os.path.isfile('/usr/bin/pacman'):
|
||||
os.system('git checkout src/ui/common.tis')
|
||||
system2('git checkout src/ui/common.tis')
|
||||
version = get_version()
|
||||
features = ','.join(get_features(args))
|
||||
flutter = args.flutter
|
||||
if not flutter:
|
||||
os.system('python3 res/inline-sciter.py')
|
||||
system2('python3 res/inline-sciter.py')
|
||||
print(args.skip_cargo)
|
||||
if args.skip_cargo:
|
||||
skip_cargo = True
|
||||
@ -397,55 +384,55 @@ def main():
|
||||
if windows:
|
||||
# build virtual display dynamic library
|
||||
os.chdir('libs/virtual_display/dylib')
|
||||
os.system('cargo build --release')
|
||||
system2('cargo build --release')
|
||||
os.chdir('../../..')
|
||||
|
||||
if flutter:
|
||||
build_flutter_windows(version, features)
|
||||
return
|
||||
os.system('cargo build --release --features ' + features)
|
||||
# os.system('upx.exe target/release/rustdesk.exe')
|
||||
os.system('mv target/release/rustdesk.exe target/release/RustDesk.exe')
|
||||
system2('cargo build --release --features ' + features)
|
||||
# system2('upx.exe target/release/rustdesk.exe')
|
||||
system2('mv target/release/rustdesk.exe target/release/RustDesk.exe')
|
||||
pa = os.environ.get('P')
|
||||
if pa:
|
||||
os.system(
|
||||
system2(
|
||||
f'signtool sign /a /v /p {pa} /debug /f .\\cert.pfx /t http://timestamp.digicert.com '
|
||||
'target\\release\\rustdesk.exe')
|
||||
else:
|
||||
print('Not signed')
|
||||
os.system(
|
||||
system2(
|
||||
f'cp -rf target/release/RustDesk.exe rustdesk-{version}-win7-install.exe')
|
||||
elif os.path.isfile('/usr/bin/pacman'):
|
||||
# pacman -S -needed base-devel
|
||||
os.system("sed -i 's/pkgver=.*/pkgver=%s/g' res/PKGBUILD" % version)
|
||||
system2("sed -i 's/pkgver=.*/pkgver=%s/g' res/PKGBUILD" % version)
|
||||
if flutter:
|
||||
build_flutter_arch_manjaro(version, features)
|
||||
else:
|
||||
os.system('cargo build --release --features ' + features)
|
||||
os.system('git checkout src/ui/common.tis')
|
||||
os.system('strip target/release/rustdesk')
|
||||
os.system('ln -s res/pacman_install && ln -s res/PKGBUILD')
|
||||
os.system('HBB=`pwd` makepkg -f')
|
||||
os.system('mv rustdesk-%s-0-x86_64.pkg.tar.zst rustdesk-%s-manjaro-arch.pkg.tar.zst' % (
|
||||
system2('cargo build --release --features ' + features)
|
||||
system2('git checkout src/ui/common.tis')
|
||||
system2('strip target/release/rustdesk')
|
||||
system2('ln -s res/pacman_install && ln -s res/PKGBUILD')
|
||||
system2('HBB=`pwd` makepkg -f')
|
||||
system2('mv rustdesk-%s-0-x86_64.pkg.tar.zst rustdesk-%s-manjaro-arch.pkg.tar.zst' % (
|
||||
version, version))
|
||||
# pacman -U ./rustdesk.pkg.tar.zst
|
||||
elif os.path.isfile('/usr/bin/yum'):
|
||||
os.system('cargo build --release --features ' + features)
|
||||
os.system('strip target/release/rustdesk')
|
||||
os.system(
|
||||
system2('cargo build --release --features ' + features)
|
||||
system2('strip target/release/rustdesk')
|
||||
system2(
|
||||
"sed -i 's/Version: .*/Version: %s/g' res/rpm.spec" % version)
|
||||
os.system('HBB=`pwd` rpmbuild -ba res/rpm.spec')
|
||||
os.system(
|
||||
system2('HBB=`pwd` rpmbuild -ba res/rpm.spec')
|
||||
system2(
|
||||
'mv $HOME/rpmbuild/RPMS/x86_64/rustdesk-%s-0.x86_64.rpm ./rustdesk-%s-fedora28-centos8.rpm' % (
|
||||
version, version))
|
||||
# yum localinstall rustdesk.rpm
|
||||
elif os.path.isfile('/usr/bin/zypper'):
|
||||
os.system('cargo build --release --features ' + features)
|
||||
os.system('strip target/release/rustdesk')
|
||||
os.system(
|
||||
system2('cargo build --release --features ' + features)
|
||||
system2('strip target/release/rustdesk')
|
||||
system2(
|
||||
"sed -i 's/Version: .*/Version: %s/g' res/rpm-suse.spec" % version)
|
||||
os.system('HBB=`pwd` rpmbuild -ba res/rpm-suse.spec')
|
||||
os.system(
|
||||
system2('HBB=`pwd` rpmbuild -ba res/rpm-suse.spec')
|
||||
system2(
|
||||
'mv $HOME/rpmbuild/RPMS/x86_64/rustdesk-%s-0.x86_64.rpm ./rustdesk-%s-suse.rpm' % (
|
||||
version, version))
|
||||
# yum localinstall rustdesk.rpm
|
||||
@ -455,18 +442,18 @@ def main():
|
||||
build_flutter_dmg(version, features)
|
||||
pass
|
||||
else:
|
||||
# os.system(
|
||||
# system2(
|
||||
# 'mv target/release/bundle/deb/rustdesk*.deb ./flutter/rustdesk.deb')
|
||||
build_flutter_deb(version, features)
|
||||
else:
|
||||
os.system('cargo bundle --release --features ' + features)
|
||||
system2('cargo bundle --release --features ' + features)
|
||||
if osx:
|
||||
os.system(
|
||||
system2(
|
||||
'strip target/release/bundle/osx/RustDesk.app/Contents/MacOS/rustdesk')
|
||||
os.system(
|
||||
system2(
|
||||
'cp libsciter.dylib target/release/bundle/osx/RustDesk.app/Contents/MacOS/')
|
||||
# https://github.com/sindresorhus/create-dmg
|
||||
os.system('/bin/rm -rf *.dmg')
|
||||
system2('/bin/rm -rf *.dmg')
|
||||
plist = "target/release/bundle/osx/RustDesk.app/Contents/Info.plist"
|
||||
txt = open(plist).read()
|
||||
with open(plist, "wt") as fh:
|
||||
@ -476,7 +463,7 @@ def main():
|
||||
</dict>"""))
|
||||
pa = os.environ.get('P')
|
||||
if pa:
|
||||
os.system('''
|
||||
system2('''
|
||||
# buggy: rcodesign sign ... path/*, have to sign one by one
|
||||
# install rcodesign via cargo install apple-codesign
|
||||
#rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/rustdesk
|
||||
@ -486,11 +473,11 @@ def main():
|
||||
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/*
|
||||
codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app
|
||||
'''.format(pa))
|
||||
os.system('create-dmg target/release/bundle/osx/RustDesk.app')
|
||||
system2('create-dmg target/release/bundle/osx/RustDesk.app')
|
||||
os.rename('RustDesk %s.dmg' %
|
||||
version, 'rustdesk-%s.dmg' % version)
|
||||
if pa:
|
||||
os.system('''
|
||||
system2('''
|
||||
# https://pyoxidizer.readthedocs.io/en/apple-codesign-0.14.0/apple_codesign.html
|
||||
# https://pyoxidizer.readthedocs.io/en/stable/tugger_code_signing.html
|
||||
# https://developer.apple.com/developer-id/
|
||||
@ -507,34 +494,32 @@ def main():
|
||||
print('Not signed')
|
||||
else:
|
||||
# buid deb package
|
||||
os.system(
|
||||
system2(
|
||||
'mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb')
|
||||
os.system('dpkg-deb -R rustdesk.deb tmpdeb')
|
||||
os.system('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||
os.system(
|
||||
system2('dpkg-deb -R rustdesk.deb tmpdeb')
|
||||
system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||
system2(
|
||||
'cp res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||
os.system(
|
||||
system2(
|
||||
'cp res/128x128@2x.png tmpdeb/usr/share/rustdesk/files/rustdesk.png')
|
||||
os.system(
|
||||
system2(
|
||||
'cp res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
|
||||
os.system(
|
||||
system2(
|
||||
'cp res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop')
|
||||
os.system('cp -a res/DEBIAN/* tmpdeb/DEBIAN/')
|
||||
os.system('strip tmpdeb/usr/bin/rustdesk')
|
||||
os.system('mkdir -p tmpdeb/usr/lib/rustdesk')
|
||||
os.system('mv tmpdeb/usr/bin/rustdesk tmpdeb/usr/lib/rustdesk/')
|
||||
os.system('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/')
|
||||
system2('cp -a res/DEBIAN/* tmpdeb/DEBIAN/')
|
||||
system2('strip tmpdeb/usr/bin/rustdesk')
|
||||
system2('mkdir -p tmpdeb/usr/lib/rustdesk')
|
||||
system2('mv tmpdeb/usr/bin/rustdesk tmpdeb/usr/lib/rustdesk/')
|
||||
system2('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/')
|
||||
md5_file('usr/share/rustdesk/files/systemd/rustdesk.service')
|
||||
md5_file('usr/lib/rustdesk/libsciter-gtk.so')
|
||||
os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')
|
||||
system2('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')
|
||||
os.rename('rustdesk.deb', 'rustdesk-%s.deb' % version)
|
||||
os.system("mv Cargo.toml.bk Cargo.toml")
|
||||
os.system("mv src/main.rs.bk src/main.rs")
|
||||
|
||||
|
||||
def md5_file(fn):
|
||||
md5 = hashlib.md5(open('tmpdeb/' + fn, 'rb').read()).hexdigest()
|
||||
os.system('echo "%s %s" >> tmpdeb/DEBIAN/md5sums' % (md5, fn))
|
||||
system2('echo "%s %s" >> tmpdeb/DEBIAN/md5sums' % (md5, fn))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
9
build.rs
@ -9,7 +9,14 @@ fn build_windows() {
|
||||
#[cfg(target_os = "macos")]
|
||||
fn build_mac() {
|
||||
let file = "src/platform/macos.mm";
|
||||
cc::Build::new().file(file).compile("macos");
|
||||
let mut b = cc::Build::new();
|
||||
if let Ok(os_version::OsVersion::MacOS(v)) = os_version::detect() {
|
||||
let v = v.version;
|
||||
if v.contains("10.14") {
|
||||
b.flag("-DNO_InputMonitoringAuthStatus=1");
|
||||
}
|
||||
}
|
||||
b.file(file).compile("macos");
|
||||
println!("cargo:rerun-if-changed={}", file);
|
||||
}
|
||||
|
||||
|
||||
136
docs/CODE_OF_CONDUCT-NL.md
Normal file
@ -0,0 +1,136 @@
|
||||
|
||||
# Gedragscode Overeenkomst Medewerkers
|
||||
|
||||
## Onze Belofte
|
||||
|
||||
Wij als leden, medewerkers en leiders beloven deelname aan onze
|
||||
gemeenschap een pesterij-vrije ervaring te maken voor iedereen, ongeacht leeftijd, lichaamsgrootte,
|
||||
zichtbare of onzichtbare handicap, etniciteit, geslachtskenmerken, gender
|
||||
identiteit en expressie, ervaringsniveau, opleiding, sociaal-economische status,
|
||||
nationaliteit, persoonlijk voorkomen, ras, religie of seksuele identiteit
|
||||
en geaardheid.
|
||||
|
||||
Wij beloven te handelen en met elkaar om te gaan op manieren die bijdragen aan een open, gastvrije,
|
||||
diverse, inclusieve en gezonde gemeenschap.
|
||||
|
||||
## Onze Normen
|
||||
|
||||
Voorbeelden van gedrag dat bijdraagt tot een positieve omgeving voor onze
|
||||
gemeenschap omvatten:
|
||||
|
||||
* Medeleven en vriendelijkheid tonen tegenover andere mensen
|
||||
* Respect hebben voor verschillende meningen, standpunten en ervaringen
|
||||
* Constructieve feedback geven en met dank aanvaarden
|
||||
* Verantwoordelijkheid accepteren en excuses aanbieden aan degenen die door onze fouten zijn getroffen,
|
||||
en leren van de ervaring
|
||||
* Focussen op wat het beste is, niet alleen voor ons als individu, maar voor de
|
||||
totale gemeenschap
|
||||
|
||||
Voorbeelden van onaanvaardbaar gedrag zijn:
|
||||
|
||||
* Het gebruik van seksueel getinte taal of beelden, en seksuele aandacht of
|
||||
alle soorten avances
|
||||
* Treiteren, beledigende of denigrerende opmerkingen en persoonlijke of politieke aanvallen.
|
||||
* Openbare of persoonlijke intimidatie
|
||||
* Publiceren van andermans persoonlijke informatie, zoals een fysiek adres of e-mail,
|
||||
zonder hun uitdrukkelijke toestemming
|
||||
* Ander gedrag dat normaal als ongepast kan worden beschouwd in een
|
||||
professionele omgeving
|
||||
|
||||
## Verantwoordelijkheden inzake Handhaving
|
||||
|
||||
De leiders van de Gemeenschap zijn verantwoordelijk voor het verduidelijken
|
||||
en handhaven van onze normen voor aanvaardbaar gedrag en zullen passende
|
||||
en billijke corrigerende maatregelen nemen als reactie op gedrag dat zij ongepast,
|
||||
bedreigend, beledigend of schadelijk achten.
|
||||
|
||||
Leiders van de Gemeenschap hebben het recht en de verantwoordelijkheid om
|
||||
commentaar, bijdragen, code, wikibewerkingen, issues en andere bijdragen die
|
||||
niet in overeenstemming zijn met deze Gedragscode te verwijderen, te bewerken of
|
||||
af te wijzen, en zullen de redenen voor moderatiebeslissingen zo nodig meedelen.
|
||||
|
||||
## Toepassingsgebied
|
||||
|
||||
Deze Gedragscode geldt binnen alle gemeenschapsruimtes en is ook van toepassing
|
||||
wanneer iemand de gemeenschap officieel vertegenwoordigt in openbare ruimtes.
|
||||
Voorbeelden van het vertegenwoordigen van onze gemeenschap zijn het gebruik van
|
||||
een officieel e-mailadres, het posten via een officieel sociaal media-account of het
|
||||
optreden als aangewezen vertegenwoordiger bij een online of offline evenement.
|
||||
|
||||
## Handhaving
|
||||
|
||||
Gevallen van beledigend, intimiderend of anderszins onaanvaardbaar gedrag kunnen
|
||||
worden gemeld aan de gemeenschapsleiders die verantwoordelijk zijn voor de
|
||||
handhaving op [info@rustdesk.com](mailto:info@rustdesk.com).
|
||||
Alle klachten zullen snel en eerlijk worden onderzocht.
|
||||
|
||||
Alle leiders van de gemeenschap zijn verplicht de privacy en de veiligheid van
|
||||
de melder van een incident te respecteren.
|
||||
|
||||
## Handhaving Richtlijnen
|
||||
|
||||
De leiders van de Gemeenschap volgen deze Communautaire Impact Richtlijnen bij
|
||||
het bepalen van de consequenties voor elke actie die zij in strijd achten
|
||||
met deze Gedragscode:
|
||||
|
||||
### 1. Rechtzetting
|
||||
|
||||
**Gevolgen Gemeenschap**: Gebruik van ongepast taalgebruik of ander gedrag
|
||||
dat onprofessioneel of ongewenst wordt geacht in de gemeenschap.
|
||||
|
||||
**Gevolgen**: Een persoonlijke, schriftelijke waarschuwing van de leiders van
|
||||
de gemeenschap, met duidelijkheid over de aard van de overtreding en een
|
||||
uitleg waarom het gedrag ongepast was.
|
||||
Een publieke verontschuldiging kan worden gevraagd.
|
||||
|
||||
### 2. Waarschuwing
|
||||
|
||||
**Gevolgen Gemeenschap**: Een overtreding door een enkel incident of
|
||||
een reeks handelingen.
|
||||
|
||||
**Gevolgen**: Geen interactie met de betrokken personen, inclusief
|
||||
ongevraagde interactie met degenen die de Gedragscode handhaven,
|
||||
gedurende een bepaalde periode. Dit omvat het vermijden van interacties
|
||||
in gemeenschapsruimtes en externe kanalen zoals sociale media.
|
||||
Overtreding van deze voorwaarden kan leiden tot een tijdelijke
|
||||
of permanente uitsluiting.
|
||||
|
||||
### 3. Tijdelijke Uitsluiting
|
||||
|
||||
**Gevolgen Gemeenschap**: Een ernstige schending van de
|
||||
gemeenschapsnormen, waaronder aanhoudend ongepast gedrag.
|
||||
|
||||
**Gevolgen**: Een tijdelijk verbod op elke vorm van interactie
|
||||
of openbare communicatie met de gemeenschap voor een bepaalde
|
||||
periode. Geen openbare of private interactie met de betrokkenen,
|
||||
inclusief ongevraagde interactie met degenen die de gedragscode
|
||||
handhaven, is gedurende deze periode toegestaan.
|
||||
Overtreding van deze voorwaarden kan leiden tot een permanente uitsluiting.
|
||||
|
||||
### 4. Permanente Uitsluiting
|
||||
|
||||
**Gevolgen Gemeenschap**: Aantonen van een patroon van schending van
|
||||
de gemeenschapsnormen, waaronder aanhoudend ongepast gedrag, intimidatie
|
||||
van een individu, of agressie tegen of vernedering van klassen van individuen.
|
||||
|
||||
**Gevolgen**: Een permanente uitsluiting van elke vorm van publieke interactie
|
||||
binnen de gemeenschap.
|
||||
|
||||
## Naamsvermelding
|
||||
|
||||
Deze gedragscode is overgenomen uit de [Bijdrager Overeenkomst][homepagina],
|
||||
versie 2.0, beschikbaar op
|
||||
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
|
||||
|
||||
De Invloed op Richtlijnen voor Gemeenschap zijn gebaseerd op
|
||||
[Mozilla's gedragscode handhavingslijst][Mozilla CoC].
|
||||
|
||||
Voor antwoorden op veelgestelde vragen over deze gedragscode, zie de FAQ op
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Vertalingen zijn beschikbaar
|
||||
op [https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepagina]: https://www.contributor-covenant.org
|
||||
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[vertalingen]: https://www.contributor-covenant.org/translations
|
||||
50
docs/CONTRIBUTING-DE.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Beiträge zu RustDesk
|
||||
|
||||
RustDesk begrüßt Beiträge von jedem. Hier sind die Richtlinien, wenn Sie uns
|
||||
helfen möchten:
|
||||
|
||||
## Beiträge
|
||||
|
||||
Beiträge zu RustDesk oder seinen Abhängigkeiten sollten in Form von Pull
|
||||
Requests auf GitHub erfolgen. Jeder Pull Request wird von einem Hauptakteur
|
||||
(jemand mit der Erlaubnis, Korrekturen einzubringen) geprüft und entweder in den
|
||||
Hauptbaum eingefügt oder Feedback für notwendige Änderungen gegeben. Alle
|
||||
Beiträge sollten diesem Format folgen, auch die von Hauptakteuren.
|
||||
|
||||
Wenn Sie an einem Problem arbeiten möchten, melden Sie es bitte zuerst an, indem
|
||||
Sie auf GitHub erklären, dass Sie daran arbeiten möchten. Damit soll verhindert
|
||||
werden, dass Beiträge zum gleichen Thema doppelt bearbeitet werden.
|
||||
|
||||
## Checkliste für Pull Requests
|
||||
|
||||
- Verzweigen Sie sich vom Master-Branch und, falls nötig, wechseln Sie zum
|
||||
aktuellen Master-Branch, bevor Sie Ihren Pull Request einreichen. Wenn das
|
||||
Zusammenführen mit dem Master nicht reibungslos funktioniert, werden Sie
|
||||
möglicherweise aufgefordert, Ihre Änderungen zu überarbeiten.
|
||||
|
||||
- Commits sollten so klein wie möglich sein und gleichzeitig sicherstellen, dass
|
||||
jeder Commit unabhängig voneinander korrekt ist (d. h., jeder Commit sollte
|
||||
sich übersetzen lassen und Tests bestehen).
|
||||
|
||||
- Commits sollten von einem "Herkunftszertifikat für Entwickler"
|
||||
(https://developercertificate.org) begleitet werden, das besagt, dass Sie (und
|
||||
ggf. Ihr Arbeitgeber) mit den Bedingungen der [Projektlizenz](../LICENCE)
|
||||
einverstanden sind. In Git ist dies die Option `-s` für `git commit`.
|
||||
|
||||
- Wenn Ihr Patch nicht begutachtet wird oder Sie eine bestimmte Person zur
|
||||
Begutachtung benötigen, können Sie einem Gutachter mit @ antworten und um eine
|
||||
Begutachtung des Pull Requests oder einen Kommentar bitten. Sie können auch
|
||||
per [E-Mail](mailto:info@rustdesk.com) um eine Begutachtung bitten.
|
||||
|
||||
- Fügen Sie Tests hinzu, die sich auf den behobenen Fehler oder die neue
|
||||
Funktion beziehen.
|
||||
|
||||
Spezifische Git-Anweisungen finden Sie im [GitHub-Workflow](https://github.com/servo/servo/wiki/GitHub-workflow).
|
||||
|
||||
## Verhalten
|
||||
|
||||
https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT.md
|
||||
|
||||
## Kommunikation
|
||||
|
||||
RustDesk-Mitarbeiter arbeiten häufig im [Discord](https://discord.gg/nDceKgxnkV).
|
||||
50
docs/CONTRIBUTING-NL.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Bijdragen aan RustDesk
|
||||
|
||||
RustDesk verwelkomt bijdragen van iedereen. Hier zijn de richtlijnen als u denkt
|
||||
ons te willen helpen:
|
||||
|
||||
## Bijdragen
|
||||
|
||||
Bijdragen aan RustDesk of haar afhankelijkheden moeten worden gedaan in de
|
||||
vorm van GitHub pull verzoeken. Elk pull verzoek zal worden beoordeeld door
|
||||
een core bijdrager (iemand met toestemming om patches te plaatsen) en ofwel
|
||||
worden geplaatst in de hoofd structuur of feedback krijgen voor veranderingen
|
||||
die nodig zouden zijn. Alle bijdragen zouden dit formaat moeten volgen,
|
||||
zelfs die van kernmedewerkers.
|
||||
|
||||
Als je aan een onderwerp wilt werken, eis het dan eerst op door commentaar
|
||||
te geven op het GitHub onderwerp dat je eraan wilt werken. Dit is om dubbele
|
||||
inspanningen van medewerkers aan hetzelfde issue te voorkomen.
|
||||
|
||||
## Checklist Pull Aanvragen
|
||||
|
||||
- Maak een vertakking vanaf de master tak en, indien nodig, veranker naar de
|
||||
huidige master tak voordat je je pull verzoek indient. Als je het niet netjes
|
||||
samenvoegt met master kan je gevraagd worden om je wijzigingen
|
||||
opnieuw op te bouwen.
|
||||
|
||||
- Toezeggingen moeten zo klein mogelijk zijn, terwijl er voor gezorgd moet
|
||||
worden dat elke toezegging onafhankelijk correct is (dat wil zeggen, elke
|
||||
toezegging moet compileren en testen doorstaan).
|
||||
|
||||
- Toezeggingen moeten vergezeld gaan van een Certificaat van Oorsprong
|
||||
van de Ontwikkelaar (http://developercertificate.org) ondertekening, die aangeeft
|
||||
dat u (en uw werkgever indien van toepassing) akkoord gaat met de
|
||||
voorwaarden van het [project licentie](../LICENCE).
|
||||
In git is dit de `-s` optie van `git commit`
|
||||
|
||||
- Als je patch niet beoordeeld wordt of je hebt een specifiek persoon nodig om hem
|
||||
te beoordelen kunt u @-reply een reviewer vragen in het pull verzoek of een
|
||||
commentaar, of je kunt om een review vragen via [email](mailto:info@rustdesk.com).
|
||||
|
||||
- Tests toevoegen die relevant zijn voor de gerepareerde bug of de nieuwe functie.
|
||||
|
||||
Voor specifieke git instructies, zie [GitHub workflow 101](https://github.com/servo/servo/wiki/GitHub-workflow).
|
||||
|
||||
## Gedrag
|
||||
|
||||
https://github.com/rustdesk/rustdesk/blob/master/docs/CODE_OF_CONDUCT.md
|
||||
|
||||
## Communicatie
|
||||
|
||||
RustDesk medewerkers bezoeken frequent [Discord](https://discord.gg/nDceKgxnkV).
|
||||
14
docs/DEVCONTAINER-DE.md
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
Nach dem Start von Dev-Container im Docker-Container wird ein Linux-Binärprogramm im Debug-Modus erstellt.
|
||||
|
||||
Derzeit bietet Dev-Container Linux- und Android-Builds sowohl im Debug- als auch im Release-Modus an.
|
||||
|
||||
Nachfolgend finden Sie eine Tabelle mit Befehlen, die im Stammverzeichnis des Projekts ausgeführt werden müssen, um bestimmte Builds zu erstellen.
|
||||
|
||||
Kommando|Build-Typ|Modus
|
||||
-|-|-|
|
||||
`.devcontainer/build.sh --debug linux`|Linux|debug
|
||||
`.devcontainer/build.sh --release linux`|Linux|release
|
||||
`.devcontainer/build.sh --debug android`|android-arm64|debug
|
||||
`.devcontainer/build.sh --release android`|android-arm64|release
|
||||
|
||||
15
docs/DEVCONTAINER-NL.md
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
Na de start van devcontainer in docker container wordt een linux binaire in foutmodus aangemaakt.
|
||||
|
||||
Momenteel biedt devcontainer linux en android builds in zowel foutopsporing- als uitgave modus.
|
||||
|
||||
Hieronder staat de tabel met commando's die vanuit de root van het project moeten worden
|
||||
uitgevoerd om specifieke builds te maken.
|
||||
|
||||
Commando|Build Type|Modus
|
||||
-|-|-|
|
||||
`.devcontainer/build.sh --debug linux`|Linux|debug
|
||||
`.devcontainer/build.sh --release linux`|Linux|release
|
||||
`.devcontainer/build.sh --debug android`|android-arm64|debug
|
||||
`.devcontainer/build.sh --release android`|android-arm64|debug
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b> لغتك الأم, <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> و <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a>, README نحن بحاجة إلى مساعدتك لترجمة هذا </b>
|
||||
</p>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Struktura</a> •
|
||||
<a href="#snapshot">Ukázky</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">uživatelského rozhraní aplikace RustDesk</a> a <a href="https://github.com/rustdesk/doc.rustdesk.com">dokumentace k ní</a> do vašeho jazyka</b>
|
||||
</p>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#sådan-bygger-du-med-docker">Docker</a> •
|
||||
<a href="#filstruktur">Filstruktur</a> •
|
||||
<a href="#skærmbilleder">Skærmbilleder</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Vi har brug for din hjælp til at oversætte denne README, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> og <a href=" https://github.com/rustdesk/doc.rustdesk.com">Dokument</a> til dit modersmål</b>
|
||||
</p>
|
||||
|
||||
|
||||
@ -5,21 +5,21 @@
|
||||
<a href="#auf-docker-kompilieren">Docker</a> •
|
||||
<a href="#dateistruktur">Dateistruktur</a> •
|
||||
<a href="#screenshots">Screenshots</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-DA.md">Dansk</a>]<br>
|
||||
<b>Wir brauchen deine Hilfe, um dieses README, die <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk-Benutzeroberfläche</a> und die <a href="https://github.com/rustdesk/doc.rustdesk.com">Dokumentation</a> in deine Muttersprache zu übersetzen.</b>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-DA.md">Dansk</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Wir brauchen Ihre Hilfe, um dieses README, die <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk-Benutzeroberfläche</a> und die <a href="https://github.com/rustdesk/doc.rustdesk.com">Dokumentation</a> in Ihre Muttersprache zu übersetzen.</b>
|
||||
</p>
|
||||
|
||||
Rede mit uns auf: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
Reden Sie mit uns auf: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
|
||||
[](https://ko-fi.com/I2I04VU09)
|
||||
|
||||
RustDesk ist eine in Rust geschriebene Remote-Desktop-Software, die out of the box ohne besondere Konfiguration funktioniert. Du hast die volle Kontrolle über deine Daten und musst dir keine Sorgen um die Sicherheit machen. Du kannst unseren Rendezvous/Relay-Server nutzen, [einen eigenen Server aufsetzen](https://rustdesk.com/server) oder [einen eigenen Server programmieren](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
RustDesk ist eine in Rust geschriebene Remote-Desktop-Software, die out of the box ohne besondere Konfiguration funktioniert. Sie haben die volle Kontrolle über Ihre Daten und müssen sich keine Sorgen um die Sicherheit machen. Sie können unseren Rendezvous/Relay-Server nutzen, [einen eigenen Server aufsetzen](https://rustdesk.com/server) oder [einen eigenen Server programmieren](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
|
||||

|
||||
|
||||
RustDesk heißt jegliche Mitarbeit willkommen. Schau dir [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) an, wenn du Unterstützung beim Start brauchst.
|
||||
RustDesk heißt jegliche Mitarbeit willkommen. Schauen Sie sich [CONTRIBUTING-DE.md](CONTRIBUTING-DE.md) an, wenn Sie Unterstützung beim Start brauchen.
|
||||
|
||||
[**Wie arbeitet RustDesk?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||
[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
|
||||
|
||||
[**Programm herunterladen**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
@ -31,21 +31,29 @@ RustDesk heißt jegliche Mitarbeit willkommen. Schau dir [`docs/CONTRIBUTING.md`
|
||||
|
||||
## Freie öffentliche Server
|
||||
|
||||
Nachfolgend sind die Server gelistet, die du kostenlos nutzen kannst. Es kann sein, dass sich diese Liste immer mal wieder ändert. Falls du nicht in der Nähe einer dieser Server bist, kann es sein, dass deine Verbindung langsam sein wird.
|
||||
Nachfolgend sind die Server gelistet, die Sie kostenlos nutzen können. Es kann sein, dass sich diese Liste immer mal wieder ändert. Falls Sie nicht in der Nähe einer dieser Server sind, kann es sein, dass Ihre Verbindung langsam sein wird.
|
||||
| Standort | Anbieter | Spezifikation |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Südkorea (Seoul) | AWS lightsail | 1 vCPU / 0,5 GB RAM |
|
||||
| Deutschland | Hetzner | 2 vCPU / 4 GB RAM |
|
||||
| Deutschland | Codext | 4 vCPU / 8 GB RAM |
|
||||
| Finnland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8 GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8 GB RAM |
|
||||
| Ukraine (Kiew) | dc.volia (2VM) | 2 vCPU / 4 GB RAM |
|
||||
| Südkorea (Seoul) | [AWS lightsail](https://aws.amazon.com/de/) | 1 vCPU / 0,5 GB RAM |
|
||||
| Deutschland | [Hetzner](https://www.hetzner.com/de/) | 2 vCPU / 4 GB RAM |
|
||||
| Deutschland | [Codext](https://codext.de/) | 4 vCPU / 8 GB RAM |
|
||||
| Finnland (Helsinki) | [Netlock](https://netlockendpoint.com/de/index.html) | 4 vCPU / 8 GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com/de/index.html) | 4 vCPU / 8 GB RAM |
|
||||
| Ukraine (Kiew) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4 GB RAM |
|
||||
|
||||
## Dev-Container
|
||||
|
||||
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
|
||||
|
||||
Wenn Sie VS Code und Docker bereits installiert haben, können Sie auf das Abzeichen oben klicken, um loszulegen. Wenn Sie darauf klicken, wird VS Code automatisch die Dev-Container-Erweiterung installieren, den Quellcode in ein Container-Volume klonen und einen Dev-Container für die Verwendung aufsetzen.
|
||||
|
||||
Weitere Informationen finden Sie in [DEVCONTAINER-DE.md](DEVCONTAINER-DE.md).
|
||||
|
||||
## Abhängigkeiten
|
||||
|
||||
Desktop-Versionen verwenden [Sciter](https://sciter.com/) oder Flutter für die GUI, dieses Tutorial ist nur für Sciter.
|
||||
|
||||
Bitte lade die dynamische Bibliothek Sciter selbst herunter.
|
||||
Bitte laden Sie die dynamische Bibliothek Sciter selbst herunter.
|
||||
|
||||
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
|
||||
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
|
||||
@ -53,14 +61,14 @@ Bitte lade die dynamische Bibliothek Sciter selbst herunter.
|
||||
|
||||
## Grobe Schritte zum Kompilieren
|
||||
|
||||
- Bereite deine Rust-Entwicklungsumgebung und C++-Build-Umgebung vor
|
||||
- Bereiten Sie Ihre Rust-Entwicklungsumgebung und C++-Build-Umgebung vor
|
||||
|
||||
- Installiere [vcpkg](https://github.com/microsoft/vcpkg) und füge die Systemumgebungsvariable `VCPKG_ROOT` hinzu
|
||||
- Installieren Sie [vcpkg](https://github.com/microsoft/vcpkg) und fügen Sie die Systemumgebungsvariable `VCPKG_ROOT` hinzu
|
||||
|
||||
- Windows: `vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static`
|
||||
- Linux/macOS: `vcpkg install libvpx libyuv opus`
|
||||
|
||||
- Nutze `cargo run`
|
||||
- Nutzen Sie `cargo run`
|
||||
|
||||
## [Erstellen](https://rustdesk.com/docs/de/dev/build/)
|
||||
|
||||
@ -159,7 +167,7 @@ method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=2
|
||||
|
||||
## Auf Docker kompilieren
|
||||
|
||||
Beginne damit, das Repository zu klonen und den Docker-Container zu bauen:
|
||||
Beginnen Sie damit, das Repository zu klonen und den Docker-Container zu bauen:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
@ -167,25 +175,25 @@ cd rustdesk
|
||||
docker build -t "rustdesk-builder" .
|
||||
```
|
||||
|
||||
Führe jedes Mal, wenn du das Programm kompilieren musst, folgenden Befehl aus:
|
||||
Führen Sie jedes Mal, wenn Sie das Programm kompilieren müssen, folgenden Befehl aus:
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Bedenke, dass das erste Kompilieren länger dauern kann, bis die Abhängigkeiten zwischengespeichert sind. Nachfolgende Kompiliervorgänge sind schneller. Wenn du verschiedene Argumente für den Kompilierbefehl angeben musst, kannst du dies am Ende des Befehls an der Position `<OPTIONAL-ARGS>` tun. Wenn du zum Beispiel eine optimierte Releaseversion kompilieren willst, kannst du `--release` am Ende des Befehls anhängen. Das daraus entstehende Programm findest du im Zielordner auf deinem System. Du kannst es mit folgendem Befehl ausführen:
|
||||
Bedenken Sie, dass das erste Kompilieren länger dauern kann, bis die Abhängigkeiten zwischengespeichert sind. Nachfolgende Kompiliervorgänge sind schneller. Wenn Sie verschiedene Argumente für den Kompilierbefehl angeben müssen, können Sie dies am Ende des Befehls an der Position `<OPTIONAL-ARGS>` tun. Wenn Sie zum Beispiel eine optimierte Releaseversion kompilieren wollen, können Sie `--release` am Ende des Befehls anhängen. Das daraus entstehende Programm finden Sie im Zielordner auf Ihrem System. Sie können es mit folgendem Befehl ausführen:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
```
|
||||
|
||||
Oder, wenn du eine Releaseversion benutzt:
|
||||
Oder, wenn Sie eine Releaseversion benutzen:
|
||||
|
||||
```sh
|
||||
target/release/rustdesk
|
||||
```
|
||||
|
||||
Bitte stelle sicher, dass du diese Befehle im Stammverzeichnis des RustDesk-Repositorys nutzt. Ansonsten kann es passieren, dass das Programm die Ressourcen nicht finden kann. Bitte bedenke auch, dass andere Cargo-Unterbefehle wie `install` oder `run` aktuell noch nicht unterstützt werden, da sie das Programm innerhalb des Containers starten oder installieren würden, anstatt auf deinem eigentlichen System.
|
||||
Bitte stellen Sie sicher, dass Sie diese Befehle im Stammverzeichnis des RustDesk-Repositorys nutzen. Ansonsten kann es passieren, dass das Programm die Ressourcen nicht finden kann. Bitte bedenken Sie auch, dass andere Cargo-Unterbefehle wie `install` oder `run` aktuell noch nicht unterstützt werden, da sie das Programm innerhalb des Containers starten oder installieren würden, anstatt auf Ihrem eigentlichen System.
|
||||
|
||||
## Dateistruktur
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#kiel-kompili-kun-docker">Docker</a> •
|
||||
<a href="#dosierstrukturo">Strukturo</a> •
|
||||
<a href="#ekrankopio">Ekrankopio</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Ni bezonas helpon traduki tiun README kaj <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">la interfacon</a> al via denaska lingvo</b>
|
||||
</p>
|
||||
|
||||
@ -27,8 +27,9 @@ Malsupre estas la serviloj, kiuj vi uzas senpage, ĝi povas ŝanĝi laŭlonge de
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dependantaĵoj
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#como-compilar-con-docker">Docker</a> •
|
||||
<a href="#estructura-de-archivos">Estructura</a> •
|
||||
<a href="#capturas-de-pantalla">Capturas de pantalla</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Necesitamos tu ayuda para traducir este README a tu idioma</b>
|
||||
</p>
|
||||
|
||||
@ -34,8 +34,9 @@ A continuación se muestran los servidores gratuitos, pueden cambiar a medida qu
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dependencias
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<a href="#ساخت">ساخت</a> •
|
||||
<a href="#سرورهای-عمومی-رایگان">سرور</a>
|
||||
</p>
|
||||
<p align="center" dir="auto">[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]</p>
|
||||
<p align="center" dir="auto">[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]</p>
|
||||
<p dir="rtl" align="center"><b>برای ترجمه این سند (README)، <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang" dir="rtl">رابط کاربری RustDesk</a>، <a href="https://github.com/rustdesk/doc.rustdesk.com" dir="rtl">و مستندات آن</a> به زبان مادری شما به کمکتان نیازمندیم. </b></p>
|
||||
|
||||
با ما گفتگو کنید: [Reddit](https://www.reddit.com/r/rustdesk) | [Twitter](https://twitter.com/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV)
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Rakenne</a> •
|
||||
<a href="#snapshot">Tilannevedos</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi</b>
|
||||
</p>
|
||||
|
||||
@ -27,8 +27,9 @@ Alla on palvelimia, joita voit käyttää ilmaiseksi, ne saattavat muuttua ajan
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Riippuvuudet
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#comment-construire-avec-docker">Docker</a> -
|
||||
<a href="#structure-du-projet">Structure</a> -
|
||||
<a href="#images">Images</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle</b>.
|
||||
</p>
|
||||
|
||||
|
||||
219
docs/README-GR.md
Normal file
@ -0,0 +1,219 @@
|
||||
<p align="center">
|
||||
<img src="../res/logo-header.svg" alt="RustDesk - Your remote desktop"><br>
|
||||
<a href="#Δωρεάν-δημόσιοι-διακομιστές">Διακομιστές</a> •
|
||||
<a href="#Γενικά-βήματα-ώστε-να-κάνετε-build">Build</a> •
|
||||
<a href="#Πως-να-κάνετε-build-στο-Docker">Docker</a> •
|
||||
<a href="#Δομή-φακέλων">Δομή</a> •
|
||||
<a href="#Στιγμιότυπα">Στιγμιότυπα</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-DA.md">Dansk</a>]<br>
|
||||
<b>Χρειαζόμαστε τη βοήθειά σας για να μεταφράσουμε αυτό το αρχείο README, το <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> και το <a href="https://github.com/rustdesk/doc.rustdesk.com">Doc</a> στη μητρική σας γλώσσα</b>
|
||||
</p>
|
||||
|
||||
Επικοινωνήστε μαζί μας μέσω: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
|
||||
[](https://ko-fi.com/I2I04VU09)
|
||||
|
||||
Ένα λογισμικό απομακρυσμένης επιφάνειας εργασίας, γραμμένο σε γλώσσα Rust. Δεν χρειάζεται κάποια παραμετροποίηση, λειτουργεί αμέσως μετά την εγκατάσταση. Έχετε τον πλήρη έλεγχο των δεδομένων σας, χωρίς να ανησυχείτε για την ασφάλειά τους. Μπορείτε να χρησιμοποιήσετε τους προκαθορισμένους διακομιστές rendezvous/αναμετάδοσης, [να εγκαταστήσετε τον δικό σας διακομιστή](https://rustdesk.com/server), ή [να αναπτύξετε ένα δικό σας διακομιστή rendezvous/αναμετάδοσης](https://github.com/rustdesk/rustdesk-server-demo).
|
||||
|
||||

|
||||
|
||||
Το RustDesk ενθαρρύνει τη συνεισφορά όλων. Διαβάστε το [`docs/CONTRIBUTING.md`](docs/CONTRIBUTING.md) για βοήθεια στο πως να ξεκινήσετε.
|
||||
|
||||
[**Συχνές ερωτήσεις**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
|
||||
|
||||
[**Κατεβάστε τα αρχεία**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
[**NIGHTLY BUILD**](https://github.com/rustdesk/rustdesk/releases/tag/nightly)
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
|
||||
|
||||
## Δωρεάν δημόσιοι διακομιστές
|
||||
|
||||
Παρακάτω είναι οι διακομιστές που χρησιμοποιούνται δωρεάν, ενδέχεται να αλλάξουν με την πάροδο του χρόνου. Εάν δεν είστε κοντά σε ένα από αυτούς, το δίκτυό σας ίσως να είναι αργό.
|
||||
| Περιοχή | Πάροχος | Προδιαγραφές |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Σεούλ | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Γερμανία | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Γερμανία | Codext | 4 vCPU / 8GB RAM |
|
||||
| Φινλανδία (Ελσίνκι) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| ΗΠΑ (Άσμπερν) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ουκρανία (Κίεβο) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dev Container
|
||||
|
||||
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
|
||||
|
||||
Αν έχετε εγκατεστημένα το VS Code και το Docker, μπορείτε να ξεκινήσετε κάνοντας κλικ στην παραπάνω εικόνα. Αυτό θα έχει ως αποτέλεσμα, το VS Code να εγκαταστήσει αυτόματα την επέκταση Dev Containers, εάν χρειάζεται, θα κλωνοποιήσει τον πηγαίο κώδικα σε έναν νέο container και θα εκκινήσει ένα Dev Container για χρήση προγραμματισμού.
|
||||
|
||||
Για περισσότερες πληροφορίες μεταβείτε στο [DEVCONTAINER.md](docs/DEVCONTAINER.md).
|
||||
|
||||
## Προαπαιτούμενα για build
|
||||
|
||||
Στις παραθυρικές εκδόσεις χρησιμοποιείται είτε το [sciter](https://sciter.com/) είτε το Flutter, τα παρακάτω βήματα είναι μόνο για το Sciter.
|
||||
|
||||
Παρακαλώ κατεβάστε μόνοι σας την δυναμική βιβλιοθήκη sciter.
|
||||
|
||||
[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) |
|
||||
[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) |
|
||||
[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib)
|
||||
|
||||
## Γενικά βήματα ώστε να κάνετε build
|
||||
|
||||
- Προετοιμάστε τα περιβάλλοντα προγραμματισμού Rust και C++
|
||||
|
||||
- Εγκαταστήσετε το [vcpkg](https://github.com/microsoft/vcpkg), και ρυθμίστε σωστά την παράμετρο συστήματος `VCPKG_ROOT`
|
||||
|
||||
- Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static
|
||||
- Linux/MacOS: vcpkg install libvpx libyuv opus
|
||||
|
||||
- Εκτελέστε `cargo run`
|
||||
|
||||
## [Build](https://rustdesk.com/docs/en/dev/build/)
|
||||
|
||||
## Πως να το κάνετε build στο Linux
|
||||
|
||||
### Ubuntu 18 (Debian 10)
|
||||
|
||||
```sh
|
||||
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
|
||||
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
|
||||
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
|
||||
```
|
||||
|
||||
### openSUSE Tumbleweed
|
||||
|
||||
```sh
|
||||
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
|
||||
```
|
||||
### Fedora 28 (CentOS 8)
|
||||
|
||||
```sh
|
||||
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel
|
||||
```
|
||||
|
||||
### Arch (Manjaro)
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pipewire
|
||||
```
|
||||
|
||||
### Εγκατάσταση vcpkg
|
||||
|
||||
```sh
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
cd vcpkg
|
||||
git checkout 2021.12.01
|
||||
cd ..
|
||||
vcpkg/bootstrap-vcpkg.sh
|
||||
export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus
|
||||
```
|
||||
|
||||
### Διόρθωση libvpx (για Fedora)
|
||||
|
||||
```sh
|
||||
cd vcpkg/buildtrees/libvpx/src
|
||||
cd *
|
||||
./configure
|
||||
sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile
|
||||
sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile
|
||||
make
|
||||
cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/
|
||||
cd
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```sh
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
source $HOME/.cargo/env
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
mkdir -p target/debug
|
||||
wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so
|
||||
mv libsciter-gtk.so target/debug
|
||||
VCPKG_ROOT=$HOME/vcpkg cargo run
|
||||
```
|
||||
|
||||
### Αλλαγή του Wayland σε X11 (Xorg)
|
||||
|
||||
Το RustDesk δεν υποστηρίζει το πρωτόκολλο Wayland. Διαβάστε [εδώ](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) ώστε να ορίσετε το Xorg ως το προκαθορισμένο GNOME περιβάλλον.
|
||||
|
||||
## Υποστήριξη Wayland
|
||||
|
||||
Το Wayland προς το παρόν δεν διαθέτει κάποιο API το οποίο να στέλνει τα πατήματα πλήκτρων στα υπόλοιπα παράθυρα. Για τον λόγο αυτό, το Rustdesk χρησιμοποιεί ένα API από κατώτερο επίπεδο, όπως το `/dev/uinput` (Linux kernel level).
|
||||
|
||||
Σε περίπτωση που το Wayland είναι η ελεγχόμενη πλευρά, θα πρέπει να ξεκινήσετε με τον παρακάτω τρόπο:
|
||||
```bash
|
||||
# Start uinput service
|
||||
$ sudo rustdesk --service
|
||||
$ rustdesk
|
||||
```
|
||||
**Σημείωση**: Η εγγραφή οθόνης του Wayland χρησιμοποιεί διαφορετικές διεπαφές. Το RustDesk προς το παρόν υποστηρίζει μόνο org.freedesktop.portal.ScreenCast.
|
||||
```bash
|
||||
$ dbus-send --session --print-reply \
|
||||
--dest=org.freedesktop.portal.Desktop \
|
||||
/org/freedesktop/portal/desktop \
|
||||
org.freedesktop.DBus.Properties.Get \
|
||||
string:org.freedesktop.portal.ScreenCast string:version
|
||||
# Not support
|
||||
Error org.freedesktop.DBus.Error.InvalidArgs: No such interface “org.freedesktop.portal.ScreenCast”
|
||||
# Support
|
||||
method return time=1662544486.931020 sender=:1.54 -> destination=:1.139 serial=257 reply_serial=2
|
||||
variant uint32 4
|
||||
```
|
||||
|
||||
## Πως να κάνετε build στο Docker
|
||||
|
||||
Ξεκινήστε κλωνοποιώντας το αποθετήριο και κάνοντας build το docker container:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/rustdesk/rustdesk
|
||||
cd rustdesk
|
||||
docker build -t "rustdesk-builder" .
|
||||
```
|
||||
|
||||
Στη συνέχεια, κάθε φορά που επιθυμείτε να κάνετε build την εφαρμογή, εκτελέστε την ακόλουθη εντολή:
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder
|
||||
```
|
||||
|
||||
Σημειώστε ότι το πρώτο build μπορεί να διαρκέσει περισσότερο, ώστε να αποθηκευτούν στην προσωρινή μνήμη οι εξαρτήσεις, τα επόμενα build θα είναι ταχύτερα. Επιπλέον, εάν πρέπει να καθορίσετε διαφορετικές παραμέτρους στην εντολή build, μπορείτε να το κάνετε στο τέλος της εντολής με την χρήση `<OPTIONAL-ARGS>`. Για παράδειγμα, εάν επιθυμείτε να δημιουργήσετε μια βελτιστοποιημένη έκδοση της εφαρμογής, θα εκτελέσετε την παραπάνω εντολή ακολουθούμενη από το `--release`. Το εκτελέσιμο αρχείο θα είναι διαθέσιμο στον προκαθορισμένο φάκελο στο σύστημά σας και μπορεί να εκτελεστεί με:
|
||||
|
||||
```sh
|
||||
target/debug/rustdesk
|
||||
```
|
||||
|
||||
Ή στην περίπτωση μιας βελτιστοποιημένης έκδοσης της εφαρμογής εκτελέστε:
|
||||
|
||||
```sh
|
||||
target/release/rustdesk
|
||||
```
|
||||
|
||||
Βεβαιωθείτε ότι εκτελείτε αυτές τις εντολές από την αρχική διαδρομή του αποθετηρίου του Rustdesk, διαφορετικά η εφαρμογή ενδέχεται να μην είναι σε θέση να βρεί τους απαιτούμενους πόρους. Σημειώστε επίσης ότι άλλες υποεντολές, όπως το `install` ή το `run` δεν υποστηρίζονται επί του παρόντος μέσω αυτής της μεθόδου καθώς θα εγκαταστήσουν ή θα εκτελέσουν το πρόγραμμα εντός του container αντί του κεντρικού υπολογιστή.
|
||||
|
||||
## Δομή φακέλων
|
||||
|
||||
- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions
|
||||
- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: screen capture
|
||||
- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: platform specific keyboard/mouse control
|
||||
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI
|
||||
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: audio/clipboard/input/video services, and network connections
|
||||
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: start a peer connection
|
||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Communicate with [rustdesk-server](https://github.com/rustdesk/rustdesk-server), wait for remote direct (TCP hole punching) or relayed connection
|
||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: platform specific code
|
||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for mobile
|
||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: JavaScript for Flutter web client
|
||||
|
||||
## Στιγμιότυπα
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#hogyan-éptís-dockerrel">Docker</a> •
|
||||
<a href="#fájl-struktúra">Struktúra</a> •
|
||||
<a href="#képernyőképek">Képernyőképek</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Kell a segítséged, hogy lefordítsuk ezt a README-t, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">a RustDesk UI-t</a> és a <a href="https://github.com/rustdesk/doc.rustdesk.com">Dokumentációt</a> az anyanyelvedre</b>
|
||||
</p>
|
||||
|
||||
@ -35,8 +35,9 @@ Ezalatt az üzenet alatt találhatóak azok a publikus szerverek, amelyeket ingy
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> ke bahasa asli anda</b>
|
||||
</p>
|
||||
|
||||
@ -27,8 +27,9 @@ Di bawah ini adalah server yang bisa Anda gunakan secara gratis, dapat berubah s
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dependencies
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#come-compilare-con-docker">Docker</a> •
|
||||
<a href="#struttura-dei-file">Struttura</a> •
|
||||
<a href="#screenshots">Screenshots</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Abbiamo bisogno del tuo aiuto per tradurre questo README e la <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> nella tua lingua nativa</b>
|
||||
</p>
|
||||
|
||||
@ -27,8 +27,9 @@ Qui sotto trovate i server che possono essere usati gratuitamente, la lista potr
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dipendenze
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>このREADMEをあなたの母国語に翻訳するために、あなたの助けが必要です。</b>
|
||||
</p>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>README를 모국어로 번역하기 위한 당신의 도움의 필요합니다.</b>
|
||||
</p>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>ഈ README നിങ്ങളുടെ മാതൃഭാഷയിലേക്ക് വിവർത്തനം ചെയ്യാൻ ഞങ്ങൾക്ക് നിങ്ങളുടെ സഹായം ആവശ്യമാണ്</b>
|
||||
</p>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structuur</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>We hebben je hulp nodig om deze README te vertalen naar jouw moedertaal</b>
|
||||
</p>
|
||||
|
||||
@ -27,8 +27,9 @@ Onderstaande servers zijn de servers die je gratis kunt gebruiken, ze kunnen op
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Afhankelijkheden
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#jak-kompilować-za-pomocą-dockera">Docker</a> •
|
||||
<a href="#struktura-plików">Struktura</a> •
|
||||
<a href="#migawkisnapshoty">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Potrzebujemy twojej pomocy w tłumaczeniu README na twój ojczysty język</b>
|
||||
</p>
|
||||
|
||||
@ -27,8 +27,9 @@ Poniżej znajdują się serwery, z których można korzystać za darmo, może si
|
||||
| Seoul | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Germany | Hetzner | 2 vCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Ukraine (Kyiv) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Zależności
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#como-compilar-com-docker">Docker</a> •
|
||||
<a href="#estrutura-de-arquivos">Estrutura</a> •
|
||||
<a href="#screenshots">Screenshots</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Precisamos de sua ajuda para traduzir este README e a <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">UI do RustDesk</a> para sua língua nativa</b>
|
||||
</p>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Нам нужна ваша помощь для перевода этого README и <a href="https://github.com/rustdesk/rustdesk/tree/master/src/rustdesk/tree/master/src/lang">RustDesk UI</a> на ваш родной язык</B>
|
||||
</p>
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
<p align="center">
|
||||
<img src="../res/logo-header.svg" alt="RustDesk - Ваш віддалений робочий стіл"><br>
|
||||
<a href="#free-public-servers">Servers</a> •
|
||||
<a href="#raw-steps-to-build">Build</a> •
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Structure</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
<a href="#безкоштовні-загальнодоступні-сервери">Сервери</a> •
|
||||
<a href="#первинні-кроки-для-складання">Складання</a> •
|
||||
<a href="#як-зібрати-за-допомогою-docker">Docker</a> •
|
||||
<a href="#структура-файлів">Структура</a> •
|
||||
<a href="#знімки">Знімки</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Нам потрібна ваша допомога для перекладу цього README і <a href="https://github.com/rustdesk/rustdesk/tree/master/src/rustdesk/tree/master/src/lang">RustDesk UI</a> на вашу рідну мову</B>
|
||||
</p>
|
||||
|
||||
@ -19,24 +19,37 @@
|
||||
|
||||
RustDesk вітає внесок кожного. Дивіться [`docs/CONTRIBUTING.md`](CONTRIBUTING.md) для допомоги на початку роботи.
|
||||
|
||||
[**FAQ**](https://github.com/rustdesk/rustdesk/wiki/FAQ)
|
||||
|
||||
[**Як працює RustDesk?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F)
|
||||
|
||||
[**ЗАВАНТАЖИТИ ДОДАТОК**](https://github.com/rustdesk/rustdesk/releases)
|
||||
[**ЗАВАНТАЖИТИ ЗАСТОСУНОК**](https://github.com/rustdesk/rustdesk/releases)
|
||||
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
|
||||
|
||||
## Безкоштовні загальнодоступні сервери
|
||||
|
||||
Нижче наведені сервери, для безкоштовного використання, вони можуть змінюватися з часом. Якщо ви не перебуваєте поруч з одним із них, ваша мережа може працювати повільно.
|
||||
| Місцезнаходження | Постачальник | Технічні характеристики |
|
||||
| --------- | ------------- | ------------------ |
|
||||
| Сеул | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Південна Корея (Сеул) | AWS lightsail | 1 vCPU / 0.5GB RAM |
|
||||
| Сінгапур | Vultr | 1 vCPU / 1GB RAM |
|
||||
| Даллас | Vultr | 1 vCPU / 1GB RAM
|
||||
Німеччина | Hetzner | 2 vCPU / 4GB RAM | 2 VCPU / 4GB RAM | Німеччина | Hetzner | 2 VCPU / 4GB RAM |
|
||||
| Germany | Codext | 4 vCPU / 8GB RAM |
|
||||
| Finland (Helsinki) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| USA (Ashburn) | 0x101 Cyber Security | 4 vCPU / 8GB RAM |
|
||||
| США (Даллас) | Vultr | 1 vCPU / 1GB RAM
|
||||
| Німеччина | Hetzner | 2 VCPU / 4GB RAM |
|
||||
| Німеччина | Codext | 4 vCPU / 8GB RAM |
|
||||
| Фінляндія (Гельсінкі) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| США (Ешберн) | [Netlock](https://netlockendpoint.com) | 4 vCPU / 8GB RAM |
|
||||
| Україна (Київ) | [dc.volia](https://dc.volia.com) | 2 vCPU / 4GB RAM |
|
||||
|
||||
## Dev Container
|
||||
|
||||
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/rustdesk/rustdesk)
|
||||
|
||||
Якщо у вас уже встановлено VS Code і Docker, ви можете натиснути значок вище, щоб почати. Клацання призведе до того, що VS Code автоматично встановить розширення Dev Containers, якщо це необхідно, клонує виcхідний код у том контейнера та розгорне контейнер dev для використання.
|
||||
|
||||
Дивіться [DEVCONTAINER.md](docs/DEVCONTAINER.md) для додаткової інфо.
|
||||
|
||||
## Залежності
|
||||
|
||||
@ -64,9 +77,16 @@ RustDesk вітає внесок кожного. Дивіться [`docs/CONTRIB
|
||||
### Ubuntu 18 (Debian 10)
|
||||
|
||||
```sh
|
||||
sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake
|
||||
sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev \
|
||||
libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake make \
|
||||
libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
|
||||
```
|
||||
|
||||
### openSUSE Tumbleweed
|
||||
|
||||
```sh
|
||||
sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel
|
||||
```
|
||||
### Fedora 28 (CentOS 8)
|
||||
|
||||
```sh
|
||||
@ -91,30 +111,6 @@ export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus
|
||||
```
|
||||
|
||||
### Fedora 28 (CentOS 8)
|
||||
|
||||
````sh
|
||||
sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel
|
||||
```
|
||||
|
||||
### Arch (Manjaro)
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pipewire
|
||||
```
|
||||
|
||||
### Встановлення vcpkg
|
||||
|
||||
```sh
|
||||
git clone https://github.com/microsoft/vcpkg
|
||||
cd vcpkg
|
||||
git checkout 2021.12.01
|
||||
cd ...
|
||||
vcpkg/bootstrap-vcpkg.sh
|
||||
export VCPKG_ROOT=$HOME/vcpkg
|
||||
vcpkg/vcpkg install libvpx libyuv opus
|
||||
```
|
||||
|
||||
### Виправлення libvpx (для Fedora)
|
||||
|
||||
```sh
|
||||
@ -183,8 +179,10 @@ target/release/rustdesk
|
||||
- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: графічний інтерфейс користувача
|
||||
- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: сервіси аудіо/буфера обміну/вводу/відео та мережевих підключень
|
||||
- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: однорангове з'єднання
|
||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: зв'яжіться з [rustdesk-server](https://github.com/rustdesk/rustdesk-server), дочекайтеся віддаленого прямого (обхід TCP NAT) або ретрансльованого з'єднання
|
||||
- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: комунікація з [rustdesk-server](https://github.com/rustdesk/rustdesk-server), очікування віддаленого прямого (обхід TCP NAT) або ретрансльованого з'єднання
|
||||
- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: специфічний для платформи код
|
||||
- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: код Flutter для мобільних пристроїв
|
||||
- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: JavaScript для Flutter веб клієнту
|
||||
|
||||
## Знімки
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#how-to-build-with-docker">Docker</a> •
|
||||
<a href="#file-structure">Cấu trúc tệp tin</a> •
|
||||
<a href="#snapshot">Snapshot</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-ZH.md">中文</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
<b>Chúng tôi cần sự gíup đỡ của bạn để dịch trang README này, <a href="https://github.com/rustdesk/rustdesk/tree/master/src/lang">RustDesk UI</a> và <a href="https://github.com/rustdesk/doc.rustdesk.com">tài liệu</a> sang ngôn ngữ bản địa của bạn</b>
|
||||
</p>
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<a href="#使用Docker编译">Docker</a> •
|
||||
<a href="#文件结构">结构</a> •
|
||||
<a href="#截图">截图</a><br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>]<br>
|
||||
[<a href="../README.md">English</a>] | [<a href="README-UA.md">Українська</a>] | [<a href="README-CS.md">česky</a>] | [<a href="README-HU.md">Magyar</a>] | [<a href="README-ES.md">Español</a>] | [<a href="README-FA.md">فارسی</a>] | [<a href="README-FR.md">Français</a>] | [<a href="README-DE.md">Deutsch</a>] | [<a href="README-PL.md">Polski</a>] | [<a href="README-ID.md">Indonesian</a>] | [<a href="README-FI.md">Suomi</a>] | [<a href="README-ML.md">മലയാളം</a>] | [<a href="README-JP.md">日本語</a>] | [<a href="README-NL.md">Nederlands</a>] | [<a href="README-IT.md">Italiano</a>] | [<a href="README-RU.md">Русский</a>] | [<a href="README-PTBR.md">Português (Brasil)</a>] | [<a href="README-EO.md">Esperanto</a>] | [<a href="README-KR.md">한국어</a>] | [<a href="README-AR.md">العربي</a>] | [<a href="README-VN.md">Tiếng Việt</a>] | [<a href="README-GR.md">Ελληνικά</a>]<br>
|
||||
</p>
|
||||
|
||||
Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
|
||||
|
||||
11
docs/SECURITY-NL.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Veiligheidsbeleid
|
||||
|
||||
## Een Kwetsbaarheid Melden
|
||||
|
||||
Wij hechten veel waarde aan de veiligheid van het project. We moedigen alle gebruikers aan om kwetsbaarheden die ze ontdekken
|
||||
aan ons te melden. Als u een beveiligingslek in het RustDesk project vindt, meld dit dan op verantwoorde wijze door
|
||||
een e-mail te sturen naar info@rustdesk.com.
|
||||
|
||||
Op dit moment hebben we geen bug premie programma. We zijn een klein team dat een groot probleem probeert op te lossen.
|
||||
We verzoeken u dringend om alle kwetsbaarheden op verantwoorde wijze te melden, zodat we verder kunnen bouwen aan
|
||||
een veilige applicatie voor de hele gemeenschap.
|
||||
@ -1,11 +0,0 @@
|
||||
Een open-source remote desktop toepassing, het open source TeamViewer alternatief.
|
||||
Broncode: https://github.com/rustdesk/rustdesk
|
||||
Doc: https://rustdesk.com/docs/en/manual/mobile/
|
||||
|
||||
Om ervoor te zorgen dat een extern apparaat uw Android-apparaat via muis of aanraking kan besturen, moet u RustDesk toestaan om de "Accessibility" service te gebruiken. RustDesk gebruikt de AccessibilityService API om Android afstandsbediening te implementeren.
|
||||
|
||||
Naast afstandsbediening kunt u met RustDesk ook eenvoudig bestanden overzetten tussen Android-apparaten en pc's.
|
||||
|
||||
U hebt volledige controle over uw gegevens, zonder zich zorgen te maken over de veiligheid. U kunt onze rendez-vous/relay server gebruiken, of zelf hosten, of uw eigen rendez-vous/relay server schrijven. Self-hosting server is gratis en open source: https://github.com/rustdesk/rustdesk-server
|
||||
|
||||
Download en installeer de desktop versie van: https://rustdesk.com, dan kunt u uw desktop benaderen en bedienen vanaf uw mobiel, of uw mobiel bedienen vanaf uw desktop.
|
||||
11
fastlane/metadata/android/nl-NL/full_description.txt
Normal file
@ -0,0 +1,11 @@
|
||||
Een open-source toepassing voor bureaublad op afstand, het open-source alternatief voor TeamViewer.
|
||||
Bron code: https://github.com/rustdesk/rustdesk
|
||||
Doc: https://rustdesk.com/docs/en/manual/mobile/
|
||||
|
||||
Om ervoor te zorgen dat een extern apparaat uw Android-apparaat via muis of aanraking kan besturen, moet u RustDesk toestaan de "Toegankelijkheid" service te gebruiken. RustDesk gebruikt AccessibilityService API om Android afstandsbediening te kunnen implementeren.
|
||||
|
||||
Naast bediening op afstand kunt u met RustDesk ook eenvoudig bestanden overzetten tussen Android-apparaten en pc's.
|
||||
|
||||
U hebt volledige controle over uw gegevens, en u hoeft zich geen zorgen te maken over de veiligheid. U kunt onze rendez-vous/relay server gebruiken, of zelf hosten, of uw eigen rendez-vous/relay server schrijven. Self-hosting server is gratis en open source: https://github.com/rustdesk/rustdesk-server
|
||||
|
||||
Download en installeer de desktop versie vanaf: https://rustdesk.com, dan kunt u uw desktop benaderen en bedienen vanaf uw mobiel, of uw mobiel bedienen vanaf uw desktop.
|
||||
1
fastlane/metadata/android/nl-NL/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
Een open-source toepassing voor bureaublad op afstand, het open-source alternatief voor TeamViewer.
|
||||
@ -1 +0,0 @@
|
||||
Een open-source remote desktop applicatie, het open source alternatief voor TeamViewer.
|
||||
@ -1,9 +1,10 @@
|
||||
{
|
||||
"app-id": "org.rustdesk.rustdesk",
|
||||
"id": "com.rustdesk.RustDesk",
|
||||
"runtime": "org.freedesktop.Platform",
|
||||
"runtime-version": "21.08",
|
||||
"sdk": "org.freedesktop.Sdk",
|
||||
"command": "rustdesk",
|
||||
"icon": "share/rustdesk/files/rustdesk.png",
|
||||
"modules": [
|
||||
"shared-modules/libappindicator/libappindicator-gtk3-12.10.json",
|
||||
"xdotool.json",
|
||||
@ -13,13 +14,22 @@
|
||||
"build-commands": [
|
||||
"bsdtar -zxvf rustdesk-1.2.0.deb",
|
||||
"tar -xvf ./data.tar.xz",
|
||||
"cp -r ./usr /app/",
|
||||
"mkdir -p /app/bin && ln -s /app/usr/lib/rustdesk/rustdesk /app/bin/rustdesk"
|
||||
"cp -r ./usr/* /app/",
|
||||
"mkdir -p /app/bin && ln -s /app/lib/rustdesk/rustdesk /app/bin/rustdesk",
|
||||
"mv /app/share/applications/rustdesk.desktop /app/share/applications/com.rustdesk.RustDesk.desktop",
|
||||
"sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/com.rustdesk.RustDesk.desktop",
|
||||
"sed -i '/^Icon=/ c\\Icon=com.rustdesk.RustDesk' /app/share/applications/rustdesk-link.desktop",
|
||||
"for size in 16 24 32 48 64 128 256 512; do\n rsvg-convert -w $size -h $size -f png -o $size.png logo.svg\n install -Dm644 $size.png /app/share/icons/hicolor/${size}x${size}/apps/com.rustdesk.RustDesk.png\n done"
|
||||
],
|
||||
"cleanup": ["/include", "/lib/pkgconfig", "/share/gtk-doc"],
|
||||
"sources": [
|
||||
{
|
||||
"type": "file",
|
||||
"path": "../rustdesk-1.2.0.deb"
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"path": "../res/logo.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -35,4 +45,4 @@
|
||||
"--socket=pulseaudio",
|
||||
"--talk-name=org.freedesktop.Flatpak"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,21 +11,25 @@
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<!--<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />-->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="RustDesk"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:supportsRtl="true"
|
||||
android:requestLegacyExternalStorage="true">
|
||||
android:supportsRtl="true">
|
||||
|
||||
<receiver
|
||||
android:name=".BootReceiver"
|
||||
android:enabled="false"
|
||||
android:exported="false">
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter android:priority="1000">
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
<!--ACTION_BOOT_COMPLETED for debug test on no root device-->
|
||||
<action android:name="com.carriez.flutter_hbb.DEBUG_BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
@ -52,8 +56,6 @@
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
@ -61,6 +63,11 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".PermissionRequestTransparentActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:theme="@style/Transparent" />
|
||||
|
||||
<service
|
||||
android:name=".MainService"
|
||||
android:enabled="true"
|
||||
@ -74,4 +81,4 @@
|
||||
android:value="2" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
||||
@ -1,21 +1,45 @@
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
import android.Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||
import android.Manifest.permission.SYSTEM_ALERT_WINDOW
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.hjq.permissions.XXPermissions
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
const val DEBUG_BOOT_COMPLETED = "com.carriez.flutter_hbb.DEBUG_BOOT_COMPLETED"
|
||||
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
private val logTag = "tagBootReceiver"
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if ("android.intent.action.BOOT_COMPLETED" == intent.action){
|
||||
val it = Intent(context,MainService::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
Log.d(logTag, "onReceive ${intent.action}")
|
||||
|
||||
if (Intent.ACTION_BOOT_COMPLETED == intent.action || DEBUG_BOOT_COMPLETED == intent.action) {
|
||||
// check SharedPreferences config
|
||||
val prefs = context.getSharedPreferences(KEY_SHARED_PREFERENCES, FlutterActivity.MODE_PRIVATE)
|
||||
if (!prefs.getBoolean(KEY_START_ON_BOOT_OPT, false)) {
|
||||
Log.d(logTag, "KEY_START_ON_BOOT_OPT is false")
|
||||
return
|
||||
}
|
||||
Toast.makeText(context, "RustDesk is Open", Toast.LENGTH_LONG).show();
|
||||
// check pre-permission
|
||||
if (!XXPermissions.isGranted(context, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, SYSTEM_ALERT_WINDOW)){
|
||||
Log.d(logTag, "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS or SYSTEM_ALERT_WINDOW is not granted")
|
||||
return
|
||||
}
|
||||
|
||||
val it = Intent(context, MainService::class.java).apply {
|
||||
action = ACT_INIT_MEDIA_PROJECTION_AND_SERVICE
|
||||
putExtra(EXT_INIT_FROM_BOOT, true)
|
||||
}
|
||||
Toast.makeText(context, "RustDesk is Open", Toast.LENGTH_LONG).show()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(it)
|
||||
}else{
|
||||
} else {
|
||||
context.startService(it)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,35 +7,29 @@ package com.carriez.flutter_hbb
|
||||
* Inspired by [droidVNC-NG] https://github.com/bk138/droidVNC-NG
|
||||
*/
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.media.projection.MediaProjectionManager
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.hjq.permissions.XXPermissions
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
const val MEDIA_REQUEST_CODE = 42
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
companion object {
|
||||
lateinit var flutterMethodChannel: MethodChannel
|
||||
var flutterMethodChannel: MethodChannel? = null
|
||||
}
|
||||
|
||||
private val channelTag = "mChannel"
|
||||
private val logTag = "mMainActivity"
|
||||
private var mediaProjectionResultIntent: Intent? = null
|
||||
private var mainService: MainService? = null
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
if (MainService.isReady) {
|
||||
@ -46,169 +40,32 @@ class MainActivity : FlutterActivity() {
|
||||
flutterMethodChannel = MethodChannel(
|
||||
flutterEngine.dartExecutor.binaryMessenger,
|
||||
channelTag
|
||||
).apply {
|
||||
// make sure result is set, otherwise flutter will await forever
|
||||
setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"init_service" -> {
|
||||
Intent(activity, MainService::class.java).also {
|
||||
bindService(it, serviceConnection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
if (MainService.isReady) {
|
||||
result.success(false)
|
||||
return@setMethodCallHandler
|
||||
}
|
||||
getMediaProjection()
|
||||
result.success(true)
|
||||
}
|
||||
"start_capture" -> {
|
||||
mainService?.let {
|
||||
result.success(it.startCapture())
|
||||
} ?: let {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"stop_service" -> {
|
||||
Log.d(logTag, "Stop service")
|
||||
mainService?.let {
|
||||
it.destroy()
|
||||
result.success(true)
|
||||
} ?: let {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"check_permission" -> {
|
||||
if (call.arguments is String) {
|
||||
result.success(checkPermission(context, call.arguments as String))
|
||||
} else {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"request_permission" -> {
|
||||
if (call.arguments is String) {
|
||||
requestPermission(context, call.arguments as String)
|
||||
result.success(true)
|
||||
} else {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"check_video_permission" -> {
|
||||
mainService?.let {
|
||||
result.success(it.checkMediaPermission())
|
||||
} ?: let {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"check_service" -> {
|
||||
flutterMethodChannel.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||
)
|
||||
flutterMethodChannel.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "media", "value" to MainService.isReady.toString())
|
||||
)
|
||||
result.success(true)
|
||||
}
|
||||
"init_input" -> {
|
||||
initInput()
|
||||
result.success(true)
|
||||
}
|
||||
"stop_input" -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
InputService.ctx?.disableSelf()
|
||||
}
|
||||
InputService.ctx = null
|
||||
flutterMethodChannel.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||
)
|
||||
result.success(true)
|
||||
}
|
||||
"cancel_notification" -> {
|
||||
try {
|
||||
val id = call.arguments as Int
|
||||
mainService?.cancelNotification(id)
|
||||
} finally {
|
||||
result.success(true)
|
||||
}
|
||||
}
|
||||
"enable_soft_keyboard" -> {
|
||||
// https://blog.csdn.net/hanye2020/article/details/105553780
|
||||
try {
|
||||
if (call.arguments as Boolean) {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
||||
} else {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
||||
}
|
||||
} finally {
|
||||
result.success(true)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
result.error("-1", "No such method", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMediaProjection() {
|
||||
val mMediaProjectionManager =
|
||||
getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||
val mIntent = mMediaProjectionManager.createScreenCaptureIntent()
|
||||
startActivityForResult(mIntent, MEDIA_REQUEST_CODE)
|
||||
}
|
||||
|
||||
private fun initService() {
|
||||
if (mediaProjectionResultIntent == null) {
|
||||
Log.w(logTag, "initService fail,mediaProjectionResultIntent is null")
|
||||
return
|
||||
}
|
||||
Log.d(logTag, "Init service")
|
||||
val serviceIntent = Intent(this, MainService::class.java)
|
||||
serviceIntent.action = INIT_SERVICE
|
||||
serviceIntent.putExtra(EXTRA_MP_DATA, mediaProjectionResultIntent)
|
||||
|
||||
launchMainService(serviceIntent)
|
||||
}
|
||||
|
||||
private fun launchMainService(intent: Intent) {
|
||||
// TEST api < O
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(intent)
|
||||
} else {
|
||||
startService(intent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initInput() {
|
||||
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent)
|
||||
}
|
||||
)
|
||||
initFlutterChannel(flutterMethodChannel!!)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val inputPer = InputService.isOpen
|
||||
activity.runOnUiThread {
|
||||
flutterMethodChannel.invokeMethod(
|
||||
flutterMethodChannel?.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "input", "value" to inputPer.toString())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestMediaProjection() {
|
||||
val intent = Intent(this, PermissionRequestTransparentActivity::class.java).apply {
|
||||
action = ACT_REQUEST_MEDIA_PROJECTION
|
||||
}
|
||||
startActivityForResult(intent, REQ_INVOKE_PERMISSION_ACTIVITY_MEDIA_PROJECTION)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == MEDIA_REQUEST_CODE) {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
mediaProjectionResultIntent = data
|
||||
initService()
|
||||
} else {
|
||||
flutterMethodChannel.invokeMethod("on_media_projection_canceled", null)
|
||||
}
|
||||
if (requestCode == REQ_INVOKE_PERMISSION_ACTIVITY_MEDIA_PROJECTION && resultCode == RES_FAILED) {
|
||||
flutterMethodChannel?.invokeMethod("on_media_projection_canceled", null)
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,4 +89,138 @@ class MainActivity : FlutterActivity() {
|
||||
mainService = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun initFlutterChannel(flutterMethodChannel: MethodChannel) {
|
||||
flutterMethodChannel.setMethodCallHandler { call, result ->
|
||||
// make sure result will be invoked, otherwise flutter will await forever
|
||||
when (call.method) {
|
||||
"init_service" -> {
|
||||
Intent(activity, MainService::class.java).also {
|
||||
bindService(it, serviceConnection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
if (MainService.isReady) {
|
||||
result.success(false)
|
||||
return@setMethodCallHandler
|
||||
}
|
||||
requestMediaProjection()
|
||||
result.success(true)
|
||||
}
|
||||
"start_capture" -> {
|
||||
mainService?.let {
|
||||
result.success(it.startCapture())
|
||||
} ?: let {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"stop_service" -> {
|
||||
Log.d(logTag, "Stop service")
|
||||
mainService?.let {
|
||||
it.destroy()
|
||||
result.success(true)
|
||||
} ?: let {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"check_permission" -> {
|
||||
if (call.arguments is String) {
|
||||
result.success(XXPermissions.isGranted(context, call.arguments as String))
|
||||
} else {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"request_permission" -> {
|
||||
if (call.arguments is String) {
|
||||
requestPermission(context, call.arguments as String)
|
||||
result.success(true)
|
||||
} else {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
START_ACTION -> {
|
||||
if (call.arguments is String) {
|
||||
startAction(context, call.arguments as String)
|
||||
result.success(true)
|
||||
} else {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"check_video_permission" -> {
|
||||
mainService?.let {
|
||||
result.success(it.checkMediaPermission())
|
||||
} ?: let {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
"check_service" -> {
|
||||
Companion.flutterMethodChannel?.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||
)
|
||||
Companion.flutterMethodChannel?.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "media", "value" to MainService.isReady.toString())
|
||||
)
|
||||
result.success(true)
|
||||
}
|
||||
"stop_input" -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
InputService.ctx?.disableSelf()
|
||||
}
|
||||
InputService.ctx = null
|
||||
Companion.flutterMethodChannel?.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||
)
|
||||
result.success(true)
|
||||
}
|
||||
"cancel_notification" -> {
|
||||
if (call.arguments is Int) {
|
||||
val id = call.arguments as Int
|
||||
mainService?.cancelNotification(id)
|
||||
} else {
|
||||
result.success(true)
|
||||
}
|
||||
}
|
||||
"enable_soft_keyboard" -> {
|
||||
// https://blog.csdn.net/hanye2020/article/details/105553780
|
||||
if (call.arguments as Boolean) {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
||||
} else {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
||||
}
|
||||
result.success(true)
|
||||
|
||||
}
|
||||
GET_START_ON_BOOT_OPT -> {
|
||||
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
|
||||
result.success(prefs.getBoolean(KEY_START_ON_BOOT_OPT, false))
|
||||
}
|
||||
SET_START_ON_BOOT_OPT -> {
|
||||
if (call.arguments is Boolean) {
|
||||
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
|
||||
val edit = prefs.edit()
|
||||
edit.putBoolean(KEY_START_ON_BOOT_OPT, call.arguments as Boolean)
|
||||
edit.apply()
|
||||
result.success(true)
|
||||
} else {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
SYNC_APP_DIR_CONFIG_PATH -> {
|
||||
if (call.arguments is String) {
|
||||
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
|
||||
val edit = prefs.edit()
|
||||
edit.putString(KEY_APP_DIR_CONFIG_PATH, call.arguments as String)
|
||||
edit.apply()
|
||||
result.success(true)
|
||||
} else {
|
||||
result.success(false)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
result.error("-1", "No such method", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@ import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.thread
|
||||
import org.json.JSONException
|
||||
@ -43,10 +44,6 @@ import java.nio.ByteBuffer
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
const val EXTRA_MP_DATA = "mp_intent"
|
||||
const val INIT_SERVICE = "init_service"
|
||||
const val ACTION_LOGIN_REQ_NOTIFY = "ACTION_LOGIN_REQ_NOTIFY"
|
||||
const val EXTRA_LOGIN_REQ_NOTIFY = "EXTRA_LOGIN_REQ_NOTIFY"
|
||||
|
||||
const val DEFAULT_NOTIFY_TITLE = "RustDesk"
|
||||
const val DEFAULT_NOTIFY_TEXT = "Service is running"
|
||||
@ -147,7 +144,11 @@ class MainService : Service() {
|
||||
|
||||
// jvm call rust
|
||||
private external fun init(ctx: Context)
|
||||
private external fun startServer()
|
||||
|
||||
/// When app start on boot, app_dir will not be passed from flutter
|
||||
/// so pass a app_dir here to rust server
|
||||
private external fun startServer(app_dir: String)
|
||||
private external fun startService()
|
||||
private external fun onVideoFrameUpdate(buf: ByteBuffer)
|
||||
private external fun onAudioFrameUpdate(buf: ByteBuffer)
|
||||
private external fun translateLocale(localeName: String, input: String): String
|
||||
@ -195,6 +196,7 @@ class MainService : Service() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.d(logTag,"MainService onCreate")
|
||||
HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply {
|
||||
start()
|
||||
serviceLooper = looper
|
||||
@ -202,7 +204,13 @@ class MainService : Service() {
|
||||
}
|
||||
updateScreenInfo(resources.configuration.orientation)
|
||||
initNotification()
|
||||
startServer()
|
||||
|
||||
// keep the config dir same with flutter
|
||||
val prefs = applicationContext.getSharedPreferences(KEY_SHARED_PREFERENCES, FlutterActivity.MODE_PRIVATE)
|
||||
val configPath = prefs.getString(KEY_APP_DIR_CONFIG_PATH, "") ?: ""
|
||||
startServer(configPath)
|
||||
|
||||
createForegroundNotification()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@ -277,22 +285,30 @@ class MainService : Service() {
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Log.d("whichService", "this service:${Thread.currentThread()}")
|
||||
Log.d("whichService", "this service: ${Thread.currentThread()}")
|
||||
super.onStartCommand(intent, flags, startId)
|
||||
if (intent?.action == INIT_SERVICE) {
|
||||
Log.d(logTag, "service starting:${startId}:${Thread.currentThread()}")
|
||||
if (intent?.action == ACT_INIT_MEDIA_PROJECTION_AND_SERVICE) {
|
||||
createForegroundNotification()
|
||||
val mMediaProjectionManager =
|
||||
|
||||
if (intent.getBooleanExtra(EXT_INIT_FROM_BOOT, false)) {
|
||||
startService()
|
||||
}
|
||||
Log.d(logTag, "service starting: ${startId}:${Thread.currentThread()}")
|
||||
val mediaProjectionManager =
|
||||
getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||
intent.getParcelableExtra<Intent>(EXTRA_MP_DATA)?.let {
|
||||
|
||||
intent.getParcelableExtra<Intent>(EXT_MEDIA_PROJECTION_RES_INTENT)?.let {
|
||||
mediaProjection =
|
||||
mMediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it)
|
||||
mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it)
|
||||
checkMediaPermission()
|
||||
init(this)
|
||||
_isReady = true
|
||||
} ?: let {
|
||||
Log.d(logTag, "getParcelableExtra intent null, invoke requestMediaProjection")
|
||||
requestMediaProjection()
|
||||
}
|
||||
}
|
||||
return START_NOT_STICKY // don't use sticky (auto restart),the new service (from auto restart) will lose control
|
||||
return START_NOT_STICKY // don't use sticky (auto restart), the new service (from auto restart) will lose control
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
@ -300,6 +316,14 @@ class MainService : Service() {
|
||||
updateScreenInfo(newConfig.orientation)
|
||||
}
|
||||
|
||||
private fun requestMediaProjection() {
|
||||
val intent = Intent(this, PermissionRequestTransparentActivity::class.java).apply {
|
||||
action = ACT_REQUEST_MEDIA_PROJECTION
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
private fun createSurface(): Surface? {
|
||||
return if (useVP9) {
|
||||
@ -400,13 +424,13 @@ class MainService : Service() {
|
||||
|
||||
fun checkMediaPermission(): Boolean {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
MainActivity.flutterMethodChannel.invokeMethod(
|
||||
MainActivity.flutterMethodChannel?.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "media", "value" to isReady.toString())
|
||||
)
|
||||
}
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
MainActivity.flutterMethodChannel.invokeMethod(
|
||||
MainActivity.flutterMethodChannel?.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||
)
|
||||
@ -599,7 +623,7 @@ class MainService : Service() {
|
||||
.setAutoCancel(true)
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setContentTitle(DEFAULT_NOTIFY_TITLE)
|
||||
.setContentText(translate(DEFAULT_NOTIFY_TEXT) + '!')
|
||||
.setContentText(translate(DEFAULT_NOTIFY_TEXT))
|
||||
.setOnlyAlertOnce(true)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setColor(ContextCompat.getColor(this, R.color.primary))
|
||||
@ -653,8 +677,8 @@ class MainService : Service() {
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
private fun genLoginRequestPendingIntent(res: Boolean): PendingIntent {
|
||||
val intent = Intent(this, MainService::class.java).apply {
|
||||
action = ACTION_LOGIN_REQ_NOTIFY
|
||||
putExtra(EXTRA_LOGIN_REQ_NOTIFY, res)
|
||||
action = ACT_LOGIN_REQ_NOTIFY
|
||||
putExtra(EXT_LOGIN_REQ_NOTIFY, res)
|
||||
}
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
PendingIntent.getService(this, 111, intent, FLAG_IMMUTABLE)
|
||||
@ -665,7 +689,7 @@ class MainService : Service() {
|
||||
|
||||
private fun setTextNotification(_title: String?, _text: String?) {
|
||||
val title = _title ?: DEFAULT_NOTIFY_TITLE
|
||||
val text = _text ?: translate(DEFAULT_NOTIFY_TEXT) + '!'
|
||||
val text = _text ?: translate(DEFAULT_NOTIFY_TEXT)
|
||||
val notification = notificationBuilder
|
||||
.clearActions()
|
||||
.setStyle(null)
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.media.projection.MediaProjectionManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
|
||||
class PermissionRequestTransparentActivity: Activity() {
|
||||
private val logTag = "permissionRequest"
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Log.d(logTag, "onCreate PermissionRequestTransparentActivity: intent.action: ${intent.action}")
|
||||
|
||||
when (intent.action) {
|
||||
ACT_REQUEST_MEDIA_PROJECTION -> {
|
||||
val mediaProjectionManager =
|
||||
getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||
val intent = mediaProjectionManager.createScreenCaptureIntent()
|
||||
startActivityForResult(intent, REQ_REQUEST_MEDIA_PROJECTION)
|
||||
}
|
||||
else -> finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == REQ_REQUEST_MEDIA_PROJECTION) {
|
||||
if (resultCode == RESULT_OK && data != null) {
|
||||
launchService(data)
|
||||
} else {
|
||||
setResult(RES_FAILED)
|
||||
}
|
||||
}
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun launchService(mediaProjectionResultIntent: Intent) {
|
||||
Log.d(logTag, "Launch MainService")
|
||||
val serviceIntent = Intent(this, MainService::class.java)
|
||||
serviceIntent.action = ACT_INIT_MEDIA_PROJECTION_AND_SERVICE
|
||||
serviceIntent.putExtra(EXT_MEDIA_PROJECTION_RES_INTENT, mediaProjectionResultIntent)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(serviceIntent)
|
||||
} else {
|
||||
startService(serviceIntent)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
import android.Manifest.permission.*
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@ -12,8 +13,8 @@ import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.PowerManager
|
||||
import android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
|
||||
import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||
import android.provider.Settings
|
||||
import android.provider.Settings.*
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import com.hjq.permissions.Permission
|
||||
@ -22,6 +23,31 @@ import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
|
||||
// intent action, extra
|
||||
const val ACT_REQUEST_MEDIA_PROJECTION = "REQUEST_MEDIA_PROJECTION"
|
||||
const val ACT_INIT_MEDIA_PROJECTION_AND_SERVICE = "INIT_MEDIA_PROJECTION_AND_SERVICE"
|
||||
const val ACT_LOGIN_REQ_NOTIFY = "LOGIN_REQ_NOTIFY"
|
||||
const val EXT_INIT_FROM_BOOT = "EXT_INIT_FROM_BOOT"
|
||||
const val EXT_MEDIA_PROJECTION_RES_INTENT = "MEDIA_PROJECTION_RES_INTENT"
|
||||
const val EXT_LOGIN_REQ_NOTIFY = "LOGIN_REQ_NOTIFY"
|
||||
|
||||
// Activity requestCode
|
||||
const val REQ_INVOKE_PERMISSION_ACTIVITY_MEDIA_PROJECTION = 101
|
||||
const val REQ_REQUEST_MEDIA_PROJECTION = 201
|
||||
|
||||
// Activity responseCode
|
||||
const val RES_FAILED = -100
|
||||
|
||||
// Flutter channel
|
||||
const val START_ACTION = "start_action"
|
||||
const val GET_START_ON_BOOT_OPT = "get_start_on_boot_opt"
|
||||
const val SET_START_ON_BOOT_OPT = "set_start_on_boot_opt"
|
||||
const val SYNC_APP_DIR_CONFIG_PATH = "sync_app_dir"
|
||||
|
||||
const val KEY_SHARED_PREFERENCES = "KEY_SHARED_PREFERENCES"
|
||||
const val KEY_START_ON_BOOT_OPT = "KEY_START_ON_BOOT_OPT"
|
||||
const val KEY_APP_DIR_CONFIG_PATH = "KEY_APP_DIR_CONFIG_PATH"
|
||||
|
||||
@SuppressLint("ConstantLocale")
|
||||
val LOCAL_NAME = Locale.getDefault().toString()
|
||||
val SCREEN_INFO = Info(0, 0, 1, 200)
|
||||
@ -30,61 +56,13 @@ data class Info(
|
||||
var width: Int, var height: Int, var scale: Int, var dpi: Int
|
||||
)
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
fun testVP9Support(): Boolean {
|
||||
return true
|
||||
val res = MediaCodecList(MediaCodecList.ALL_CODECS)
|
||||
.findEncoderForFormat(
|
||||
MediaFormat.createVideoFormat(
|
||||
MediaFormat.MIMETYPE_VIDEO_VP9,
|
||||
SCREEN_INFO.width,
|
||||
SCREEN_INFO.width
|
||||
)
|
||||
)
|
||||
return res != null
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun requestPermission(context: Context, type: String) {
|
||||
val permission = when (type) {
|
||||
"ignore_battery_optimizations" -> {
|
||||
try {
|
||||
context.startActivity(Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
||||
data = Uri.parse("package:" + context.packageName)
|
||||
})
|
||||
} catch (e:Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return
|
||||
}
|
||||
"application_details_settings" -> {
|
||||
try {
|
||||
context.startActivity(Intent().apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
action = "android.settings.APPLICATION_DETAILS_SETTINGS"
|
||||
data = Uri.parse("package:" + context.packageName)
|
||||
})
|
||||
} catch (e:Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return
|
||||
}
|
||||
"audio" -> {
|
||||
Permission.RECORD_AUDIO
|
||||
}
|
||||
"file" -> {
|
||||
Permission.MANAGE_EXTERNAL_STORAGE
|
||||
}
|
||||
else -> {
|
||||
return
|
||||
}
|
||||
}
|
||||
XXPermissions.with(context)
|
||||
.permission(permission)
|
||||
.permission(type)
|
||||
.request { _, all ->
|
||||
if (all) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
MainActivity.flutterMethodChannel.invokeMethod(
|
||||
MainActivity.flutterMethodChannel?.invokeMethod(
|
||||
"on_android_permission_result",
|
||||
mapOf("type" to type, "result" to all)
|
||||
)
|
||||
@ -93,24 +71,18 @@ fun requestPermission(context: Context, type: String) {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
fun checkPermission(context: Context, type: String): Boolean {
|
||||
val permission = when (type) {
|
||||
"ignore_battery_optimizations" -> {
|
||||
val pw = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
return pw.isIgnoringBatteryOptimizations(context.packageName)
|
||||
}
|
||||
"audio" -> {
|
||||
Permission.RECORD_AUDIO
|
||||
}
|
||||
"file" -> {
|
||||
Permission.MANAGE_EXTERNAL_STORAGE
|
||||
}
|
||||
else -> {
|
||||
return false
|
||||
}
|
||||
fun startAction(context: Context, action: String) {
|
||||
try {
|
||||
context.startActivity(Intent(action).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
// don't pass package name when launch ACTION_ACCESSIBILITY_SETTINGS
|
||||
if (ACTION_ACCESSIBILITY_SETTINGS != action) {
|
||||
data = Uri.parse("package:" + context.packageName)
|
||||
}
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return XXPermissions.isGranted(context, permission)
|
||||
}
|
||||
|
||||
class AudioReader(val bufSize: Int, private val maxFrames: Int) {
|
||||
|
||||
@ -15,4 +15,12 @@
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
<style name="Transparent" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="android:backgroundDimEnabled">false</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="496.063 521.772 32 32"><path fill="none" d="M496.063 521.772h32v32h-32v-32Z"/><path d="m513.817 535.858.87-8.052c.136-1.26-.279-1.399-.927-.31l-6.856 11.532c-.216.363-.049.658.374.658h3.031l-.87 8.052c-.136 1.26.279 1.399.927.309l6.856-11.531c.216-.363.048-.658-.374-.658h-3.031Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="496.063 521.772 32 32"><path fill="none" d="M496.063 521.772h32v32h-32v-32Z"/><path d="m513.817 535.858.87-8.052c.136-1.26-.279-1.399-.927-.31l-6.856 11.532c-.216.363-.049.658.374.658h3.031l-.87 8.052c-.136 1.26.279 1.399.927.309l6.856-11.531c.216-.363.048-.658-.374-.658h-3.031Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 390 B After Width: | Height: | Size: 386 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="91.748 41.508 32 32"><path fill="none" d="M91.748 41.508h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M104.884 48.3h5.728c1.363 0 2.47.94 2.47 2.098v11.448c0 1.158-1.107 2.098-2.47 2.098h-5.728c-1.363 0-2.469-.94-2.469-2.098V50.398c0-1.158 1.106-2.098 2.469-2.098Zm-.004-2.692h5.729a5.162 5.162 0 0 1 5.164 5.164v13.472a5.164 5.164 0 0 1-1.514 3.649 5.143 5.143 0 0 1-3.65 1.515h-5.729a5.164 5.164 0 0 1-5.157-5.164V50.772c0-1.374.538-2.687 1.508-3.656a5.182 5.182 0 0 1 3.649-1.508Zm1.407 21.076a1.462 1.462 0 0 1 2.922 0 1.461 1.461 0 0 1-2.922 0Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="91.748 41.508 32 32"><path fill="none" d="M91.748 41.508h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M104.884 48.3h5.728c1.363 0 2.47.94 2.47 2.098v11.448c0 1.158-1.107 2.098-2.47 2.098h-5.728c-1.363 0-2.469-.94-2.469-2.098V50.398c0-1.158 1.106-2.098 2.469-2.098Zm-.004-2.692h5.729a5.162 5.162 0 0 1 5.164 5.164v13.472a5.164 5.164 0 0 1-1.514 3.649 5.143 5.143 0 0 1-3.65 1.515h-5.729a5.164 5.164 0 0 1-5.157-5.164V50.772c0-1.374.538-2.687 1.508-3.656a5.182 5.182 0 0 1 3.649-1.508Zm1.407 21.076a1.462 1.462 0 0 1 2.922 0 1.461 1.461 0 0 1-2.922 0Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 661 B After Width: | Height: | Size: 657 B |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="305.118 110.478 32 32" width="32pt" height="32pt"><g><path d=" M 305.118 110.478 L 337.118 110.478 L 337.118 142.478 L 305.118 142.478 L 305.118 110.478 Z " fill="none"/><path d=" M 322.26 126.052 L 316.297 120.09 C 315.72 119.512 315.72 118.574 316.297 117.997 L 316.297 117.997 C 316.875 117.419 317.813 117.419 318.39 117.997 L 326.02 125.627 C 326.49 126.097 326.49 126.86 326.02 127.33 L 318.39 134.959 C 317.813 135.537 316.875 135.537 316.297 134.959 L 316.297 134.959 C 315.72 134.382 315.72 133.444 316.297 132.866 L 322.26 126.904 C 322.495 126.669 322.495 126.287 322.26 126.052 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="305.118 110.478 32 32" width="32" height="32"><g><path d=" M 305.118 110.478 L 337.118 110.478 L 337.118 142.478 L 305.118 142.478 L 305.118 110.478 Z " fill="none"/><path d=" M 322.26 126.052 L 316.297 120.09 C 315.72 119.512 315.72 118.574 316.297 117.997 L 316.297 117.997 C 316.875 117.419 317.813 117.419 318.39 117.997 L 326.02 125.627 C 326.49 126.097 326.49 126.86 326.02 127.33 L 318.39 134.959 C 317.813 135.537 316.875 135.537 316.297 134.959 L 316.297 134.959 C 315.72 134.382 315.72 133.444 316.297 132.866 L 322.26 126.904 C 322.495 126.669 322.495 126.287 322.26 126.052 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 796 B After Width: | Height: | Size: 792 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="587.811 560.459 32 32"><path fill="none" d="M587.811 560.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="m601.469 580.246-3.859 3.858a1.02 1.02 0 1 1-1.444-1.444l3.858-3.859 2.554-2.553 3.85-3.851a1.022 1.022 0 0 1 1.445 1.445l-3.851 3.85 2.299 2.299a1.23 1.23 0 0 0 1.717.014q3.089-2.988 6.218-.117.03.026.089.083c.34.34.527.8.527 1.276a1.78 1.78 0 0 1-.528 1.277l-1.366 1.366a5.557 5.557 0 0 1-7.861.004l-3.648-3.648Zm-.923-6.031-.267-.266a1.23 1.23 0 0 1-.014-1.717q2.988-3.09.117-6.218-.026-.031-.083-.089c-.341-.34-.8-.527-1.276-.527a1.78 1.78 0 0 0-1.277.528l-1.366 1.366a5.555 5.555 0 0 0-.004 7.861l1.616 1.616a.832.832 0 0 0 1.175 0l1.379-1.378a.833.833 0 0 0 0-1.176Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="587.811 560.459 32 32"><path fill="none" d="M587.811 560.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="m601.469 580.246-3.859 3.858a1.02 1.02 0 1 1-1.444-1.444l3.858-3.859 2.554-2.553 3.85-3.851a1.022 1.022 0 0 1 1.445 1.445l-3.851 3.85 2.299 2.299a1.23 1.23 0 0 0 1.717.014q3.089-2.988 6.218-.117.03.026.089.083c.34.34.527.8.527 1.276a1.78 1.78 0 0 1-.528 1.277l-1.366 1.366a5.557 5.557 0 0 1-7.861.004l-3.648-3.648Zm-.923-6.031-.267-.266a1.23 1.23 0 0 1-.014-1.717q2.988-3.09.117-6.218-.026-.031-.083-.089c-.341-.34-.8-.527-1.276-.527a1.78 1.78 0 0 0-1.277.528l-1.366 1.366a5.555 5.555 0 0 0-.004 7.861l1.616 1.616a.832.832 0 0 0 1.175 0l1.379-1.378a.833.833 0 0 0 0-1.176Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 786 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="587.811 592.459 32 32"><path fill="none" d="M587.811 592.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M595.385 608.153a5.553 5.553 0 0 1 .004-7.852l1.365-1.365c.34-.34.795-.531 1.275-.527.476 0 .935.187 1.275.526l.083.089q2.868 3.125-.117 6.212a1.226 1.226 0 0 0 .014 1.714l6.036 6.036a1.226 1.226 0 0 0 1.714.014q3.086-2.985 6.212-.117l.089.083c.339.34.526.799.526 1.275.004.48-.187.935-.527 1.275l-1.365 1.365a5.553 5.553 0 0 1-7.852.004l-8.732-8.732Zm7.426-1.824a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Zm3.811 0a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Zm3.811 0a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="587.811 592.459 32 32"><path fill="none" d="M587.811 592.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M595.385 608.153a5.553 5.553 0 0 1 .004-7.852l1.365-1.365c.34-.34.795-.531 1.275-.527.476 0 .935.187 1.275.526l.083.089q2.868 3.125-.117 6.212a1.226 1.226 0 0 0 .014 1.714l6.036 6.036a1.226 1.226 0 0 0 1.714.014q3.086-2.985 6.212-.117l.089.083c.339.34.526.799.526 1.275.004.48-.187.935-.527 1.275l-1.365 1.365a5.553 5.553 0 0 1-7.852.004l-8.732-8.732Zm7.426-1.824a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Zm3.811 0a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Zm3.811 0a1.215 1.215 0 1 1 2.43 0 1.215 1.215 0 0 1-2.43 0Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 746 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="587.811 521.772 32 32"><path fill="none" d="M587.811 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M601.822 543.465h7.624a3.94 3.94 0 0 0 3.942-3.938v-7.213a3.944 3.944 0 0 0-3.942-3.942h-11.27a3.944 3.944 0 0 0-3.942 3.942v7.213a3.944 3.944 0 0 0 2.901 3.798v2.543c0 1.413.787 1.726 1.757.699l2.93-3.102Zm-1.593-7.668a2.203 2.203 0 0 1 .001-3.112l.541-.541a.714.714 0 0 1 1.011 0l.033.035q1.136 1.239-.047 2.462a.486.486 0 0 0 .006.679l2.392 2.392c.186.186.49.189.679.006q1.224-1.183 2.462-.046l.035.033a.71.71 0 0 1 0 1.01l-.541.541a2.199 2.199 0 0 1-3.112.002l-3.46-3.461Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="587.811 521.772 32 32"><path fill="none" d="M587.811 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M601.822 543.465h7.624a3.94 3.94 0 0 0 3.942-3.938v-7.213a3.944 3.944 0 0 0-3.942-3.942h-11.27a3.944 3.944 0 0 0-3.942 3.942v7.213a3.944 3.944 0 0 0 2.901 3.798v2.543c0 1.413.787 1.726 1.757.699l2.93-3.102Zm-1.593-7.668a2.203 2.203 0 0 1 .001-3.112l.541-.541a.714.714 0 0 1 1.011 0l.033.035q1.136 1.239-.047 2.462a.486.486 0 0 0 .006.679l2.392 2.392c.186.186.49.189.679.006q1.224-1.183 2.462-.046l.035.033a.71.71 0 0 1 0 1.01l-.541.541a2.199 2.199 0 0 1-3.112.002l-3.46-3.461Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 694 B After Width: | Height: | Size: 690 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="679.559 521.772 32 32"><path fill="none" d="M679.559 521.772h32v32h-32v-32Z"/><path d="m695.559 536.034-5.305-5.305a1.229 1.229 0 1 0-1.738 1.738l5.305 5.305-5.305 5.305a1.229 1.229 0 0 0 0 1.738 1.23 1.23 0 0 0 1.738 0l5.305-5.305 5.305 5.305a1.229 1.229 0 1 0 1.738-1.738l-5.305-5.305 5.305-5.305a1.229 1.229 0 1 0-1.738-1.738l-5.305 5.305Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="679.559 521.772 32 32"><path fill="none" d="M679.559 521.772h32v32h-32v-32Z"/><path d="m695.559 536.034-5.305-5.305a1.229 1.229 0 1 0-1.738 1.738l5.305 5.305-5.305 5.305a1.229 1.229 0 0 0 0 1.738 1.23 1.23 0 0 0 1.738 0l5.305-5.305 5.305 5.305a1.229 1.229 0 1 0 1.738-1.738l-5.305-5.305 5.305-5.305a1.229 1.229 0 1 0-1.738-1.738l-5.305 5.305Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 453 B After Width: | Height: | Size: 449 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="450.189 521.772 32 32"><path fill="none" d="M450.189 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M470.477 545.236h2.795a5.13 5.13 0 0 0 3.639-1.515 5.126 5.126 0 0 0 1.515-3.639v-9.07a5.126 5.126 0 0 0-1.515-3.639 5.13 5.13 0 0 0-3.639-1.515h-14.166a5.13 5.13 0 0 0-3.639 1.515 5.127 5.127 0 0 0-1.516 3.639v9.07c0 1.363.544 2.675 1.516 3.639a5.13 5.13 0 0 0 3.639 1.515h2.795v1.234a3.217 3.217 0 0 0 3.216 3.216h2.144a3.217 3.217 0 0 0 3.216-3.216v-1.234Zm-11.371-15.753h14.166c.406 0 .79.166 1.08.449.283.29.449.674.449 1.08v9.07c0 .406-.166.79-.449 1.08-.29.283-.674.449-1.08.449h-14.166c-.406 0-.79-.166-1.08-.449a1.55 1.55 0 0 1-.45-1.08v-9.07c0-.406.167-.79.45-1.08.29-.283.674-.449 1.08-.449Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="450.189 521.772 32 32"><path fill="none" d="M450.189 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M470.477 545.236h2.795a5.13 5.13 0 0 0 3.639-1.515 5.126 5.126 0 0 0 1.515-3.639v-9.07a5.126 5.126 0 0 0-1.515-3.639 5.13 5.13 0 0 0-3.639-1.515h-14.166a5.13 5.13 0 0 0-3.639 1.515 5.127 5.127 0 0 0-1.516 3.639v9.07c0 1.363.544 2.675 1.516 3.639a5.13 5.13 0 0 0 3.639 1.515h2.795v1.234a3.217 3.217 0 0 0 3.216 3.216h2.144a3.217 3.217 0 0 0 3.216-3.216v-1.234Zm-11.371-15.753h14.166c.406 0 .79.166 1.08.449.283.29.449.674.449 1.08v9.07c0 .406-.166.79-.449 1.08-.29.283-.674.449-1.08.449h-14.166c-.406 0-.79-.166-1.08-.449a1.55 1.55 0 0 1-.45-1.08v-9.07c0-.406.167-.79.45-1.08.29-.283.674-.449 1.08-.449Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 820 B After Width: | Height: | Size: 816 B |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="105.516 106.603 32 32" width="32pt" height="32pt"><g><path d=" M 105.516 106.603 L 137.516 106.603 L 137.516 138.603 L 105.516 138.603 L 105.516 106.603 Z " fill="none"/><path d=" M 118.954 115.165 C 118.954 113.751 120.102 112.603 121.516 112.603 C 122.929 112.603 124.077 113.751 124.077 115.165 C 124.077 116.579 122.929 117.727 121.516 117.727 C 120.102 117.727 118.954 116.579 118.954 115.165 L 118.954 115.165 Z M 118.954 122.603 C 118.954 121.189 120.102 120.041 121.516 120.041 C 122.929 120.041 124.077 121.189 124.077 122.603 C 124.077 124.017 122.929 125.165 121.516 125.165 C 120.102 125.165 118.954 124.017 118.954 122.603 L 118.954 122.603 Z M 118.954 130.041 C 118.954 128.627 120.102 127.479 121.516 127.479 C 122.929 127.479 124.077 128.627 124.077 130.041 C 124.077 131.455 122.929 132.603 121.516 132.603 C 120.102 132.603 118.954 131.455 118.954 130.041 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="105.516 106.603 32 32" width="32" height="32"><g><path d=" M 105.516 106.603 L 137.516 106.603 L 137.516 138.603 L 105.516 138.603 L 105.516 106.603 Z " fill="none"/><path d=" M 118.954 115.165 C 118.954 113.751 120.102 112.603 121.516 112.603 C 122.929 112.603 124.077 113.751 124.077 115.165 C 124.077 116.579 122.929 117.727 121.516 117.727 C 120.102 117.727 118.954 116.579 118.954 115.165 L 118.954 115.165 Z M 118.954 122.603 C 118.954 121.189 120.102 120.041 121.516 120.041 C 122.929 120.041 124.077 121.189 124.077 122.603 C 124.077 124.017 122.929 125.165 121.516 125.165 C 120.102 125.165 118.954 124.017 118.954 122.603 L 118.954 122.603 Z M 118.954 130.041 C 118.954 128.627 120.102 127.479 121.516 127.479 C 122.929 127.479 124.077 128.627 124.077 130.041 C 124.077 131.455 122.929 132.603 121.516 132.603 C 120.102 132.603 118.954 131.455 118.954 130.041 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="187.624 70.761 32 32" width="32pt" height="32pt"><g><path d=" M 187.624 70.761 L 219.624 70.761 L 219.624 102.761 L 187.624 102.761 L 187.624 70.761 Z " fill="none"/><path d=" M 199.771 89.326 L 207.477 89.326 C 207.944 89.326 208.324 89.705 208.324 90.173 L 208.324 90.173 C 208.324 90.64 207.944 91.019 207.477 91.019 L 199.771 91.019 C 199.304 91.019 198.924 90.64 198.924 90.173 L 198.924 90.173 C 198.924 89.705 199.304 89.326 199.771 89.326 L 199.771 89.326 L 199.771 89.326 Z M 199.771 85.914 L 207.477 85.914 C 207.944 85.914 208.324 86.294 208.324 86.761 L 208.324 86.761 C 208.324 87.228 207.944 87.608 207.477 87.608 L 199.771 87.608 C 199.304 87.608 198.924 87.228 198.924 86.761 L 198.924 86.761 C 198.924 86.294 199.304 85.914 199.771 85.914 L 199.771 85.914 Z M 196.935 81.117 L 196.935 92.406 C 196.935 93.974 198.21 95.248 199.78 95.248 L 207.468 95.248 C 209.038 95.248 210.313 93.974 210.313 92.406 L 210.313 81.117 C 210.313 79.548 209.038 78.274 207.468 78.274 L 199.78 78.274 C 198.21 78.274 196.935 79.548 196.935 81.117 L 196.935 81.117 L 196.935 81.117 Z M 199.771 82.503 L 207.477 82.503 C 207.944 82.503 208.324 82.882 208.324 83.35 L 208.324 83.35 C 208.324 83.817 207.944 84.196 207.477 84.196 L 199.771 84.196 C 199.304 84.196 198.924 83.817 198.924 83.35 L 198.924 83.35 C 198.924 82.882 199.304 82.503 199.771 82.503 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="187.624 70.761 32 32" width="32" height="32"><g><path d=" M 187.624 70.761 L 219.624 70.761 L 219.624 102.761 L 187.624 102.761 L 187.624 70.761 Z " fill="none"/><path d=" M 199.771 89.326 L 207.477 89.326 C 207.944 89.326 208.324 89.705 208.324 90.173 L 208.324 90.173 C 208.324 90.64 207.944 91.019 207.477 91.019 L 199.771 91.019 C 199.304 91.019 198.924 90.64 198.924 90.173 L 198.924 90.173 C 198.924 89.705 199.304 89.326 199.771 89.326 L 199.771 89.326 L 199.771 89.326 Z M 199.771 85.914 L 207.477 85.914 C 207.944 85.914 208.324 86.294 208.324 86.761 L 208.324 86.761 C 208.324 87.228 207.944 87.608 207.477 87.608 L 199.771 87.608 C 199.304 87.608 198.924 87.228 198.924 86.761 L 198.924 86.761 C 198.924 86.294 199.304 85.914 199.771 85.914 L 199.771 85.914 Z M 196.935 81.117 L 196.935 92.406 C 196.935 93.974 198.21 95.248 199.78 95.248 L 207.468 95.248 C 209.038 95.248 210.313 93.974 210.313 92.406 L 210.313 81.117 C 210.313 79.548 209.038 78.274 207.468 78.274 L 199.78 78.274 C 198.21 78.274 196.935 79.548 196.935 81.117 L 196.935 81.117 L 196.935 81.117 Z M 199.771 82.503 L 207.477 82.503 C 207.944 82.503 208.324 82.882 208.324 83.35 L 208.324 83.35 C 208.324 83.817 207.944 84.196 207.477 84.196 L 199.771 84.196 C 199.304 84.196 198.924 83.817 198.924 83.35 L 198.924 83.35 C 198.924 82.882 199.304 82.503 199.771 82.503 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="129.858 75.669 32 32" width="32pt" height="32pt"><g><path d=" M 129.858 75.669 L 161.858 75.669 L 161.858 107.669 L 129.858 107.669 L 129.858 75.669 Z " fill="none"/><path d=" M 138.617 99.535 L 153.1 99.535 C 154.622 99.535 155.858 98.299 155.858 96.777 L 155.858 89.32 C 155.858 87.797 154.622 86.561 153.1 86.561 L 147.703 86.561 C 147.399 86.561 147.151 86.314 147.151 86.009 L 147.151 85.182 C 147.151 84.421 146.533 83.802 145.772 83.802 L 139.996 83.802 C 139.235 83.802 138.18 84.239 137.641 84.778 L 136.833 85.586 C 136.295 86.124 135.858 87.179 135.858 87.94 L 135.858 96.777 C 135.858 98.299 137.094 99.535 138.617 99.535 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="129.858 75.669 32 32" width="32" height="32"><g><path d=" M 129.858 75.669 L 161.858 75.669 L 161.858 107.669 L 129.858 107.669 L 129.858 75.669 Z " fill="none"/><path d=" M 138.617 99.535 L 153.1 99.535 C 154.622 99.535 155.858 98.299 155.858 96.777 L 155.858 89.32 C 155.858 87.797 154.622 86.561 153.1 86.561 L 147.703 86.561 C 147.399 86.561 147.151 86.314 147.151 86.009 L 147.151 85.182 C 147.151 84.421 146.533 83.802 145.772 83.802 L 139.996 83.802 C 139.235 83.802 138.18 84.239 137.641 84.778 L 136.833 85.586 C 136.295 86.124 135.858 87.179 135.858 87.94 L 135.858 96.777 C 135.858 98.299 137.094 99.535 138.617 99.535 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 840 B After Width: | Height: | Size: 836 B |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="59.535 106.603 32 32" width="32pt" height="32pt"><g><path d=" M 59.535 106.603 L 91.535 106.603 L 91.535 138.603 L 59.535 138.603 L 59.535 106.603 Z " fill="none"/><path d=" M 68.294 130.469 L 82.776 130.469 C 84.299 130.469 85.535 129.233 85.535 127.711 L 85.535 120.254 C 85.535 118.731 84.299 117.495 82.776 117.495 L 77.38 117.495 C 77.075 117.495 76.828 117.248 76.828 116.944 L 76.828 116.116 C 76.828 115.355 76.21 114.737 75.449 114.737 L 69.673 114.737 C 68.912 114.737 67.857 115.174 67.318 115.712 L 66.51 116.52 C 65.972 117.058 65.535 118.113 65.535 118.875 L 65.535 127.711 C 65.535 129.233 66.771 130.469 68.294 130.469 Z M 80.242 125.161 L 81.924 125.161 C 82.362 125.161 82.718 124.806 82.718 124.368 L 82.718 124.368 C 82.718 123.93 82.362 123.575 81.924 123.575 L 80.242 123.575 L 80.242 121.892 C 80.242 121.454 79.886 121.098 79.448 121.098 L 79.448 121.098 C 79.01 121.098 78.655 121.454 78.655 121.892 L 78.655 123.575 L 76.972 123.575 C 76.534 123.575 76.178 123.93 76.178 124.368 L 76.178 124.368 C 76.178 124.806 76.534 125.161 76.972 125.161 L 78.655 125.161 L 78.655 126.844 C 78.655 127.282 79.01 127.638 79.448 127.638 L 79.448 127.638 C 79.886 127.638 80.242 127.282 80.242 126.844 L 80.242 125.161 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="59.535 106.603 32 32" width="32" height="32"><g><path d=" M 59.535 106.603 L 91.535 106.603 L 91.535 138.603 L 59.535 138.603 L 59.535 106.603 Z " fill="none"/><path d=" M 68.294 130.469 L 82.776 130.469 C 84.299 130.469 85.535 129.233 85.535 127.711 L 85.535 120.254 C 85.535 118.731 84.299 117.495 82.776 117.495 L 77.38 117.495 C 77.075 117.495 76.828 117.248 76.828 116.944 L 76.828 116.116 C 76.828 115.355 76.21 114.737 75.449 114.737 L 69.673 114.737 C 68.912 114.737 67.857 115.174 67.318 115.712 L 66.51 116.52 C 65.972 117.058 65.535 118.113 65.535 118.875 L 65.535 127.711 C 65.535 129.233 66.771 130.469 68.294 130.469 Z M 80.242 125.161 L 81.924 125.161 C 82.362 125.161 82.718 124.806 82.718 124.368 L 82.718 124.368 C 82.718 123.93 82.362 123.575 81.924 123.575 L 80.242 123.575 L 80.242 121.892 C 80.242 121.454 79.886 121.098 79.448 121.098 L 79.448 121.098 C 79.01 121.098 78.655 121.454 78.655 121.892 L 78.655 123.575 L 76.972 123.575 C 76.534 123.575 76.178 123.93 76.178 124.368 L 76.178 124.368 C 76.178 124.806 76.534 125.161 76.972 125.161 L 78.655 125.161 L 78.655 126.844 C 78.655 127.282 79.01 127.638 79.448 127.638 L 79.448 127.638 C 79.886 127.638 80.242 127.282 80.242 126.844 L 80.242 125.161 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="404.315 521.772 32 32"><path fill-rule="evenodd" d="M411.985 533.763a1.815 1.815 0 0 1-1.812 1.812c-.479 0-.943-.196-1.284-.536a1.834 1.834 0 0 1-.529-1.276v-2.806c0-1.312.522-2.566 1.45-3.494a4.938 4.938 0 0 1 3.494-1.45h2.799c.486 0 .942.196 1.283.536.341.334.529.798.529 1.276a1.8 1.8 0 0 1-.529 1.283c-.341.341-.797.53-1.283.53h-2.799c-.348 0-.688.137-.935.391-.246.247-.384.58-.384.928v2.806Zm4.118 12.143a1.802 1.802 0 0 1 1.812 1.812c0 .479-.188.943-.529 1.276a1.81 1.81 0 0 1-1.283.537h-2.799a4.938 4.938 0 0 1-3.494-1.45 4.939 4.939 0 0 1-1.45-3.495v-2.805c0-.479.196-.935.529-1.276a1.824 1.824 0 0 1 1.284-.537c.485 0 .942.196 1.283.537.341.341.529.797.529 1.276v2.805c0 .348.138.682.384.928.247.254.587.392.935.392h2.799Zm8.424-16.268c-.486 0-.943-.189-1.283-.53a1.8 1.8 0 0 1-.529-1.283c0-.478.188-.942.529-1.276.34-.34.797-.536 1.283-.536h2.798a4.94 4.94 0 0 1 3.495 1.45 4.938 4.938 0 0 1 1.45 3.494v2.806a1.823 1.823 0 0 1-1.813 1.812c-.486 0-.942-.196-1.283-.536a1.8 1.8 0 0 1-.529-1.276v-2.806a1.31 1.31 0 0 0-.385-.928 1.302 1.302 0 0 0-.935-.391h-2.798Zm4.118 12.143c0-.479.188-.935.529-1.276a1.81 1.81 0 0 1 1.283-.537 1.82 1.82 0 0 1 1.813 1.813v2.805a4.939 4.939 0 0 1-1.45 3.495 4.94 4.94 0 0 1-3.495 1.45h-2.798c-.486 0-.943-.196-1.283-.537a1.784 1.784 0 0 1-.529-1.276 1.804 1.804 0 0 1 1.812-1.812h2.798c.348 0 .689-.138.935-.392a1.31 1.31 0 0 0 .385-.928v-2.805Z"/><path fill="none" d="M404.315 521.772h32v32h-32v-32Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="404.315 521.772 32 32"><path fill-rule="evenodd" d="M411.985 533.763a1.815 1.815 0 0 1-1.812 1.812c-.479 0-.943-.196-1.284-.536a1.834 1.834 0 0 1-.529-1.276v-2.806c0-1.312.522-2.566 1.45-3.494a4.938 4.938 0 0 1 3.494-1.45h2.799c.486 0 .942.196 1.283.536.341.334.529.798.529 1.276a1.8 1.8 0 0 1-.529 1.283c-.341.341-.797.53-1.283.53h-2.799c-.348 0-.688.137-.935.391-.246.247-.384.58-.384.928v2.806Zm4.118 12.143a1.802 1.802 0 0 1 1.812 1.812c0 .479-.188.943-.529 1.276a1.81 1.81 0 0 1-1.283.537h-2.799a4.938 4.938 0 0 1-3.494-1.45 4.939 4.939 0 0 1-1.45-3.495v-2.805c0-.479.196-.935.529-1.276a1.824 1.824 0 0 1 1.284-.537c.485 0 .942.196 1.283.537.341.341.529.797.529 1.276v2.805c0 .348.138.682.384.928.247.254.587.392.935.392h2.799Zm8.424-16.268c-.486 0-.943-.189-1.283-.53a1.8 1.8 0 0 1-.529-1.283c0-.478.188-.942.529-1.276.34-.34.797-.536 1.283-.536h2.798a4.94 4.94 0 0 1 3.495 1.45 4.938 4.938 0 0 1 1.45 3.494v2.806a1.823 1.823 0 0 1-1.813 1.812c-.486 0-.942-.196-1.283-.536a1.8 1.8 0 0 1-.529-1.276v-2.806a1.31 1.31 0 0 0-.385-.928 1.302 1.302 0 0 0-.935-.391h-2.798Zm4.118 12.143c0-.479.188-.935.529-1.276a1.81 1.81 0 0 1 1.283-.537 1.82 1.82 0 0 1 1.813 1.813v2.805a4.939 4.939 0 0 1-1.45 3.495 4.94 4.94 0 0 1-3.495 1.45h-2.798c-.486 0-.943-.196-1.283-.537a1.784 1.784 0 0 1-.529-1.276 1.804 1.804 0 0 1 1.812-1.812h2.798c.348 0 .689-.138.935-.392a1.31 1.31 0 0 0 .385-.928v-2.805Z"/><path fill="none" d="M404.315 521.772h32v32h-32v-32Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="404.315 560.459 32 32"><path fill="none" d="M404.315 560.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M414.29 566.512c0-.478.189-.935.53-1.275.34-.341.797-.537 1.283-.537a1.824 1.824 0 0 1 1.812 1.812v2.806a4.938 4.938 0 0 1-1.45 3.494 4.938 4.938 0 0 1-3.494 1.45h-2.798c-.486 0-.943-.195-1.284-.536a1.792 1.792 0 0 1-.529-1.276 1.805 1.805 0 0 1 1.813-1.813h2.798c.348 0 .689-.137.935-.391.247-.246.384-.58.384-.928v-2.806Zm-4.117 15.768a1.804 1.804 0 0 1-1.813-1.812c0-.478.189-.942.529-1.276a1.811 1.811 0 0 1 1.284-.536h2.798c1.312 0 2.566.522 3.494 1.449a4.94 4.94 0 0 1 1.45 3.495v2.805a1.824 1.824 0 0 1-1.812 1.813c-.486 0-.943-.196-1.283-.537a1.797 1.797 0 0 1-.53-1.276V583.6c0-.348-.137-.682-.384-.928a1.303 1.303 0 0 0-.935-.392h-2.798Zm20.284-11.643c.486 0 .943.189 1.283.53.341.34.53.797.53 1.283 0 .478-.189.942-.53 1.276-.34.341-.797.536-1.283.536h-2.798a4.94 4.94 0 0 1-3.495-1.45 4.937 4.937 0 0 1-1.449-3.494v-2.806a1.82 1.82 0 0 1 1.812-1.812c.486 0 .942.196 1.283.537.341.34.529.797.529 1.275v2.806c0 .348.138.682.385.928.246.254.587.391.935.391h2.798Zm-4.118 15.768c0 .479-.188.936-.529 1.276a1.81 1.81 0 0 1-1.283.537 1.82 1.82 0 0 1-1.812-1.813V583.6a4.944 4.944 0 0 1 4.944-4.944h2.798c.486 0 .943.195 1.283.536.341.334.53.798.53 1.276 0 .486-.189.942-.53 1.283a1.8 1.8 0 0 1-1.283.529h-2.798c-.348 0-.689.138-.935.392a1.31 1.31 0 0 0-.385.928v2.805Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="404.315 560.459 32 32"><path fill="none" d="M404.315 560.459h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M414.29 566.512c0-.478.189-.935.53-1.275.34-.341.797-.537 1.283-.537a1.824 1.824 0 0 1 1.812 1.812v2.806a4.938 4.938 0 0 1-1.45 3.494 4.938 4.938 0 0 1-3.494 1.45h-2.798c-.486 0-.943-.195-1.284-.536a1.792 1.792 0 0 1-.529-1.276 1.805 1.805 0 0 1 1.813-1.813h2.798c.348 0 .689-.137.935-.391.247-.246.384-.58.384-.928v-2.806Zm-4.117 15.768a1.804 1.804 0 0 1-1.813-1.812c0-.478.189-.942.529-1.276a1.811 1.811 0 0 1 1.284-.536h2.798c1.312 0 2.566.522 3.494 1.449a4.94 4.94 0 0 1 1.45 3.495v2.805a1.824 1.824 0 0 1-1.812 1.813c-.486 0-.943-.196-1.283-.537a1.797 1.797 0 0 1-.53-1.276V583.6c0-.348-.137-.682-.384-.928a1.303 1.303 0 0 0-.935-.392h-2.798Zm20.284-11.643c.486 0 .943.189 1.283.53.341.34.53.797.53 1.283 0 .478-.189.942-.53 1.276-.34.341-.797.536-1.283.536h-2.798a4.94 4.94 0 0 1-3.495-1.45 4.937 4.937 0 0 1-1.449-3.494v-2.806a1.82 1.82 0 0 1 1.812-1.812c.486 0 .942.196 1.283.537.341.34.529.797.529 1.275v2.806c0 .348.138.682.385.928.246.254.587.391.935.391h2.798Zm-4.118 15.768c0 .479-.188.936-.529 1.276a1.81 1.81 0 0 1-1.283.537 1.82 1.82 0 0 1-1.812-1.813V583.6a4.944 4.944 0 0 1 4.944-4.944h2.798c.486 0 .943.195 1.283.536.341.334.53.798.53 1.276 0 .486-.189.942-.53 1.283a1.8 1.8 0 0 1-1.283.529h-2.798c-.348 0-.689.138-.935.392a1.31 1.31 0 0 0-.385.928v2.805Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="13.554 106.603 32 32" width="32pt" height="32pt"><g><path d=" M 13.554 106.603 L 45.554 106.603 L 45.554 138.603 L 13.554 138.603 L 13.554 106.603 Z " fill="none"/><path d=" M 32.944 132.507 L 36.605 132.507 C 37.157 132.507 37.605 132.059 37.605 131.507 L 37.605 123.67 L 39.94 123.67 C 40.492 123.67 40.623 123.354 40.233 122.963 L 30.261 112.992 C 29.871 112.601 29.237 112.601 28.847 112.992 L 18.876 122.963 C 18.485 123.354 18.617 123.67 19.169 123.67 L 21.503 123.67 L 21.503 131.507 C 21.503 132.059 21.951 132.507 22.503 132.507 L 26.164 132.507 C 26.716 132.507 27.164 132.059 27.164 131.507 L 27.164 128.541 C 27.164 128.265 27.388 128.041 27.664 128.041 L 31.444 128.041 C 31.72 128.041 31.944 128.265 31.944 128.541 L 31.944 131.507 C 31.944 132.059 32.393 132.507 32.944 132.507 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="13.554 106.603 32 32" width="32" height="32"><g><path d=" M 13.554 106.603 L 45.554 106.603 L 45.554 138.603 L 13.554 138.603 L 13.554 106.603 Z " fill="none"/><path d=" M 32.944 132.507 L 36.605 132.507 C 37.157 132.507 37.605 132.059 37.605 131.507 L 37.605 123.67 L 39.94 123.67 C 40.492 123.67 40.623 123.354 40.233 122.963 L 30.261 112.992 C 29.871 112.601 29.237 112.601 28.847 112.992 L 18.876 122.963 C 18.485 123.354 18.617 123.67 19.169 123.67 L 21.503 123.67 L 21.503 131.507 C 21.503 132.059 21.951 132.507 22.503 132.507 L 26.164 132.507 C 26.716 132.507 27.164 132.059 27.164 131.507 L 27.164 128.541 C 27.164 128.265 27.388 128.041 27.664 128.041 L 31.444 128.041 C 31.72 128.041 31.944 128.265 31.944 128.541 L 31.944 131.507 C 31.944 132.059 32.393 132.507 32.944 132.507 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 999 B After Width: | Height: | Size: 995 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="541.937 521.772 32 32"><path fill="none" d="M541.937 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M552.145 539.981h11.584c.446 0 .808.362.808.808v.536c0 .786-.639 1.425-1.425 1.425h-10.35a1.426 1.426 0 0 1-1.425-1.425v-.536c0-.446.362-.808.808-.808Zm-1.761-3.511h.899c.536 0 .971.435.971.971v.899a.971.971 0 0 1-.971.971h-.899a.971.971 0 0 1-.971-.971v-.899c0-.536.435-.971.971-.971Zm3.552 0h.899c.536 0 .971.435.971.971v.899a.971.971 0 0 1-.971.971h-.899a.972.972 0 0 1-.972-.971v-.899c0-.536.436-.971.972-.971Zm3.551 0h.9c.536 0 .971.435.971.971v.899a.971.971 0 0 1-.971.971h-.9a.971.971 0 0 1-.971-.971v-.899c0-.536.435-.971.971-.971Zm3.552 0h.899c.536 0 .972.435.972.971v.899a.972.972 0 0 1-.972.971h-.899a.971.971 0 0 1-.971-.971v-.899c0-.536.435-.971.971-.971Zm3.552 0h.899c.536 0 .971.435.971.971v.899a.971.971 0 0 1-.971.971h-.899a.971.971 0 0 1-.971-.971v-.899c0-.536.435-.971.971-.971Zm-14.383-3.512h1.25c.44 0 .796.357.796.796v1.25a.796.796 0 0 1-.796.796h-1.25a.796.796 0 0 1-.795-.796v-1.25c0-.439.356-.796.795-.796Zm3.552 0h1.25c.439 0 .796.357.796.796v1.25a.797.797 0 0 1-.796.796h-1.25a.797.797 0 0 1-.796-.796v-1.25c0-.439.357-.796.796-.796Zm3.552 0h1.25c.439 0 .796.357.796.796v1.25a.797.797 0 0 1-.796.796h-1.25a.797.797 0 0 1-.796-.796v-1.25c0-.439.357-.796.796-.796Zm3.552 0h1.25c.439 0 .796.357.796.796v1.25a.797.797 0 0 1-.796.796h-1.25a.797.797 0 0 1-.796-.796v-1.25c0-.439.357-.796.796-.796Zm-9.553-3.85h13.252c1.407 0 2.755.507 3.748 1.409.993.902 1.552 2.127 1.552 3.404v7.702c0 1.277-.559 2.501-1.552 3.403-.993.902-2.341 1.409-3.748 1.409h-13.252c-1.407 0-2.755-.507-3.748-1.409-.993-.902-1.552-2.126-1.552-3.403v-7.702c0-1.277.559-2.502 1.552-3.404.993-.902 2.341-1.409 3.748-1.409Zm13.105 3.85h1.25c.439 0 .795.357.795.796v1.25a.796.796 0 0 1-.795.796h-1.25a.796.796 0 0 1-.796-.796v-1.25c0-.439.356-.796.796-.796Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="541.937 521.772 32 32"><path fill="none" d="M541.937 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M552.145 539.981h11.584c.446 0 .808.362.808.808v.536c0 .786-.639 1.425-1.425 1.425h-10.35a1.426 1.426 0 0 1-1.425-1.425v-.536c0-.446.362-.808.808-.808Zm-1.761-3.511h.899c.536 0 .971.435.971.971v.899a.971.971 0 0 1-.971.971h-.899a.971.971 0 0 1-.971-.971v-.899c0-.536.435-.971.971-.971Zm3.552 0h.899c.536 0 .971.435.971.971v.899a.971.971 0 0 1-.971.971h-.899a.972.972 0 0 1-.972-.971v-.899c0-.536.436-.971.972-.971Zm3.551 0h.9c.536 0 .971.435.971.971v.899a.971.971 0 0 1-.971.971h-.9a.971.971 0 0 1-.971-.971v-.899c0-.536.435-.971.971-.971Zm3.552 0h.899c.536 0 .972.435.972.971v.899a.972.972 0 0 1-.972.971h-.899a.971.971 0 0 1-.971-.971v-.899c0-.536.435-.971.971-.971Zm3.552 0h.899c.536 0 .971.435.971.971v.899a.971.971 0 0 1-.971.971h-.899a.971.971 0 0 1-.971-.971v-.899c0-.536.435-.971.971-.971Zm-14.383-3.512h1.25c.44 0 .796.357.796.796v1.25a.796.796 0 0 1-.796.796h-1.25a.796.796 0 0 1-.795-.796v-1.25c0-.439.356-.796.795-.796Zm3.552 0h1.25c.439 0 .796.357.796.796v1.25a.797.797 0 0 1-.796.796h-1.25a.797.797 0 0 1-.796-.796v-1.25c0-.439.357-.796.796-.796Zm3.552 0h1.25c.439 0 .796.357.796.796v1.25a.797.797 0 0 1-.796.796h-1.25a.797.797 0 0 1-.796-.796v-1.25c0-.439.357-.796.796-.796Zm3.552 0h1.25c.439 0 .796.357.796.796v1.25a.797.797 0 0 1-.796.796h-1.25a.797.797 0 0 1-.796-.796v-1.25c0-.439.357-.796.796-.796Zm-9.553-3.85h13.252c1.407 0 2.755.507 3.748 1.409.993.902 1.552 2.127 1.552 3.404v7.702c0 1.277-.559 2.501-1.552 3.403-.993.902-2.341 1.409-3.748 1.409h-13.252c-1.407 0-2.755-.507-3.748-1.409-.993-.902-1.552-2.126-1.552-3.403v-7.702c0-1.277.559-2.502 1.552-3.404.993-.902 2.341-1.409 3.748-1.409Zm13.105 3.85h1.25c.439 0 .795.357.795.796v1.25a.796.796 0 0 1-.795.796h-1.25a.796.796 0 0 1-.796-.796v-1.25c0-.439.356-.796.796-.796Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="358.441 560.459 32 32"><path fill="none" d="M358.441 560.459h32v32h-32v-32Z"/><path d="m371.985 579.94-5.191 5.191a.726.726 0 0 1-1.025-1.026l5.191-5.19-2.333-2.333c-1.132-1.131-.755-2.152.841-2.277l1.449-.114c.399-.032.952-.287 1.235-.57l2.909-2.908a.726.726 0 0 0 0-1.025l-.257-.257a1.089 1.089 0 0 1 0-1.538 1.089 1.089 0 0 1 1.538 0l6.664 6.665a1.087 1.087 0 1 1-1.538 1.537l-.256-.256a.725.725 0 0 0-1.025 0l-2.908 2.908c-.283.283-.538.837-.57 1.236l-.114 1.449c-.125 1.596-1.146 1.972-2.278.841l-2.332-2.333Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="358.441 560.459 32 32"><path fill="none" d="M358.441 560.459h32v32h-32v-32Z"/><path d="m371.985 579.94-5.191 5.191a.726.726 0 0 1-1.025-1.026l5.191-5.19-2.333-2.333c-1.132-1.131-.755-2.152.841-2.277l1.449-.114c.399-.032.952-.287 1.235-.57l2.909-2.908a.726.726 0 0 0 0-1.025l-.257-.257a1.089 1.089 0 0 1 0-1.538 1.089 1.089 0 0 1 1.538 0l6.664 6.665a1.087 1.087 0 1 1-1.538 1.537l-.256-.256a.725.725 0 0 0-1.025 0l-2.908 2.908c-.283.283-.538.837-.57 1.236l-.114 1.449c-.125 1.596-1.146 1.972-2.278.841l-2.332-2.333Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 625 B After Width: | Height: | Size: 621 B |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="633.685 521.772 32 32"><path fill="none" d="M633.685 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M638.136 537.772a2.972 2.972 0 0 1 2.97-2.97 2.972 2.972 0 0 1 2.97 2.97 2.972 2.972 0 0 1-2.97 2.97 2.972 2.972 0 0 1-2.97-2.97Zm17.155 3.24a.818.818 0 0 0 .283-.052.693.693 0 0 0 .243-.15.603.603 0 0 0 .161-.243.743.743 0 0 0 0-.566.681.681 0 0 0-.408-.383.799.799 0 0 0-.279-.048h-2.575v-1.103h1.743a.844.844 0 0 0 .284-.052.69.69 0 0 0 .404-.389.728.728 0 0 0 0-.56.689.689 0 0 0-.404-.389.844.844 0 0 0-.284-.052h-1.743v-1.052h2.575a.777.777 0 0 0 .522-.195.687.687 0 0 0 .22-.518.74.74 0 0 0-.055-.283.603.603 0 0 0-.161-.243.696.696 0 0 0-.243-.151.841.841 0 0 0-.283-.051h-3.325a.74.74 0 0 0-.283.055.76.76 0 0 0-.247.154.734.734 0 0 0-.221.541v4.98a.764.764 0 0 0 .217.533.74.74 0 0 0 .534.217h3.325Zm5.921-5.447a.6.6 0 0 0-.037-.118 1.013 1.013 0 0 0-.062-.106.635.635 0 0 0-.077-.096l-.089-.077a3.669 3.669 0 0 0-.474-.327 2.477 2.477 0 0 0-.934-.335 3.834 3.834 0 0 0-.615-.048c-.224 0-.445.022-.665.066a2.863 2.863 0 0 0-.6.195 3.578 3.578 0 0 0-.548.305 3.12 3.12 0 0 0-.471.405 3.15 3.15 0 0 0-.386.485 3.245 3.245 0 0 0-.474 1.177 3.757 3.757 0 0 0 0 1.357c.04.214.099.42.18.618.081.199.18.39.294.57.114.173.243.339.386.486.144.151.302.287.475.405.173.117.357.22.548.305.195.084.397.147.607.187a3.247 3.247 0 0 0 1.732-.117c.169-.059.331-.136.489-.225a3.26 3.26 0 0 0 .478-.323.795.795 0 0 0 .184-.261.673.673 0 0 0 .059-.287.782.782 0 0 0-.048-.273.594.594 0 0 0-.143-.235.712.712 0 0 0-.449-.217.816.816 0 0 0-.511.155 2.906 2.906 0 0 1-.283.183 2.476 2.476 0 0 1-.276.129 1.593 1.593 0 0 1-.578.103 1.598 1.598 0 0 1-.96-.309 1.866 1.866 0 0 1-.639-.831 2.052 2.052 0 0 1-.1-1.133c.022-.117.055-.235.1-.345a1.832 1.832 0 0 1 .937-1.005 1.538 1.538 0 0 1 .662-.143 2.6 2.6 0 0 1 .412.033.786.786 0 0 1 .21.063 1.702 1.702 0 0 1 .474.305c.078.062.166.11.258.14a.65.65 0 0 0 .279.036.8.8 0 0 0 .247-.062.728.728 0 0 0 .375-.368.671.671 0 0 0 .055-.272c0-.033 0-.066-.004-.103a.747.747 0 0 0-.018-.092Zm-12.972 3.233h-.555v1.464a.762.762 0 0 1-.055.283.764.764 0 0 1-.155.246.748.748 0 0 1-.246.166.816.816 0 0 1-.295.055.758.758 0 0 1-.533-.217.737.737 0 0 1-.217-.533v-4.98c0-.099.019-.199.055-.294a.752.752 0 0 1 .166-.247.745.745 0 0 1 .246-.154.746.746 0 0 1 .283-.055h1.324c.21 0 .416.025.622.073.184.044.364.11.533.202.166.092.32.206.46.335a2.013 2.013 0 0 1 .559.956c.052.199.077.405.077.611 0 .195-.025.39-.077.577-.048.177-.121.35-.217.504a2.019 2.019 0 0 1-.504.555l.889 1.539a.758.758 0 0 1 .094.273.766.766 0 0 1-.011.29.74.74 0 0 1-.358.462.775.775 0 0 1-.57.078.73.73 0 0 1-.264-.132.78.78 0 0 1-.191-.221l-1.06-1.836Zm.018-2.825c.081 0 .166.011.247.03a.7.7 0 0 1 .438.32.662.662 0 0 1 .066.165c.018.07.029.147.029.221a.602.602 0 0 1-.088.327.579.579 0 0 1-.107.129.94.94 0 0 1-.154.103.93.93 0 0 1-.431.088h-.573v-1.383h.573Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="633.685 521.772 32 32"><path fill="none" d="M633.685 521.772h32v32h-32v-32Z"/><path fill-rule="evenodd" d="M638.136 537.772a2.972 2.972 0 0 1 2.97-2.97 2.972 2.972 0 0 1 2.97 2.97 2.972 2.972 0 0 1-2.97 2.97 2.972 2.972 0 0 1-2.97-2.97Zm17.155 3.24a.818.818 0 0 0 .283-.052.693.693 0 0 0 .243-.15.603.603 0 0 0 .161-.243.743.743 0 0 0 0-.566.681.681 0 0 0-.408-.383.799.799 0 0 0-.279-.048h-2.575v-1.103h1.743a.844.844 0 0 0 .284-.052.69.69 0 0 0 .404-.389.728.728 0 0 0 0-.56.689.689 0 0 0-.404-.389.844.844 0 0 0-.284-.052h-1.743v-1.052h2.575a.777.777 0 0 0 .522-.195.687.687 0 0 0 .22-.518.74.74 0 0 0-.055-.283.603.603 0 0 0-.161-.243.696.696 0 0 0-.243-.151.841.841 0 0 0-.283-.051h-3.325a.74.74 0 0 0-.283.055.76.76 0 0 0-.247.154.734.734 0 0 0-.221.541v4.98a.764.764 0 0 0 .217.533.74.74 0 0 0 .534.217h3.325Zm5.921-5.447a.6.6 0 0 0-.037-.118 1.013 1.013 0 0 0-.062-.106.635.635 0 0 0-.077-.096l-.089-.077a3.669 3.669 0 0 0-.474-.327 2.477 2.477 0 0 0-.934-.335 3.834 3.834 0 0 0-.615-.048c-.224 0-.445.022-.665.066a2.863 2.863 0 0 0-.6.195 3.578 3.578 0 0 0-.548.305 3.12 3.12 0 0 0-.471.405 3.15 3.15 0 0 0-.386.485 3.245 3.245 0 0 0-.474 1.177 3.757 3.757 0 0 0 0 1.357c.04.214.099.42.18.618.081.199.18.39.294.57.114.173.243.339.386.486.144.151.302.287.475.405.173.117.357.22.548.305.195.084.397.147.607.187a3.247 3.247 0 0 0 1.732-.117c.169-.059.331-.136.489-.225a3.26 3.26 0 0 0 .478-.323.795.795 0 0 0 .184-.261.673.673 0 0 0 .059-.287.782.782 0 0 0-.048-.273.594.594 0 0 0-.143-.235.712.712 0 0 0-.449-.217.816.816 0 0 0-.511.155 2.906 2.906 0 0 1-.283.183 2.476 2.476 0 0 1-.276.129 1.593 1.593 0 0 1-.578.103 1.598 1.598 0 0 1-.96-.309 1.866 1.866 0 0 1-.639-.831 2.052 2.052 0 0 1-.1-1.133c.022-.117.055-.235.1-.345a1.832 1.832 0 0 1 .937-1.005 1.538 1.538 0 0 1 .662-.143 2.6 2.6 0 0 1 .412.033.786.786 0 0 1 .21.063 1.702 1.702 0 0 1 .474.305c.078.062.166.11.258.14a.65.65 0 0 0 .279.036.8.8 0 0 0 .247-.062.728.728 0 0 0 .375-.368.671.671 0 0 0 .055-.272c0-.033 0-.066-.004-.103a.747.747 0 0 0-.018-.092Zm-12.972 3.233h-.555v1.464a.762.762 0 0 1-.055.283.764.764 0 0 1-.155.246.748.748 0 0 1-.246.166.816.816 0 0 1-.295.055.758.758 0 0 1-.533-.217.737.737 0 0 1-.217-.533v-4.98c0-.099.019-.199.055-.294a.752.752 0 0 1 .166-.247.745.745 0 0 1 .246-.154.746.746 0 0 1 .283-.055h1.324c.21 0 .416.025.622.073.184.044.364.11.533.202.166.092.32.206.46.335a2.013 2.013 0 0 1 .559.956c.052.199.077.405.077.611 0 .195-.025.39-.077.577-.048.177-.121.35-.217.504a2.019 2.019 0 0 1-.504.555l.889 1.539a.758.758 0 0 1 .094.273.766.766 0 0 1-.011.29.74.74 0 0 1-.358.462.775.775 0 0 1-.57.078.73.73 0 0 1-.264-.132.78.78 0 0 1-.191-.221l-1.06-1.836Zm.018-2.825c.081 0 .166.011.247.03a.7.7 0 0 1 .438.32.662.662 0 0 1 .066.165c.018.07.029.147.029.221a.602.602 0 0 1-.088.327.579.579 0 0 1-.107.129.94.94 0 0 1-.154.103.93.93 0 0 1-.431.088h-.573v-1.383h.573Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="259.244 114.353 32 32" width="32pt" height="32pt"><g><path d=" M 259.244 114.353 L 291.244 114.353 L 291.244 146.353 L 259.244 146.353 L 259.244 114.353 Z " fill="none"/><path d=" M 268.254 130.003 C 268.254 130.725 267.668 131.311 266.945 131.311 C 266.223 131.311 265.637 130.725 265.637 130.003 C 265.639 126.264 267.81 122.867 271.202 121.295 C 274.594 119.724 278.59 120.264 281.442 122.68 L 281.442 121.661 C 281.442 120.939 282.028 120.353 282.751 120.353 C 283.473 120.353 284.059 120.939 284.059 121.661 L 284.059 125.786 C 284.059 126.505 283.476 127.088 282.757 127.088 L 278.788 127.458 C 278.072 127.522 277.438 126.994 277.373 126.277 C 277.309 125.56 277.837 124.927 278.554 124.862 L 279.856 124.743 C 277.79 122.938 274.86 122.506 272.362 123.638 C 269.863 124.771 268.257 127.259 268.254 130.003 Z M 271.959 133.192 C 272.678 133.127 273.313 133.658 273.378 134.377 C 273.443 135.096 272.912 135.731 272.193 135.796 L 270.553 135.944 C 272.603 137.801 275.556 138.275 278.085 137.151 C 280.613 136.027 282.24 133.518 282.235 130.751 C 282.235 130.029 282.821 129.443 283.543 129.443 C 284.266 129.443 284.851 130.029 284.851 130.751 C 284.852 134.439 282.74 137.801 279.417 139.402 C 276.095 141.002 272.149 140.558 269.266 138.259 L 269.266 138.939 C 269.266 139.662 268.68 140.248 267.957 140.248 C 267.235 140.248 266.649 139.662 266.649 138.939 L 266.649 134.865 L 266.649 134.865 C 266.647 134.189 267.162 133.623 267.835 133.563 L 271.954 133.189 L 271.959 133.192 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="259.244 114.353 32 32" width="32" height="32"><g><path d=" M 259.244 114.353 L 291.244 114.353 L 291.244 146.353 L 259.244 146.353 L 259.244 114.353 Z " fill="none"/><path d=" M 268.254 130.003 C 268.254 130.725 267.668 131.311 266.945 131.311 C 266.223 131.311 265.637 130.725 265.637 130.003 C 265.639 126.264 267.81 122.867 271.202 121.295 C 274.594 119.724 278.59 120.264 281.442 122.68 L 281.442 121.661 C 281.442 120.939 282.028 120.353 282.751 120.353 C 283.473 120.353 284.059 120.939 284.059 121.661 L 284.059 125.786 C 284.059 126.505 283.476 127.088 282.757 127.088 L 278.788 127.458 C 278.072 127.522 277.438 126.994 277.373 126.277 C 277.309 125.56 277.837 124.927 278.554 124.862 L 279.856 124.743 C 277.79 122.938 274.86 122.506 272.362 123.638 C 269.863 124.771 268.257 127.259 268.254 130.003 Z M 271.959 133.192 C 272.678 133.127 273.313 133.658 273.378 134.377 C 273.443 135.096 272.912 135.731 272.193 135.796 L 270.553 135.944 C 272.603 137.801 275.556 138.275 278.085 137.151 C 280.613 136.027 282.24 133.518 282.235 130.751 C 282.235 130.029 282.821 129.443 283.543 129.443 C 284.266 129.443 284.851 130.029 284.851 130.751 C 284.852 134.439 282.74 137.801 279.417 139.402 C 276.095 141.002 272.149 140.558 269.266 138.259 L 269.266 138.939 C 269.266 139.662 268.68 140.248 267.957 140.248 C 267.235 140.248 266.649 139.662 266.649 138.939 L 266.649 134.865 L 266.649 134.865 C 266.647 134.189 267.162 133.623 267.835 133.563 L 271.954 133.189 L 271.959 133.192 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
2
flutter/assets/screen.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="321.118 40.669 32 32" width="32" height="32"><g><path d=" M 321.118 40.669 L 353.118 40.669 L 353.118 72.669 L 321.118 72.669 L 321.118 40.669 Z " fill="none"/><path d=" M 344.588 47.817 L 329.648 47.817 C 327.572 47.817 325.887 49.504 325.887 51.581 L 325.887 61.757 C 325.887 63.834 327.572 65.521 329.648 65.521 L 344.588 65.521 C 346.664 65.521 348.35 63.834 348.35 61.757 L 348.35 51.581 C 348.35 49.504 346.664 47.817 344.588 47.817 L 344.588 47.817 L 344.588 47.817 Z " fill="rgb(0,0,0)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 679 B |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="204.996 114.353 32 32" width="32pt" height="32pt"><g><path d=" M 204.996 114.353 L 236.996 114.353 L 236.996 146.353 L 204.996 146.353 L 204.996 114.353 Z " fill="none"/><path d=" M 230.603 139.96 L 230.603 139.96 C 230.08 140.484 229.229 140.484 228.706 139.96 L 223.712 134.966 C 220.568 137.205 216.172 136.915 213.353 134.096 C 210.211 130.954 210.211 125.852 213.353 122.71 C 216.495 119.567 221.597 119.567 224.739 122.71 C 227.558 125.529 227.848 129.925 225.609 133.068 L 230.603 138.063 C 231.127 138.586 231.127 139.436 230.603 139.96 Z M 213.456 128.413 C 213.456 125.328 215.961 122.822 219.047 122.822 C 222.133 122.822 224.638 125.328 224.638 128.413 C 224.638 131.499 222.133 134.005 219.047 134.005 C 215.961 134.005 213.456 131.499 213.456 128.413 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="204.996 114.353 32 32" width="32" height="32"><g><path d=" M 204.996 114.353 L 236.996 114.353 L 236.996 146.353 L 204.996 146.353 L 204.996 114.353 Z " fill="none"/><path d=" M 230.603 139.96 L 230.603 139.96 C 230.08 140.484 229.229 140.484 228.706 139.96 L 223.712 134.966 C 220.568 137.205 216.172 136.915 213.353 134.096 C 210.211 130.954 210.211 125.852 213.353 122.71 C 216.495 119.567 221.597 119.567 224.739 122.71 C 227.558 125.529 227.848 129.925 225.609 133.068 L 230.603 138.063 C 231.127 138.586 231.127 139.436 230.603 139.96 Z M 213.456 128.413 C 213.456 125.328 215.961 122.822 219.047 122.822 C 222.133 122.822 224.638 125.328 224.638 128.413 C 224.638 131.499 222.133 134.005 219.047 134.005 C 215.961 134.005 213.456 131.499 213.456 128.413 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 992 B After Width: | Height: | Size: 988 B |
2
flutter/assets/transfer.svg
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="298.171 98.799 32 32" width="32" height="32"><g><path d=" M 298.171 98.799 L 330.171 98.799 L 330.171 130.799 L 298.171 130.799 L 298.171 98.799 Z " fill="none"/><path d=" M 310.278 119.949 L 321.825 119.949 C 322.772 119.949 323.542 119.18 323.542 118.233 L 323.542 118.233 C 323.542 117.285 322.772 116.516 321.825 116.516 L 307.25 116.516 C 304.69 116.516 304.031 118.035 305.779 119.905 L 311.537 126.064 C 312.207 126.734 313.295 126.734 313.965 126.064 L 313.965 126.064 C 314.635 125.394 314.635 124.306 313.965 123.636 L 310.278 119.949 Z M 318.065 109.649 L 306.518 109.649 C 305.57 109.649 304.801 110.419 304.801 111.366 L 304.801 111.366 C 304.801 112.313 305.57 113.083 306.518 113.083 L 321.092 113.083 C 323.653 113.083 324.312 111.564 322.563 109.693 L 316.806 103.535 C 316.136 102.865 315.048 102.865 314.378 103.535 L 314.378 103.535 C 313.708 104.205 313.708 105.293 314.378 105.963 L 318.065 109.649 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -1,2 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="151.496 106.603 32 32" width="32pt" height="32pt"><g><path d=" M 151.496 106.603 L 183.496 106.603 L 183.496 138.603 L 151.496 138.603 L 151.496 106.603 Z " fill="none"/><path d=" M 162.872 114.091 L 161.304 114.091 C 160.527 114.091 159.897 114.721 159.897 115.498 L 159.897 115.498 C 159.897 116.275 160.527 116.905 161.304 116.905 L 173.688 116.905 C 174.465 116.905 175.096 116.275 175.096 115.498 L 175.096 115.498 C 175.096 114.721 174.465 114.091 173.688 114.091 L 172.12 114.091 L 172.12 113.784 C 172.12 113.132 171.591 112.603 170.939 112.603 L 164.053 112.603 C 163.401 112.603 162.872 113.132 162.872 113.784 L 162.872 114.091 Z M 161.497 117.967 L 173.495 117.967 C 173.779 117.967 174.01 118.198 174.01 118.482 L 174.01 130.03 C 174.01 131.45 172.857 132.603 171.437 132.603 L 163.556 132.603 C 162.135 132.603 160.982 131.45 160.982 130.03 L 160.982 118.482 C 160.982 118.198 161.213 117.967 161.497 117.967 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="151.496 106.603 32 32" width="32" height="32"><g><path d=" M 151.496 106.603 L 183.496 106.603 L 183.496 138.603 L 151.496 138.603 L 151.496 106.603 Z " fill="none"/><path d=" M 162.872 114.091 L 161.304 114.091 C 160.527 114.091 159.897 114.721 159.897 115.498 L 159.897 115.498 C 159.897 116.275 160.527 116.905 161.304 116.905 L 173.688 116.905 C 174.465 116.905 175.096 116.275 175.096 115.498 L 175.096 115.498 C 175.096 114.721 174.465 114.091 173.688 114.091 L 172.12 114.091 L 172.12 113.784 C 172.12 113.132 171.591 112.603 170.939 112.603 L 164.053 112.603 C 163.401 112.603 162.872 113.132 162.872 113.784 L 162.872 114.091 Z M 161.497 117.967 L 173.495 117.967 C 173.779 117.967 174.01 118.198 174.01 118.482 L 174.01 130.03 C 174.01 131.45 172.857 132.603 171.437 132.603 L 163.556 132.603 C 162.135 132.603 160.982 131.45 160.982 130.03 L 160.982 118.482 C 160.982 118.198 161.213 117.967 161.497 117.967 Z " fill-rule="evenodd" fill="rgb(0,0,0)"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32pt" height="32pt" style="isolation:isolate" viewBox="358.441 521.772 32 32"><path fill="none" d="M358.441 521.772h32v32h-32v-32Z"/><path d="M375.166 539.539v7.34a.726.726 0 0 1-1.45 0v-7.34h-3.299c-1.6 0-2.056-.988-1.016-2.205l.944-1.106c.26-.304.471-.876.471-1.276v-4.113c0-.4-.325-.725-.725-.725h-.362a1.088 1.088 0 0 1 0-2.175h9.424a1.088 1.088 0 0 1 0 2.175h-.362a.725.725 0 0 0-.725.725v4.113c0 .4.211.972.47 1.276l.945 1.106c1.039 1.217.584 2.205-1.017 2.205h-3.298Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" style="isolation:isolate" viewBox="358.441 521.772 32 32"><path fill="none" d="M358.441 521.772h32v32h-32v-32Z"/><path d="M375.166 539.539v7.34a.726.726 0 0 1-1.45 0v-7.34h-3.299c-1.6 0-2.056-.988-1.016-2.205l.944-1.106c.26-.304.471-.876.471-1.276v-4.113c0-.4-.325-.725-.725-.725h-.362a1.088 1.088 0 0 1 0-2.175h9.424a1.088 1.088 0 0 1 0 2.175h-.362a.725.725 0 0 0-.725.725v4.113c0 .4.211.972.47 1.276l.945 1.106c1.039 1.217.584 2.205-1.017 2.205h-3.298Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 530 B After Width: | Height: | Size: 526 B |
@ -109,28 +109,48 @@ class IconFont {
|
||||
class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
const ColorThemeExtension({
|
||||
required this.border,
|
||||
required this.border2,
|
||||
required this.highlight,
|
||||
required this.drag_indicator,
|
||||
required this.shadow,
|
||||
});
|
||||
|
||||
final Color? border;
|
||||
final Color? border2;
|
||||
final Color? highlight;
|
||||
final Color? drag_indicator;
|
||||
final Color? shadow;
|
||||
|
||||
static const light = ColorThemeExtension(
|
||||
static final light = ColorThemeExtension(
|
||||
border: Color(0xFFCCCCCC),
|
||||
border2: Color(0xFFBBBBBB),
|
||||
highlight: Color(0xFFE5E5E5),
|
||||
drag_indicator: Colors.grey[800],
|
||||
shadow: Colors.black,
|
||||
);
|
||||
|
||||
static const dark = ColorThemeExtension(
|
||||
static final dark = ColorThemeExtension(
|
||||
border: Color(0xFF555555),
|
||||
border2: Color(0xFFE5E5E5),
|
||||
highlight: Color(0xFF3F3F3F),
|
||||
drag_indicator: Colors.grey,
|
||||
shadow: Colors.grey,
|
||||
);
|
||||
|
||||
@override
|
||||
ThemeExtension<ColorThemeExtension> copyWith(
|
||||
{Color? border, Color? highlight}) {
|
||||
ThemeExtension<ColorThemeExtension> copyWith({
|
||||
Color? border,
|
||||
Color? border2,
|
||||
Color? highlight,
|
||||
Color? drag_indicator,
|
||||
Color? shadow,
|
||||
}) {
|
||||
return ColorThemeExtension(
|
||||
border: border ?? this.border,
|
||||
border2: border2 ?? this.border2,
|
||||
highlight: highlight ?? this.highlight,
|
||||
drag_indicator: drag_indicator ?? this.drag_indicator,
|
||||
shadow: shadow ?? this.shadow,
|
||||
);
|
||||
}
|
||||
|
||||
@ -142,7 +162,10 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
}
|
||||
return ColorThemeExtension(
|
||||
border: Color.lerp(border, other.border, t),
|
||||
border2: Color.lerp(border2, other.border2, t),
|
||||
highlight: Color.lerp(highlight, other.highlight, t),
|
||||
drag_indicator: Color.lerp(drag_indicator, other.drag_indicator, t),
|
||||
shadow: Color.lerp(shadow, other.shadow, t),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -150,8 +173,7 @@ class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||
class MyTheme {
|
||||
MyTheme._();
|
||||
|
||||
static const Color grayBg = Color(0xFFEEEEEE);
|
||||
static const Color white = Color(0xFFFFFFFF);
|
||||
static const Color grayBg = Color(0xFFEFEFF2);
|
||||
static const Color accent = Color(0xFF0071FF);
|
||||
static const Color accent50 = Color(0x770071FF);
|
||||
static const Color accent80 = Color(0xAA0071FF);
|
||||
@ -167,7 +189,28 @@ class MyTheme {
|
||||
static ThemeData lightTheme = ThemeData(
|
||||
brightness: Brightness.light,
|
||||
hoverColor: Color.fromARGB(255, 224, 224, 224),
|
||||
scaffoldBackgroundColor: Color(0xFFFFFFFF),
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
dialogBackgroundColor: Colors.white,
|
||||
dialogTheme: DialogTheme(
|
||||
elevation: 15,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: grayBg,
|
||||
),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: isDesktop
|
||||
? InputDecorationTheme(
|
||||
fillColor: grayBg,
|
||||
filled: true,
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
textTheme: const TextTheme(
|
||||
titleLarge: TextStyle(fontSize: 19, color: Colors.black87),
|
||||
titleSmall: TextStyle(fontSize: 14, color: Colors.black87),
|
||||
@ -175,7 +218,7 @@ class MyTheme {
|
||||
bodyMedium:
|
||||
TextStyle(fontSize: 14, color: Colors.black87, height: 1.25),
|
||||
labelLarge: TextStyle(fontSize: 16.0, color: MyTheme.accent80)),
|
||||
cardColor: Color(0xFFEEEEEE),
|
||||
cardColor: grayBg,
|
||||
hintColor: Color(0xFFAAAAAA),
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
tabBarTheme: const TabBarTheme(
|
||||
@ -186,13 +229,51 @@ class MyTheme {
|
||||
splashFactory: isDesktop ? NoSplash.splashFactory : null,
|
||||
textButtonTheme: isDesktop
|
||||
? TextButtonThemeData(
|
||||
style: ButtonStyle(splashFactory: NoSplash.splashFactory),
|
||||
style: TextButton.styleFrom(
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue).copyWith(
|
||||
brightness: Brightness.light,
|
||||
background: Color(0xFFEEEEEE),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: MyTheme.accent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: grayBg,
|
||||
foregroundColor: Colors.black87,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkboxTheme: const CheckboxThemeData(
|
||||
splashRadius: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(5),
|
||||
),
|
||||
),
|
||||
),
|
||||
listTileTheme: ListTileThemeData(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(5),
|
||||
),
|
||||
),
|
||||
),
|
||||
menuBarTheme: MenuBarThemeData(
|
||||
style:
|
||||
MenuStyle(backgroundColor: MaterialStatePropertyAll(Colors.white))),
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: Colors.blue, secondary: accent, background: grayBg),
|
||||
).copyWith(
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
ColorThemeExtension.light,
|
||||
@ -203,6 +284,27 @@ class MyTheme {
|
||||
brightness: Brightness.dark,
|
||||
hoverColor: Color.fromARGB(255, 45, 46, 53),
|
||||
scaffoldBackgroundColor: Color(0xFF18191E),
|
||||
dialogBackgroundColor: Color(0xFF18191E),
|
||||
dialogTheme: DialogTheme(
|
||||
elevation: 15,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
side: BorderSide(
|
||||
width: 1,
|
||||
color: Color(0xFF24252B),
|
||||
),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: isDesktop
|
||||
? InputDecorationTheme(
|
||||
fillColor: Color(0xFF24252B),
|
||||
filled: true,
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
textTheme: const TextTheme(
|
||||
titleLarge: TextStyle(fontSize: 19),
|
||||
titleSmall: TextStyle(fontSize: 14),
|
||||
@ -215,23 +317,69 @@ class MyTheme {
|
||||
tabBarTheme: const TabBarTheme(
|
||||
labelColor: Colors.white70,
|
||||
),
|
||||
scrollbarTheme: ScrollbarThemeData(
|
||||
thumbColor: MaterialStateProperty.all(Colors.grey[500]),
|
||||
),
|
||||
splashColor: Colors.transparent,
|
||||
highlightColor: Colors.transparent,
|
||||
splashFactory: isDesktop ? NoSplash.splashFactory : null,
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style:
|
||||
OutlinedButton.styleFrom(side: BorderSide(color: Colors.white38))),
|
||||
textButtonTheme: isDesktop
|
||||
? TextButtonThemeData(
|
||||
style: ButtonStyle(splashFactory: NoSplash.splashFactory),
|
||||
style: TextButton.styleFrom(
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
disabledForegroundColor: Colors.white70,
|
||||
foregroundColor: Colors.white70,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
checkboxTheme:
|
||||
const CheckboxThemeData(checkColor: MaterialStatePropertyAll(dark)),
|
||||
colorScheme: ColorScheme.fromSwatch(
|
||||
brightness: Brightness.dark,
|
||||
primarySwatch: Colors.blue,
|
||||
).copyWith(background: Color(0xFF24252B)),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: MyTheme.accent,
|
||||
foregroundColor: Colors.white,
|
||||
disabledForegroundColor: Colors.white70,
|
||||
disabledBackgroundColor: Colors.white10,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF24252B),
|
||||
side: BorderSide(color: Colors.white12, width: 0.5),
|
||||
disabledForegroundColor: Colors.white70,
|
||||
foregroundColor: Colors.white70,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkboxTheme: const CheckboxThemeData(
|
||||
splashRadius: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(5),
|
||||
),
|
||||
),
|
||||
),
|
||||
listTileTheme: ListTileThemeData(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(5),
|
||||
),
|
||||
),
|
||||
),
|
||||
menuBarTheme: MenuBarThemeData(
|
||||
style: MenuStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(Color(0xFF121212)))),
|
||||
colorScheme: ColorScheme.dark(
|
||||
primary: Colors.blue,
|
||||
secondary: accent,
|
||||
background: Color(0xFF24252B),
|
||||
),
|
||||
).copyWith(
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
ColorThemeExtension.dark,
|
||||
@ -245,7 +393,7 @@ class MyTheme {
|
||||
|
||||
static void changeDarkMode(ThemeMode mode) async {
|
||||
Get.changeThemeMode(mode);
|
||||
if (desktopType == DesktopType.main) {
|
||||
if (desktopType == DesktopType.main || isAndroid || isIOS) {
|
||||
if (mode == ThemeMode.system) {
|
||||
await bind.mainSetLocalOption(key: kCommConfKeyTheme, value: '');
|
||||
} else {
|
||||
@ -307,7 +455,7 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom(
|
||||
);
|
||||
|
||||
List<Locale> supportedLocales = const [
|
||||
// specify CN/TW to fix CJK issue in flutter
|
||||
Locale('en', 'US'),
|
||||
Locale('zh', 'CN'),
|
||||
Locale('zh', 'TW'),
|
||||
Locale('zh', 'SG'),
|
||||
@ -329,7 +477,7 @@ List<Locale> supportedLocales = const [
|
||||
Locale('vi'),
|
||||
Locale('pl'),
|
||||
Locale('kz'),
|
||||
Locale('en', 'US'),
|
||||
Locale('es'),
|
||||
];
|
||||
|
||||
String formatDurationToTime(Duration duration) {
|
||||
@ -456,7 +604,7 @@ class OverlayDialogManager {
|
||||
BackButtonInterceptor.removeByName(dialogTag);
|
||||
}
|
||||
|
||||
dialog.entry = OverlayEntry(builder: (_) {
|
||||
dialog.entry = OverlayEntry(builder: (context) {
|
||||
bool innerClicked = false;
|
||||
return Listener(
|
||||
onPointerUp: (_) {
|
||||
@ -466,7 +614,9 @@ class OverlayDialogManager {
|
||||
innerClicked = false;
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.black12,
|
||||
color: Theme.of(context).brightness == Brightness.light
|
||||
? Colors.black12
|
||||
: Colors.black45,
|
||||
child: StatefulBuilder(builder: (context, setState) {
|
||||
return Listener(
|
||||
onPointerUp: (_) => innerClicked = true,
|
||||
@ -648,7 +798,7 @@ class CustomAlertDialog extends StatelessWidget {
|
||||
Future.delayed(Duration.zero, () {
|
||||
if (!scopeNode.hasFocus) scopeNode.requestFocus();
|
||||
});
|
||||
const double padding = 16;
|
||||
const double padding = 30;
|
||||
bool tabTapped = false;
|
||||
return FocusScope(
|
||||
node: scopeNode,
|
||||
@ -677,15 +827,15 @@ class CustomAlertDialog extends StatelessWidget {
|
||||
scrollable: true,
|
||||
title: title,
|
||||
titlePadding: EdgeInsets.fromLTRB(padding, 24, padding, 0),
|
||||
contentPadding: EdgeInsets.fromLTRB(contentPadding ?? padding, 25,
|
||||
contentPadding ?? padding, actions is List ? 10 : padding),
|
||||
contentPadding: EdgeInsets.fromLTRB(
|
||||
contentPadding ?? padding,
|
||||
25,
|
||||
contentPadding ?? padding,
|
||||
actions is List ? 10 : padding,
|
||||
),
|
||||
content: ConstrainedBox(
|
||||
constraints: contentBoxConstraints,
|
||||
child: Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
isDense: true, contentPadding: EdgeInsets.all(15))),
|
||||
child: content),
|
||||
child: content,
|
||||
),
|
||||
actions: actions,
|
||||
actionsPadding: EdgeInsets.fromLTRB(padding, 0, padding, padding),
|
||||
@ -820,7 +970,6 @@ Widget msgboxContent(String type, String title, String text) {
|
||||
void msgBoxCommon(OverlayDialogManager dialogManager, String title,
|
||||
Widget content, List<Widget> buttons,
|
||||
{bool hasCancel = true}) {
|
||||
dialogManager.dismissAll();
|
||||
dialogManager.show((setState, close) => CustomAlertDialog(
|
||||
title: Text(
|
||||
translate(title),
|
||||
@ -903,21 +1052,14 @@ class AccessibilityListener extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class PermissionManager {
|
||||
class AndroidPermissionManager {
|
||||
static Completer<bool>? _completer;
|
||||
static Timer? _timer;
|
||||
static var _current = "";
|
||||
|
||||
static final permissions = [
|
||||
"audio",
|
||||
"file",
|
||||
"ignore_battery_optimizations",
|
||||
"application_details_settings"
|
||||
];
|
||||
|
||||
static bool isWaitingFile() {
|
||||
if (_completer != null) {
|
||||
return !_completer!.isCompleted && _current == "file";
|
||||
return !_completer!.isCompleted && _current == kManageExternalStorage;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -926,31 +1068,33 @@ class PermissionManager {
|
||||
if (isDesktop) {
|
||||
return Future.value(true);
|
||||
}
|
||||
if (!permissions.contains(type)) {
|
||||
return Future.error("Wrong permission!$type");
|
||||
}
|
||||
return gFFI.invokeMethod("check_permission", type);
|
||||
}
|
||||
|
||||
// startActivity goto Android Setting's page to request permission manually by user
|
||||
static void startAction(String action) {
|
||||
gFFI.invokeMethod(AndroidChannel.kStartAction, action);
|
||||
}
|
||||
|
||||
/// We use XXPermissions to request permissions,
|
||||
/// for supported types, see https://github.com/getActivity/XXPermissions/blob/e46caea32a64ad7819df62d448fb1c825481cd28/library/src/main/java/com/hjq/permissions/Permission.java
|
||||
static Future<bool> request(String type) {
|
||||
if (isDesktop) {
|
||||
return Future.value(true);
|
||||
}
|
||||
if (!permissions.contains(type)) {
|
||||
return Future.error("Wrong permission!$type");
|
||||
}
|
||||
|
||||
gFFI.invokeMethod("request_permission", type);
|
||||
if (type == "ignore_battery_optimizations") {
|
||||
return Future.value(false);
|
||||
|
||||
// clear last task
|
||||
if (_completer?.isCompleted == false) {
|
||||
_completer?.complete(false);
|
||||
}
|
||||
_timer?.cancel();
|
||||
|
||||
_current = type;
|
||||
_completer = Completer<bool>();
|
||||
gFFI.invokeMethod("request_permission", type);
|
||||
|
||||
// timeout
|
||||
_timer?.cancel();
|
||||
_timer = Timer(Duration(seconds: 60), () {
|
||||
_timer = Timer(Duration(seconds: 120), () {
|
||||
if (_completer == null) return;
|
||||
if (!_completer!.isCompleted) {
|
||||
_completer!.complete(false);
|
||||
@ -1379,6 +1523,11 @@ bool checkArguments() {
|
||||
}
|
||||
String? id =
|
||||
kBootArgs.length < connectIndex + 1 ? null : kBootArgs[connectIndex + 1];
|
||||
String? password =
|
||||
kBootArgs.length < connectIndex + 2 ? null : kBootArgs[connectIndex + 2];
|
||||
if (password != null && password.startsWith("--")) {
|
||||
password = null;
|
||||
}
|
||||
final switchUuidIndex = kBootArgs.indexOf("--switch_uuid");
|
||||
String? switchUuid = kBootArgs.length < switchUuidIndex + 1
|
||||
? null
|
||||
@ -1392,7 +1541,8 @@ bool checkArguments() {
|
||||
kBootArgs.removeAt(connectIndex);
|
||||
// fallback to peer id
|
||||
Future.delayed(Duration.zero, () {
|
||||
rustDeskWinManager.newRemoteDesktop(id, switch_uuid: switchUuid);
|
||||
rustDeskWinManager.newRemoteDesktop(id,
|
||||
password: password, switch_uuid: switchUuid);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@ -1453,10 +1603,12 @@ connectMainDesktop(String id,
|
||||
connect(BuildContext context, String id,
|
||||
{bool isFileTransfer = false,
|
||||
bool isTcpTunneling = false,
|
||||
bool isRDP = false,
|
||||
bool forceRelay = false}) async {
|
||||
bool isRDP = false}) async {
|
||||
if (id == '') return;
|
||||
id = id.replaceAll(' ', '');
|
||||
final oldId = id;
|
||||
id = await bind.mainHandleRelayId(id: id);
|
||||
final forceRelay = id != oldId;
|
||||
assert(!(isFileTransfer && isTcpTunneling && isRDP),
|
||||
"more than one connect type");
|
||||
|
||||
@ -1478,8 +1630,8 @@ connect(BuildContext context, String id,
|
||||
}
|
||||
} else {
|
||||
if (isFileTransfer) {
|
||||
if (!await PermissionManager.check("file")) {
|
||||
if (!await PermissionManager.request("file")) {
|
||||
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
|
||||
if (!await AndroidPermissionManager.request(kManageExternalStorage)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1671,10 +1823,15 @@ class ServerConfig {
|
||||
/// also see [encode]
|
||||
/// throw when decoding failure
|
||||
ServerConfig.decode(String msg) {
|
||||
final input = msg.split('').reversed.join('');
|
||||
final bytes = base64Decode(base64.normalize(input));
|
||||
final json = jsonDecode(utf8.decode(bytes));
|
||||
|
||||
var json = {};
|
||||
try {
|
||||
// back compatible
|
||||
json = jsonDecode(msg);
|
||||
} catch (err) {
|
||||
final input = msg.split('').reversed.join('');
|
||||
final bytes = base64Decode(base64.normalize(input));
|
||||
json = jsonDecode(utf8.decode(bytes));
|
||||
}
|
||||
idServer = json['host'] ?? '';
|
||||
relayServer = json['relay'] ?? '';
|
||||
apiServer = json['api'] ?? '';
|
||||
@ -1706,28 +1863,43 @@ class ServerConfig {
|
||||
Widget dialogButton(String text,
|
||||
{required VoidCallback? onPressed,
|
||||
bool isOutline = false,
|
||||
Widget? icon,
|
||||
TextStyle? style,
|
||||
ButtonStyle? buttonStyle}) {
|
||||
if (isDesktop) {
|
||||
if (isOutline) {
|
||||
return OutlinedButton(
|
||||
onPressed: onPressed,
|
||||
child: Text(translate(text), style: style),
|
||||
);
|
||||
return icon == null
|
||||
? OutlinedButton(
|
||||
onPressed: onPressed,
|
||||
child: Text(translate(text), style: style),
|
||||
)
|
||||
: OutlinedButton.icon(
|
||||
icon: icon,
|
||||
onPressed: onPressed,
|
||||
label: Text(translate(text), style: style),
|
||||
);
|
||||
} else {
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(elevation: 0).merge(buttonStyle),
|
||||
onPressed: onPressed,
|
||||
child: Text(translate(text), style: style),
|
||||
);
|
||||
return icon == null
|
||||
? ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(elevation: 0).merge(buttonStyle),
|
||||
onPressed: onPressed,
|
||||
child: Text(translate(text), style: style),
|
||||
)
|
||||
: ElevatedButton.icon(
|
||||
icon: icon,
|
||||
style: ElevatedButton.styleFrom(elevation: 0).merge(buttonStyle),
|
||||
onPressed: onPressed,
|
||||
label: Text(translate(text), style: style),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return TextButton(
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
translate(text),
|
||||
style: style,
|
||||
));
|
||||
onPressed: onPressed,
|
||||
child: Text(
|
||||
translate(text),
|
||||
style: style,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -19,6 +19,8 @@ class PrivacyModeState {
|
||||
final key = tag(id);
|
||||
if (Get.isRegistered(tag: key)) {
|
||||
Get.delete(tag: key);
|
||||
} else {
|
||||
Get.find<RxBool>(tag: key).value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +35,8 @@ class BlockInputState {
|
||||
if (!Get.isRegistered(tag: key)) {
|
||||
final RxBool state = false.obs;
|
||||
Get.put(state, tag: key);
|
||||
} else {
|
||||
Get.find<RxBool>(tag: key).value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +58,8 @@ class CurrentDisplayState {
|
||||
if (!Get.isRegistered(tag: key)) {
|
||||
final RxInt state = RxInt(0);
|
||||
Get.put(state, tag: key);
|
||||
} else {
|
||||
Get.find<RxInt>(tag: key).value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,6 +129,8 @@ class ShowRemoteCursorState {
|
||||
if (!Get.isRegistered(tag: key)) {
|
||||
final RxBool state = false.obs;
|
||||
Get.put(state, tag: key);
|
||||
} else {
|
||||
Get.find<RxBool>(tag: key).value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,6 +153,8 @@ class KeyboardEnabledState {
|
||||
// Server side, default true
|
||||
final RxBool state = true.obs;
|
||||
Get.put(state, tag: key);
|
||||
} else {
|
||||
Get.find<RxBool>(tag: key).value = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,9 +174,10 @@ class RemoteCursorMovedState {
|
||||
static void init(String id) {
|
||||
final key = tag(id);
|
||||
if (!Get.isRegistered(tag: key)) {
|
||||
// Server side, default true
|
||||
final RxBool state = false.obs;
|
||||
Get.put(state, tag: key);
|
||||
} else {
|
||||
Get.find<RxBool>(tag: key).value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,9 +197,10 @@ class RemoteCountState {
|
||||
static void init() {
|
||||
final key = tag();
|
||||
if (!Get.isRegistered(tag: key)) {
|
||||
// Server side, default true
|
||||
final RxInt state = 1.obs;
|
||||
Get.put(state, tag: key);
|
||||
} else {
|
||||
Get.find<RxInt>(tag: key).value = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,6 +222,8 @@ class PeerBoolOption {
|
||||
if (!Get.isRegistered(tag: key)) {
|
||||
final RxBool value = RxBool(init_getter());
|
||||
Get.put(value, tag: key);
|
||||
} else {
|
||||
Get.find<RxBool>(tag: key).value = init_getter();
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,6 +246,8 @@ class PeerStringOption {
|
||||
if (!Get.isRegistered(tag: key)) {
|
||||
final RxString value = RxString(init_getter());
|
||||
Get.put(value, tag: key);
|
||||
} else {
|
||||
Get.find<RxString>(tag: key).value = init_getter();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -274,11 +274,7 @@ class _AddressBookState extends State<AddressBook> {
|
||||
TextField(
|
||||
controller: idController,
|
||||
inputFormatters: [IDTextInputFormatter()],
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
errorText: errorMsg),
|
||||
style: style,
|
||||
decoration: InputDecoration(errorText: errorMsg),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
@ -289,11 +285,6 @@ class _AddressBookState extends State<AddressBook> {
|
||||
).marginOnly(top: 8, bottom: 2),
|
||||
TextField(
|
||||
controller: aliasController,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
isDense: true,
|
||||
),
|
||||
style: style,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
@ -379,7 +370,6 @@ class _AddressBookState extends State<AddressBook> {
|
||||
child: TextField(
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: msg.isEmpty ? null : translate(msg),
|
||||
),
|
||||
controller: controller,
|
||||
|
||||
@ -73,7 +73,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
||||
? InputDecoration(
|
||||
isDense: true,
|
||||
hintText:
|
||||
"${translate('Write a message')}...",
|
||||
"${translate('Write a message')}",
|
||||
filled: true,
|
||||
fillColor:
|
||||
Theme.of(context).colorScheme.background,
|
||||
@ -88,7 +88,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
||||
)
|
||||
: defaultInputDecoration(
|
||||
hintText:
|
||||
"${translate('Write a message')}...",
|
||||
"${translate('Write a message')}",
|
||||
fillColor:
|
||||
Theme.of(context).colorScheme.background),
|
||||
sendButtonBuilder: defaultSendButton(
|
||||
|
||||
@ -63,8 +63,9 @@ void changeIdDialog() {
|
||||
final Iterable violations = rules.where((r) => !r.validate(newId));
|
||||
if (violations.isNotEmpty) {
|
||||
setState(() {
|
||||
msg =
|
||||
'${translate('Prompt')}: ${violations.map((r) => r.name).join(', ')}';
|
||||
msg = isDesktop
|
||||
? '${translate('Prompt')}: ${violations.map((r) => r.name).join(', ')}'
|
||||
: violations.map((r) => r.name).join(', ');
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -87,7 +88,9 @@ void changeIdDialog() {
|
||||
}
|
||||
setState(() {
|
||||
isInProgress = false;
|
||||
msg = '${translate('Prompt')}: ${translate(status)}';
|
||||
msg = isDesktop
|
||||
? '${translate('Prompt')}: ${translate(status)}'
|
||||
: translate(status);
|
||||
});
|
||||
}
|
||||
|
||||
@ -103,7 +106,6 @@ void changeIdDialog() {
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: translate('Your new ID'),
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: msg.isEmpty ? null : translate(msg),
|
||||
suffixText: '${rxId.value.length}/16',
|
||||
suffixStyle: const TextStyle(fontSize: 12, color: Colors.grey)),
|
||||
@ -123,27 +125,26 @@ void changeIdDialog() {
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Obx(() => Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 4,
|
||||
children: rules.map((e) {
|
||||
var checked = e.validate(rxId.value);
|
||||
return Chip(
|
||||
label: Text(
|
||||
e.name,
|
||||
style: TextStyle(
|
||||
color: checked
|
||||
? const Color(0xFF0A9471)
|
||||
: Color.fromARGB(255, 198, 86, 157)),
|
||||
),
|
||||
backgroundColor: checked
|
||||
? const Color(0xFFD0F7ED)
|
||||
: Color.fromARGB(255, 247, 205, 232));
|
||||
}).toList(),
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
isDesktop
|
||||
? Obx(() => Wrap(
|
||||
runSpacing: 8,
|
||||
spacing: 4,
|
||||
children: rules.map((e) {
|
||||
var checked = e.validate(rxId.value);
|
||||
return Chip(
|
||||
label: Text(
|
||||
e.name,
|
||||
style: TextStyle(
|
||||
color: checked
|
||||
? const Color(0xFF0A9471)
|
||||
: Color.fromARGB(255, 198, 86, 157)),
|
||||
),
|
||||
backgroundColor: checked
|
||||
? const Color(0xFFD0F7ED)
|
||||
: Color.fromARGB(255, 247, 205, 232));
|
||||
}).toList(),
|
||||
)).marginOnly(bottom: 8)
|
||||
: SizedBox.shrink(),
|
||||
Offstage(
|
||||
offstage: !isInProgress, child: const LinearProgressIndicator())
|
||||
],
|
||||
@ -180,7 +181,6 @@ void changeWhiteList({Function()? callback}) async {
|
||||
child: TextField(
|
||||
maxLines: null,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: msg.isEmpty ? null : translate(msg),
|
||||
),
|
||||
controller: controller,
|
||||
|
||||
@ -405,7 +405,6 @@ class DialogTextField extends StatelessWidget {
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: title,
|
||||
border: const OutlineInputBorder(),
|
||||
prefixIcon: prefixIcon,
|
||||
helperText: helperText,
|
||||
helperMaxLines: 8,
|
||||
@ -635,9 +634,7 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
|
||||
offstage: user?.email == null,
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: "Email",
|
||||
prefixIcon: Icon(Icons.email),
|
||||
border: InputBorder.none),
|
||||
labelText: "Email", prefixIcon: Icon(Icons.email)),
|
||||
readOnly: true,
|
||||
controller: TextEditingController(text: user?.email),
|
||||
)),
|
||||
|
||||
@ -331,7 +331,7 @@ class QualityMonitor extends StatelessWidget {
|
||||
Expanded(
|
||||
flex: 8,
|
||||
child: AutoSizeText(info,
|
||||
style: TextStyle(color: MyTheme.darkGray),
|
||||
style: TextStyle(color: Color.fromARGB(255, 210, 210, 210)),
|
||||
textAlign: TextAlign.right,
|
||||
maxLines: 1)),
|
||||
Spacer(flex: 1),
|
||||
@ -353,7 +353,7 @@ class QualityMonitor extends StatelessWidget {
|
||||
? Container(
|
||||
constraints: BoxConstraints(maxWidth: 200),
|
||||
padding: const EdgeInsets.all(8),
|
||||
color: MyTheme.canvasColor.withAlpha(120),
|
||||
color: MyTheme.canvasColor.withAlpha(150),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
@ -42,6 +42,7 @@ class _PeerCardState extends State<_PeerCard>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
var _menuPos = RelativeRect.fill;
|
||||
final double _cardRadius = 16;
|
||||
final double _tileRadius = 5;
|
||||
final double _borderWidth = 2;
|
||||
|
||||
@override
|
||||
@ -116,27 +117,32 @@ class _PeerCardState extends State<_PeerCard>
|
||||
|
||||
Widget _buildDesktop() {
|
||||
final peer = super.widget.peer;
|
||||
var deco = Rx<BoxDecoration?>(BoxDecoration(
|
||||
var deco = Rx<BoxDecoration?>(
|
||||
BoxDecoration(
|
||||
border: Border.all(color: Colors.transparent, width: _borderWidth),
|
||||
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||
? BorderRadius.circular(_cardRadius)
|
||||
: null));
|
||||
borderRadius: BorderRadius.circular(
|
||||
peerCardUiType.value == PeerUiType.grid ? _cardRadius : _tileRadius,
|
||||
),
|
||||
),
|
||||
);
|
||||
return MouseRegion(
|
||||
onEnter: (evt) {
|
||||
deco.value = BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: _borderWidth),
|
||||
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||
? BorderRadius.circular(_cardRadius)
|
||||
: null);
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
width: _borderWidth),
|
||||
borderRadius: BorderRadius.circular(
|
||||
peerCardUiType.value == PeerUiType.grid ? _cardRadius : _tileRadius,
|
||||
),
|
||||
);
|
||||
},
|
||||
onExit: (evt) {
|
||||
deco.value = BoxDecoration(
|
||||
border: Border.all(color: Colors.transparent, width: _borderWidth),
|
||||
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||
? BorderRadius.circular(_cardRadius)
|
||||
: null);
|
||||
border: Border.all(color: Colors.transparent, width: _borderWidth),
|
||||
borderRadius: BorderRadius.circular(
|
||||
peerCardUiType.value == PeerUiType.grid ? _cardRadius : _tileRadius,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: GestureDetector(
|
||||
onDoubleTap: () => widget.connect(context, peer.id),
|
||||
@ -163,6 +169,10 @@ class _PeerCardState extends State<_PeerCard>
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: str2color('${peer.id}${peer.platform}', 0x7f),
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(_tileRadius),
|
||||
bottomLeft: Radius.circular(_tileRadius),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
width: 42,
|
||||
@ -171,7 +181,12 @@ class _PeerCardState extends State<_PeerCard>
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background),
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(_tileRadius),
|
||||
bottomRight: Radius.circular(_tileRadius),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -532,19 +547,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
proc: () {
|
||||
() async {
|
||||
if (isLan) {
|
||||
bind.mainRemoveDiscovered(id: id);
|
||||
} else {
|
||||
final favs = (await bind.mainGetFav()).toList();
|
||||
if (favs.remove(id)) {
|
||||
await bind.mainStoreFav(favs: favs);
|
||||
}
|
||||
await bind.mainRemovePeer(id: id);
|
||||
}
|
||||
removePreference(id);
|
||||
await reloadFunc();
|
||||
}();
|
||||
_delete(id, isLan, reloadFunc);
|
||||
},
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
@ -673,7 +676,13 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: Text(translate('Rename')),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.edit_rounded, color: MyTheme.accent),
|
||||
Text(translate('Rename')).paddingOnly(left: 10),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -682,9 +691,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: translate('Name')),
|
||||
decoration: InputDecoration(labelText: translate('Name')),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -694,8 +701,17 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
dialogButton("Cancel", onPressed: close, isOutline: true),
|
||||
dialogButton("OK", onPressed: submit),
|
||||
dialogButton(
|
||||
"Cancel",
|
||||
icon: Icon(Icons.close_rounded),
|
||||
onPressed: close,
|
||||
isOutline: true,
|
||||
),
|
||||
dialogButton(
|
||||
"OK",
|
||||
icon: Icon(Icons.done_rounded),
|
||||
onPressed: submit,
|
||||
),
|
||||
],
|
||||
onSubmit: submit,
|
||||
onCancel: close,
|
||||
@ -705,6 +721,58 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
|
||||
@protected
|
||||
void _update();
|
||||
|
||||
void _delete(String id, bool isLan, Function reloadFunc) async {
|
||||
gFFI.dialogManager.show(
|
||||
(setState, close) {
|
||||
submit() async {
|
||||
if (isLan) {
|
||||
bind.mainRemoveDiscovered(id: id);
|
||||
} else {
|
||||
final favs = (await bind.mainGetFav()).toList();
|
||||
if (favs.remove(id)) {
|
||||
await bind.mainStoreFav(favs: favs);
|
||||
}
|
||||
await bind.mainRemovePeer(id: id);
|
||||
}
|
||||
removePreference(id);
|
||||
await reloadFunc();
|
||||
close();
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete_rounded,
|
||||
color: Colors.red,
|
||||
),
|
||||
Text(translate('Delete')).paddingOnly(
|
||||
left: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
content: SizedBox.shrink(),
|
||||
actions: [
|
||||
dialogButton(
|
||||
"Cancel",
|
||||
icon: Icon(Icons.close_rounded),
|
||||
onPressed: close,
|
||||
isOutline: true,
|
||||
),
|
||||
dialogButton(
|
||||
"OK",
|
||||
icon: Icon(Icons.done_rounded),
|
||||
onPressed: submit,
|
||||
),
|
||||
],
|
||||
onSubmit: submit,
|
||||
onCancel: close,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RecentPeerCard extends BasePeerCard {
|
||||
@ -837,13 +905,10 @@ class DiscoveredPeerCard extends BasePeerCard {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
|
||||
final inRecent = await bind.mainIsInRecentPeers(id: peer.id);
|
||||
if (inRecent) {
|
||||
if (!favs.contains(peer.id)) {
|
||||
menuItems.add(_addFavAction(peer.id));
|
||||
} else {
|
||||
menuItems.add(_rmFavAction(peer.id, () async {}));
|
||||
}
|
||||
if (!favs.contains(peer.id)) {
|
||||
menuItems.add(_addFavAction(peer.id));
|
||||
} else {
|
||||
menuItems.add(_rmFavAction(peer.id, () async {}));
|
||||
}
|
||||
|
||||
if (gFFI.userModel.userName.isNotEmpty) {
|
||||
@ -1065,7 +1130,7 @@ void _rdpDialog(String id) async {
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: Text('RDP ${translate('Settings')}'),
|
||||
title: Text(translate('RDP Settings')),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 500),
|
||||
child: Column(
|
||||
@ -1076,56 +1141,63 @@ void _rdpDialog(String id) async {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
child: Text(
|
||||
"${translate('Port')}:",
|
||||
textAlign: TextAlign.right,
|
||||
).marginOnly(right: 10)),
|
||||
isDesktop
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
child: Text(
|
||||
"${translate('Port')}:",
|
||||
textAlign: TextAlign.right,
|
||||
).marginOnly(right: 10))
|
||||
: SizedBox.shrink(),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(
|
||||
r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$'))
|
||||
],
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(), hintText: '3389'),
|
||||
decoration: InputDecoration(
|
||||
labelText: isDesktop ? null : translate('Port'),
|
||||
hintText: '3389'),
|
||||
controller: portController,
|
||||
autofocus: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: 8),
|
||||
).marginOnly(bottom: isDesktop ? 8 : 0),
|
||||
Row(
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
child: Text(
|
||||
"${translate('Username')}:",
|
||||
textAlign: TextAlign.right,
|
||||
).marginOnly(right: 10)),
|
||||
isDesktop
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
child: Text(
|
||||
"${translate('Username')}:",
|
||||
textAlign: TextAlign.right,
|
||||
).marginOnly(right: 10))
|
||||
: SizedBox.shrink(),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration:
|
||||
const InputDecoration(border: OutlineInputBorder()),
|
||||
decoration: InputDecoration(
|
||||
labelText: isDesktop ? null : translate('Username')),
|
||||
controller: userController,
|
||||
),
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: 8),
|
||||
).marginOnly(bottom: isDesktop ? 8 : 0),
|
||||
Row(
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
child: Text(
|
||||
"${translate('Password')}:",
|
||||
textAlign: TextAlign.right,
|
||||
).marginOnly(right: 10)),
|
||||
isDesktop
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
child: Text(
|
||||
"${translate('Password')}:",
|
||||
textAlign: TextAlign.right,
|
||||
).marginOnly(right: 10))
|
||||
: SizedBox.shrink(),
|
||||
Expanded(
|
||||
child: Obx(() => TextField(
|
||||
obscureText: secure.value,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: isDesktop ? null : translate('Password'),
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () => secure.value = !secure.value,
|
||||
icon: Icon(secure.value
|
||||
@ -1135,7 +1207,7 @@ void _rdpDialog(String id) async {
|
||||
)),
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: 8),
|
||||
).marginOnly(bottom: isDesktop ? 8 : 0),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
@ -17,6 +18,7 @@ import 'package:get/get.dart';
|
||||
import 'package:get/get_rx/src/rx_workers/utils/debouncer.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
@ -39,6 +41,8 @@ EdgeInsets? _menuPadding() {
|
||||
|
||||
class _PeerTabPageState extends State<PeerTabPage>
|
||||
with SingleTickerProviderStateMixin {
|
||||
bool _hideSort = bind.getLocalFlutterConfig(k: 'peer-tab-index') == '0';
|
||||
|
||||
final List<_TabEntry> entries = [
|
||||
_TabEntry(
|
||||
RecentPeersView(
|
||||
@ -83,6 +87,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
if (tabIndex < entries.length) {
|
||||
gFFI.peerTabModel.setCurrentTab(tabIndex);
|
||||
entries[tabIndex].load();
|
||||
_hideSort = tabIndex == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,22 +100,27 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
SizedBox(
|
||||
height: 28,
|
||||
child: Container(
|
||||
padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2),
|
||||
constraints: isDesktop ? null : kMobilePageConstraints,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: visibleContextMenuListener(
|
||||
_createSwitchBar(context))),
|
||||
buildScrollJumper(),
|
||||
const PeerSearchBar(),
|
||||
Offstage(
|
||||
offstage: !isDesktop,
|
||||
child: _createPeerViewTypeSwitch(context)
|
||||
.marginOnly(left: 13)),
|
||||
],
|
||||
)),
|
||||
padding: isDesktop ? null : EdgeInsets.symmetric(horizontal: 2),
|
||||
constraints: isDesktop ? null : kMobilePageConstraints,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child:
|
||||
visibleContextMenuListener(_createSwitchBar(context))),
|
||||
buildScrollJumper(),
|
||||
const PeerSearchBar(),
|
||||
Offstage(
|
||||
offstage: !isDesktop,
|
||||
child: _createPeerViewTypeSwitch(context)
|
||||
.marginOnly(left: 13)),
|
||||
Offstage(
|
||||
offstage: _hideSort,
|
||||
child: PeerSortDropdown().marginOnly(left: 8),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
_createPeersView(),
|
||||
],
|
||||
@ -158,7 +168,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
color: model.currentTab == t
|
||||
? Theme.of(context).colorScheme.background
|
||||
: null,
|
||||
borderRadius: BorderRadius.circular(isDesktop ? 2 : 6),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Align(
|
||||
alignment: Alignment.center,
|
||||
@ -231,32 +241,32 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
|
||||
Widget _createPeerViewTypeSwitch(BuildContext context) {
|
||||
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
||||
final activeDeco =
|
||||
BoxDecoration(color: Theme.of(context).colorScheme.background);
|
||||
return Row(
|
||||
children: [PeerUiType.grid, PeerUiType.list]
|
||||
.map((type) => Obx(
|
||||
() => Container(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
decoration: peerCardUiType.value == type ? activeDeco : null,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
await bind.setLocalFlutterConfig(
|
||||
k: 'peer-card-ui-type', v: type.index.toString());
|
||||
peerCardUiType.value = type;
|
||||
},
|
||||
child: Icon(
|
||||
type == PeerUiType.grid
|
||||
? Icons.grid_view_rounded
|
||||
: Icons.list,
|
||||
size: 18,
|
||||
color:
|
||||
peerCardUiType.value == type ? textColor : textColor
|
||||
?..withOpacity(0.5),
|
||||
)),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
final deco = BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
);
|
||||
final types = [PeerUiType.grid, PeerUiType.list];
|
||||
|
||||
return Obx(
|
||||
() => Container(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
decoration: deco,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final type = types.elementAt(
|
||||
peerCardUiType.value == types.elementAt(0) ? 1 : 0);
|
||||
await bind.setLocalFlutterConfig(
|
||||
k: 'peer-card-ui-type', v: type.index.toString());
|
||||
peerCardUiType.value = type;
|
||||
},
|
||||
child: Icon(
|
||||
peerCardUiType.value == PeerUiType.grid
|
||||
? Icons.list_rounded
|
||||
: Icons.grid_view_rounded,
|
||||
size: 18,
|
||||
color: textColor,
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -417,3 +427,98 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PeerSortDropdown extends StatefulWidget {
|
||||
const PeerSortDropdown({super.key});
|
||||
|
||||
@override
|
||||
State<PeerSortDropdown> createState() => _PeerSortDropdownState();
|
||||
}
|
||||
|
||||
class _PeerSortDropdownState extends State<PeerSortDropdown> {
|
||||
@override
|
||||
void initState() {
|
||||
if (!PeerSortType.values.contains(peerSort.value)) {
|
||||
peerSort.value = PeerSortType.remoteId;
|
||||
bind.setLocalFlutterConfig(
|
||||
k: "peer-sorting",
|
||||
v: peerSort.value,
|
||||
);
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final deco = BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
);
|
||||
|
||||
final translated_text =
|
||||
PeerSortType.values.map((e) => translate(e)).toList();
|
||||
|
||||
final double max_width =
|
||||
50 + translated_text.map((e) => e.length).reduce(max) * 10;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(4.0),
|
||||
decoration: deco,
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton2<String>(
|
||||
onChanged: (v) async {
|
||||
if (v != null) {
|
||||
setState(() => peerSort.value = v);
|
||||
await bind.setLocalFlutterConfig(
|
||||
k: "peer-sorting",
|
||||
v: peerSort.value,
|
||||
);
|
||||
}
|
||||
},
|
||||
customButton: Icon(
|
||||
Icons.sort,
|
||||
size: 18,
|
||||
),
|
||||
isExpanded: true,
|
||||
dropdownStyleData: DropdownStyleData(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
width: max_width,
|
||||
),
|
||||
items: [
|
||||
DropdownMenuItem<String>(
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
translate("Sort by"),
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
enabled: false,
|
||||
),
|
||||
...translated_text
|
||||
.map<DropdownMenuItem<String>>(
|
||||
(String value) => DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
value == peerSort.value
|
||||
? Icons.radio_button_checked_rounded
|
||||
: Icons.radio_button_off_rounded,
|
||||
size: 18,
|
||||
).paddingOnly(right: 12),
|
||||
Text(
|
||||
value,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
@ -16,8 +16,36 @@ import 'peer_card.dart';
|
||||
typedef PeerFilter = bool Function(Peer peer);
|
||||
typedef PeerCardBuilder = Widget Function(Peer peer);
|
||||
|
||||
class PeerSortType {
|
||||
static const String remoteId = 'Remote ID';
|
||||
static const String remoteHost = 'Remote Host';
|
||||
static const String username = 'Username';
|
||||
// static const String status = 'Status';
|
||||
|
||||
static List<String> values = [
|
||||
PeerSortType.remoteId,
|
||||
PeerSortType.remoteHost,
|
||||
PeerSortType.username,
|
||||
// PeerSortType.status
|
||||
];
|
||||
}
|
||||
|
||||
class LoadEvent {
|
||||
static const String recent = 'load_recent_peers';
|
||||
static const String favorite = 'load_fav_peers';
|
||||
static const String lan = 'load_lan_peers';
|
||||
static const String addressBook = 'load_address_book_peers';
|
||||
}
|
||||
|
||||
/// for peer search text, global obs value
|
||||
final peerSearchText = "".obs;
|
||||
|
||||
/// for peer sort, global obs value
|
||||
final peerSort = bind.getLocalFlutterConfig(k: 'peer-sorting').obs;
|
||||
|
||||
// list for listener
|
||||
final obslist = [peerSearchText, peerSort].obs;
|
||||
|
||||
final peerSearchTextController =
|
||||
TextEditingController(text: peerSearchText.value);
|
||||
|
||||
@ -40,12 +68,19 @@ class _PeersView extends StatefulWidget {
|
||||
/// State for the peer widget.
|
||||
class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
static const int _maxQueryCount = 3;
|
||||
final HashMap<String, String> _emptyMessages = HashMap.from({
|
||||
LoadEvent.recent: 'empty_recent_tip',
|
||||
LoadEvent.favorite: 'empty_favorite_tip',
|
||||
LoadEvent.lan: 'empty_lan_tip',
|
||||
LoadEvent.addressBook: 'empty_address_book_tip',
|
||||
});
|
||||
final space = isDesktop ? 12.0 : 8.0;
|
||||
final _curPeers = <String>{};
|
||||
var _lastChangeTime = DateTime.now();
|
||||
var _lastQueryPeers = <String>{};
|
||||
var _lastQueryTime = DateTime.now().subtract(const Duration(hours: 1));
|
||||
var _queryCoun = 0;
|
||||
var _queryCount = 0;
|
||||
var _loaded = false;
|
||||
var _exit = false;
|
||||
|
||||
late final mobileWidth = () {
|
||||
@ -78,12 +113,12 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
|
||||
@override
|
||||
void onWindowFocus() {
|
||||
_queryCoun = 0;
|
||||
_queryCount = 0;
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowMinimize() {
|
||||
_queryCoun = _maxQueryCount;
|
||||
_queryCount = _maxQueryCount;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -91,17 +126,49 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
return ChangeNotifierProvider<Peers>(
|
||||
create: (context) => widget.peers,
|
||||
child: Consumer<Peers>(
|
||||
builder: (context, peers, child) => peers.peers.isEmpty
|
||||
? Container(
|
||||
margin: EdgeInsets.only(top: kEmptyMarginTop),
|
||||
alignment: Alignment.topCenter,
|
||||
child: Text(translate("Empty")))
|
||||
: _buildPeersView(peers)),
|
||||
builder: (context, peers, child) => peers.peers.isEmpty && _loaded
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.sentiment_very_dissatisfied_rounded,
|
||||
color: Theme.of(context).tabBarTheme.labelColor,
|
||||
size: 40,
|
||||
).paddingOnly(bottom: 10),
|
||||
Text(
|
||||
translate(
|
||||
_emptyMessages[widget.peers.loadEvent] ?? 'Empty',
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).tabBarTheme.labelColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: _buildPeersView(peers),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
onVisibilityChanged(VisibilityInfo info) {
|
||||
final peerId = _peerId((info.key as ValueKey).value);
|
||||
if (info.visibleFraction > 0.00001) {
|
||||
_curPeers.add(peerId);
|
||||
} else {
|
||||
_curPeers.remove(peerId);
|
||||
}
|
||||
_lastChangeTime = DateTime.now();
|
||||
}
|
||||
|
||||
String _cardId(String id) => widget.peers.name + id;
|
||||
String _peerId(String cardId) => cardId.replaceAll(widget.peers.name, '');
|
||||
|
||||
Widget _buildPeersView(Peers peers) {
|
||||
final body = ObxValue<RxString>((searchText) {
|
||||
_loaded = true;
|
||||
final body = ObxValue<RxList>((filters) {
|
||||
return FutureBuilder<List<Peer>>(
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
@ -109,16 +176,8 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
final cards = <Widget>[];
|
||||
for (final peer in peers) {
|
||||
final visibilityChild = VisibilityDetector(
|
||||
key: ValueKey(peer.id),
|
||||
onVisibilityChanged: (info) {
|
||||
final peerId = (info.key as ValueKey).value;
|
||||
if (info.visibleFraction > 0.00001) {
|
||||
_curPeers.add(peerId);
|
||||
} else {
|
||||
_curPeers.remove(peerId);
|
||||
}
|
||||
_lastChangeTime = DateTime.now();
|
||||
},
|
||||
key: ValueKey(_cardId(peer.id)),
|
||||
onVisibilityChanged: onVisibilityChanged,
|
||||
child: widget.peerCardBuilder(peer),
|
||||
);
|
||||
cards.add(isDesktop
|
||||
@ -139,9 +198,9 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
);
|
||||
}
|
||||
},
|
||||
future: matchPeers(searchText.value, peers.peers),
|
||||
future: matchPeers(filters[0].value, filters[1].value, peers.peers),
|
||||
);
|
||||
}, peerSearchText);
|
||||
}, obslist);
|
||||
|
||||
return body;
|
||||
}
|
||||
@ -149,6 +208,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
// ignore: todo
|
||||
// TODO: variables walk through async tasks?
|
||||
void _startCheckOnlines() {
|
||||
final queryInterval = const Duration(seconds: 20);
|
||||
() async {
|
||||
while (!_exit) {
|
||||
final now = DateTime.now();
|
||||
@ -158,18 +218,18 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
platformFFI.ffiBind
|
||||
.queryOnlines(ids: _curPeers.toList(growable: false));
|
||||
_lastQueryPeers = {..._curPeers};
|
||||
_lastQueryTime = DateTime.now();
|
||||
_queryCoun = 0;
|
||||
_lastQueryTime = DateTime.now().subtract(queryInterval);
|
||||
_queryCount = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_queryCoun < _maxQueryCount) {
|
||||
if (now.difference(_lastQueryTime) > const Duration(seconds: 20)) {
|
||||
if (_queryCount < _maxQueryCount) {
|
||||
if (now.difference(_lastQueryTime) >= queryInterval) {
|
||||
if (_curPeers.isNotEmpty) {
|
||||
platformFFI.ffiBind
|
||||
.queryOnlines(ids: _curPeers.toList(growable: false));
|
||||
_lastQueryTime = DateTime.now();
|
||||
_queryCoun += 1;
|
||||
_queryCount += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -179,11 +239,40 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
}();
|
||||
}
|
||||
|
||||
Future<List<Peer>>? matchPeers(String searchText, List<Peer> peers) async {
|
||||
Future<List<Peer>>? matchPeers(
|
||||
String searchText, String sortedBy, List<Peer> peers) async {
|
||||
if (widget.peerFilter != null) {
|
||||
peers = peers.where((peer) => widget.peerFilter!(peer)).toList();
|
||||
}
|
||||
|
||||
// fallback to id sorting
|
||||
if (!PeerSortType.values.contains(sortedBy)) {
|
||||
sortedBy = PeerSortType.remoteId;
|
||||
bind.setLocalFlutterConfig(
|
||||
k: "peer-sorting",
|
||||
v: sortedBy,
|
||||
);
|
||||
}
|
||||
|
||||
if (widget.peers.loadEvent != LoadEvent.recent) {
|
||||
switch (sortedBy) {
|
||||
case PeerSortType.remoteId:
|
||||
peers.sort((p1, p2) => p1.getId().compareTo(p2.getId()));
|
||||
break;
|
||||
case PeerSortType.remoteHost:
|
||||
peers.sort((p1, p2) =>
|
||||
p1.hostname.toLowerCase().compareTo(p2.hostname.toLowerCase()));
|
||||
break;
|
||||
case PeerSortType.username:
|
||||
peers.sort((p1, p2) =>
|
||||
p1.username.toLowerCase().compareTo(p2.username.toLowerCase()));
|
||||
break;
|
||||
// case PeerSortType.status:
|
||||
// peers.sort((p1, p2) => p1.online ? -1 : 1);
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
searchText = searchText.trim();
|
||||
if (searchText.isEmpty) {
|
||||
return peers;
|
||||
@ -197,6 +286,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
|
||||
filteredList.add(peers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredList;
|
||||
}
|
||||
}
|
||||
@ -232,7 +322,7 @@ class RecentPeersView extends BasePeersView {
|
||||
: super(
|
||||
key: key,
|
||||
name: 'recent peer',
|
||||
loadEvent: 'load_recent_peers',
|
||||
loadEvent: LoadEvent.recent,
|
||||
peerCardBuilder: (Peer peer) => RecentPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
@ -254,7 +344,7 @@ class FavoritePeersView extends BasePeersView {
|
||||
: super(
|
||||
key: key,
|
||||
name: 'favorite peer',
|
||||
loadEvent: 'load_fav_peers',
|
||||
loadEvent: LoadEvent.favorite,
|
||||
peerCardBuilder: (Peer peer) => FavoritePeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
@ -276,7 +366,7 @@ class DiscoveredPeersView extends BasePeersView {
|
||||
: super(
|
||||
key: key,
|
||||
name: 'discovered peer',
|
||||
loadEvent: 'load_lan_peers',
|
||||
loadEvent: LoadEvent.lan,
|
||||
peerCardBuilder: (Peer peer) => DiscoveredPeerCard(
|
||||
peer: peer,
|
||||
menuPadding: menuPadding,
|
||||
@ -301,7 +391,7 @@ class AddressBookPeersView extends BasePeersView {
|
||||
: super(
|
||||
key: key,
|
||||
name: 'address book peer',
|
||||
loadEvent: 'load_address_book_peers',
|
||||
loadEvent: LoadEvent.addressBook,
|
||||
peerFilter: (Peer peer) =>
|
||||
_hitTag(gFFI.abModel.selectedTags, peer.tags),
|
||||
peerCardBuilder: (Peer peer) => AddressBookPeerCard(
|
||||
|
||||
@ -2,6 +2,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
|
||||
const double kDesktopRemoteTabBarHeight = 28.0;
|
||||
const int kMainWindowId = 0;
|
||||
@ -13,7 +14,10 @@ const String kPeerPlatformAndroid = "Android";
|
||||
|
||||
/// [kAppTypeMain] used by 'Desktop Main Page' , 'Mobile (Client and Server)', "Install Page"
|
||||
const String kAppTypeMain = "main";
|
||||
|
||||
/// [kAppTypeConnectionManager] only for 'Desktop CM Page'
|
||||
const String kAppTypeConnectionManager = "cm";
|
||||
|
||||
const String kAppTypeDesktopRemote = "remote";
|
||||
const String kAppTypeDesktopFileTransfer = "file transfer";
|
||||
const String kAppTypeDesktopPortForward = "port forward";
|
||||
@ -58,6 +62,12 @@ const double kDesktopFileTransferMaximumWidth = 300;
|
||||
const double kDesktopFileTransferRowHeight = 30.0;
|
||||
const double kDesktopFileTransferHeaderHeight = 25.0;
|
||||
|
||||
EdgeInsets get kDragToResizeAreaPadding =>
|
||||
!kUseCompatibleUiMode && Platform.isLinux
|
||||
? stateGlobal.fullscreen || stateGlobal.maximize
|
||||
? EdgeInsets.zero
|
||||
: EdgeInsets.all(5.0)
|
||||
: EdgeInsets.zero;
|
||||
// https://en.wikipedia.org/wiki/Non-breaking_space
|
||||
const int $nbsp = 0x00A0;
|
||||
|
||||
@ -79,6 +89,7 @@ const kDefaultScrollAmountMultiplier = 5.0;
|
||||
const kDefaultScrollDuration = Duration(milliseconds: 50);
|
||||
const kDefaultMouseWheelThrottleDuration = Duration(milliseconds: 50);
|
||||
const kFullScreenEdgeSize = 0.0;
|
||||
const kMaximizeEdgeSize = 0.0;
|
||||
var kWindowEdgeSize = Platform.isWindows ? 1.0 : 5.0;
|
||||
const kWindowBorderWidth = 1.0;
|
||||
const kDesktopMenuPadding = EdgeInsets.only(left: 12.0, right: 3.0);
|
||||
@ -129,6 +140,25 @@ const kRemoteAudioDualWay = 'dual-way';
|
||||
|
||||
const kIgnoreDpi = true;
|
||||
|
||||
/// Android constants
|
||||
const kActionApplicationDetailsSettings =
|
||||
"android.settings.APPLICATION_DETAILS_SETTINGS";
|
||||
const kActionAccessibilitySettings = "android.settings.ACCESSIBILITY_SETTINGS";
|
||||
|
||||
const kRecordAudio = "android.permission.RECORD_AUDIO";
|
||||
const kManageExternalStorage = "android.permission.MANAGE_EXTERNAL_STORAGE";
|
||||
const kRequestIgnoreBatteryOptimizations =
|
||||
"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
|
||||
const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW";
|
||||
|
||||
/// Android channel invoke type key
|
||||
class AndroidChannel {
|
||||
static final kStartAction = "start_action";
|
||||
static final kGetStartOnBootOpt = "get_start_on_boot_opt";
|
||||
static final kSetStartOnBootOpt = "set_start_on_boot_opt";
|
||||
static final kSyncAppDirConfigPath = "sync_app_dir";
|
||||
}
|
||||
|
||||
/// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _keyLabels
|
||||
/// see [LogicalKeyboardKey.keyLabel]
|
||||
const Map<int, String> logicalKeyMap = <int, String>{
|
||||
|
||||
@ -151,10 +151,7 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
/// Connects to the selected peer.
|
||||
void onConnect({bool isFileTransfer = false}) {
|
||||
var id = _idController.id;
|
||||
var forceRelay = id.endsWith(r'/r');
|
||||
if (forceRelay) id = id.substring(0, id.length - 2);
|
||||
connect(context, id,
|
||||
isFileTransfer: isFileTransfer, forceRelay: forceRelay);
|
||||
connect(context, id, isFileTransfer: isFileTransfer);
|
||||
}
|
||||
|
||||
/// UI for the remote ID TextField.
|
||||
@ -164,9 +161,8 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
width: 320 + 20 * 2,
|
||||
padding: const EdgeInsets.fromLTRB(20, 24, 20, 22),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(13)),
|
||||
),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(13)),
|
||||
border: Border.all(color: Theme.of(context).colorScheme.background)),
|
||||
child: Ink(
|
||||
child: Column(
|
||||
children: [
|
||||
@ -197,32 +193,19 @@ class _ConnectionPageState extends State<ConnectionPage>
|
||||
style: const TextStyle(
|
||||
fontFamily: 'WorkSans',
|
||||
fontSize: 22,
|
||||
height: 1.25,
|
||||
height: 1.4,
|
||||
),
|
||||
maxLines: 1,
|
||||
cursorColor:
|
||||
Theme.of(context).textTheme.titleLarge?.color,
|
||||
decoration: InputDecoration(
|
||||
filled: false,
|
||||
counterText: '',
|
||||
hintText: _idInputFocused.value
|
||||
? null
|
||||
: translate('Enter Remote ID'),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.zero,
|
||||
borderSide: BorderSide(
|
||||
color: MyTheme.color(context).border!)),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.zero,
|
||||
borderSide: BorderSide(
|
||||
color: MyTheme.color(context).border!)),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.zero,
|
||||
borderSide:
|
||||
BorderSide(color: MyTheme.button, width: 3),
|
||||
),
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 10, vertical: 12)),
|
||||
horizontal: 15, vertical: 13)),
|
||||
controller: _idController,
|
||||
inputFormatters: [IDTextInputFormatter()],
|
||||
onSubmitted: (s) {
|
||||
|
||||
@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart' hide MenuItem;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/common/widgets/custom_password.dart';
|
||||
@ -14,7 +14,6 @@ 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/utils/multi_window_manager.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -55,10 +54,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildLeftPane(context),
|
||||
const VerticalDivider(
|
||||
width: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
const VerticalDivider(width: 1),
|
||||
Expanded(
|
||||
child: buildRightPane(context),
|
||||
),
|
||||
@ -158,7 +154,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
readOnly: true,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.only(bottom: 20),
|
||||
contentPadding: EdgeInsets.only(top: 10, bottom: 10),
|
||||
),
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
@ -242,7 +238,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
readOnly: true,
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.only(bottom: 2),
|
||||
contentPadding:
|
||||
EdgeInsets.only(top: 14, bottom: 10),
|
||||
),
|
||||
style: TextStyle(fontSize: 15),
|
||||
),
|
||||
@ -254,9 +251,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
Icons.refresh,
|
||||
color: refreshHover.value
|
||||
? textColor
|
||||
: Color(0xFFDDDDDD), // TODO
|
||||
: Color(0xFFDDDDDD),
|
||||
size: 22,
|
||||
).marginOnly(right: 8, bottom: 2),
|
||||
).marginOnly(right: 8, top: 4),
|
||||
),
|
||||
onTap: () => bind.mainUpdateTemporaryPassword(),
|
||||
onHover: (value) => refreshHover.value = value,
|
||||
@ -265,11 +262,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
child: Obx(
|
||||
() => Icon(
|
||||
Icons.edit,
|
||||
color: editHover.value
|
||||
? textColor
|
||||
: Color(0xFFDDDDDD), // TODO
|
||||
color:
|
||||
editHover.value ? textColor : Color(0xFFDDDDDD),
|
||||
size: 22,
|
||||
).marginOnly(right: 8, bottom: 2),
|
||||
).marginOnly(right: 8, top: 4),
|
||||
),
|
||||
onTap: () => DesktopSettingPage.switch2page(1),
|
||||
onHover: (value) => editHover.value = value,
|
||||
@ -638,7 +634,6 @@ void setPasswordDialog() async {
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: translate('Password'),
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: errMsg0.isNotEmpty ? errMsg0 : null),
|
||||
controller: p0,
|
||||
autofocus: true,
|
||||
@ -666,7 +661,6 @@ void setPasswordDialog() async {
|
||||
child: TextField(
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
labelText: translate('Confirmation'),
|
||||
errorText: errMsg1.isNotEmpty ? errMsg1 : null),
|
||||
controller: p1,
|
||||
|
||||
@ -19,7 +19,7 @@ import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
|
||||
import '../../common/widgets/dialog.dart';
|
||||
import '../../common/widgets/login.dart';
|
||||
|
||||
const double _kTabWidth = 235;
|
||||
const double _kTabWidth = 200;
|
||||
const double _kTabHeight = 42;
|
||||
const double _kCardFixedWidth = 540;
|
||||
const double _kCardLeftMargin = 15;
|
||||
@ -120,7 +120,7 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
||||
],
|
||||
),
|
||||
),
|
||||
const VerticalDivider(thickness: 1, width: 1),
|
||||
const VerticalDivider(width: 1),
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
@ -381,8 +381,13 @@ class _GeneralState extends State<_General> {
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
String? initialDirectory;
|
||||
if (await Directory.fromUri(Uri.directory(dir))
|
||||
.exists()) {
|
||||
initialDirectory = dir;
|
||||
}
|
||||
String? selectedDirectory = await FilePicker.platform
|
||||
.getDirectoryPath(initialDirectory: dir);
|
||||
.getDirectoryPath(initialDirectory: initialDirectory);
|
||||
if (selectedDirectory != null) {
|
||||
await bind.mainSetOption(
|
||||
key: 'video-save-directory',
|
||||
@ -538,6 +543,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
translate('Screen Share'),
|
||||
translate('Deny remote access'),
|
||||
],
|
||||
enabled: enabled,
|
||||
initialKey: initialKey,
|
||||
onChanged: (mode) async {
|
||||
String modeValue;
|
||||
@ -667,6 +673,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
|
||||
return _Card(title: 'Password', children: [
|
||||
_ComboBox(
|
||||
enabled: !locked,
|
||||
keys: modeKeys,
|
||||
values: modeValues,
|
||||
initialKey: modeInitialKey,
|
||||
@ -762,7 +769,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
'Port',
|
||||
Row(children: [
|
||||
SizedBox(
|
||||
width: 80,
|
||||
width: 95,
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
enabled: enabled && !locked,
|
||||
@ -771,13 +778,10 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
||||
FilteringTextInputFormatter.allow(RegExp(
|
||||
r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')),
|
||||
],
|
||||
textAlign: TextAlign.end,
|
||||
decoration: const InputDecoration(
|
||||
hintText: '21118',
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding:
|
||||
EdgeInsets.only(bottom: 10, top: 10, right: 10),
|
||||
isCollapsed: true,
|
||||
EdgeInsets.symmetric(vertical: 12, horizontal: 12),
|
||||
),
|
||||
).marginOnly(right: 15),
|
||||
),
|
||||
@ -1311,6 +1315,8 @@ class _DisplayState extends State<_Display> {
|
||||
|
||||
Widget other(BuildContext context) {
|
||||
return _Card(title: 'Other Default Options', children: [
|
||||
otherRow('View Mode', 'view_only'),
|
||||
otherRow('show_monitors_tip', 'show_monitors_toolbar'),
|
||||
otherRow('Show remote cursor', 'show_remote_cursor'),
|
||||
otherRow('Zoom cursor', 'zoom-cursor'),
|
||||
otherRow('Show quality monitor', 'show_quality_monitor'),
|
||||
@ -1695,9 +1701,6 @@ _LabeledTextField(
|
||||
enabled: enabled,
|
||||
obscureText: secure,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
border: OutlineInputBorder(),
|
||||
contentPadding: EdgeInsets.fromLTRB(14, 15, 14, 15),
|
||||
errorText: errorText.isNotEmpty ? errorText : null),
|
||||
style: TextStyle(
|
||||
color: _disabledTextColor(context, enabled),
|
||||
@ -1722,7 +1725,6 @@ class _ComboBox extends StatelessWidget {
|
||||
required this.values,
|
||||
required this.initialKey,
|
||||
required this.onChanged,
|
||||
// ignore: unused_element
|
||||
this.enabled = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -1735,19 +1737,29 @@ class _ComboBox extends StatelessWidget {
|
||||
var ref = values[index].obs;
|
||||
current = keys[index];
|
||||
return Container(
|
||||
decoration: BoxDecoration(border: Border.all(color: MyTheme.border)),
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: enabled
|
||||
? MyTheme.color(context).border2 ?? MyTheme.border
|
||||
: MyTheme.border,
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(8), //border raiuds of dropdown button
|
||||
),
|
||||
height: 42, // should be the height of a TextField
|
||||
child: Obx(() => DropdownButton<String>(
|
||||
isExpanded: true,
|
||||
value: ref.value,
|
||||
elevation: 16,
|
||||
underline: Container(
|
||||
height: 25,
|
||||
),
|
||||
underline: Container(),
|
||||
style: TextStyle(
|
||||
color: enabled
|
||||
? Theme.of(context).textTheme.titleMedium?.color
|
||||
: _disabledTextColor(context, enabled)),
|
||||
icon: const Icon(
|
||||
Icons.expand_more_sharp,
|
||||
size: 20,
|
||||
),
|
||||
).marginOnly(right: 15),
|
||||
onChanged: enabled
|
||||
? (String? newValue) {
|
||||
if (newValue != null && newValue != ref.value) {
|
||||
@ -1764,11 +1776,11 @@ class _ComboBox extends StatelessWidget {
|
||||
value,
|
||||
style: const TextStyle(fontSize: _kContentFontSize),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
).marginOnly(left: 5),
|
||||
).marginOnly(left: 15),
|
||||
);
|
||||
}).toList(),
|
||||
)),
|
||||
);
|
||||
).marginOnly(bottom: 5);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1845,7 +1857,6 @@ void changeSocks5Proxy() async {
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
errorText: proxyMsg.isNotEmpty ? proxyMsg : null),
|
||||
controller: proxyController,
|
||||
autofocus: true,
|
||||
@ -1863,9 +1874,6 @@ void changeSocks5Proxy() async {
|
||||
).marginOnly(right: 10)),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
controller: userController,
|
||||
),
|
||||
),
|
||||
@ -1883,7 +1891,6 @@ void changeSocks5Proxy() async {
|
||||
child: Obx(() => TextField(
|
||||
obscureText: obscure.value,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () => obscure.value = !obscure.value,
|
||||
icon: Icon(obscure.value
|
||||
|
||||
@ -75,7 +75,7 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
|
||||
isClose: false,
|
||||
),
|
||||
)));
|
||||
return Platform.isMacOS
|
||||
return Platform.isMacOS || kUseCompatibleUiMode
|
||||
? tabWidget
|
||||
: Obx(
|
||||
() => DragToResizeArea(
|
||||
|
||||
@ -98,7 +98,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
||||
labelGetter: DesktopTab.labelGetterAlias,
|
||||
)),
|
||||
);
|
||||
return Platform.isMacOS
|
||||
return Platform.isMacOS || kUseCompatibleUiMode
|
||||
? tabWidget
|
||||
: SubWindowDragToResizeArea(
|
||||
child: tabWidget,
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
@ -13,10 +17,55 @@ class InstallPage extends StatefulWidget {
|
||||
State<InstallPage> createState() => _InstallPageState();
|
||||
}
|
||||
|
||||
class _InstallPageState extends State<InstallPage> with WindowListener {
|
||||
class _InstallPageState extends State<InstallPage> {
|
||||
final tabController = DesktopTabController(tabType: DesktopTabType.main);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Get.put<DesktopTabController>(tabController);
|
||||
const lable = "install";
|
||||
tabController.add(TabInfo(
|
||||
key: lable,
|
||||
label: lable,
|
||||
closable: false,
|
||||
page: _InstallPageBody(
|
||||
key: const ValueKey(lable),
|
||||
)));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
Get.delete<DesktopTabController>();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DragToResizeArea(
|
||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||
child: Container(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
body: DesktopTab(controller: tabController)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InstallPageBody extends StatefulWidget {
|
||||
const _InstallPageBody({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<_InstallPageBody> createState() => _InstallPageBodyState();
|
||||
}
|
||||
|
||||
class _InstallPageBodyState extends State<_InstallPageBody>
|
||||
with WindowListener {
|
||||
late final TextEditingController controller;
|
||||
final RxBool startmenu = true.obs;
|
||||
final RxBool desktopicon = true.obs;
|
||||
final RxBool driverCert = true.obs;
|
||||
final RxBool showProgress = false.obs;
|
||||
final RxBool btnEnabled = true.obs;
|
||||
|
||||
@ -46,15 +95,19 @@ class _InstallPageState extends State<InstallPage> with WindowListener {
|
||||
final double em = 13;
|
||||
final btnFontSize = 0.9 * em;
|
||||
final double button_radius = 6;
|
||||
final isDarkTheme = MyTheme.currentThemeMode() == ThemeMode.dark;
|
||||
final buttonStyle = OutlinedButton.styleFrom(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(button_radius)),
|
||||
));
|
||||
final inputBorder = OutlineInputBorder(
|
||||
borderRadius: BorderRadius.zero,
|
||||
borderSide: BorderSide(color: Colors.black12));
|
||||
borderSide:
|
||||
BorderSide(color: isDarkTheme ? Colors.white70 : Colors.black12));
|
||||
final textColor = isDarkTheme ? null : Colors.black87;
|
||||
final dividerColor = isDarkTheme ? Colors.white70 : Colors.black87;
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: null,
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
@ -91,30 +144,66 @@ class _InstallPageState extends State<InstallPage> with WindowListener {
|
||||
style: buttonStyle,
|
||||
child: Text(translate('Change Path'),
|
||||
style: TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: btnFontSize)))
|
||||
color: textColor, fontSize: btnFontSize)))
|
||||
.marginOnly(left: em))
|
||||
],
|
||||
).marginSymmetric(vertical: 2 * em),
|
||||
Row(
|
||||
children: [
|
||||
Obx(() => Checkbox(
|
||||
value: startmenu.value,
|
||||
onChanged: (b) {
|
||||
if (b != null) startmenu.value = b;
|
||||
})),
|
||||
Text(translate('Create start menu shortcuts'))
|
||||
],
|
||||
TextButton(
|
||||
onPressed: () => startmenu.value = !startmenu.value,
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(() => Checkbox(
|
||||
value: startmenu.value,
|
||||
onChanged: (b) {
|
||||
if (b != null) startmenu.value = b;
|
||||
})),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: translate('Create start menu shortcuts'),
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Obx(() => Checkbox(
|
||||
value: desktopicon.value,
|
||||
onChanged: (b) {
|
||||
if (b != null) desktopicon.value = b;
|
||||
})),
|
||||
Text(translate('Create desktop icon'))
|
||||
],
|
||||
TextButton(
|
||||
onPressed: () => desktopicon.value = !desktopicon.value,
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(() => Checkbox(
|
||||
value: desktopicon.value,
|
||||
onChanged: (b) {
|
||||
if (b != null) desktopicon.value = b;
|
||||
})),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: translate('Create desktop icon'),
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Offstage(
|
||||
offstage: !Platform.isWindows,
|
||||
child: TextButton(
|
||||
onPressed: () => driverCert.value = !driverCert.value,
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(() => Checkbox(
|
||||
value: driverCert.value,
|
||||
onChanged: (b) {
|
||||
if (b != null) driverCert.value = b;
|
||||
})),
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: translate('idd_driver_tip'),
|
||||
style: DefaultTextStyle.of(context).style,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => launchUrlString('http://rustdesk.com/privacy'),
|
||||
@ -127,8 +216,7 @@ class _InstallPageState extends State<InstallPage> with WindowListener {
|
||||
)).marginOnly(top: 2 * em),
|
||||
Row(children: [Text(translate('agreement_tip'))])
|
||||
.marginOnly(top: em),
|
||||
Divider(color: Colors.black87)
|
||||
.marginSymmetric(vertical: 0.5 * em),
|
||||
Divider(color: dividerColor).marginSymmetric(vertical: 0.5 * em),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -143,8 +231,7 @@ class _InstallPageState extends State<InstallPage> with WindowListener {
|
||||
style: buttonStyle,
|
||||
child: Text(translate('Cancel'),
|
||||
style: TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: btnFontSize)))
|
||||
color: textColor, fontSize: btnFontSize)))
|
||||
.marginOnly(right: 2 * em)),
|
||||
Obx(() => ElevatedButton(
|
||||
onPressed: btnEnabled.value ? install : null,
|
||||
@ -167,8 +254,7 @@ class _InstallPageState extends State<InstallPage> with WindowListener {
|
||||
style: buttonStyle,
|
||||
child: Text(translate('Run without install'),
|
||||
style: TextStyle(
|
||||
color: Colors.black87,
|
||||
fontSize: btnFontSize)))
|
||||
color: textColor, fontSize: btnFontSize)))
|
||||
.marginOnly(left: 2 * em)),
|
||||
),
|
||||
],
|
||||
@ -179,12 +265,47 @@ class _InstallPageState extends State<InstallPage> with WindowListener {
|
||||
}
|
||||
|
||||
void install() {
|
||||
btnEnabled.value = false;
|
||||
showProgress.value = true;
|
||||
String args = '';
|
||||
if (startmenu.value) args += ' startmenu';
|
||||
if (desktopicon.value) args += ' desktopicon';
|
||||
bind.installInstallMe(options: args, path: controller.text);
|
||||
do_install() {
|
||||
btnEnabled.value = false;
|
||||
showProgress.value = true;
|
||||
String args = '';
|
||||
if (startmenu.value) args += ' startmenu';
|
||||
if (desktopicon.value) args += ' desktopicon';
|
||||
if (driverCert.value) args += ' driverCert';
|
||||
bind.installInstallMe(options: args, path: controller.text);
|
||||
}
|
||||
|
||||
if (driverCert.isTrue) {
|
||||
final tag = 'install-info-install-cert-confirm';
|
||||
final btns = [
|
||||
dialogButton(
|
||||
'Cancel',
|
||||
onPressed: () => gFFI.dialogManager.dismissByTag(tag),
|
||||
isOutline: true,
|
||||
),
|
||||
dialogButton(
|
||||
'OK',
|
||||
onPressed: () {
|
||||
gFFI.dialogManager.dismissByTag(tag);
|
||||
do_install();
|
||||
},
|
||||
isOutline: false,
|
||||
),
|
||||
];
|
||||
gFFI.dialogManager.show(
|
||||
(setState, close) => CustomAlertDialog(
|
||||
title: null,
|
||||
content: SelectionArea(
|
||||
child:
|
||||
msgboxContent('info', 'Warning', 'confirm_idd_driver_tip')),
|
||||
actions: btns,
|
||||
onCancel: close,
|
||||
),
|
||||
tag: tag,
|
||||
);
|
||||
} else {
|
||||
do_install();
|
||||
}
|
||||
}
|
||||
|
||||
void selectInstallPath() async {
|
||||
|
||||
@ -11,7 +11,7 @@ import 'package:wakelock/wakelock.dart';
|
||||
|
||||
const double _kColumn1Width = 30;
|
||||
const double _kColumn4Width = 100;
|
||||
const double _kRowHeight = 50;
|
||||
const double _kRowHeight = 60;
|
||||
const double _kTextLeftMargin = 20;
|
||||
|
||||
class _PortForward {
|
||||
@ -183,8 +183,6 @@ class _PortForwardPageState extends State<PortForwardPage>
|
||||
controller: remotePortController,
|
||||
inputFormatters: portInputFormatter),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0, side: const BorderSide(color: MyTheme.border)),
|
||||
onPressed: () async {
|
||||
int? localPort = int.tryParse(localPortController.text);
|
||||
int? remotePort = int.tryParse(remotePortController.text);
|
||||
@ -208,7 +206,7 @@ class _PortForwardPageState extends State<PortForwardPage>
|
||||
child: Text(
|
||||
translate('Add'),
|
||||
),
|
||||
).marginAll(10),
|
||||
).marginSymmetric(horizontal: 10),
|
||||
]),
|
||||
);
|
||||
}
|
||||
@ -217,26 +215,15 @@ class _PortForwardPageState extends State<PortForwardPage>
|
||||
{required TextEditingController controller,
|
||||
List<TextInputFormatter>? inputFormatters,
|
||||
String? hint}) {
|
||||
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
||||
return Expanded(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
inputFormatters: inputFormatters,
|
||||
cursorColor: textColor,
|
||||
cursorHeight: 20,
|
||||
cursorWidth: 1,
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: MyTheme.color(context).border!)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: MyTheme.color(context).border!)),
|
||||
fillColor: Theme.of(context).colorScheme.background,
|
||||
contentPadding: const EdgeInsets.all(10),
|
||||
hintText: hint,
|
||||
hintStyle:
|
||||
TextStyle(color: Theme.of(context).hintColor, fontSize: 16)),
|
||||
style: TextStyle(color: textColor, fontSize: 16),
|
||||
).marginAll(10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
inputFormatters: inputFormatters,
|
||||
decoration: InputDecoration(
|
||||
hintText: hint,
|
||||
))),
|
||||
);
|
||||
}
|
||||
|
||||
@ -322,14 +309,9 @@ class _PortForwardPageState extends State<PortForwardPage>
|
||||
child: SizedBox(
|
||||
width: 120,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 0,
|
||||
side: const BorderSide(color: MyTheme.border)),
|
||||
onPressed: () => bind.sessionNewRdp(id: widget.id),
|
||||
child: Text(
|
||||
translate('New RDP'),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w300, fontSize: 14),
|
||||
),
|
||||
).marginSymmetric(vertical: 10),
|
||||
).marginOnly(left: 20),
|
||||
|
||||
@ -107,13 +107,15 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
||||
labelGetter: DesktopTab.labelGetterAlias,
|
||||
)),
|
||||
);
|
||||
return Platform.isMacOS
|
||||
return Platform.isMacOS || kUseCompatibleUiMode
|
||||
? tabWidget
|
||||
: SubWindowDragToResizeArea(
|
||||
child: tabWidget,
|
||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||
windowId: stateGlobal.windowId,
|
||||
);
|
||||
: Obx(
|
||||
() => SubWindowDragToResizeArea(
|
||||
child: tabWidget,
|
||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||
windowId: stateGlobal.windowId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onRemoveId(String id) {
|
||||
|
||||
@ -22,22 +22,23 @@ import '../../models/model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../../common/shared_state.dart';
|
||||
import '../../utils/image.dart';
|
||||
import '../widgets/remote_menubar.dart';
|
||||
import '../widgets/remote_toolbar.dart';
|
||||
import '../widgets/kb_layout_type_chooser.dart';
|
||||
|
||||
bool _isCustomCursorInited = false;
|
||||
final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false);
|
||||
|
||||
class RemotePage extends StatefulWidget {
|
||||
RemotePage({
|
||||
Key? key,
|
||||
required this.id,
|
||||
required this.password,
|
||||
required this.menubarState,
|
||||
this.switchUuid,
|
||||
this.forceRelay,
|
||||
}) : super(key: key);
|
||||
|
||||
final String id;
|
||||
final String? password;
|
||||
final MenubarState menubarState;
|
||||
final String? switchUuid;
|
||||
final bool? forceRelay;
|
||||
@ -113,6 +114,7 @@ class _RemotePageState extends State<RemotePage>
|
||||
});
|
||||
_ffi.start(
|
||||
widget.id,
|
||||
password: widget.password,
|
||||
switchUuid: widget.switchUuid,
|
||||
forceRelay: widget.forceRelay,
|
||||
);
|
||||
@ -308,6 +310,10 @@ class _RemotePageState extends State<RemotePage>
|
||||
}
|
||||
|
||||
void leaveView(PointerExitEvent evt) {
|
||||
if (_ffi.ffiModel.keyboard()) {
|
||||
_ffi.inputModel.tryMoveEdgeOnExit(evt.position);
|
||||
}
|
||||
|
||||
_cursorOverImage.value = false;
|
||||
_firstEnterImage.value = false;
|
||||
if (_onEnterOrLeaveImage4Menubar != null) {
|
||||
@ -329,8 +335,8 @@ class _RemotePageState extends State<RemotePage>
|
||||
PointerExitEventListener? onExit,
|
||||
) {
|
||||
return RawPointerMouseRegion(
|
||||
onEnter: enterView,
|
||||
onExit: leaveView,
|
||||
onEnter: onEnter,
|
||||
onExit: onExit,
|
||||
onPointerDown: (event) {
|
||||
// A double check for blur status.
|
||||
// Note: If there's an `onPointerDown` event is triggered, `_isWindowBlur` is expected being false.
|
||||
|
||||
@ -9,7 +9,7 @@ import 'package:flutter_hbb/common/shared_state.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/remote_menubar.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart'
|
||||
as mod_menu;
|
||||
@ -23,9 +23,6 @@ import '../../models/platform_model.dart';
|
||||
|
||||
class _MenuTheme {
|
||||
static const Color blueColor = MyTheme.button;
|
||||
static const Color hoverBlueColor = MyTheme.accent;
|
||||
static const Color redColor = Colors.redAccent;
|
||||
static const Color hoverRedColor = Colors.red;
|
||||
// kMinInteractiveDimension
|
||||
static const double height = 20.0;
|
||||
static const double dividerHeight = 12.0;
|
||||
@ -71,6 +68,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
page: RemotePage(
|
||||
key: ValueKey(peerId),
|
||||
id: peerId,
|
||||
password: params['password'],
|
||||
menubarState: _menubarState,
|
||||
switchUuid: params['switch_uuid'],
|
||||
forceRelay: params['forceRelay'],
|
||||
@ -106,6 +104,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
page: RemotePage(
|
||||
key: ValueKey(id),
|
||||
id: id,
|
||||
password: args['password'],
|
||||
menubarState: _menubarState,
|
||||
switchUuid: switchUuid,
|
||||
forceRelay: args['forceRelay'],
|
||||
@ -205,11 +204,13 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
),
|
||||
),
|
||||
);
|
||||
return Platform.isMacOS
|
||||
return Platform.isMacOS || kUseCompatibleUiMode
|
||||
? tabWidget
|
||||
: Obx(() => SubWindowDragToResizeArea(
|
||||
key: contentKey,
|
||||
child: tabWidget,
|
||||
// Specially configured for a better resize area and remote control.
|
||||
childPadding: kDragToResizeAreaPadding,
|
||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||
windowId: stateGlobal.windowId,
|
||||
));
|
||||
@ -258,7 +259,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
),
|
||||
]);
|
||||
|
||||
if (!ffi.canvasModel.cursorEmbedded) {
|
||||
if (!ffi.canvasModel.cursorEmbedded && !ffi.ffiModel.viewOnly) {
|
||||
menu.add(MenuEntryDivider<String>());
|
||||
menu.add(RemoteMenuEntry.showRemoteCursor(
|
||||
key,
|
||||
@ -267,7 +268,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
));
|
||||
}
|
||||
|
||||
if (perms['keyboard'] != false) {
|
||||
if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) {
|
||||
if (perms['clipboard'] != false) {
|
||||
menu.add(RemoteMenuEntry.disableClipboard(key, padding,
|
||||
dismissFunc: cancelFunc));
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/remote_tab_page.dart';
|
||||
@ -26,6 +28,9 @@ class DesktopRemoteScreen extends StatelessWidget {
|
||||
ChangeNotifierProvider.value(value: gFFI.canvasModel),
|
||||
],
|
||||
child: Scaffold(
|
||||
// Set transparent background for padding the resize area out of the flutter view.
|
||||
// This allows the wallpaper goes through our resize area. (Linux only now).
|
||||
backgroundColor: Platform.isLinux ? Colors.transparent : null,
|
||||
body: ConnectionTabPage(
|
||||
params: params,
|
||||
),
|
||||
|
||||
@ -37,7 +37,7 @@ class _MenuButtonState extends State<MenuButton> {
|
||||
message: widget.tooltip,
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Ink(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(_borderRadius),
|
||||
color: _isHover ? widget.hoverColor : widget.color,
|
||||
|
||||
@ -23,6 +23,10 @@ import '../../common/shared_state.dart';
|
||||
import './popup_menu.dart';
|
||||
import './kb_layout_type_chooser.dart';
|
||||
|
||||
const _kKeyLegacyMode = 'legacy';
|
||||
const _kKeyMapMode = 'map';
|
||||
const _kKeyTranslateMode = 'translate';
|
||||
|
||||
class MenubarState {
|
||||
final kStoreKey = 'remoteMenubarState';
|
||||
late RxBool show;
|
||||
@ -104,6 +108,7 @@ class _MenubarTheme {
|
||||
static const double buttonHMargin = 3;
|
||||
static const double buttonVMargin = 6;
|
||||
static const double iconRadius = 8;
|
||||
static const double elevation = 3;
|
||||
}
|
||||
|
||||
typedef DismissFunc = void Function();
|
||||
@ -351,7 +356,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
return Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: Obx(() => show.value
|
||||
? _buildMenubar(context)
|
||||
? _buildToolbar(context)
|
||||
: _buildDraggableShowHide(context)),
|
||||
);
|
||||
}
|
||||
@ -365,59 +370,78 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
alignment: FractionalOffset(_fractionX.value, 0),
|
||||
child: Offstage(
|
||||
offstage: _dragging.isTrue,
|
||||
child: _DraggableShowHide(
|
||||
dragging: _dragging,
|
||||
fractionX: _fractionX,
|
||||
show: show,
|
||||
child: Material(
|
||||
elevation: _MenubarTheme.elevation,
|
||||
shadowColor: MyTheme.color(context).shadow,
|
||||
child: _DraggableShowHide(
|
||||
dragging: _dragging,
|
||||
fractionX: _fractionX,
|
||||
show: show,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildMenubar(BuildContext context) {
|
||||
final List<Widget> menubarItems = [];
|
||||
Widget _buildToolbar(BuildContext context) {
|
||||
final List<Widget> toolbarItems = [];
|
||||
if (!isWebDesktop) {
|
||||
menubarItems.add(_PinMenu(state: widget.state));
|
||||
menubarItems.add(
|
||||
toolbarItems.add(_PinMenu(state: widget.state));
|
||||
toolbarItems.add(
|
||||
_FullscreenMenu(state: widget.state, setFullscreen: _setFullscreen));
|
||||
menubarItems.add(_MobileActionMenu(ffi: widget.ffi));
|
||||
toolbarItems.add(_MobileActionMenu(ffi: widget.ffi));
|
||||
}
|
||||
menubarItems.add(_MonitorMenu(id: widget.id, ffi: widget.ffi));
|
||||
menubarItems
|
||||
|
||||
if (PrivacyModeState.find(widget.id).isFalse &&
|
||||
stateGlobal.displaysCount.value > 1) {
|
||||
toolbarItems.add(
|
||||
bind.mainGetUserDefaultOption(key: 'show_monitors_toolbar') == 'Y'
|
||||
? _MultiMonitorMenu(id: widget.id, ffi: widget.ffi)
|
||||
: _MonitorMenu(id: widget.id, ffi: widget.ffi),
|
||||
);
|
||||
}
|
||||
|
||||
toolbarItems
|
||||
.add(_ControlMenu(id: widget.id, ffi: widget.ffi, state: widget.state));
|
||||
menubarItems.add(_DisplayMenu(
|
||||
toolbarItems.add(_DisplayMenu(
|
||||
id: widget.id,
|
||||
ffi: widget.ffi,
|
||||
state: widget.state,
|
||||
setFullscreen: _setFullscreen,
|
||||
));
|
||||
menubarItems.add(_KeyboardMenu(id: widget.id, ffi: widget.ffi));
|
||||
toolbarItems.add(_KeyboardMenu(id: widget.id, ffi: widget.ffi));
|
||||
if (!isWeb) {
|
||||
menubarItems.add(_ChatMenu(id: widget.id, ffi: widget.ffi));
|
||||
menubarItems.add(_VoiceCallMenu(id: widget.id, ffi: widget.ffi));
|
||||
toolbarItems.add(_ChatMenu(id: widget.id, ffi: widget.ffi));
|
||||
toolbarItems.add(_VoiceCallMenu(id: widget.id, ffi: widget.ffi));
|
||||
}
|
||||
menubarItems.add(_RecordMenu());
|
||||
menubarItems.add(_CloseMenu(id: widget.id, ffi: widget.ffi));
|
||||
toolbarItems.add(_RecordMenu());
|
||||
toolbarItems.add(_CloseMenu(id: widget.id, ffi: widget.ffi));
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
),
|
||||
Material(
|
||||
elevation: _MenubarTheme.elevation,
|
||||
shadowColor: MyTheme.color(context).shadow,
|
||||
borderRadius: BorderRadius.all(Radius.circular(4.0)),
|
||||
color: Theme.of(context)
|
||||
.menuBarTheme
|
||||
.style
|
||||
?.backgroundColor
|
||||
?.resolve(MaterialState.values.toSet()),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Theme(
|
||||
data: themeData(),
|
||||
child: MenuBar(
|
||||
children: [
|
||||
SizedBox(width: _MenubarTheme.buttonHMargin),
|
||||
...menubarItems,
|
||||
SizedBox(width: _MenubarTheme.buttonHMargin)
|
||||
],
|
||||
),
|
||||
)),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Theme(
|
||||
data: themeData(),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: _MenubarTheme.buttonHMargin * 2),
|
||||
...toolbarItems,
|
||||
SizedBox(width: _MenubarTheme.buttonHMargin * 2)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildDraggableShowHide(context),
|
||||
],
|
||||
@ -427,11 +451,22 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
ThemeData themeData() {
|
||||
return Theme.of(context).copyWith(
|
||||
menuButtonTheme: MenuButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
minimumSize: MaterialStatePropertyAll(Size(64, 36)),
|
||||
textStyle: MaterialStatePropertyAll(
|
||||
TextStyle(fontWeight: FontWeight.normal)))),
|
||||
style: ButtonStyle(
|
||||
minimumSize: MaterialStatePropertyAll(Size(64, 36)),
|
||||
textStyle: MaterialStatePropertyAll(
|
||||
TextStyle(fontWeight: FontWeight.normal),
|
||||
),
|
||||
),
|
||||
),
|
||||
dividerTheme: DividerThemeData(space: 4),
|
||||
menuBarTheme: MenuBarThemeData(
|
||||
style: MenuStyle(
|
||||
padding: MaterialStatePropertyAll(EdgeInsets.zero),
|
||||
elevation: MaterialStatePropertyAll(0),
|
||||
shape: MaterialStatePropertyAll(BeveledRectangleBorder()),
|
||||
).copyWith(
|
||||
backgroundColor:
|
||||
Theme.of(context).menuBarTheme.style?.backgroundColor)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -501,8 +536,8 @@ class _MonitorMenu extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (stateGlobal.displaysCount.value < 2) return Offstage();
|
||||
return _IconSubmenuButton(
|
||||
tooltip: 'Select Monitor',
|
||||
icon: icon(),
|
||||
ffi: ffi,
|
||||
color: _MenubarTheme.blueColor,
|
||||
@ -519,19 +554,20 @@ class _MonitorMenu extends StatelessWidget {
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
"assets/display.svg",
|
||||
"assets/screen.svg",
|
||||
color: Colors.white,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 3.9),
|
||||
child: Obx(() {
|
||||
RxInt display = CurrentDisplayState.find(id);
|
||||
return Text(
|
||||
'${display.value + 1}/${pi.displays.length}',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 8),
|
||||
);
|
||||
}),
|
||||
)
|
||||
Obx(() {
|
||||
RxInt display = CurrentDisplayState.find(id);
|
||||
return Text(
|
||||
'${display.value + 1}/${pi.displays.length}',
|
||||
style: const TextStyle(
|
||||
color: _MenubarTheme.blueColor,
|
||||
fontSize: 8,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -541,6 +577,7 @@ class _MonitorMenu extends StatelessWidget {
|
||||
final pi = ffi.ffiModel.pi;
|
||||
for (int i = 0; i < pi.displays.length; i++) {
|
||||
rowChildren.add(_IconMenuButton(
|
||||
topLevel: false,
|
||||
color: _MenubarTheme.blueColor,
|
||||
hoverColor: _MenubarTheme.hoverBlueColor,
|
||||
tooltip: "",
|
||||
@ -553,19 +590,17 @@ class _MonitorMenu extends StatelessWidget {
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
"assets/display.svg",
|
||||
"assets/screen.svg",
|
||||
color: Colors.white,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 3.5 /*2.5*/),
|
||||
child: Text(
|
||||
(i + 1).toString(),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
),
|
||||
Text(
|
||||
(i + 1).toString(),
|
||||
style: TextStyle(
|
||||
color: _MenubarTheme.blueColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -593,6 +628,7 @@ class _ControlMenu extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _IconSubmenuButton(
|
||||
tooltip: 'Control Actions',
|
||||
svg: "assets/actions.svg",
|
||||
color: _MenubarTheme.blueColor,
|
||||
hoverColor: _MenubarTheme.hoverBlueColor,
|
||||
@ -651,26 +687,44 @@ class _ControlMenu extends StatelessWidget {
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: Text(translate('OS Password')),
|
||||
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
PasswordWidget(controller: controller),
|
||||
CheckboxListTile(
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
dense: true,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
title: Text(
|
||||
translate('Auto Login'),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.password_rounded, color: MyTheme.accent),
|
||||
Text(translate('OS Password')).paddingOnly(left: 10),
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
PasswordWidget(controller: controller),
|
||||
CheckboxListTile(
|
||||
contentPadding: const EdgeInsets.all(0),
|
||||
dense: true,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
title: Text(
|
||||
translate('Auto Login'),
|
||||
),
|
||||
value: autoLogin,
|
||||
onChanged: (v) {
|
||||
if (v == null) return;
|
||||
setState(() => autoLogin = v);
|
||||
},
|
||||
),
|
||||
value: autoLogin,
|
||||
onChanged: (v) {
|
||||
if (v == null) return;
|
||||
setState(() => autoLogin = v);
|
||||
},
|
||||
),
|
||||
]),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
dialogButton('Cancel', onPressed: close, isOutline: true),
|
||||
dialogButton('OK', onPressed: submit),
|
||||
dialogButton(
|
||||
"Cancel",
|
||||
icon: Icon(Icons.close_rounded),
|
||||
onPressed: close,
|
||||
isOutline: true,
|
||||
),
|
||||
dialogButton(
|
||||
"OK",
|
||||
icon: Icon(Icons.done_rounded),
|
||||
onPressed: submit,
|
||||
),
|
||||
],
|
||||
onSubmit: submit,
|
||||
onCancel: close,
|
||||
@ -766,8 +820,10 @@ class _ControlMenu extends StatelessWidget {
|
||||
|
||||
ctrlAltDel() {
|
||||
final perms = ffi.ffiModel.permissions;
|
||||
final viewOnly = ffi.ffiModel.viewOnly;
|
||||
final pi = ffi.ffiModel.pi;
|
||||
final visible = perms['keyboard'] != false &&
|
||||
final visible = !viewOnly &&
|
||||
perms['keyboard'] != false &&
|
||||
(pi.platform == kPeerPlatformLinux || pi.sasEnabled);
|
||||
if (!visible) return Offstage();
|
||||
return _MenuItemButton(
|
||||
@ -792,7 +848,8 @@ class _ControlMenu extends StatelessWidget {
|
||||
|
||||
insertLock() {
|
||||
final perms = ffi.ffiModel.permissions;
|
||||
final visible = perms['keyboard'] != false;
|
||||
final viewOnly = ffi.ffiModel.viewOnly;
|
||||
final visible = !viewOnly && perms['keyboard'] != false;
|
||||
if (!visible) return Offstage();
|
||||
return _MenuItemButton(
|
||||
child: Text(translate('Insert Lock')),
|
||||
@ -896,6 +953,7 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
||||
Widget build(BuildContext context) {
|
||||
_updateScreen();
|
||||
return _IconSubmenuButton(
|
||||
tooltip: 'Display Settings',
|
||||
svg: "assets/display.svg",
|
||||
ffi: widget.ffi,
|
||||
color: _MenubarTheme.blueColor,
|
||||
@ -916,6 +974,7 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
||||
disableClipboard(),
|
||||
lockAfterSessionEnd(),
|
||||
privacyMode(),
|
||||
swapKey(),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -949,12 +1008,13 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
||||
|
||||
final canvasModel = widget.ffi.canvasModel;
|
||||
final width = (canvasModel.getDisplayWidth() * canvasModel.scale +
|
||||
canvasModel.windowBorderWidth * 2) *
|
||||
CanvasModel.leftToEdge +
|
||||
CanvasModel.rightToEdge) *
|
||||
scale +
|
||||
magicWidth;
|
||||
final height = (canvasModel.getDisplayHeight() * canvasModel.scale +
|
||||
canvasModel.tabBarHeight +
|
||||
canvasModel.windowBorderWidth * 2) *
|
||||
CanvasModel.topToEdge +
|
||||
CanvasModel.bottomToEdge) *
|
||||
scale +
|
||||
magicHeight;
|
||||
double left = wndRect.left + (wndRect.width - width) / 2;
|
||||
@ -1023,10 +1083,10 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
||||
final canvasModel = widget.ffi.canvasModel;
|
||||
final displayWidth = canvasModel.getDisplayWidth();
|
||||
final displayHeight = canvasModel.getDisplayHeight();
|
||||
final requiredWidth = displayWidth +
|
||||
(canvasModel.tabBarHeight + canvasModel.windowBorderWidth * 2);
|
||||
final requiredHeight = displayHeight +
|
||||
(canvasModel.tabBarHeight + canvasModel.windowBorderWidth * 2);
|
||||
final requiredWidth =
|
||||
CanvasModel.leftToEdge + displayWidth + CanvasModel.rightToEdge;
|
||||
final requiredHeight =
|
||||
CanvasModel.topToEdge + displayHeight + CanvasModel.bottomToEdge;
|
||||
return selfWidth > (requiredWidth * scale) &&
|
||||
selfHeight > (requiredHeight * scale);
|
||||
}
|
||||
@ -1344,14 +1404,14 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
||||
child: Text(translate('H264')),
|
||||
value: 'h264',
|
||||
groupValue: groupValue,
|
||||
onChanged: onChanged,
|
||||
onChanged: codecs[0] ? onChanged : null,
|
||||
ffi: widget.ffi,
|
||||
),
|
||||
_RadioMenuButton<String>(
|
||||
child: Text(translate('H265')),
|
||||
value: 'h265',
|
||||
groupValue: groupValue,
|
||||
onChanged: onChanged,
|
||||
onChanged: codecs[1] ? onChanged : null,
|
||||
ffi: widget.ffi,
|
||||
),
|
||||
]);
|
||||
@ -1400,23 +1460,33 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
||||
}
|
||||
|
||||
showRemoteCursor() {
|
||||
if (widget.ffi.ffiModel.pi.platform == kPeerPlatformAndroid) {
|
||||
return Offstage();
|
||||
}
|
||||
final ffiModel = widget.ffi.ffiModel;
|
||||
final visible = !widget.ffi.canvasModel.cursorEmbedded;
|
||||
if (!visible) return Offstage();
|
||||
final enabled = !ffiModel.viewOnly;
|
||||
final state = ShowRemoteCursorState.find(widget.id);
|
||||
final option = 'show-remote-cursor';
|
||||
return _CheckboxMenuButton(
|
||||
value: state.value,
|
||||
onChanged: (value) async {
|
||||
if (value == null) return;
|
||||
await bind.sessionToggleOption(id: widget.id, value: option);
|
||||
state.value =
|
||||
bind.sessionGetToggleOptionSync(id: widget.id, arg: option);
|
||||
},
|
||||
onChanged: enabled
|
||||
? (value) async {
|
||||
if (value == null) return;
|
||||
await bind.sessionToggleOption(id: widget.id, value: option);
|
||||
state.value =
|
||||
bind.sessionGetToggleOptionSync(id: widget.id, arg: option);
|
||||
}
|
||||
: null,
|
||||
ffi: widget.ffi,
|
||||
child: Text(translate('Show remote cursor')));
|
||||
}
|
||||
|
||||
zoomCursor() {
|
||||
if (widget.ffi.ffiModel.pi.platform == kPeerPlatformAndroid) {
|
||||
return Offstage();
|
||||
}
|
||||
final visible = widget.state.viewStyle.value != kRemoteViewStyleOriginal;
|
||||
if (!visible) return Offstage();
|
||||
final option = 'zoom-cursor';
|
||||
@ -1480,16 +1550,21 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
||||
}
|
||||
|
||||
disableClipboard() {
|
||||
final ffiModel = widget.ffi.ffiModel;
|
||||
final visible = perms['keyboard'] != false && perms['clipboard'] != false;
|
||||
if (!visible) return Offstage();
|
||||
final enabled = !ffiModel.viewOnly;
|
||||
final option = 'disable-clipboard';
|
||||
final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option);
|
||||
var value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option);
|
||||
if (ffiModel.viewOnly) value = true;
|
||||
return _CheckboxMenuButton(
|
||||
value: value,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
bind.sessionToggleOption(id: widget.id, value: option);
|
||||
},
|
||||
onChanged: enabled
|
||||
? (value) {
|
||||
if (value == null) return;
|
||||
bind.sessionToggleOption(id: widget.id, value: option);
|
||||
}
|
||||
: null,
|
||||
ffi: widget.ffi,
|
||||
child: Text(translate('Disable clipboard')));
|
||||
}
|
||||
@ -1518,11 +1593,38 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
||||
value: rxValue.value,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
if (widget.ffi.ffiModel.pi.currentDisplay != 0) {
|
||||
msgBox(
|
||||
widget.id,
|
||||
'custom-nook-nocancel-hasclose',
|
||||
'info',
|
||||
'Please switch to Display 1 first',
|
||||
'',
|
||||
widget.ffi.dialogManager);
|
||||
return;
|
||||
}
|
||||
bind.sessionToggleOption(id: widget.id, value: option);
|
||||
},
|
||||
ffi: widget.ffi,
|
||||
child: Text(translate('Privacy mode')));
|
||||
}
|
||||
|
||||
swapKey() {
|
||||
final visible = perms['keyboard'] != false &&
|
||||
((Platform.isMacOS && pi.platform != kPeerPlatformMacOS) ||
|
||||
(!Platform.isMacOS && pi.platform == kPeerPlatformMacOS));
|
||||
if (!visible) return Offstage();
|
||||
final option = 'allow_swap_key';
|
||||
final value = bind.sessionGetToggleOptionSync(id: widget.id, arg: option);
|
||||
return _CheckboxMenuButton(
|
||||
value: value,
|
||||
onChanged: (value) {
|
||||
if (value == null) return;
|
||||
bind.sessionToggleOption(id: widget.id, value: option);
|
||||
},
|
||||
ffi: widget.ffi,
|
||||
child: Text(translate('Swap control-command key')));
|
||||
}
|
||||
}
|
||||
|
||||
class _KeyboardMenu extends StatelessWidget {
|
||||
@ -1540,30 +1642,41 @@ class _KeyboardMenu extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
var ffiModel = Provider.of<FfiModel>(context);
|
||||
if (ffiModel.permissions['keyboard'] == false) return Offstage();
|
||||
// Do not support peer 1.1.9.
|
||||
if (stateGlobal.grabKeyboard) {
|
||||
bind.sessionSetKeyboardMode(id: id, value: 'map');
|
||||
if (bind.sessionIsKeyboardModeSupported(id: id, mode: _kKeyMapMode)) {
|
||||
bind.sessionSetKeyboardMode(id: id, value: _kKeyMapMode);
|
||||
} else if (bind.sessionIsKeyboardModeSupported(
|
||||
id: id, mode: _kKeyLegacyMode)) {
|
||||
bind.sessionSetKeyboardMode(id: id, value: _kKeyLegacyMode);
|
||||
}
|
||||
return Offstage();
|
||||
}
|
||||
return _IconSubmenuButton(
|
||||
tooltip: 'Keyboard Settings',
|
||||
svg: "assets/keyboard.svg",
|
||||
ffi: ffi,
|
||||
color: _MenubarTheme.blueColor,
|
||||
hoverColor: _MenubarTheme.hoverBlueColor,
|
||||
menuChildren: [mode(), localKeyboardType()]);
|
||||
menuChildren: [
|
||||
mode(),
|
||||
localKeyboardType(),
|
||||
Divider(),
|
||||
view_mode(),
|
||||
]);
|
||||
}
|
||||
|
||||
mode() {
|
||||
return futureBuilder(future: () async {
|
||||
return await bind.sessionGetKeyboardMode(id: id) ?? 'legacy';
|
||||
return await bind.sessionGetKeyboardMode(id: id) ?? _kKeyLegacyMode;
|
||||
}(), hasData: (data) {
|
||||
final groupValue = data as String;
|
||||
List<KeyboardModeMenu> modes = [
|
||||
KeyboardModeMenu(key: 'legacy', menu: 'Legacy mode'),
|
||||
KeyboardModeMenu(key: 'map', menu: 'Map mode'),
|
||||
KeyboardModeMenu(key: 'translate', menu: 'Translate mode'),
|
||||
KeyboardModeMenu(key: _kKeyLegacyMode, menu: 'Legacy mode'),
|
||||
KeyboardModeMenu(key: _kKeyMapMode, menu: 'Map mode'),
|
||||
KeyboardModeMenu(key: _kKeyTranslateMode, menu: 'Translate mode'),
|
||||
];
|
||||
List<_RadioMenuButton> list = [];
|
||||
final enabled = !ffi.ffiModel.viewOnly;
|
||||
onChanged(String? value) async {
|
||||
if (value == null) return;
|
||||
await bind.sessionSetKeyboardMode(id: id, value: value);
|
||||
@ -1571,20 +1684,20 @@ class _KeyboardMenu extends StatelessWidget {
|
||||
|
||||
for (KeyboardModeMenu mode in modes) {
|
||||
if (bind.sessionIsKeyboardModeSupported(id: id, mode: mode.key)) {
|
||||
if (mode.key == 'translate') {
|
||||
if (Platform.isLinux || pi.platform == kPeerPlatformLinux) {
|
||||
if (mode.key == _kKeyTranslateMode) {
|
||||
if (Platform.isLinux) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
var text = translate(mode.menu);
|
||||
if (mode.key == 'translate') {
|
||||
if (mode.key == _kKeyTranslateMode) {
|
||||
text = '$text beta';
|
||||
}
|
||||
list.add(_RadioMenuButton<String>(
|
||||
child: Text(text),
|
||||
value: mode.key,
|
||||
groupValue: groupValue,
|
||||
onChanged: onChanged,
|
||||
onChanged: enabled ? onChanged : null,
|
||||
ffi: ffi,
|
||||
));
|
||||
}
|
||||
@ -1597,6 +1710,7 @@ class _KeyboardMenu extends StatelessWidget {
|
||||
final localPlatform = getLocalPlatformForKBLayoutType(pi.platform);
|
||||
final visible = localPlatform != '';
|
||||
if (!visible) return Offstage();
|
||||
final enabled = !ffi.ffiModel.viewOnly;
|
||||
return Column(
|
||||
children: [
|
||||
Divider(),
|
||||
@ -1605,12 +1719,30 @@ class _KeyboardMenu extends StatelessWidget {
|
||||
'${translate('Local keyboard type')}: ${KBLayoutType.value}'),
|
||||
trailingIcon: const Icon(Icons.settings),
|
||||
ffi: ffi,
|
||||
onPressed: () =>
|
||||
showKBLayoutTypeChooser(localPlatform, ffi.dialogManager),
|
||||
onPressed: enabled
|
||||
? () => showKBLayoutTypeChooser(localPlatform, ffi.dialogManager)
|
||||
: null,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
view_mode() {
|
||||
final ffiModel = ffi.ffiModel;
|
||||
final enabled = version_cmp(pi.version, '1.2.0') >= 0 &&
|
||||
ffiModel.permissions["keyboard"] != false;
|
||||
return _CheckboxMenuButton(
|
||||
value: ffiModel.viewOnly,
|
||||
onChanged: enabled
|
||||
? (value) async {
|
||||
if (value == null) return;
|
||||
await bind.sessionToggleOption(id: id, value: 'view-only');
|
||||
ffiModel.setViewOnly(id, value);
|
||||
}
|
||||
: null,
|
||||
ffi: ffi,
|
||||
child: Text(translate('View Mode')));
|
||||
}
|
||||
}
|
||||
|
||||
class _ChatMenu extends StatefulWidget {
|
||||
@ -1633,6 +1765,7 @@ class _ChatMenuState extends State<_ChatMenu> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _IconSubmenuButton(
|
||||
tooltip: 'Chat',
|
||||
key: chatButtonKey,
|
||||
svg: 'assets/chat.svg',
|
||||
ffi: widget.ffi,
|
||||
@ -1751,22 +1884,24 @@ class _CloseMenu extends StatelessWidget {
|
||||
class _IconMenuButton extends StatefulWidget {
|
||||
final String? assetName;
|
||||
final Widget? icon;
|
||||
final String tooltip;
|
||||
final String? tooltip;
|
||||
final Color color;
|
||||
final Color hoverColor;
|
||||
final VoidCallback? onPressed;
|
||||
final double? hMargin;
|
||||
final double? vMargin;
|
||||
final bool topLevel;
|
||||
const _IconMenuButton({
|
||||
Key? key,
|
||||
this.assetName,
|
||||
this.icon,
|
||||
required this.tooltip,
|
||||
this.tooltip,
|
||||
required this.color,
|
||||
required this.hoverColor,
|
||||
required this.onPressed,
|
||||
this.hMargin,
|
||||
this.vMargin,
|
||||
this.topLevel = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -1786,36 +1921,40 @@ class _IconMenuButtonState extends State<_IconMenuButton> {
|
||||
width: _MenubarTheme.buttonSize,
|
||||
height: _MenubarTheme.buttonSize,
|
||||
);
|
||||
return SizedBox(
|
||||
final button = SizedBox(
|
||||
width: _MenubarTheme.buttonSize,
|
||||
height: _MenubarTheme.buttonSize,
|
||||
child: MenuItemButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(Colors.transparent),
|
||||
padding: MaterialStatePropertyAll(EdgeInsets.zero),
|
||||
overlayColor: MaterialStatePropertyAll(Colors.transparent)),
|
||||
onHover: (value) => setState(() {
|
||||
hover = value;
|
||||
}),
|
||||
onPressed: widget.onPressed,
|
||||
child: Tooltip(
|
||||
message: translate(widget.tooltip),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(_MenubarTheme.iconRadius),
|
||||
color: hover ? widget.hoverColor : widget.color,
|
||||
),
|
||||
child: icon))),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(_MenubarTheme.iconRadius),
|
||||
color: hover ? widget.hoverColor : widget.color,
|
||||
),
|
||||
child: icon)),
|
||||
),
|
||||
).marginSymmetric(
|
||||
horizontal: widget.hMargin ?? _MenubarTheme.buttonHMargin,
|
||||
vertical: widget.vMargin ?? _MenubarTheme.buttonVMargin);
|
||||
if (widget.topLevel) {
|
||||
return MenuBar(children: [button]);
|
||||
} else {
|
||||
return button;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _IconSubmenuButton extends StatefulWidget {
|
||||
final String tooltip;
|
||||
final String? svg;
|
||||
final Widget? icon;
|
||||
final Color color;
|
||||
@ -1828,6 +1967,7 @@ class _IconSubmenuButton extends StatefulWidget {
|
||||
{Key? key,
|
||||
this.svg,
|
||||
this.icon,
|
||||
required this.tooltip,
|
||||
required this.color,
|
||||
required this.hoverColor,
|
||||
required this.menuChildren,
|
||||
@ -1852,32 +1992,35 @@ class _IconSubmenuButtonState extends State<_IconSubmenuButton> {
|
||||
width: _MenubarTheme.buttonSize,
|
||||
height: _MenubarTheme.buttonSize,
|
||||
);
|
||||
return SizedBox(
|
||||
width: _MenubarTheme.buttonSize,
|
||||
height: _MenubarTheme.buttonSize,
|
||||
child: SubmenuButton(
|
||||
menuStyle: widget.menuStyle,
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStatePropertyAll(EdgeInsets.zero),
|
||||
overlayColor: MaterialStatePropertyAll(Colors.transparent)),
|
||||
onHover: (value) => setState(() {
|
||||
hover = value;
|
||||
}),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(_MenubarTheme.iconRadius),
|
||||
color: hover ? widget.hoverColor : widget.color,
|
||||
),
|
||||
child: icon)),
|
||||
menuChildren: widget.menuChildren
|
||||
.map((e) => _buildPointerTrackWidget(e, widget.ffi))
|
||||
.toList()))
|
||||
.marginSymmetric(
|
||||
horizontal: _MenubarTheme.buttonHMargin,
|
||||
vertical: _MenubarTheme.buttonVMargin);
|
||||
final button = SizedBox(
|
||||
width: _MenubarTheme.buttonSize,
|
||||
height: _MenubarTheme.buttonSize,
|
||||
child: SubmenuButton(
|
||||
menuStyle: widget.menuStyle,
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(Colors.transparent),
|
||||
padding: MaterialStatePropertyAll(EdgeInsets.zero),
|
||||
overlayColor: MaterialStatePropertyAll(Colors.transparent)),
|
||||
onHover: (value) => setState(() {
|
||||
hover = value;
|
||||
}),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(_MenubarTheme.iconRadius),
|
||||
color: hover ? widget.hoverColor : widget.color,
|
||||
),
|
||||
child: icon)),
|
||||
menuChildren: widget.menuChildren
|
||||
.map((e) => _buildPointerTrackWidget(e, widget.ffi))
|
||||
.toList()));
|
||||
return MenuBar(children: [
|
||||
button.marginSymmetric(
|
||||
horizontal: _MenubarTheme.buttonHMargin,
|
||||
vertical: _MenubarTheme.buttonVMargin)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2016,7 +2159,7 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
|
||||
child: Icon(
|
||||
Icons.drag_indicator,
|
||||
size: 20,
|
||||
color: Colors.grey[800],
|
||||
color: MyTheme.color(context).drag_indicator,
|
||||
),
|
||||
feedback: widget,
|
||||
onDragStarted: (() {
|
||||
@ -2068,7 +2211,11 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
|
||||
data: TextButtonThemeData(style: buttonStyle),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: Theme.of(context)
|
||||
.menuBarTheme
|
||||
.style
|
||||
?.backgroundColor
|
||||
?.resolve(MaterialState.values.toSet()),
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(5),
|
||||
),
|
||||
@ -2100,3 +2247,69 @@ Widget _buildPointerTrackWidget(Widget child, FFI ffi) {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _MultiMonitorMenu extends StatelessWidget {
|
||||
final String id;
|
||||
final FFI ffi;
|
||||
|
||||
const _MultiMonitorMenu({
|
||||
super.key,
|
||||
required this.id,
|
||||
required this.ffi,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<Widget> rowChildren = [];
|
||||
final pi = ffi.ffiModel.pi;
|
||||
|
||||
for (int i = 0; i < pi.displays.length; i++) {
|
||||
rowChildren.add(
|
||||
Obx(() {
|
||||
RxInt display = CurrentDisplayState.find(id);
|
||||
return _IconMenuButton(
|
||||
topLevel: false,
|
||||
color: i == display.value
|
||||
? _MenubarTheme.blueColor
|
||||
: Colors.grey[800]!,
|
||||
hoverColor: i == display.value
|
||||
? _MenubarTheme.hoverBlueColor
|
||||
: Colors.grey[850]!,
|
||||
icon: Container(
|
||||
alignment: AlignmentDirectional.center,
|
||||
constraints:
|
||||
const BoxConstraints(minHeight: _MenubarTheme.height),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
"assets/screen.svg",
|
||||
color: Colors.white,
|
||||
),
|
||||
Obx(
|
||||
() => Text(
|
||||
(i + 1).toString(),
|
||||
style: TextStyle(
|
||||
color: i == display.value
|
||||
? _MenubarTheme.blueColor
|
||||
: Colors.grey[800]!,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
if (display.value != i) {
|
||||
bind.sessionSwitchDisplay(id: id, value: i);
|
||||
}
|
||||
},
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
return Row(children: rowChildren);
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@ class DesktopScrollWrapper extends StatelessWidget {
|
||||
return ImprovedScrolling(
|
||||
scrollController: scrollController,
|
||||
enableCustomMouseWheelScrolling: true,
|
||||
// enableKeyboardScrolling: true, // strange behavior on mac
|
||||
customMouseWheelScrollConfig: CustomMouseWheelScrollConfig(
|
||||
scrollDuration: kDefaultScrollDuration,
|
||||
scrollCurve: Curves.linearToEaseOut,
|
||||
|
||||