diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index fea1a3672..d8781bb0c 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -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
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml
index 29b0d0e0f..ae2d0aa4c 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yaml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yaml
@@ -1,5 +1,6 @@
name: 🛠️ Feature request
description: Suggest an idea for RustDesk
+labels: ["enhancement"]
body:
- type: textarea
id: desc
diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml
index 74e4efa99..74f233276 100644
--- a/.github/workflows/flutter-ci.yml
+++ b/.github/workflows/flutter-ci.yml
@@ -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:
@@ -330,13 +331,13 @@ jobs:
- {
arch: x86_64,
target: aarch64-linux-android,
- os: ubuntu-18.04,
+ os: ubuntu-20.04,
extra-build-features: "",
}
# - {
# arch: x86_64,
# target: armv7-linux-androideabi,
- # os: ubuntu-18.04,
+ # os: ubuntu-20.04,
# extra-build-features: "",
# }
steps:
@@ -354,7 +355,7 @@ 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
@@ -907,19 +908,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 }
diff --git a/.github/workflows/flutter-nightly.yml b/.github/workflows/flutter-nightly.yml
index b08193971..6ec398c73 100644
--- a/.github/workflows/flutter-nightly.yml
+++ b/.github/workflows/flutter-nightly.yml
@@ -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
@@ -430,7 +431,7 @@ jobs:
- {
arch: x86_64,
target: aarch64-linux-android,
- os: ubuntu-18.04,
+ os: ubuntu-20.04,
extra-build-features: "",
}
# - {
@@ -454,7 +455,7 @@ 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
@@ -732,7 +733,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 +901,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 +911,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 +1512,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
diff --git a/Cargo.lock b/Cargo.lock
index a2cdf91a4..7652f4430 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "addr2line"
-version = "0.17.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
+checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
dependencies = [
"gimli",
]
@@ -17,12 +17,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
-[[package]]
-name = "adler32"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
-
[[package]]
name = "ahash"
version = "0.7.6"
@@ -111,12 +105,12 @@ dependencies = [
[[package]]
name = "android_logger"
-version = "0.11.1"
+version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e9dd62f37dea550caf48c77591dc50bd1a378ce08855be1a0c42a97b7550fb"
+checksum = "8619b80c242aa7bd638b5c7ddd952addeecb71f69c75e33f1d47b2804f8f883a"
dependencies = [
"android_log-sys",
- "env_logger 0.9.3",
+ "env_logger 0.10.0",
"log",
"once_cell",
]
@@ -141,23 +135,24 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.66"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "arboard"
-version = "2.1.1"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc120354d1b5ec6d7aaf4876b602def75595937b5e15d356eb554ab5177e08bb"
+checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854"
dependencies = [
"clipboard-win",
"core-graphics 0.22.3",
- "image 0.23.14",
+ "image",
"log",
"objc",
"objc-foundation",
"objc_id",
+ "once_cell",
"parking_lot 0.12.1",
"thiserror",
"winapi 0.3.9",
@@ -166,13 +161,12 @@ dependencies = [
[[package]]
name = "async-broadcast"
-version = "0.4.1"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d26004fe83b2d1cd3a97609b21e39f9a31535822210fe83205d2ce48866ea61"
+checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b"
dependencies = [
"event-listener",
"futures-core",
- "parking_lot 0.12.1",
]
[[package]]
@@ -200,6 +194,18 @@ dependencies = [
"slab",
]
+[[package]]
+name = "async-fs"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
+dependencies = [
+ "async-lock",
+ "autocfg 1.1.0",
+ "blocking",
+ "futures-lite",
+]
+
[[package]]
name = "async-io"
version = "1.12.0"
@@ -215,19 +221,18 @@ dependencies = [
"parking",
"polling",
"slab",
- "socket2 0.4.7",
+ "socket2 0.4.9",
"waker-fn",
"windows-sys 0.42.0",
]
[[package]]
name = "async-lock"
-version = "2.6.0"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685"
+checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
dependencies = [
"event-listener",
- "futures-lite",
]
[[package]]
@@ -250,13 +255,13 @@ dependencies = [
[[package]]
name = "async-recursion"
-version = "1.0.0"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
+checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -267,13 +272,13 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
[[package]]
name = "async-trait"
-version = "0.1.59"
+version = "0.1.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
+checksum = "b84f9ebcc6c1f5b8cb160f6990096a5c127f423fcb6e1ccc46c370cbdfb75dfc"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -284,7 +289,7 @@ checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf"
dependencies = [
"atk-sys",
"bitflags",
- "glib 0.16.5",
+ "glib 0.16.7",
"libc",
]
@@ -311,9 +316,9 @@ dependencies = [
[[package]]
name = "atomic-waker"
-version = "1.0.0"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
+checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599"
[[package]]
name = "atty"
@@ -321,7 +326,7 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.1.19",
"libc",
"winapi 0.3.9",
]
@@ -343,24 +348,30 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
-version = "0.3.66"
+version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
+checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
dependencies = [
"addr2line",
"cc",
"cfg-if 1.0.0",
"libc",
- "miniz_oxide 0.5.4",
+ "miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
-name = "base64"
-version = "0.13.1"
+name = "base-x"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
+checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270"
+
+[[package]]
+name = "base64"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]]
name = "bindgen"
@@ -377,12 +388,12 @@ dependencies = [
"lazycell",
"log",
"peeking_take_while",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"regex",
"rustc-hash",
"shlex",
- "which 4.3.0",
+ "which",
]
[[package]]
@@ -397,19 +408,41 @@ dependencies = [
"lazy_static",
"lazycell",
"peeking_take_while",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"regex",
"rustc-hash",
"shlex",
- "syn 1.0.105",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "bindgen"
+version = "0.64.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "log",
+ "peeking_take_while",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn 1.0.109",
+ "which",
]
[[package]]
name = "bit_field"
-version = "0.10.1"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
+checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
@@ -437,9 +470,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
-version = "0.10.3"
+version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
@@ -471,9 +504,9 @@ dependencies = [
[[package]]
name = "brotli-decompressor"
-version = "2.3.2"
+version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80"
+checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
@@ -487,15 +520,15 @@ checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b"
[[package]]
name = "bumpalo"
-version = "3.11.1"
+version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "bytemuck"
-version = "1.12.3"
+version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
+checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
[[package]]
name = "byteorder"
@@ -505,11 +538,11 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
dependencies = [
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
@@ -520,7 +553,7 @@ checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d"
dependencies = [
"bitflags",
"cairo-sys-rs",
- "glib 0.16.5",
+ "glib 0.16.7",
"libc",
"once_cell",
"thiserror",
@@ -549,11 +582,11 @@ dependencies = [
[[package]]
name = "camino"
-version = "1.1.1"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e"
+checksum = "6031a462f977dd38968b6f23378356512feeace69cef817e1a4475108093cec3"
dependencies = [
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
@@ -562,7 +595,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
dependencies = [
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
@@ -573,9 +606,9 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
dependencies = [
"camino",
"cargo-platform",
- "semver 1.0.14",
- "serde 1.0.149",
- "serde_json 1.0.89",
+ "semver 1.0.16",
+ "serde 1.0.154",
+ "serde_json 1.0.94",
]
[[package]]
@@ -585,23 +618,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb"
dependencies = [
"clap 3.2.23",
- "heck 0.4.0",
+ "heck 0.4.1",
"indexmap",
"log",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "serde 1.0.149",
- "serde_json 1.0.89",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "serde 1.0.154",
+ "serde_json 1.0.94",
+ "syn 1.0.109",
"tempfile",
- "toml",
+ "toml 0.5.11",
]
[[package]]
name = "cc"
-version = "1.0.77"
+version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
dependencies = [
"jobserver",
]
@@ -659,9 +692,9 @@ dependencies = [
[[package]]
name = "cidr-utils"
-version = "0.5.9"
+version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "355d5b5df67e58b523953d0c1a8d3d2c05f5af51f1332b0199b9c92263614ed0"
+checksum = "fdfa36f04861d39453affe1cf084ce2d6554021a84eb6f31ebdeafb6fb92a01c"
dependencies = [
"debug-helper",
"num-bigint",
@@ -672,9 +705,9 @@ dependencies = [
[[package]]
name = "clang-sys"
-version = "1.4.0"
+version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3"
+checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a"
dependencies = [
"glob",
"libc",
@@ -705,7 +738,7 @@ dependencies = [
"atty",
"bitflags",
"clap_derive",
- "clap_lex",
+ "clap_lex 0.2.4",
"indexmap",
"once_cell",
"strsim 0.10.0",
@@ -713,17 +746,30 @@ dependencies = [
"textwrap 0.16.0",
]
+[[package]]
+name = "clap"
+version = "4.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
+dependencies = [
+ "bitflags",
+ "clap_lex 0.3.2",
+ "is-terminal",
+ "strsim 0.10.0",
+ "termcolor",
+]
+
[[package]]
name = "clap_derive"
version = "3.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
dependencies = [
- "heck 0.4.0",
+ "heck 0.4.1",
"proc-macro-error",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -735,6 +781,15 @@ dependencies = [
"os_str_bytes",
]
+[[package]]
+name = "clap_lex"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
+dependencies = [
+ "os_str_bytes",
+]
+
[[package]]
name = "clipboard"
version = "0.1.0"
@@ -742,16 +797,16 @@ dependencies = [
"cc",
"hbb_common",
"lazy_static",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_derive",
"thiserror",
]
[[package]]
name = "clipboard-win"
-version = "4.4.2"
+version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219"
+checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362"
dependencies = [
"error-code",
"str-buf",
@@ -823,6 +878,17 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+[[package]]
+name = "colored"
+version = "1.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59"
+dependencies = [
+ "atty",
+ "lazy_static",
+ "winapi 0.3.9",
+]
+
[[package]]
name = "combine"
version = "4.6.6"
@@ -835,9 +901,9 @@ dependencies = [
[[package]]
name = "concurrent-queue"
-version = "2.0.0"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b"
+checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e"
dependencies = [
"crossbeam-utils",
]
@@ -848,9 +914,9 @@ version = "0.4.0"
source = "git+https://github.com/open-trade/confy#630cc28a396cb7d01eefdd9f3824486fe4d8554b"
dependencies = [
"directories-next",
- "serde 1.0.149",
+ "serde 1.0.154",
"thiserror",
- "toml",
+ "toml 0.5.11",
]
[[package]]
@@ -972,27 +1038,26 @@ dependencies = [
[[package]]
name = "cpal"
-version = "0.13.5"
+version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74117836a5124f3629e4b474eed03e479abaf98988b4bb317e29f08cfe0e4116"
+checksum = "f342c1b63e185e9953584ff2199726bf53850d96610a310e3aca09e9405a2d0b"
dependencies = [
"alsa",
"core-foundation-sys 0.8.3",
"coreaudio-rs",
"jni 0.19.0",
"js-sys",
- "lazy_static",
"libc",
"mach",
- "ndk 0.6.0",
- "ndk-glue 0.6.2",
- "nix 0.23.2",
+ "ndk 0.7.0",
+ "ndk-context",
"oboe",
- "parking_lot 0.11.2",
+ "once_cell",
+ "parking_lot 0.12.1",
"stdweb",
"thiserror",
"web-sys",
- "winapi 0.3.9",
+ "windows 0.37.0",
]
[[package]]
@@ -1015,9 +1080,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
-version = "0.5.6"
+version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
+checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@@ -1025,9 +1090,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
-version = "0.8.2"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
@@ -1036,14 +1101,14 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
-version = "0.9.13"
+version = "0.9.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
+checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
dependencies = [
"autocfg 1.1.0",
"cfg-if 1.0.0",
"crossbeam-utils",
- "memoffset 0.7.1",
+ "memoffset 0.8.0",
"scopeguard",
]
@@ -1059,9 +1124,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
-version = "0.8.14"
+version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
+checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [
"cfg-if 1.0.0",
]
@@ -1084,12 +1149,12 @@ dependencies = [
[[package]]
name = "ctrlc"
-version = "3.2.4"
+version = "3.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1631ca6e3c59112501a9d87fd86f21591ff77acd31331e8a73f8d80a65bbdd71"
+checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639"
dependencies = [
- "nix 0.26.1",
- "windows-sys 0.42.0",
+ "nix 0.26.2",
+ "windows-sys 0.45.0",
]
[[package]]
@@ -1100,9 +1165,9 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "cxx"
-version = "1.0.83"
+version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdf07d07d6531bfcdbe9b8b739b104610c6508dcc4d63b410585faf338241daf"
+checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72"
dependencies = [
"cc",
"cxxbridge-flags",
@@ -1112,34 +1177,34 @@ dependencies = [
[[package]]
name = "cxx-build"
-version = "1.0.83"
+version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2eb5b96ecdc99f72657332953d4d9c50135af1bac34277801cc3937906ebd39"
+checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"scratch",
- "syn 1.0.105",
+ "syn 1.0.109",
]
[[package]]
name = "cxxbridge-flags"
-version = "1.0.83"
+version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac040a39517fd1674e0f32177648334b0f4074625b5588a64519804ba0553b12"
+checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97"
[[package]]
name = "cxxbridge-macro"
-version = "1.0.83"
+version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1362b0ddcfc4eb0a1f57b68bd77dd99f0e826958a96abd0ae9bd092e114ffed6"
+checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -1187,10 +1252,10 @@ checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
dependencies = [
"fnv",
"ident_case",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"strsim 0.9.3",
- "syn 1.0.105",
+ "syn 1.0.109",
]
[[package]]
@@ -1201,10 +1266,10 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
"fnv",
"ident_case",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"strsim 0.10.0",
- "syn 1.0.105",
+ "syn 1.0.109",
]
[[package]]
@@ -1214,8 +1279,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
dependencies = [
"darling_core 0.10.2",
- "quote 1.0.21",
- "syn 1.0.105",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -1225,8 +1290,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core 0.13.4",
- "quote 1.0.21",
- "syn 1.0.105",
+ "quote 1.0.23",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "dart-sys"
+version = "4.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d8b5680b5c2cc52f50acb2457d9b3a3b58adcca785db13a0e3655626f601de6"
+dependencies = [
+ "cc",
]
[[package]]
@@ -1350,9 +1424,9 @@ dependencies = [
[[package]]
name = "dbus"
-version = "0.9.6"
+version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6"
+checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b"
dependencies = [
"libc",
"libdbus-sys",
@@ -1361,9 +1435,9 @@ dependencies = [
[[package]]
name = "dbus-crossroads"
-version = "0.5.1"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "554114296d012b33fdaf362a733db6dc5f73c4c9348b8b620ddd42e61b406e30"
+checksum = "3a4c83437187544ba5142427746835061b330446ca8902eabd70e4afb8f76de0"
dependencies = [
"dbus",
]
@@ -1392,25 +1466,15 @@ dependencies = [
"windows 0.30.0",
]
-[[package]]
-name = "deflate"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
-dependencies = [
- "adler32",
- "byteorder",
-]
-
[[package]]
name = "delegate"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "082a24a9967533dc5d743c602157637116fc1b52806d694a5a45e6f32567fcdd"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -1419,9 +1483,9 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -1431,9 +1495,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1cf41b4580a37cca5ef2ada2cc43cf5d6be3983f4522e83010d67ab6925e84b"
dependencies = [
"darling 0.10.2",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -1513,6 +1577,12 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "discard"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
+
[[package]]
name = "dispatch"
version = "0.2.0"
@@ -1565,7 +1635,7 @@ checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f"
dependencies = [
"lazy_static",
"regex",
- "serde 1.0.149",
+ "serde 1.0.154",
"strsim 0.10.0",
]
@@ -1588,25 +1658,25 @@ dependencies = [
"cc",
"hbb_common",
"lazy_static",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_derive",
"thiserror",
]
[[package]]
name = "ed25519"
-version = "1.5.2"
+version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369"
+checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7"
dependencies = [
"signature",
]
[[package]]
name = "either"
-version = "1.8.0"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "embed-resource"
@@ -1616,16 +1686,16 @@ checksum = "e62abb876c07e4754fae5c14cafa77937841f01740637e17d78dc04352f32a5e"
dependencies = [
"cc",
"rustc_version 0.4.0",
- "toml",
+ "toml 0.5.11",
"vswhom",
"winreg 0.10.1",
]
[[package]]
name = "encoding_rs"
-version = "0.8.31"
+version = "0.8.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
+checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
dependencies = [
"cfg-if 1.0.0",
]
@@ -1640,7 +1710,7 @@ dependencies = [
"objc",
"pkg-config",
"rdev",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_derive",
"tfc",
"unicode-segmentation",
@@ -1649,34 +1719,34 @@ dependencies = [
[[package]]
name = "enum-map"
-version = "2.4.1"
+version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5a56d54c8dd9b3ad34752ed197a4eb2a6601bc010808eb097a04a58ae4c43e1"
+checksum = "50c25992259941eb7e57b936157961b217a4fc8597829ddef0596d6c3cd86e1a"
dependencies = [
"enum-map-derive",
]
[[package]]
name = "enum-map-derive"
-version = "0.10.0"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9045e2676cd5af83c3b167d917b0a5c90a4d8e266e2683d6631b235c457fc27"
+checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
name = "enum_dispatch"
-version = "0.3.8"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb"
+checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2"
dependencies = [
"once_cell",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -1686,7 +1756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e75d4cd21b95383444831539909fbb14b9dc3fdceb2a6f5d36577329a1f55ccb"
dependencies = [
"enumflags2_derive",
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
@@ -1695,9 +1765,9 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f58dc3c5e468259f19f2d46304a6b28f1c3d034442e14b322d2b850e36f6d5ae"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -1723,6 +1793,19 @@ dependencies = [
"termcolor",
]
+[[package]]
+name = "env_logger"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
[[package]]
name = "epoll"
version = "4.3.1"
@@ -1740,10 +1823,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34a887c8df3ed90498c1c437ce21f211c8e27672921a8ffa293cb8d6d4caa9e"
dependencies = [
"proc-macro-error",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"rustversion",
- "syn 1.0.105",
+ "syn 1.0.109",
"synstructure",
]
@@ -1758,6 +1841,17 @@ dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "errno"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
+dependencies = [
+ "errno-dragonfly",
+ "libc",
+ "windows-sys 0.45.0",
+]
+
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
@@ -1804,62 +1898,52 @@ dependencies = [
"flume",
"half",
"lebe",
- "miniz_oxide 0.6.2",
+ "miniz_oxide",
"smallvec",
"threadpool",
"zune-inflate",
]
-[[package]]
-name = "extend"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c5216e387a76eebaaf11f6d871ec8a4aae0b25f05456ee21f228e024b1b3610"
-dependencies = [
- "proc-macro-error",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
-]
-
-[[package]]
-name = "failure"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
-dependencies = [
- "backtrace",
-]
-
[[package]]
name = "fastrand"
-version = "1.8.0"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]]
-name = "field-offset"
-version = "0.3.4"
+name = "fern"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92"
+checksum = "3bdd7b0849075e79ee9a1836df22c717d1eba30451796fdc631b04565dd11e2a"
dependencies = [
- "memoffset 0.6.5",
- "rustc_version 0.3.3",
+ "chrono",
+ "colored",
+ "log",
+]
+
+[[package]]
+name = "field-offset"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535"
+dependencies = [
+ "memoffset 0.8.0",
+ "rustc_version 0.4.0",
]
[[package]]
name = "filetime"
-version = "0.2.19"
+version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9"
+checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
]
[[package]]
@@ -1869,27 +1953,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
- "miniz_oxide 0.6.2",
+ "miniz_oxide",
]
[[package]]
name = "flexi_logger"
-version = "0.22.6"
+version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c76a80dd14a27fc3d8bc696502132cb52b3f227256fd8601166c3a35e45f409"
+checksum = "6eae57842a8221ef13f1f207632d786a175dd13bd8fbdc8be9d852f7c9cf1046"
dependencies = [
- "ansi_term",
- "atty",
"chrono",
"crossbeam-channel",
"crossbeam-queue",
"glob",
+ "is-terminal",
"lazy_static",
"log",
+ "nu-ansi-term",
"regex",
- "rustversion",
"thiserror",
- "time 0.3.9",
]
[[package]]
@@ -1907,9 +1989,9 @@ dependencies = [
[[package]]
name = "flutter_rust_bridge"
-version = "1.61.1"
+version = "1.68.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8079119bbe8fb63d7ebb731fa2aa68c6c8375f4ac95ca26d5868e64c0f4b9244"
+checksum = "54f0d71ff30fc2ae7c18508b517488a89051d81e3bfc4d48d4a6cf54b5dab789"
dependencies = [
"allo-isolate",
"anyhow",
@@ -1918,6 +2000,7 @@ dependencies = [
"cc",
"chrono",
"console_error_panic_hook",
+ "dart-sys",
"flutter_rust_bridge_macros",
"js-sys",
"lazy_static",
@@ -1931,39 +2014,41 @@ dependencies = [
[[package]]
name = "flutter_rust_bridge_codegen"
-version = "1.61.1"
+version = "1.68.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efd7396bc479eae8aa24243e4c0e3d3dbda1909134f8de6bde4f080d262c9a0d"
+checksum = "2a2a75a72411f0c5b480e4671417f52780172053128cf87d5614a9757d7680a0"
dependencies = [
"anyhow",
+ "atty",
"cargo_metadata",
"cbindgen",
+ "chrono",
"clap 3.2.23",
"convert_case",
"delegate",
"enum_dispatch",
- "env_logger 0.9.3",
- "extend",
+ "fern",
"itertools 0.10.5",
"lazy_static",
"log",
"pathdiff",
- "quote 1.0.21",
+ "quote 1.0.23",
"regex",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_yaml",
- "syn 1.0.105",
+ "strum_macros 0.24.3",
+ "syn 1.0.109",
"tempfile",
"thiserror",
- "toml",
+ "toml 0.5.11",
"topological-sort",
]
[[package]]
name = "flutter_rust_bridge_macros"
-version = "1.61.1"
+version = "1.68.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d5cd827645690ef378be57a890d0581e17c28d07b712872af7d744f454fd27d"
+checksum = "f6187d1635afede47c23a9979f85c3dc57e3391eb9c690b7fe95715bf945da72"
[[package]]
name = "fnv"
@@ -2038,9 +2123,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
+checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
dependencies = [
"futures-channel",
"futures-core",
@@ -2053,9 +2138,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
+checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
dependencies = [
"futures-core",
"futures-sink",
@@ -2063,15 +2148,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
+checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
[[package]]
name = "futures-executor"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
+checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
dependencies = [
"futures-core",
"futures-task",
@@ -2080,9 +2165,9 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
+checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
[[package]]
name = "futures-lite"
@@ -2101,32 +2186,32 @@ dependencies = [
[[package]]
name = "futures-macro"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
+checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
name = "futures-sink"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
+checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
[[package]]
name = "futures-task"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
+checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
[[package]]
name = "futures-util"
-version = "0.3.25"
+version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
+checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
dependencies = [
"futures-channel",
"futures-core",
@@ -2140,15 +2225,6 @@ dependencies = [
"slab",
]
-[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
[[package]]
name = "gdk"
version = "0.16.2"
@@ -2160,7 +2236,7 @@ dependencies = [
"gdk-pixbuf",
"gdk-sys",
"gio",
- "glib 0.16.5",
+ "glib 0.16.7",
"libc",
"pango",
]
@@ -2174,7 +2250,7 @@ dependencies = [
"bitflags",
"gdk-pixbuf-sys",
"gio",
- "glib 0.16.5",
+ "glib 0.16.7",
"libc",
]
@@ -2232,7 +2308,7 @@ dependencies = [
"glib-sys 0.16.3",
"libc",
"system-deps 6.0.3",
- "x11 2.20.1",
+ "x11 2.21.0",
]
[[package]]
@@ -2280,9 +2356,9 @@ dependencies = [
[[package]]
name = "gimli"
-version = "0.26.2"
+version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
+checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4"
[[package]]
name = "gio"
@@ -2296,7 +2372,7 @@ dependencies = [
"futures-io",
"futures-util",
"gio-sys",
- "glib 0.16.5",
+ "glib 0.16.7",
"libc",
"once_cell",
"pin-project-lite",
@@ -2338,9 +2414,9 @@ dependencies = [
[[package]]
name = "glib"
-version = "0.16.5"
+version = "0.16.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0cd04d150a2c63e6779f43aec7e04f5374252479b7bed5f45146d9c0e821f161"
+checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f"
dependencies = [
"bitflags",
"futures-channel",
@@ -2369,9 +2445,9 @@ dependencies = [
"itertools 0.9.0",
"proc-macro-crate 0.1.5",
"proc-macro-error",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -2381,12 +2457,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf"
dependencies = [
"anyhow",
- "heck 0.4.0",
- "proc-macro-crate 1.2.1",
+ "heck 0.4.1",
+ "proc-macro-crate 1.3.1",
"proc-macro-error",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -2411,9 +2487,9 @@ dependencies = [
[[package]]
name = "glob"
-version = "0.3.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "gobject-sys"
@@ -2584,7 +2660,7 @@ dependencies = [
"gdk",
"gdk-pixbuf",
"gio",
- "glib 0.16.5",
+ "glib 0.16.7",
"gtk-sys",
"gtk3-macros",
"libc",
@@ -2618,18 +2694,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff"
dependencies = [
"anyhow",
- "proc-macro-crate 1.2.1",
+ "proc-macro-crate 1.3.1",
"proc-macro-error",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
name = "h2"
-version = "0.3.15"
+version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
+checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
dependencies = [
"bytes",
"fnv",
@@ -2673,8 +2749,9 @@ dependencies = [
"confy",
"directories-next",
"dirs-next",
- "env_logger 0.9.3",
+ "env_logger 0.10.0",
"filetime",
+ "flexi_logger",
"futures",
"futures-util",
"lazy_static",
@@ -2688,16 +2765,16 @@ dependencies = [
"quinn",
"rand 0.8.5",
"regex",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_derive",
- "serde_json 1.0.89",
+ "serde_json 1.0.94",
"socket2 0.3.19",
"sodiumoxide",
"sysinfo",
"tokio",
"tokio-socks",
"tokio-util",
- "toml",
+ "toml 0.7.2",
"winapi 0.3.9",
"zstd",
]
@@ -2713,9 +2790,9 @@ dependencies = [
[[package]]
name = "heck"
-version = "0.4.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
@@ -2726,6 +2803,21 @@ dependencies = [
"libc",
]
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+
[[package]]
name = "hex"
version = "0.4.3"
@@ -2740,13 +2832,13 @@ checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1"
[[package]]
name = "http"
-version = "0.2.8"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
+checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
dependencies = [
"bytes",
"fnv",
- "itoa 1.0.4",
+ "itoa 1.0.6",
]
[[package]]
@@ -2781,21 +2873,21 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hwcodec"
version = "0.1.0"
-source = "git+https://github.com/21pages/hwcodec#64f885b3787694b16dfcff08256750b0376b2eba"
+source = "git+https://github.com/21pages/hwcodec#d55f7761ef692fae738259d8c14506d901eb824c"
dependencies = [
"bindgen 0.59.2",
"cc",
"log",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_derive",
- "serde_json 1.0.89",
+ "serde_json 1.0.94",
]
[[package]]
name = "hyper"
-version = "0.14.23"
+version = "0.14.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
+checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
dependencies = [
"bytes",
"futures-channel",
@@ -2806,9 +2898,9 @@ dependencies = [
"http-body",
"httparse",
"httpdate",
- "itoa 1.0.4",
+ "itoa 1.0.6",
"pin-project-lite",
- "socket2 0.4.7",
+ "socket2 0.4.9",
"tokio",
"tower-service",
"tracing",
@@ -2868,22 +2960,6 @@ dependencies = [
"unicode-normalization",
]
-[[package]]
-name = "image"
-version = "0.23.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
-dependencies = [
- "bytemuck",
- "byteorder",
- "color_quant",
- "num-iter",
- "num-rational 0.3.2",
- "num-traits 0.2.15",
- "png 0.16.8",
- "tiff 0.6.1",
-]
-
[[package]]
name = "image"
version = "0.24.5"
@@ -2895,12 +2971,12 @@ dependencies = [
"color_quant",
"exr",
"gif",
- "jpeg-decoder 0.3.0",
+ "jpeg-decoder",
"num-rational 0.4.1",
"num-traits 0.2.15",
- "png 0.17.7",
+ "png",
"scoped_threadpool",
- "tiff 0.8.1",
+ "tiff",
]
[[package]]
@@ -2926,8 +3002,8 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
]
[[package]]
@@ -2972,6 +3048,16 @@ dependencies = [
"web-sys",
]
+[[package]]
+name = "io-lifetimes"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3"
+dependencies = [
+ "libc",
+ "windows-sys 0.45.0",
+]
+
[[package]]
name = "iovec"
version = "0.1.4"
@@ -2983,9 +3069,21 @@ dependencies = [
[[package]]
name = "ipnet"
-version = "2.6.0"
+version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec947b7a4ce12e3b87e353abae7ce124d025b6c7d6c5aea5cc0bcf92e9510ded"
+checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
+
+[[package]]
+name = "is-terminal"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
+dependencies = [
+ "hermit-abi 0.3.1",
+ "io-lifetimes",
+ "rustix",
+ "windows-sys 0.45.0",
+]
[[package]]
name = "itertools"
@@ -3013,9 +3111,9 @@ checksum = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
[[package]]
name = "itoa"
-version = "1.0.4"
+version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "jni"
@@ -3053,19 +3151,13 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "jobserver"
-version = "0.1.25"
+version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
+checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
dependencies = [
"libc",
]
-[[package]]
-name = "jpeg-decoder"
-version = "0.1.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
-
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
@@ -3077,9 +3169,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.60"
+version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
@@ -3101,7 +3193,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68"
dependencies = [
"bitflags",
- "serde 1.0.149",
+ "serde 1.0.154",
"unicode-segmentation",
]
@@ -3129,7 +3221,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89e1edfdc9b0853358306c6dfb4b77c79c779174256fe93d80c0b5ebca451a2f"
dependencies = [
- "glib 0.16.5",
+ "glib 0.16.7",
"gtk",
"gtk-sys",
"libappindicator-sys",
@@ -3149,15 +3241,15 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.138"
+version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libdbus-sys"
-version = "0.2.2"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b"
+checksum = "9f8d7ae751e1cb825c840ae5e682f59b098cdfd213c350ac268b61449a5f58a0"
dependencies = [
"pkg-config",
]
@@ -3174,9 +3266,9 @@ dependencies = [
[[package]]
name = "libpulse-binding"
-version = "2.26.0"
+version = "2.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17be42160017e0ae993c03bfdab4ecb6f82ce3f8d515bd8da8fdf18d10703663"
+checksum = "1745b20bfc194ac12ef828f144f0ec2d4a7fe993281fa3567a0bd4969aee6890"
dependencies = [
"bitflags",
"libc",
@@ -3188,9 +3280,9 @@ dependencies = [
[[package]]
name = "libpulse-simple-binding"
-version = "2.25.0"
+version = "2.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cbf1a1dfd69a48cb60906399fa1d17f1b75029ef51c0789597be792dfd0bcd5"
+checksum = "5ced94199e6e44133431374e4043f34e1f0697ebfb7b7d6c244a65bfaedf0e31"
dependencies = [
"libpulse-binding",
"libpulse-simple-sys",
@@ -3199,9 +3291,9 @@ dependencies = [
[[package]]
name = "libpulse-simple-sys"
-version = "1.19.2"
+version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c73f96f9ca34809692c4760cfe421225860aa000de50edab68a16221fd27cc1"
+checksum = "84e423d9c619c908ce9b4916080e65ab586ca55b8c4939379f15e6e72fb43842"
dependencies = [
"libpulse-sys",
"pkg-config",
@@ -3209,9 +3301,9 @@ dependencies = [
[[package]]
name = "libpulse-sys"
-version = "1.19.3"
+version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "991e6bd0efe2a36e6534e136e7996925e4c1a8e35b7807fe533f2beffff27c30"
+checksum = "2191e6880818d1df4cf72eac8e91dce7a5a52ba0da4b2a5cdafabc22b937eadb"
dependencies = [
"libc",
"num-derive",
@@ -3257,14 +3349,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212"
dependencies = [
"libc",
- "x11 2.20.1",
+ "x11 2.21.0",
+]
+
+[[package]]
+name = "line-wrap"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
+dependencies = [
+ "safemem",
]
[[package]]
name = "link-cplusplus"
-version = "1.0.7"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
+checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
@@ -3275,6 +3376,12 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
+[[package]]
+name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+
[[package]]
name = "lock_api"
version = "0.4.9"
@@ -3385,6 +3492,15 @@ dependencies = [
"autocfg 1.1.0",
]
+[[package]]
+name = "memoffset"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
+dependencies = [
+ "autocfg 1.1.0",
+]
+
[[package]]
name = "mime"
version = "0.3.16"
@@ -3397,34 +3513,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
-[[package]]
-name = "miniz_oxide"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
-dependencies = [
- "adler32",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
-dependencies = [
- "adler",
- "autocfg 1.1.0",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
-dependencies = [
- "adler",
-]
-
[[package]]
name = "miniz_oxide"
version = "0.6.2"
@@ -3455,14 +3543,14 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.8.5"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
+checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
]
[[package]]
@@ -3517,9 +3605,9 @@ dependencies = [
[[package]]
name = "muda"
-version = "0.4.1"
+version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c66365a21dc5e322c6b6ba25c735d00153c57dd2eb377926aa50e3caf547b6f6"
+checksum = "0ee091608fe840d80c6b25e8f838964b264ee8e02e82ae0a38b2d900464d1582"
dependencies = [
"cocoa",
"crossbeam-channel",
@@ -3530,7 +3618,7 @@ dependencies = [
"libxdo",
"objc",
"once_cell",
- "png 0.17.7",
+ "png",
"thiserror",
"windows-sys 0.45.0",
]
@@ -3586,7 +3674,7 @@ dependencies = [
"jni-sys",
"ndk-sys 0.4.1+23.1.7779620",
"num_enum",
- "raw-window-handle 0.5.0",
+ "raw-window-handle 0.5.1",
"thiserror",
]
@@ -3611,21 +3699,6 @@ dependencies = [
"ndk-sys 0.2.2",
]
-[[package]]
-name = "ndk-glue"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f"
-dependencies = [
- "lazy_static",
- "libc",
- "log",
- "ndk 0.6.0",
- "ndk-context",
- "ndk-macro",
- "ndk-sys 0.3.0",
-]
-
[[package]]
name = "ndk-macro"
version = "0.3.0"
@@ -3633,10 +3706,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c"
dependencies = [
"darling 0.13.4",
- "proc-macro-crate 1.2.1",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro-crate 1.3.1",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -3714,35 +3787,23 @@ dependencies = [
[[package]]
name = "nix"
-version = "0.25.1"
+version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
+checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [
- "autocfg 1.1.0",
"bitflags",
"cfg-if 1.0.0",
"libc",
- "memoffset 0.6.5",
+ "memoffset 0.7.1",
"pin-utils",
-]
-
-[[package]]
-name = "nix"
-version = "0.26.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694"
-dependencies = [
- "bitflags",
- "cfg-if 1.0.0",
- "libc",
"static_assertions",
]
[[package]]
name = "nom"
-version = "7.1.1"
+version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
@@ -3750,13 +3811,23 @@ dependencies = [
[[package]]
name = "ntapi"
-version = "0.3.7"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
+checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc"
dependencies = [
"winapi 0.3.9",
]
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi 0.3.9",
+]
+
[[package]]
name = "num-bigint"
version = "0.4.3"
@@ -3770,9 +3841,9 @@ dependencies = [
[[package]]
name = "num-complex"
-version = "0.4.2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
+checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d"
dependencies = [
"num-traits 0.2.15",
]
@@ -3783,9 +3854,9 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -3798,17 +3869,6 @@ dependencies = [
"num-traits 0.2.15",
]
-[[package]]
-name = "num-iter"
-version = "0.1.43"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
-dependencies = [
- "autocfg 1.1.0",
- "num-integer",
- "num-traits 0.2.15",
-]
-
[[package]]
name = "num-rational"
version = "0.3.2"
@@ -3851,42 +3911,33 @@ dependencies = [
[[package]]
name = "num_cpus"
-version = "1.14.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
- "hermit-abi",
+ "hermit-abi 0.2.6",
"libc",
]
[[package]]
name = "num_enum"
-version = "0.5.7"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
+checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
-version = "0.5.7"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
+checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [
- "proc-macro-crate 1.2.1",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
-]
-
-[[package]]
-name = "num_threads"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
-dependencies = [
- "libc",
+ "proc-macro-crate 1.3.1",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -3930,9 +3981,9 @@ dependencies = [
[[package]]
name = "object"
-version = "0.29.0"
+version = "0.30.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
+checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
dependencies = [
"memchr",
]
@@ -3962,9 +4013,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.17.0"
+version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "openssl-probe"
@@ -3984,14 +4035,26 @@ dependencies = [
[[package]]
name = "ordered-stream"
-version = "0.1.2"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01ca8c99d73c6e92ac1358f9f692c22c0bfd9c4701fa086f5d365c0d4ea818ea"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
]
+[[package]]
+name = "os-version"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a8a1fed76ac765e39058ca106b6229a93c5a60292a1bd4b602ce2be11e1c020"
+dependencies = [
+ "anyhow",
+ "plist",
+ "uname",
+ "winapi 0.3.9",
+]
+
[[package]]
name = "os_str_bytes"
version = "6.4.1"
@@ -4004,11 +4067,17 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38731fa859ef679f1aec66ca9562165926b442f298467f76f5990f431efe87dc"
dependencies = [
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_derive",
- "serde_json 1.0.89",
+ "serde_json 1.0.94",
]
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
[[package]]
name = "pango"
version = "0.16.5"
@@ -4017,7 +4086,7 @@ checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94"
dependencies = [
"bitflags",
"gio",
- "glib 0.16.5",
+ "glib 0.16.7",
"libc",
"once_cell",
"pango-sys",
@@ -4064,7 +4133,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
- "parking_lot_core 0.8.5",
+ "parking_lot_core 0.8.6",
]
[[package]]
@@ -4074,14 +4143,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
- "parking_lot_core 0.9.5",
+ "parking_lot_core 0.9.7",
]
[[package]]
name = "parking_lot_core"
-version = "0.8.5"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if 1.0.0",
"instant",
@@ -4093,22 +4162,22 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.5"
+version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
+checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"smallvec",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
]
[[package]]
name = "paste"
-version = "1.0.9"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
+checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "pathdiff"
@@ -4128,16 +4197,6 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
-[[package]]
-name = "pest"
-version = "2.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc8bed3549e0f9b0a2a78bf7c0018237a2cdf085eecbbc048e52612438e4e9d0"
-dependencies = [
- "thiserror",
- "ucd-trie",
-]
-
[[package]]
name = "phf"
version = "0.7.24"
@@ -4191,9 +4250,9 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -4215,15 +4274,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
-name = "png"
-version = "0.16.8"
+name = "plist"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
+checksum = "ffac6a51110e97610dd3ac73e34a65b27e56a1e305df41bad1f616d8e1cb22f4"
dependencies = [
- "bitflags",
- "crc32fast",
- "deflate",
- "miniz_oxide 0.3.7",
+ "base64",
+ "indexmap",
+ "line-wrap",
+ "quick-xml",
+ "serde 1.0.154",
+ "time 0.3.20",
]
[[package]]
@@ -4235,21 +4296,23 @@ dependencies = [
"bitflags",
"crc32fast",
"flate2",
- "miniz_oxide 0.6.2",
+ "miniz_oxide",
]
[[package]]
name = "polling"
-version = "2.5.1"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "166ca89eb77fd403230b9c156612965a81e094ec6ec3aa13663d4c8b113fa748"
+checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa"
dependencies = [
"autocfg 1.1.0",
+ "bitflags",
"cfg-if 1.0.0",
+ "concurrent-queue",
"libc",
"log",
- "wepoll-ffi",
- "windows-sys 0.42.0",
+ "pin-project-lite",
+ "windows-sys 0.45.0",
]
[[package]]
@@ -4279,18 +4342,17 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
dependencies = [
- "toml",
+ "toml 0.5.11",
]
[[package]]
name = "proc-macro-crate"
-version = "1.2.1"
+version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9"
+checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
dependencies = [
"once_cell",
- "thiserror",
- "toml",
+ "toml_edit",
]
[[package]]
@@ -4300,9 +4362,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
"version_check",
]
@@ -4312,8 +4374,8 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"version_check",
]
@@ -4328,9 +4390,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.47"
+version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [
"unicode-ident",
]
@@ -4375,7 +4437,7 @@ dependencies = [
"protobuf-support",
"tempfile",
"thiserror",
- "which 4.3.0",
+ "which",
]
[[package]]
@@ -4401,17 +4463,25 @@ dependencies = [
]
[[package]]
-name = "quinn"
-version = "0.8.5"
+name = "quick-xml"
+version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f"
+checksum = "ffc053f057dd768a56f62cd7e434c42c831d296968997e9ac1f76ea7c2d14c41"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quinn"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "445cbfe2382fa023c4f2f3c7e1c95c03dcc1df2bf23cebcb2b13e1402c4394d1"
dependencies = [
"bytes",
- "futures-channel",
- "futures-util",
- "fxhash",
+ "pin-project-lite",
"quinn-proto",
"quinn-udp",
+ "rustc-hash",
"rustls",
"thiserror",
"tokio",
@@ -4421,17 +4491,16 @@ dependencies = [
[[package]]
name = "quinn-proto"
-version = "0.8.4"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fce546b9688f767a57530652488420d419a8b1f44a478b451c3d1ab6d992a55"
+checksum = "72ef4ced82a24bb281af338b9e8f94429b6eca01b4e66d899f40031f074e74c9"
dependencies = [
"bytes",
- "fxhash",
"rand 0.8.5",
"ring",
+ "rustc-hash",
"rustls",
"rustls-native-certs",
- "rustls-pemfile 0.2.1",
"slab",
"thiserror",
"tinyvec",
@@ -4441,16 +4510,15 @@ dependencies = [
[[package]]
name = "quinn-udp"
-version = "0.1.4"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b07946277141531aea269befd949ed16b2c85a780ba1043244eda0969e538e54"
+checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4"
dependencies = [
- "futures-util",
"libc",
"quinn-proto",
- "socket2 0.4.7",
- "tokio",
+ "socket2 0.4.9",
"tracing",
+ "windows-sys 0.42.0",
]
[[package]]
@@ -4464,11 +4532,11 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.21"
+version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
- "proc-macro2 1.0.47",
+ "proc-macro2 1.0.51",
]
[[package]]
@@ -4624,18 +4692,15 @@ dependencies = [
[[package]]
name = "raw-window-handle"
-version = "0.5.0"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed7e3d950b66e19e0c372f3fa3fbbcf85b1746b571f74e0c2af6042a5c93420a"
-dependencies = [
- "cty",
-]
+checksum = "4f851a03551ceefd30132e447f07f96cb7011d6b658374f3aed847333adb5559"
[[package]]
name = "rayon"
-version = "1.6.1"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
@@ -4643,9 +4708,9 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.10.1"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
@@ -4656,7 +4721,7 @@ dependencies = [
[[package]]
name = "rdev"
version = "0.5.0-2"
-source = "git+https://github.com/fufesou/rdev#5b9fb5e42117f44e0ce0fe7cf2bddf270c75f1dc"
+source = "git+https://github.com/fufesou/rdev#25a99ce71ab42843ad253dd51e6a35e83e87a8a4"
dependencies = [
"cocoa",
"core-foundation 0.9.3",
@@ -4669,12 +4734,12 @@ dependencies = [
"lazy_static",
"libc",
"log",
- "mio 0.8.5",
+ "mio 0.8.6",
"strum 0.24.1",
"strum_macros 0.24.3",
"widestring 1.0.2",
"winapi 0.3.9",
- "x11 2.20.1",
+ "x11 2.21.0",
]
[[package]]
@@ -4717,9 +4782,9 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.7.0"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
"aho-corasick",
"memchr",
@@ -4732,15 +4797,6 @@ version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
-[[package]]
-name = "remove_dir_all"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
-dependencies = [
- "winapi 0.3.9",
-]
-
[[package]]
name = "repng"
version = "0.2.2"
@@ -4753,9 +4809,9 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.11.13"
+version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
+checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
dependencies = [
"base64",
"bytes",
@@ -4775,9 +4831,9 @@ dependencies = [
"percent-encoding",
"pin-project-lite",
"rustls",
- "rustls-pemfile 1.0.1",
- "serde 1.0.149",
- "serde_json 1.0.89",
+ "rustls-pemfile",
+ "serde 1.0.154",
+ "serde_json 1.0.94",
"serde_urlencoded",
"tokio",
"tokio-rustls",
@@ -4851,12 +4907,12 @@ dependencies = [
[[package]]
name = "runas"
-version = "0.2.1"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a620b0994a180cdfa25c0439e6d58c0628272571501880d626ffff58e96a0799"
+checksum = "ed87390fefd18965ff20baae5aeb9913bcf82d2b59dc04c0f6d8f17f7be56ff2"
dependencies = [
"cc",
- "which 3.1.1",
+ "which",
]
[[package]]
@@ -4891,11 +4947,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
-version = "0.3.3"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
- "semver 0.11.0",
+ "semver 0.9.0",
]
[[package]]
@@ -4904,14 +4960,14 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
- "semver 1.0.14",
+ "semver 1.0.16",
]
[[package]]
name = "rustdesk"
version = "1.2.0"
dependencies = [
- "android_logger 0.11.1",
+ "android_logger 0.11.3",
"arboard",
"async-process",
"async-trait",
@@ -4921,7 +4977,7 @@ dependencies = [
"cfg-if 1.0.0",
"chrono",
"cidr-utils",
- "clap 3.2.23",
+ "clap 4.1.8",
"clipboard",
"cocoa",
"core-foundation 0.9.3",
@@ -4936,15 +4992,15 @@ dependencies = [
"dispatch",
"dlopen",
"enigo",
- "errno",
+ "errno 0.3.0",
"evdev",
- "flexi_logger",
"flutter_rust_bridge",
"flutter_rust_bridge_codegen",
"fruitbasket",
"hbb_common",
+ "hex",
"hound",
- "image 0.24.5",
+ "image",
"impersonate_system",
"include_dir",
"jni 0.19.0",
@@ -4958,6 +5014,7 @@ dependencies = [
"num_cpus",
"objc",
"objc_id",
+ "os-version",
"parity-tokio-ipc",
"rdev",
"repng",
@@ -4969,9 +5026,9 @@ dependencies = [
"samplerate",
"sciter-rs",
"scrap",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_derive",
- "serde_json 1.0.89",
+ "serde_json 1.0.94",
"sha2",
"shared_memory",
"shutdown_hooks",
@@ -5020,10 +5077,24 @@ dependencies = [
]
[[package]]
-name = "rustls"
-version = "0.20.7"
+name = "rustix"
+version = "0.36.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
+checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc"
+dependencies = [
+ "bitflags",
+ "errno 0.2.8",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.20.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
dependencies = [
"log",
"ring",
@@ -5038,40 +5109,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
dependencies = [
"openssl-probe",
- "rustls-pemfile 1.0.1",
+ "rustls-pemfile",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-pemfile"
-version = "0.2.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9"
-dependencies = [
- "base64",
-]
-
-[[package]]
-name = "rustls-pemfile"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
+checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
dependencies = [
"base64",
]
[[package]]
name = "rustversion"
-version = "1.0.9"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
+checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
[[package]]
name = "ryu"
-version = "1.0.11"
+version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "safemem"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "same-file"
@@ -5093,12 +5161,11 @@ dependencies = [
[[package]]
name = "schannel"
-version = "0.1.20"
+version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
+checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
dependencies = [
- "lazy_static",
- "windows-sys 0.36.1",
+ "windows-sys 0.42.0",
]
[[package]]
@@ -5135,7 +5202,7 @@ name = "scrap"
version = "0.5.0"
dependencies = [
"android_logger 0.10.1",
- "bindgen 0.59.2",
+ "bindgen 0.64.0",
"block",
"cfg-if 1.0.0",
"dbus",
@@ -5152,8 +5219,8 @@ dependencies = [
"num_cpus",
"quest",
"repng",
- "serde 1.0.149",
- "serde_json 1.0.89",
+ "serde 1.0.154",
+ "serde_json 1.0.94",
"target_build_utils",
"tracing",
"webm",
@@ -5162,9 +5229,9 @@ dependencies = [
[[package]]
name = "scratch"
-version = "1.0.2"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
+checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
[[package]]
name = "sct"
@@ -5178,9 +5245,9 @@ dependencies = [
[[package]]
name = "security-framework"
-version = "2.7.0"
+version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
+checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254"
dependencies = [
"bitflags",
"core-foundation 0.9.3",
@@ -5191,9 +5258,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.6.1"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
+checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4"
dependencies = [
"core-foundation-sys 0.8.3",
"libc",
@@ -5201,30 +5268,27 @@ dependencies = [
[[package]]
name = "semver"
-version = "0.11.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver"
-version = "1.0.14"
+version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
+checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
dependencies = [
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
name = "semver-parser"
-version = "0.10.2"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
-dependencies = [
- "pest",
-]
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
@@ -5234,22 +5298,22 @@ checksum = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af"
[[package]]
name = "serde"
-version = "1.0.149"
+version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
+checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.149"
+version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
+checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -5266,24 +5330,33 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.89"
+version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
+checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
dependencies = [
- "itoa 1.0.4",
+ "itoa 1.0.6",
"ryu",
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
name = "serde_repr"
-version = "0.1.9"
+version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca"
+checksum = "395627de918015623b32e7669714206363a7fc00382bf477e72c1f7533e8eafc"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
+dependencies = [
+ "serde 1.0.154",
]
[[package]]
@@ -5293,9 +5366,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
- "itoa 1.0.4",
+ "itoa 1.0.6",
"ryu",
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
@@ -5306,10 +5379,19 @@ checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
dependencies = [
"indexmap",
"ryu",
- "serde 1.0.149",
+ "serde 1.0.154",
"yaml-rust",
]
+[[package]]
+name = "sha1"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
+dependencies = [
+ "sha1_smol",
+]
+
[[package]]
name = "sha1"
version = "0.10.5"
@@ -5321,6 +5403,12 @@ dependencies = [
"digest",
]
+[[package]]
+name = "sha1_smol"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
+
[[package]]
name = "sha2"
version = "0.10.6"
@@ -5359,9 +5447,9 @@ checksum = "6057adedbec913419c92996f395ba69931acbd50b7d56955394cd3f7bedbfa45"
[[package]]
name = "signal-hook"
-version = "0.3.14"
+version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
+checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
dependencies = [
"libc",
"signal-hook-registry",
@@ -5369,9 +5457,9 @@ dependencies = [
[[package]]
name = "signal-hook-registry"
-version = "1.4.0"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
@@ -5394,7 +5482,7 @@ version = "0.1.0"
dependencies = [
"confy",
"hbb_common",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_derive",
"walkdir",
]
@@ -5407,9 +5495,9 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
[[package]]
name = "slab"
-version = "0.4.7"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [
"autocfg 1.1.0",
]
@@ -5452,9 +5540,9 @@ dependencies = [
[[package]]
name = "socket2"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
+checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi 0.3.9",
@@ -5469,7 +5557,7 @@ dependencies = [
"ed25519",
"libc",
"libsodium-sys",
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
@@ -5495,9 +5583,52 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stdweb"
-version = "0.1.3"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
+checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
+dependencies = [
+ "discard",
+ "rustc_version 0.2.3",
+ "stdweb-derive",
+ "stdweb-internal-macros",
+ "stdweb-internal-runtime",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "stdweb-derive"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
+dependencies = [
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "serde 1.0.154",
+ "serde_derive",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "stdweb-internal-macros"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
+dependencies = [
+ "base-x",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "serde 1.0.154",
+ "serde_derive",
+ "serde_json 1.0.94",
+ "sha1 0.6.1",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "stdweb-internal-runtime"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "str-buf"
@@ -5548,9 +5679,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c"
dependencies = [
"heck 0.3.3",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -5559,11 +5690,11 @@ version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
- "heck 0.4.0",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "heck 0.4.1",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"rustversion",
- "syn 1.0.105",
+ "syn 1.0.109",
]
[[package]]
@@ -5579,12 +5710,12 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.105"
+version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"unicode-ident",
]
@@ -5594,30 +5725,30 @@ version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
"unicode-xid 0.2.4",
]
[[package]]
name = "sys-locale"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3358acbb4acd4146138b9bda219e904a6bb5aaaa237f8eed06f4d6bc1580ecee"
+checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee"
dependencies = [
"js-sys",
"libc",
"wasm-bindgen",
"web-sys",
- "winapi 0.3.9",
+ "windows-sys 0.45.0",
]
[[package]]
name = "sysinfo"
-version = "0.24.7"
+version = "0.28.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54cb4ebf3d49308b99e6e9dc95e989e2fdbdc210e4f67c39db0bb89ba927001c"
+checksum = "d3e847e2de7a137c8c2cede5095872dbb00f4f9bf34d061347e36b43322acd56"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys 0.8.3",
@@ -5660,7 +5791,7 @@ dependencies = [
"strum 0.18.0",
"strum_macros 0.18.0",
"thiserror",
- "toml",
+ "toml 0.5.11",
"version-compare 0.0.10",
]
@@ -5671,19 +5802,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff"
dependencies = [
"cfg-expr",
- "heck 0.4.0",
+ "heck 0.4.1",
"pkg-config",
- "toml",
+ "toml 0.5.11",
"version-compare 0.1.1",
]
[[package]]
name = "system_shutdown"
-version = "3.0.0"
+version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "035e081d603551d8d78db27d2232913269c749ea67648c369100049820406a14"
+checksum = "7567f71160af5e9abfb4f5a21532cf2174cefe91ac5c336419295685a695cc66"
dependencies = [
- "winapi 0.3.9",
+ "windows 0.44.0",
+ "zbus",
]
[[package]]
@@ -5705,10 +5837,10 @@ dependencies = [
"gdkwayland-sys",
"gdkx11-sys",
"gio",
- "glib 0.16.5",
+ "glib 0.16.7",
"glib-sys 0.16.3",
"gtk",
- "image 0.24.5",
+ "image",
"instant",
"jni 0.20.0",
"lazy_static",
@@ -5720,8 +5852,8 @@ dependencies = [
"objc",
"once_cell",
"parking_lot 0.12.1",
- "png 0.17.7",
- "raw-window-handle 0.5.0",
+ "png",
+ "raw-window-handle 0.5.1",
"scopeguard",
"tao-macros",
"unicode-segmentation",
@@ -5736,9 +5868,9 @@ name = "tao-macros"
version = "0.1.0"
source = "git+https://github.com/tauri-apps/tao?branch=muda#676bd90a80286b893d8850cc4e3813a0c4a27dcf"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -5760,23 +5892,22 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.3.0"
+version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95"
dependencies = [
"cfg-if 1.0.0",
"fastrand",
- "libc",
"redox_syscall",
- "remove_dir_all",
- "winapi 0.3.9",
+ "rustix",
+ "windows-sys 0.42.0",
]
[[package]]
name = "termcolor"
-version = "1.1.3"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
+checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
@@ -5808,8 +5939,9 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "tfc"
version = "0.6.1"
-source = "git+https://github.com/fufesou/The-Fat-Controller#a5f13e6ef80327eb8d860aeb26b0af93eb5aee2b"
+source = "git+https://github.com/fufesou/The-Fat-Controller#102f2ec2cb2bbbd64413d20d28323e5e77e0fe71"
dependencies = [
+ "anyhow",
"core-graphics 0.22.3",
"unicode-segmentation",
"winapi 0.3.9",
@@ -5818,22 +5950,22 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.37"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.37"
+version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -5845,17 +5977,6 @@ dependencies = [
"num_cpus",
]
-[[package]]
-name = "tiff"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
-dependencies = [
- "jpeg-decoder 0.1.22",
- "miniz_oxide 0.4.4",
- "weezl",
-]
-
[[package]]
name = "tiff"
version = "0.8.1"
@@ -5863,7 +5984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
dependencies = [
"flate2",
- "jpeg-decoder 0.3.0",
+ "jpeg-decoder",
"weezl",
]
@@ -5880,21 +6001,30 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.9"
+version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd"
+checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [
- "itoa 1.0.4",
- "libc",
- "num_threads",
+ "itoa 1.0.6",
+ "serde 1.0.154",
+ "time-core",
"time-macros",
]
[[package]]
-name = "time-macros"
-version = "0.2.4"
+name = "time-core"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
+checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
+
+[[package]]
+name = "time-macros"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
+dependencies = [
+ "time-core",
+]
[[package]]
name = "tinyvec"
@@ -5907,28 +6037,28 @@ dependencies = [
[[package]]
name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
-version = "1.23.0"
+version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
+checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
dependencies = [
"autocfg 1.1.0",
"bytes",
"libc",
"memchr",
- "mio 0.8.5",
+ "mio 0.8.6",
"num_cpus",
"parking_lot 0.12.1",
"pin-project-lite",
"signal-hook-registry",
- "socket2 0.4.7",
+ "socket2 0.4.9",
"tokio-macros",
- "windows-sys 0.42.0",
+ "windows-sys 0.45.0",
]
[[package]]
@@ -5937,9 +6067,9 @@ version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -5971,9 +6101,9 @@ dependencies = [
[[package]]
name = "tokio-util"
-version = "0.7.4"
+version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
+checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
dependencies = [
"bytes",
"futures-core",
@@ -5989,11 +6119,45 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.5.9"
+version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
- "serde 1.0.149",
+ "serde 1.0.154",
+]
+
+[[package]]
+name = "toml"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
+dependencies = [
+ "serde 1.0.154",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
+dependencies = [
+ "serde 1.0.154",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.19.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
+dependencies = [
+ "indexmap",
+ "serde 1.0.154",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
]
[[package]]
@@ -6026,9 +6190,9 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -6052,9 +6216,9 @@ dependencies = [
[[package]]
name = "tray-icon"
-version = "0.4.2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d62801a4da61bb100b8d3174a5a46fed7b6ea03cc2ae93ee7340793b09a94ce3"
+checksum = "f87445e3a107818c17d87e8369db30a6fc25539bface8351efe2132b22e47dbc"
dependencies = [
"cocoa",
"core-graphics 0.22.3",
@@ -6064,7 +6228,7 @@ dependencies = [
"muda",
"objc",
"once_cell",
- "png 0.17.7",
+ "png",
"thiserror",
"windows-sys 0.45.0",
]
@@ -6080,9 +6244,9 @@ dependencies = [
[[package]]
name = "try-lock"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "typenum"
@@ -6090,12 +6254,6 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
-[[package]]
-name = "ucd-trie"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
-
[[package]]
name = "uds_windows"
version = "1.0.2"
@@ -6107,16 +6265,25 @@ dependencies = [
]
[[package]]
-name = "unicode-bidi"
-version = "0.3.8"
+name = "uname"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
+checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c"
[[package]]
name = "unicode-ident"
-version = "1.0.5"
+version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-normalization"
@@ -6129,9 +6296,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
-version = "1.10.0"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
+checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
@@ -6166,7 +6333,7 @@ dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
@@ -6272,9 +6439,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.83"
+version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
@@ -6282,24 +6449,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.83"
+version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.33"
+version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@@ -6309,32 +6476,32 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.83"
+version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
- "quote 1.0.21",
+ "quote 1.0.23",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.83"
+version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.83"
+version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "wayland-client"
@@ -6393,8 +6560,8 @@ version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"xml-rs",
]
@@ -6411,9 +6578,9 @@ dependencies = [
[[package]]
name = "web-sys"
-version = "0.3.60"
+version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -6462,30 +6629,11 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
-[[package]]
-name = "wepoll-ffi"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb"
-dependencies = [
- "cc",
-]
-
[[package]]
name = "which"
-version = "3.1.1"
+version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
-dependencies = [
- "failure",
- "libc",
-]
-
-[[package]]
-name = "which"
-version = "4.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
+checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
dependencies = [
"either",
"libc",
@@ -6494,11 +6642,10 @@ dependencies = [
[[package]]
name = "whoami"
-version = "1.2.3"
+version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6631b6a2fd59b1841b622e8f1a7ad241ef0a46f2d580464ce8140ac94cbd571"
+checksum = "45dbc71f0cdca27dc261a9bd37ddec174e4a0af2b900b890f378460f745426e3"
dependencies = [
- "bumpalo",
"wasm-bindgen",
"web-sys",
]
@@ -6602,6 +6749,19 @@ dependencies = [
"windows_x86_64_msvc 0.34.0",
]
+[[package]]
+name = "windows"
+version = "0.37.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57b543186b344cc61c85b5aab0d2e3adf4e0f99bc076eff9aa5927bcc0b8a647"
+dependencies = [
+ "windows_aarch64_msvc 0.37.0",
+ "windows_i686_gnu 0.37.0",
+ "windows_i686_msvc 0.37.0",
+ "windows_x86_64_gnu 0.37.0",
+ "windows_x86_64_msvc 0.37.0",
+]
+
[[package]]
name = "windows"
version = "0.44.0"
@@ -6619,9 +6779,9 @@ version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -6630,9 +6790,9 @@ version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f"
dependencies = [
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
[[package]]
@@ -6660,19 +6820,6 @@ dependencies = [
"windows_x86_64_msvc 0.28.0",
]
-[[package]]
-name = "windows-sys"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
-dependencies = [
- "windows_aarch64_msvc 0.36.1",
- "windows_i686_gnu 0.36.1",
- "windows_i686_msvc 0.36.1",
- "windows_x86_64_gnu 0.36.1",
- "windows_x86_64_msvc 0.36.1",
-]
-
[[package]]
name = "windows-sys"
version = "0.42.0"
@@ -6738,9 +6885,9 @@ checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.36.1"
+version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
[[package]]
name = "windows_aarch64_msvc"
@@ -6768,9 +6915,9 @@ checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
[[package]]
name = "windows_i686_gnu"
-version = "0.36.1"
+version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
[[package]]
name = "windows_i686_gnu"
@@ -6798,9 +6945,9 @@ checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
[[package]]
name = "windows_i686_msvc"
-version = "0.36.1"
+version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
[[package]]
name = "windows_i686_msvc"
@@ -6828,9 +6975,9 @@ checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.36.1"
+version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
[[package]]
name = "windows_x86_64_gnu"
@@ -6864,9 +7011,9 @@ checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.36.1"
+version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
[[package]]
name = "windows_x86_64_msvc"
@@ -6890,9 +7037,9 @@ dependencies = [
"lazy_static",
"libc",
"log",
- "mio 0.8.5",
+ "mio 0.8.6",
"ndk 0.5.0",
- "ndk-glue 0.5.2",
+ "ndk-glue",
"ndk-sys 0.2.2",
"objc",
"parking_lot 0.11.2",
@@ -6907,6 +7054,15 @@ dependencies = [
"x11-dl",
]
+[[package]]
+name = "winnow"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "winreg"
version = "0.6.2"
@@ -6931,17 +7087,14 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
dependencies = [
- "toml",
+ "toml 0.5.11",
]
[[package]]
name = "wol-rs"
-version = "0.9.1"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7f97e69b28b256ccfb02472c25057132e234aa8368fea3bb0268def564ce1f2"
-dependencies = [
- "clap 3.2.23",
-]
+checksum = "48dc5e486e34a31515518d370cdd8bf59ec696323fe8f92b858e43942e84a765"
[[package]]
name = "ws2_32-sys"
@@ -6973,9 +7126,9 @@ dependencies = [
[[package]]
name = "x11"
-version = "2.20.1"
+version = "2.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2638d5b9c17ac40575fb54bb461a4b1d2a8d1b4ffcc4ff237d254ec59ddeb82"
+checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e"
dependencies = [
"libc",
"pkg-config",
@@ -6994,14 +7147,24 @@ dependencies = [
[[package]]
name = "x11rb"
-version = "0.9.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6e99be55648b3ae2a52342f9a870c0e138709a3493261ce9b469afe6e4df6d8a"
+checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507"
dependencies = [
"gethostname",
- "nix 0.22.3",
+ "nix 0.24.3",
"winapi 0.3.9",
"winapi-wsapoll",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67"
+dependencies = [
+ "nix 0.24.3",
]
[[package]]
@@ -7026,7 +7189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5af43ba661cee58bd86b9f81a899e45a15ac7f42fa4401340f73c0c2950030c1"
dependencies = [
"derive_setters",
- "serde 1.0.149",
+ "serde 1.0.154",
]
[[package]]
@@ -7040,13 +7203,13 @@ dependencies = [
[[package]]
name = "zbus"
-version = "3.6.2"
+version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "938ea6da98c75c2c37a86007bd17fd8e208cbec24e086108c87ece98e9edec0d"
+checksum = "20aae5dd5b051971cd2f49f9f3b860e57b2b495ba5ba254eaec42d34ede57e97"
dependencies = [
"async-broadcast",
- "async-channel",
"async-executor",
+ "async-fs",
"async-io",
"async-lock",
"async-recursion",
@@ -7061,13 +7224,13 @@ dependencies = [
"futures-sink",
"futures-util",
"hex",
- "nix 0.25.1",
+ "nix 0.26.2",
"once_cell",
"ordered-stream",
"rand 0.8.5",
- "serde 1.0.149",
+ "serde 1.0.154",
"serde_repr",
- "sha1",
+ "sha1 0.10.5",
"static_assertions",
"tracing",
"uds_windows",
@@ -7079,24 +7242,25 @@ dependencies = [
[[package]]
name = "zbus_macros"
-version = "3.6.2"
+version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45066039ebf3330820e495e854f8b312abb68f0a39e97972d092bd72e8bb3e8e"
+checksum = "9264b3a1bcf5503d4e0348b6e7efe1da58d4f92a913c15ed9e63b52de85faaa1"
dependencies = [
- "proc-macro-crate 1.2.1",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
+ "proc-macro-crate 1.3.1",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
"regex",
- "syn 1.0.105",
+ "syn 1.0.109",
+ "zvariant_utils",
]
[[package]]
name = "zbus_names"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c737644108627748a660d038974160e0cbb62605536091bdfa28fd7f64d43c8"
+checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3"
dependencies = [
- "serde 1.0.149",
+ "serde 1.0.154",
"static_assertions",
"zvariant",
]
@@ -7132,35 +7296,47 @@ dependencies = [
[[package]]
name = "zune-inflate"
-version = "0.2.42"
+version = "0.2.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c473377c11c4a3ac6a2758f944cd336678e9c977aa0abf54f6450cf77e902d6d"
+checksum = "a01728b79fb9b7e28a8c11f715e1cd8dc2cda7416a007d66cac55cebb3a8ac6b"
dependencies = [
"simd-adler32",
]
[[package]]
name = "zvariant"
-version = "3.9.0"
+version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56f8c89c183461e11867ded456db252eae90874bc6769b7adbea464caa777e51"
+checksum = "46fe4914a985446d6fd287019b5fceccce38303d71407d9e6e711d44954a05d8"
dependencies = [
"byteorder",
"enumflags2",
"libc",
- "serde 1.0.149",
+ "serde 1.0.154",
"static_assertions",
"zvariant_derive",
]
[[package]]
name = "zvariant_derive"
-version = "3.9.0"
+version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "155247a5d1ab55e335421c104ccd95d64f17cebbd02f50cdbc1c33385f9c4d81"
+checksum = "34c20260af4b28b3275d6676c7e2a6be0d4332e8e0aba4616d34007fd84e462a"
dependencies = [
- "proc-macro-crate 1.2.1",
- "proc-macro2 1.0.47",
- "quote 1.0.21",
- "syn 1.0.105",
+ "proc-macro-crate 1.3.1",
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b"
+dependencies = [
+ "proc-macro2 1.0.51",
+ "quote 1.0.23",
+ "syn 1.0.109",
]
diff --git a/Cargo.toml b/Cargo.toml
index f93f776a0..7ad979f8c 100644
--- a/Cargo.toml
+++ b/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"
diff --git a/README.md b/README.md
index 8af79915b..4e3b309c5 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
Docker •
Structure •
Snapshot
- [Українська] | [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Dansk]
+ [Українська] | [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Dansk] | [Ελληνικά]
We need your help to translate this README, RustDesk UI and Doc to your native language
@@ -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
diff --git a/build.py b/build.py
index 727b53fe0..4a39f596d 100755
--- a/build.py
+++ b/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> 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():
"""))
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__":
diff --git a/build.rs b/build.rs
index d15f27424..bf141e539 100644
--- a/build.rs
+++ b/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);
}
diff --git a/docs/CONTRIBUTING-DE.md b/docs/CONTRIBUTING-DE.md
new file mode 100644
index 000000000..6258a9a7a
--- /dev/null
+++ b/docs/CONTRIBUTING-DE.md
@@ -0,0 +1,50 @@
+# Beitrge zu RustDesk
+
+RustDesk begrt Beitrge von jedem. Hier sind die Richtlinien, wenn Sie uns
+helfen mchten:
+
+## Beitrge
+
+Beitrge zu RustDesk oder seinen Abhngigkeiten sollten in Form von Pull
+Requests auf GitHub erfolgen. Jeder Pull Request wird von einem Hauptakteur
+(jemand mit der Erlaubnis, Korrekturen einzubringen) geprft und entweder in den
+Hauptbaum eingefgt oder Feedback fr notwendige nderungen gegeben. Alle
+Beitrge sollten diesem Format folgen, auch die von Hauptakteuren.
+
+Wenn Sie an einem Problem arbeiten mchten, melden Sie es bitte zuerst an, indem
+Sie auf GitHub erklren, dass Sie daran arbeiten mchten. Damit soll verhindert
+werden, dass Beitrge zum gleichen Thema doppelt bearbeitet werden.
+
+## Checkliste fr Pull Requests
+
+- Verzweigen Sie sich vom Master-Branch und, falls ntig, wechseln Sie zum
+ aktuellen Master-Branch, bevor Sie Ihren Pull Request einreichen. Wenn das
+ Zusammenfhren mit dem Master nicht reibungslos funktioniert, werden Sie
+ mglicherweise aufgefordert, Ihre nderungen zu berarbeiten.
+
+- Commits sollten so klein wie mglich sein und gleichzeitig sicherstellen, dass
+ jeder Commit unabhngig voneinander korrekt ist (d. h., jeder Commit sollte
+ sich bersetzen lassen und Tests bestehen).
+
+- Commits sollten von einem "Herkunftszertifikat fr 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` fr `git commit`.
+
+- Wenn Ihr Patch nicht begutachtet wird oder Sie eine bestimmte Person zur
+ Begutachtung bentigen, knnen Sie einem Gutachter mit @ antworten und um eine
+ Begutachtung des Pull Requests oder einen Kommentar bitten. Sie knnen auch
+ per [E-Mail](mailto:info@rustdesk.com) um eine Begutachtung bitten.
+
+- Fgen 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 hufig im [Discord](https://discord.gg/nDceKgxnkV).
diff --git a/docs/DEVCONTAINER-DE.md b/docs/DEVCONTAINER-DE.md
new file mode 100644
index 000000000..2a0d73f17
--- /dev/null
+++ b/docs/DEVCONTAINER-DE.md
@@ -0,0 +1,14 @@
+
+Nach dem Start von Dev-Container im Docker-Container wird ein Linux-Binrprogramm 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 ausgefhrt werden mssen, 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
+
diff --git a/docs/README-AR.md b/docs/README-AR.md
index ad7303806..4f5769839 100644
--- a/docs/README-AR.md
+++ b/docs/README-AR.md
@@ -5,7 +5,7 @@
Docker •
Structure •
Snapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [Tiếng Việt] | [Ελληνικά]
لغتك الأم, Doc و RustDesk UI, README نحن بحاجة إلى مساعدتك لترجمة هذا
diff --git a/docs/README-CS.md b/docs/README-CS.md
index d56464eff..74c6fcb19 100644
--- a/docs/README-CS.md
+++ b/docs/README-CS.md
@@ -5,7 +5,7 @@
Docker •
Struktura •
Ukázky
- [English] | [Українська] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, uživatelského rozhraní aplikace RustDesk a dokumentace k ní do vašeho jazyka
diff --git a/docs/README-DA.md b/docs/README-DA.md
index dde5c7a0d..d7283d8ab 100644
--- a/docs/README-DA.md
+++ b/docs/README-DA.md
@@ -5,7 +5,7 @@
Docker •
Filstruktur •
Skærmbilleder
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Vi har brug for din hjælp til at oversætte denne README, RustDesk UI og Dokument til dit modersmål
diff --git a/docs/README-DE.md b/docs/README-DE.md
index 8ee4a51fa..2c159bd07 100644
--- a/docs/README-DE.md
+++ b/docs/README-DE.md
@@ -5,21 +5,21 @@
Docker •
Dateistruktur •
Screenshots
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Dansk]
- Wir brauchen deine Hilfe, um dieses README, die RustDesk-Benutzeroberfläche und die Dokumentation in deine Muttersprache zu übersetzen.
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Dansk] | [Ελληνικά]
+ Wir brauchen Ihre Hilfe, um dieses README, die RustDesk-Benutzeroberfläche und die Dokumentation in Ihre Muttersprache zu übersetzen.
-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 `` 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 `` 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
diff --git a/docs/README-EO.md b/docs/README-EO.md
index 7471636eb..4bca4a793 100644
--- a/docs/README-EO.md
+++ b/docs/README-EO.md
@@ -5,7 +5,7 @@
Docker •
Strukturo •
Ekrankopio
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Ni bezonas helpon traduki tiun README kaj la interfacon al via denaska lingvo
@@ -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
diff --git a/docs/README-ES.md b/docs/README-ES.md
index 16f65adcc..66fc609fb 100644
--- a/docs/README-ES.md
+++ b/docs/README-ES.md
@@ -5,7 +5,7 @@
Docker •
Estructura •
Capturas de pantalla
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Necesitamos tu ayuda para traducir este README a tu idioma
@@ -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
diff --git a/docs/README-FA.md b/docs/README-FA.md
index 496e81849..177e3c122 100644
--- a/docs/README-FA.md
+++ b/docs/README-FA.md
@@ -6,7 +6,7 @@
ساخت •
سرور
-[English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+[English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
برای ترجمه این سند (README)، رابط کاربری RustDesk، و مستندات آن به زبان مادری شما به کمکتان نیازمندیم.
با ما گفتگو کنید: [Reddit](https://www.reddit.com/r/rustdesk) | [Twitter](https://twitter.com/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV)
diff --git a/docs/README-FI.md b/docs/README-FI.md
index f7a087087..8674bc1b3 100644
--- a/docs/README-FI.md
+++ b/docs/README-FI.md
@@ -5,7 +5,7 @@
Docker •
Rakenne •
Tilannevedos
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi
@@ -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
diff --git a/docs/README-FR.md b/docs/README-FR.md
index fdb253bd0..c11edc211 100644
--- a/docs/README-FR.md
+++ b/docs/README-FR.md
@@ -5,7 +5,7 @@
Docker -
Structure -
Images
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle.
diff --git a/docs/README-GR.md b/docs/README-GR.md
new file mode 100644
index 000000000..8ec98030d
--- /dev/null
+++ b/docs/README-GR.md
@@ -0,0 +1,219 @@
+
+ 
+ Διακομιστές •
+ Build •
+ Docker •
+ Δομή •
+ Στιγμιότυπα
+ [English] | [Українська] | [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Dansk]
+ Χρειαζόμαστε τη βοήθειά σας για να μεταφράσουμε αυτό το αρχείο README, το RustDesk UI και το Doc στη μητρική σας γλώσσα
+
+
+Επικοινωνήστε μαζί μας μέσω: [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)
+
+[
](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, μπορείτε να το κάνετε στο τέλος της εντολής με την χρήση ``. Για παράδειγμα, εάν επιθυμείτε να δημιουργήσετε μια βελτιστοποιημένη έκδοση της εφαρμογής, θα εκτελέσετε την παραπάνω εντολή ακολουθούμενη από το `--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
+
+## Στιγμιότυπα
+
+
+
+
+
+
+
+
diff --git a/docs/README-HU.md b/docs/README-HU.md
index 6c22a3b7c..9582cf1c6 100644
--- a/docs/README-HU.md
+++ b/docs/README-HU.md
@@ -5,7 +5,7 @@
Docker •
Struktúra •
Képernyőképek
- [English] | [Українська] | [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Kell a segítséged, hogy lefordítsuk ezt a README-t, a RustDesk UI-t és a Dokumentációt az anyanyelvedre
@@ -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
diff --git a/docs/README-ID.md b/docs/README-ID.md
index 9616cd31d..702966566 100644
--- a/docs/README-ID.md
+++ b/docs/README-ID.md
@@ -5,7 +5,7 @@
Docker •
Structure •
Snapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan RustDesk UI ke bahasa asli anda
@@ -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
diff --git a/docs/README-IT.md b/docs/README-IT.md
index f074510c9..2dec27e40 100644
--- a/docs/README-IT.md
+++ b/docs/README-IT.md
@@ -5,7 +5,7 @@
Docker •
Struttura •
Screenshots
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Abbiamo bisogno del tuo aiuto per tradurre questo README e la RustDesk UI nella tua lingua nativa
@@ -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
diff --git a/docs/README-JP.md b/docs/README-JP.md
index 36c74dfed..fafc5ef8c 100644
--- a/docs/README-JP.md
+++ b/docs/README-JP.md
@@ -5,7 +5,7 @@
Docker •
Structure •
Snapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
このREADMEをあなたの母国語に翻訳するために、あなたの助けが必要です。
diff --git a/docs/README-KR.md b/docs/README-KR.md
index 8cefbbcee..6f9ba2221 100644
--- a/docs/README-KR.md
+++ b/docs/README-KR.md
@@ -5,7 +5,7 @@
Docker •
Structure •
Snapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [العربي] | [Tiếng Việt] | [Ελληνικά]
README를 모국어로 번역하기 위한 당신의 도움의 필요합니다.
diff --git a/docs/README-ML.md b/docs/README-ML.md
index 288a78db8..5b4c3782a 100644
--- a/docs/README-ML.md
+++ b/docs/README-ML.md
@@ -5,7 +5,7 @@
Docker •
Structure •
Snapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
ഈ README നിങ്ങളുടെ മാതൃഭാഷയിലേക്ക് വിവർത്തനം ചെയ്യാൻ ഞങ്ങൾക്ക് നിങ്ങളുടെ സഹായം ആവശ്യമാണ്
diff --git a/docs/README-PL.md b/docs/README-PL.md
index 85c5f4a61..df8254f3d 100644
--- a/docs/README-PL.md
+++ b/docs/README-PL.md
@@ -5,7 +5,7 @@
Docker •
Struktura •
Snapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Potrzebujemy twojej pomocy w tłumaczeniu README na twój ojczysty język
@@ -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
diff --git a/docs/README-PTBR.md b/docs/README-PTBR.md
index f9d5e0fc3..491d53154 100644
--- a/docs/README-PTBR.md
+++ b/docs/README-PTBR.md
@@ -5,7 +5,7 @@
Docker •
Estrutura •
Screenshots
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Precisamos de sua ajuda para traduzir este README e a UI do RustDesk para sua língua nativa
diff --git a/docs/README-RU.md b/docs/README-RU.md
index 242341a6b..b050d40ac 100644
--- a/docs/README-RU.md
+++ b/docs/README-RU.md
@@ -5,7 +5,7 @@
Docker •
Structure •
Snapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Нам нужна ваша помощь для перевода этого README и RustDesk UI на ваш родной язык
diff --git a/docs/README-UA.md b/docs/README-UA.md
index 3615b9064..222da34d2 100644
--- a/docs/README-UA.md
+++ b/docs/README-UA.md
@@ -1,11 +1,11 @@

- Servers •
- Build •
- Docker •
- Structure •
- Snapshot
- [English] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ Сервери •
+ Складання •
+ Docker •
+ Структура •
+ Знімки
+ [English] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Нам потрібна ваша допомога для перекладу цього README і RustDesk UI на вашу рідну мову
@@ -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)
-[
](https://f-droid.org/en/packages/com.carriez.flutter_hbb)
+[
](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 веб клієнту
## Знімки
diff --git a/docs/README-VN.md b/docs/README-VN.md
index 295f54c6b..2f66d011d 100644
--- a/docs/README-VN.md
+++ b/docs/README-VN.md
@@ -5,7 +5,7 @@
Docker •
Cấu trúc tệp tin •
Snapshot
- [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [English] | [Українська] | [česky] | [中文] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Ελληνικά]
Chúng tôi cần sự gíup đỡ của bạn để dịch trang README này, RustDesk UI và tài liệu sang ngôn ngữ bản địa của bạn
diff --git a/docs/README-ZH.md b/docs/README-ZH.md
index 7ec87ec50..27c35ff57 100644
--- a/docs/README-ZH.md
+++ b/docs/README-ZH.md
@@ -5,7 +5,7 @@
Docker •
结构 •
截图
- [English] | [Українська] | [česky] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt]
+ [English] | [Українська] | [česky] | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي] | [Tiếng Việt] | [Ελληνικά]
Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk)
diff --git a/flatpak/rustdesk.json b/flatpak/rustdesk.json
index d7f6e316e..3cfca4d30 100644
--- a/flatpak/rustdesk.json
+++ b/flatpak/rustdesk.json
@@ -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"
]
-}
\ No newline at end of file
+}
diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml
index 9b25f4973..b3c655917 100644
--- a/flutter/android/app/src/main/AndroidManifest.xml
+++ b/flutter/android/app/src/main/AndroidManifest.xml
@@ -11,21 +11,25 @@
-
+
+
+ android:supportsRtl="true">
+ android:enabled="true"
+ android:exported="true">
+
+
+
@@ -52,8 +56,6 @@
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
-
-
@@ -61,6 +63,11 @@
+
+
-
+
\ No newline at end of file
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt
index 328701567..71bbba754 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt
@@ -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)
}
}
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
index fd340f7ed..52a5ff75e 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
@@ -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)
+ }
+ }
+ }
+ }
}
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
index cf8e12e92..1c3fbce6c 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
@@ -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(EXTRA_MP_DATA)?.let {
+
+ intent.getParcelableExtra(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)
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/PermissionRequestTransparentActivity.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/PermissionRequestTransparentActivity.kt
new file mode 100644
index 000000000..3beb7ec6b
--- /dev/null
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/PermissionRequestTransparentActivity.kt
@@ -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)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt
index 4bf244a06..f8ef07fd1 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt
@@ -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) {
diff --git a/flutter/android/app/src/main/res/values/styles.xml b/flutter/android/app/src/main/res/values/styles.xml
index d74aa35c2..146267c91 100644
--- a/flutter/android/app/src/main/res/values/styles.xml
+++ b/flutter/android/app/src/main/res/values/styles.xml
@@ -15,4 +15,12 @@
+
diff --git a/flutter/assets/transfer.svg b/flutter/assets/transfer.svg
new file mode 100644
index 000000000..24149bf58
--- /dev/null
+++ b/flutter/assets/transfer.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart
index ff373cc9c..21dc427cc 100644
--- a/flutter/lib/common.dart
+++ b/flutter/lib/common.dart
@@ -109,29 +109,41 @@ class IconFont {
class ColorThemeExtension extends ThemeExtension {
const ColorThemeExtension({
required this.border,
+ required this.border2,
required this.highlight,
+ required this.drag_indicator,
});
final Color? border;
+ final Color? border2;
final Color? highlight;
+ final Color? drag_indicator;
- static const light = ColorThemeExtension(
+ static final light = ColorThemeExtension(
border: Color(0xFFCCCCCC),
+ border2: Color(0xFFBBBBBB),
highlight: Color(0xFFE5E5E5),
+ drag_indicator: Colors.grey[800],
);
- static const dark = ColorThemeExtension(
+ static final dark = ColorThemeExtension(
border: Color(0xFF555555),
+ border2: Color(0xFFE5E5E5),
highlight: Color(0xFF3F3F3F),
+ drag_indicator: Colors.grey,
);
@override
ThemeExtension copyWith(
- {Color? border, Color? highlight}) {
+ {Color? border,
+ Color? border2,
+ Color? highlight,
+ Color? drag_indicator}) {
return ColorThemeExtension(
- border: border ?? this.border,
- highlight: highlight ?? this.highlight,
- );
+ border: border ?? this.border,
+ border2: border2 ?? this.border2,
+ highlight: highlight ?? this.highlight,
+ drag_indicator: drag_indicator ?? this.drag_indicator);
}
@override
@@ -142,7 +154,9 @@ class ColorThemeExtension extends ThemeExtension {
}
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),
);
}
}
@@ -150,8 +164,7 @@ class ColorThemeExtension extends ThemeExtension {
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 +180,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: InputDecorationTheme(
+ fillColor: grayBg,
+ filled: true,
+ isDense: true,
+ contentPadding: EdgeInsets.all(15),
+ border: UnderlineInputBorder(
+ borderRadius: BorderRadius.circular(18),
+ borderSide: BorderSide.none,
+ ),
+ ),
textTheme: const TextTheme(
titleLarge: TextStyle(fontSize: 19, color: Colors.black87),
titleSmall: TextStyle(fontSize: 14, color: Colors.black87),
@@ -175,7 +209,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 +220,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: >[
ColorThemeExtension.light,
@@ -203,6 +275,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: InputDecorationTheme(
+ fillColor: Color(0xFF24252B),
+ filled: true,
+ isDense: true,
+ contentPadding: EdgeInsets.all(15),
+ border: UnderlineInputBorder(
+ borderRadius: BorderRadius.circular(18),
+ borderSide: BorderSide.none,
+ ),
+ ),
textTheme: const TextTheme(
titleLarge: TextStyle(fontSize: 19),
titleSmall: TextStyle(fontSize: 14),
@@ -215,23 +308,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: >[
ColorThemeExtension.dark,
@@ -245,7 +384,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 +446,7 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom(
);
List 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 +468,7 @@ List supportedLocales = const [
Locale('vi'),
Locale('pl'),
Locale('kz'),
- Locale('en', 'US'),
+ Locale('es'),
];
String formatDurationToTime(Duration duration) {
@@ -456,7 +595,7 @@ class OverlayDialogManager {
BackButtonInterceptor.removeByName(dialogTag);
}
- dialog.entry = OverlayEntry(builder: (_) {
+ dialog.entry = OverlayEntry(builder: (context) {
bool innerClicked = false;
return Listener(
onPointerUp: (_) {
@@ -466,7 +605,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 +789,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,18 +818,19 @@ 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),
+ actionsAlignment: MainAxisAlignment.center,
),
);
}
@@ -820,7 +962,6 @@ Widget msgboxContent(String type, String title, String text) {
void msgBoxCommon(OverlayDialogManager dialogManager, String title,
Widget content, List buttons,
{bool hasCancel = true}) {
- dialogManager.dismissAll();
dialogManager.show((setState, close) => CustomAlertDialog(
title: Text(
translate(title),
@@ -903,21 +1044,14 @@ class AccessibilityListener extends StatelessWidget {
}
}
-class PermissionManager {
+class AndroidPermissionManager {
static Completer? _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 +1060,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 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();
- 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);
@@ -1453,10 +1589,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 +1616,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;
}
}
@@ -1706,28 +1844,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,
+ ),
+ );
}
}
diff --git a/flutter/lib/common/shared_state.dart b/flutter/lib/common/shared_state.dart
index ebac18dac..bc1a562b9 100644
--- a/flutter/lib/common/shared_state.dart
+++ b/flutter/lib/common/shared_state.dart
@@ -19,6 +19,8 @@ class PrivacyModeState {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
+ } else {
+ Get.find(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(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(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(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(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(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(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(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(tag: key).value = init_getter();
}
}
diff --git a/flutter/lib/common/widgets/chat_page.dart b/flutter/lib/common/widgets/chat_page.dart
index c1991633a..9460f4f41 100644
--- a/flutter/lib/common/widgets/chat_page.dart
+++ b/flutter/lib/common/widgets/chat_page.dart
@@ -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(
diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart
index cdce6f12a..83be522fa 100644
--- a/flutter/lib/common/widgets/dialog.dart
+++ b/flutter/lib/common/widgets/dialog.dart
@@ -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,7 @@ void changeIdDialog() {
TextField(
decoration: InputDecoration(
labelText: translate('Your new ID'),
- border: const OutlineInputBorder(),
+ border: isDesktop ? const OutlineInputBorder() : null,
errorText: msg.isEmpty ? null : translate(msg),
suffixText: '${rxId.value.length}/16',
suffixStyle: const TextStyle(fontSize: 12, color: Colors.grey)),
@@ -123,27 +126,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 +182,7 @@ void changeWhiteList({Function()? callback}) async {
child: TextField(
maxLines: null,
decoration: InputDecoration(
- border: const OutlineInputBorder(),
+ border: isDesktop ? const OutlineInputBorder() : null,
errorText: msg.isEmpty ? null : translate(msg),
),
controller: controller,
diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart
index ba7b8a059..c67f0f7fb 100644
--- a/flutter/lib/common/widgets/overlay.dart
+++ b/flutter/lib/common/widgets/overlay.dart
@@ -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: [
diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart
index a69fc3bbe..124057662 100644
--- a/flutter/lib/common/widgets/peer_card.dart
+++ b/flutter/lib/common/widgets/peer_card.dart
@@ -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(
+ var deco = Rx(
+ 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,67 @@ 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'),
+ border: isDesktop ? const OutlineInputBorder() : null,
+ 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'),
+ border: isDesktop ? const OutlineInputBorder() : null),
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'),
+ border:
+ isDesktop ? const OutlineInputBorder() : null,
suffixIcon: IconButton(
onPressed: () => secure.value = !secure.value,
icon: Icon(secure.value
@@ -1135,7 +1211,7 @@ void _rdpDialog(String id) async {
)),
),
],
- ).marginOnly(bottom: 8),
+ ).marginOnly(bottom: isDesktop ? 8 : 0),
],
),
),
diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart
index da7e37e6b..2d36d9150 100644
--- a/flutter/lib/common/widgets/peer_tab_page.dart
+++ b/flutter/lib/common/widgets/peer_tab_page.dart
@@ -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
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
if (tabIndex < entries.length) {
gFFI.peerTabModel.setCurrentTab(tabIndex);
entries[tabIndex].load();
+ _hideSort = tabIndex == 0;
}
}
@@ -95,22 +100,27 @@ class _PeerTabPageState extends State
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
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
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 {
);
}
}
+
+class PeerSortDropdown extends StatefulWidget {
+ const PeerSortDropdown({super.key});
+
+ @override
+ State createState() => _PeerSortDropdownState();
+}
+
+class _PeerSortDropdownState extends State {
+ @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(
+ 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(
+ alignment: Alignment.center,
+ child: Text(
+ translate("Sort by"),
+ style: TextStyle(fontWeight: FontWeight.bold),
+ ),
+ enabled: false,
+ ),
+ ...translated_text
+ .map>(
+ (String value) => DropdownMenuItem(
+ 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(),
+ ]),
+ ),
+ );
+ }
+}
diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart
index 9c98f24b8..43f65b4b4 100644
--- a/flutter/lib/common/widgets/peers_view.dart
+++ b/flutter/lib/common/widgets/peers_view.dart
@@ -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 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,18 @@ class _PeersView extends StatefulWidget {
/// State for the peer widget.
class _PeersViewState extends State<_PeersView> with WindowListener {
static const int _maxQueryCount = 3;
+ final HashMap _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 = {};
var _lastChangeTime = DateTime.now();
var _lastQueryPeers = {};
var _lastQueryTime = DateTime.now().subtract(const Duration(hours: 1));
- var _queryCoun = 0;
+ var _queryCount = 0;
var _exit = false;
late final mobileWidth = () {
@@ -78,12 +112,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 +125,48 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
return ChangeNotifierProvider(
create: (context) => widget.peers,
child: Consumer(
- 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
+ ? 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((searchText) {
+ final body = ObxValue((filters) {
return FutureBuilder>(
builder: (context, snapshot) {
if (snapshot.hasData) {
@@ -109,16 +174,8 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
final cards = [];
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 +196,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 +206,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 +216,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 +237,40 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
}();
}
- Future>? matchPeers(String searchText, List peers) async {
+ Future>? matchPeers(
+ String searchText, String sortedBy, List 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 +284,7 @@ class _PeersViewState extends State<_PeersView> with WindowListener {
filteredList.add(peers[i]);
}
}
+
return filteredList;
}
}
@@ -232,7 +320,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 +342,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 +364,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 +389,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(
diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart
index 537784918..e2a3c6f0b 100644
--- a/flutter/lib/consts.dart
+++ b/flutter/lib/consts.dart
@@ -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 logicalKeyMap = {
diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart
index 4aad66eee..30fe07b95 100644
--- a/flutter/lib/desktop/pages/connection_page.dart
+++ b/flutter/lib/desktop/pages/connection_page.dart
@@ -151,10 +151,7 @@ class _ConnectionPageState extends State
/// 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
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: [
@@ -203,6 +199,7 @@ class _ConnectionPageState extends State
cursorColor:
Theme.of(context).textTheme.titleLarge?.color,
decoration: InputDecoration(
+ filled: false,
counterText: '',
hintText: _idInputFocused.value
? null
diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart
index dfa5762b0..541ec6aa8 100644
--- a/flutter/lib/desktop/pages/desktop_home_page.dart
+++ b/flutter/lib/desktop/pages/desktop_home_page.dart
@@ -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
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
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
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
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
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,
diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart
index e041b591d..4609d4f47 100644
--- a/flutter/lib/desktop/pages/desktop_setting_page.dart
+++ b/flutter/lib/desktop/pages/desktop_setting_page.dart
@@ -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
],
),
),
- 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,
@@ -1722,7 +1729,6 @@ class _ComboBox extends StatelessWidget {
required this.values,
required this.initialKey,
required this.onChanged,
- // ignore: unused_element
this.enabled = true,
}) : super(key: key);
@@ -1735,7 +1741,12 @@ class _ComboBox extends StatelessWidget {
var ref = values[index].obs;
current = keys[index];
return Container(
- decoration: BoxDecoration(border: Border.all(color: MyTheme.border)),
+ decoration: BoxDecoration(
+ border: Border.all(
+ color: enabled
+ ? MyTheme.color(context).border2 ?? MyTheme.border
+ : MyTheme.border,
+ )),
height: 30,
child: Obx(() => DropdownButton(
isExpanded: true,
@@ -1744,6 +1755,10 @@ class _ComboBox extends StatelessWidget {
underline: Container(
height: 25,
),
+ style: TextStyle(
+ color: enabled
+ ? Theme.of(context).textTheme.titleMedium?.color
+ : _disabledTextColor(context, enabled)),
icon: const Icon(
Icons.expand_more_sharp,
size: 20,
diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart
index 053a2d8a2..4a1a40242 100644
--- a/flutter/lib/desktop/pages/desktop_tab_page.dart
+++ b/flutter/lib/desktop/pages/desktop_tab_page.dart
@@ -75,7 +75,7 @@ class _DesktopTabPageState extends State {
isClose: false,
),
)));
- return Platform.isMacOS
+ return Platform.isMacOS || kUseCompatibleUiMode
? tabWidget
: Obx(
() => DragToResizeArea(
diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart
index c8cb7c935..44def46c2 100644
--- a/flutter/lib/desktop/pages/file_manager_page.dart
+++ b/flutter/lib/desktop/pages/file_manager_page.dart
@@ -15,7 +15,6 @@ import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/file_model.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
-import 'package:provider/provider.dart';
import 'package:wakelock/wakelock.dart';
import '../../consts.dart';
@@ -61,52 +60,15 @@ class FileManagerPage extends StatefulWidget {
class _FileManagerPageState extends State
with AutomaticKeepAliveClientMixin {
- final _localSelectedItems = SelectedItems();
- final _remoteSelectedItems = SelectedItems();
-
- final _locationStatusLocal = LocationStatus.bread.obs;
- final _locationStatusRemote = LocationStatus.bread.obs;
- final _locationNodeLocal = FocusNode(debugLabel: "locationNodeLocal");
- final _locationNodeRemote = FocusNode(debugLabel: "locationNodeRemote");
- final _locationBarKeyLocal = GlobalKey(debugLabel: "locationBarKeyLocal");
- final _locationBarKeyRemote = GlobalKey(debugLabel: "locationBarKeyRemote");
- final _searchTextLocal = "".obs;
- final _searchTextRemote = "".obs;
- final _breadCrumbScrollerLocal = ScrollController();
- final _breadCrumbScrollerRemote = ScrollController();
final _mouseFocusScope = Rx(MouseFocusScope.none);
- final _keyboardNodeLocal = FocusNode(debugLabel: "keyboardNodeLocal");
- final _keyboardNodeRemote = FocusNode(debugLabel: "keyboardNodeRemote");
- final _listSearchBufferLocal = TimeoutStringBuffer();
- final _listSearchBufferRemote = TimeoutStringBuffer();
- final _nameColWidthLocal = kDesktopFileTransferNameColWidth.obs;
- final _modifiedColWidthLocal = kDesktopFileTransferModifiedColWidth.obs;
- final _nameColWidthRemote = kDesktopFileTransferNameColWidth.obs;
- final _modifiedColWidthRemote = kDesktopFileTransferModifiedColWidth.obs;
-
- /// [_lastClickTime], [_lastClickEntry] help to handle double click
- int _lastClickTime =
- DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000;
- Entry? _lastClickEntry;
final _dropMaskVisible = false.obs; // TODO impl drop mask
final _overlayKeyState = OverlayKeyState();
- ScrollController getBreadCrumbScrollController(bool isLocal) {
- return isLocal ? _breadCrumbScrollerLocal : _breadCrumbScrollerRemote;
- }
-
- GlobalKey getLocationBarKey(bool isLocal) {
- return isLocal ? _locationBarKeyLocal : _locationBarKeyRemote;
- }
-
late FFI _ffi;
FileModel get model => _ffi.fileModel;
-
- SelectedItems getSelectedItems(bool isLocal) {
- return isLocal ? _localSelectedItems : _remoteSelectedItems;
- }
+ JobController get jobController => model.jobController;
@override
void initState() {
@@ -122,613 +84,353 @@ class _FileManagerPageState extends State
Wakelock.enable();
}
debugPrint("File manager page init success with id ${widget.id}");
- model.onDirChanged = breadCrumbScrollToEnd;
- // register location listener
- _locationNodeLocal.addListener(onLocalLocationFocusChanged);
- _locationNodeRemote.addListener(onRemoteLocationFocusChanged);
_ffi.dialogManager.setOverlayState(_overlayKeyState);
}
@override
void dispose() {
- model.onClose().whenComplete(() {
+ model.close().whenComplete(() {
_ffi.close();
_ffi.dialogManager.dismissAll();
if (!Platform.isLinux) {
Wakelock.disable();
}
Get.delete(tag: 'ft_${widget.id}');
- _locationNodeLocal.removeListener(onLocalLocationFocusChanged);
- _locationNodeRemote.removeListener(onRemoteLocationFocusChanged);
- _locationNodeLocal.dispose();
- _locationNodeRemote.dispose();
});
super.dispose();
}
+ @override
+ bool get wantKeepAlive => true;
+
@override
Widget build(BuildContext context) {
super.build(context);
return Overlay(key: _overlayKeyState.key, initialEntries: [
OverlayEntry(builder: (_) {
- return ChangeNotifierProvider.value(
- value: _ffi.fileModel,
- child: Consumer(builder: (context, model, child) {
- return Scaffold(
- backgroundColor: Theme.of(context).scaffoldBackgroundColor,
- body: Row(
- children: [
- Flexible(flex: 3, child: body(isLocal: true)),
- Flexible(flex: 3, child: body(isLocal: false)),
- Flexible(flex: 2, child: statusList())
- ],
- ),
- );
- }));
+ return Scaffold(
+ backgroundColor: Theme.of(context).scaffoldBackgroundColor,
+ body: Row(
+ children: [
+ Flexible(
+ flex: 3,
+ child: dropArea(FileManagerView(
+ model.localController, _ffi, _mouseFocusScope))),
+ Flexible(
+ flex: 3,
+ child: dropArea(FileManagerView(
+ model.remoteController, _ffi, _mouseFocusScope))),
+ Flexible(flex: 2, child: statusList())
+ ],
+ ),
+ );
})
]);
}
- Widget menu({bool isLocal = false}) {
- var menuPos = RelativeRect.fill;
-
- final List> items = [
- MenuEntrySwitch(
- switchType: SwitchType.scheckbox,
- text: translate("Show Hidden Files"),
- getter: () async {
- return model.getCurrentShowHidden(isLocal);
- },
- setter: (bool v) async {
- model.toggleShowHidden(local: isLocal);
- },
- padding: kDesktopMenuPadding,
- dismissOnClicked: true,
- ),
- MenuEntryButton(
- childBuilder: (style) => Text(translate("Select All"), style: style),
- proc: () => setState(() => getSelectedItems(isLocal)
- .selectAll(model.getCurrentDir(isLocal).entries)),
- padding: kDesktopMenuPadding,
- dismissOnClicked: true),
- MenuEntryButton(
- childBuilder: (style) =>
- Text(translate("Unselect All"), style: style),
- proc: () => setState(() => getSelectedItems(isLocal).clear()),
- padding: kDesktopMenuPadding,
- dismissOnClicked: true)
- ];
-
- return Listener(
- onPointerDown: (e) {
- final x = e.position.dx;
- final y = e.position.dy;
- menuPos = RelativeRect.fromLTRB(x, y, x, y);
- },
- child: MenuButton(
- onPressed: () => mod_menu.showMenu(
- context: context,
- position: menuPos,
- items: items
- .map(
- (e) => e.build(
- context,
- MenuConfig(
- commonColor: CustomPopupMenuTheme.commonColor,
- height: CustomPopupMenuTheme.height,
- dividerHeight: CustomPopupMenuTheme.dividerHeight),
- ),
- )
- .expand((i) => i)
- .toList(),
- elevation: 8,
- ),
- child: SvgPicture.asset(
- "assets/dots.svg",
- color: Theme.of(context).tabBarTheme.labelColor,
- ),
- color: Theme.of(context).cardColor,
- hoverColor: Theme.of(context).hoverColor,
- ),
- );
- }
-
- Widget body({bool isLocal = false}) {
- final scrollController = ScrollController();
- return Container(
- margin: const EdgeInsets.all(16.0),
- padding: const EdgeInsets.all(8.0),
- child: DropTarget(
- onDragDone: (detail) => handleDragDone(detail, isLocal),
+ Widget dropArea(FileManagerView fileView) {
+ return DropTarget(
+ onDragDone: (detail) =>
+ handleDragDone(detail, fileView.controller.isLocal),
onDragEntered: (enter) {
_dropMaskVisible.value = true;
},
onDragExited: (exit) {
_dropMaskVisible.value = false;
},
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- headTools(isLocal),
- Expanded(
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Expanded(
- child: _buildFileList(context, isLocal, scrollController),
- )
- ],
- ),
- ),
- ],
+ child: fileView);
+ }
+
+ Widget generateCard(Widget child) {
+ return Container(
+ decoration: BoxDecoration(
+ color: Theme.of(context).cardColor,
+ borderRadius: BorderRadius.all(
+ Radius.circular(15.0),
),
),
+ child: child,
);
}
- Widget _buildFileList(
- BuildContext context, bool isLocal, ScrollController scrollController) {
- final fd = model.getCurrentDir(isLocal);
- final entries = fd.entries;
- final selectedEntries = getSelectedItems(isLocal);
-
- return MouseRegion(
- onEnter: (evt) {
- _mouseFocusScope.value =
- isLocal ? MouseFocusScope.local : MouseFocusScope.remote;
- if (isLocal) {
- _keyboardNodeLocal.requestFocus();
- } else {
- _keyboardNodeRemote.requestFocus();
- }
- },
- onExit: (evt) {
- _mouseFocusScope.value = MouseFocusScope.none;
- },
- child: ListSearchActionListener(
- node: isLocal ? _keyboardNodeLocal : _keyboardNodeRemote,
- buffer: isLocal ? _listSearchBufferLocal : _listSearchBufferRemote,
- onNext: (buffer) {
- debugPrint("searching next for $buffer");
- assert(buffer.length == 1);
- assert(selectedEntries.length <= 1);
- var skipCount = 0;
- if (selectedEntries.items.isNotEmpty) {
- final index = entries.indexOf(selectedEntries.items.first);
- if (index < 0) {
- return;
- }
- skipCount = index + 1;
- }
- var searchResult = entries
- .skip(skipCount)
- .where((element) => element.name.toLowerCase().startsWith(buffer));
- if (searchResult.isEmpty) {
- // cannot find next, lets restart search from head
- debugPrint("restart search from head");
- searchResult =
- entries.where((element) => element.name.toLowerCase().startsWith(buffer));
- }
- if (searchResult.isEmpty) {
- setState(() {
- getSelectedItems(isLocal).clear();
- });
- return;
- }
- _jumpToEntry(isLocal, searchResult.first, scrollController,
- kDesktopFileTransferRowHeight);
- },
- onSearch: (buffer) {
- debugPrint("searching for $buffer");
- final selectedEntries = getSelectedItems(isLocal);
- final searchResult =
- entries.where((element) => element.name.toLowerCase().startsWith(buffer));
- selectedEntries.clear();
- if (searchResult.isEmpty) {
- setState(() {
- getSelectedItems(isLocal).clear();
- });
- return;
- }
- _jumpToEntry(isLocal, searchResult.first, scrollController,
- kDesktopFileTransferRowHeight);
- },
- child: ObxValue(
- (searchText) {
- final filteredEntries = searchText.isNotEmpty
- ? entries.where((element) {
- return element.name.contains(searchText.value);
- }).toList(growable: false)
- : entries;
- final rows = filteredEntries.map((entry) {
- final sizeStr =
- entry.isFile ? readableFileSize(entry.size.toDouble()) : "";
- final lastModifiedStr = entry.isDrive
- ? " "
- : "${entry.lastModified().toString().replaceAll(".000", "")} ";
- final isSelected = selectedEntries.contains(entry);
- return Padding(
- padding: EdgeInsets.symmetric(vertical: 1),
- child: Container(
- decoration: BoxDecoration(
- color: isSelected
- ? Theme.of(context).hoverColor
- : Theme.of(context).cardColor,
- borderRadius: BorderRadius.all(
- Radius.circular(5.0),
- ),
- ),
- key: ValueKey(entry.name),
- height: kDesktopFileTransferRowHeight,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.spaceAround,
- children: [
- Expanded(
- child: InkWell(
- child: Row(
- children: [
- GestureDetector(
- child: Obx(
- () => Container(
- width: isLocal
- ? _nameColWidthLocal.value
- : _nameColWidthRemote.value,
- child: Tooltip(
- waitDuration:
- Duration(milliseconds: 500),
- message: entry.name,
- child: Row(children: [
- entry.isDrive
- ? Image(
- image: iconHardDrive,
- fit: BoxFit.scaleDown,
- color: Theme.of(context)
- .iconTheme
- .color
- ?.withOpacity(0.7))
- .paddingAll(4)
- : SvgPicture.asset(
- entry.isFile
- ? "assets/file.svg"
- : "assets/folder.svg",
- color: Theme.of(context)
- .tabBarTheme
- .labelColor,
- ),
- Expanded(
- child: Text(
- entry.name.nonBreaking,
- overflow:
- TextOverflow.ellipsis))
- ]),
- )),
- ),
- onTap: () {
- final items = getSelectedItems(isLocal);
- // handle double click
- if (_checkDoubleClick(entry)) {
- openDirectory(entry.path,
- isLocal: isLocal);
- items.clear();
- return;
- }
- _onSelectedChanged(
- items, filteredEntries, entry, isLocal);
- },
- ),
- SizedBox(
- width: 2.0,
- ),
- GestureDetector(
- child: Obx(
- () => SizedBox(
- width: isLocal
- ? _modifiedColWidthLocal.value
- : _modifiedColWidthRemote.value,
- child: Tooltip(
- waitDuration:
- Duration(milliseconds: 500),
- message: lastModifiedStr,
- child: Text(
- lastModifiedStr,
- style: TextStyle(
- fontSize: 12,
- color: MyTheme.darkGray,
- ),
- )),
- ),
- ),
- ),
- // Divider from header.
- SizedBox(
- width: 2.0,
- ),
- Expanded(
- // width: 100,
- child: GestureDetector(
- child: Tooltip(
- waitDuration: Duration(milliseconds: 500),
- message: sizeStr,
- child: Text(
- sizeStr,
- overflow: TextOverflow.ellipsis,
- style: TextStyle(
- fontSize: 10,
- color: MyTheme.darkGray),
- ),
- ),
- ),
- ),
- ],
- ),
- ),
- ),
- ],
- )),
- );
- }).toList(growable: false);
-
- return Column(
- children: [
- // Header
- Row(
- children: [
- Expanded(child: _buildFileBrowserHeader(context, isLocal)),
- ],
- ),
- // Body
- Expanded(
- child: ListView.builder(
- controller: scrollController,
- itemExtent: kDesktopFileTransferRowHeight,
- itemBuilder: (context, index) {
- return rows[index];
- },
- itemCount: rows.length,
- ),
- ),
- ],
- );
- },
- isLocal ? _searchTextLocal : _searchTextRemote,
- ),
- ),
- );
- }
-
- void _jumpToEntry(bool isLocal, Entry entry,
- ScrollController scrollController, double rowHeight) {
- final entries = model.getCurrentDir(isLocal).entries;
- final index = entries.indexOf(entry);
- if (index == -1) {
- debugPrint("entry is not valid: ${entry.path}");
- }
- final selectedEntries = getSelectedItems(isLocal);
- final searchResult =
- entries.where((element) => element == entry);
- selectedEntries.clear();
- if (searchResult.isEmpty) {
- return;
- }
- final offset = min(
- max(scrollController.position.minScrollExtent,
- entries.indexOf(searchResult.first) * rowHeight),
- scrollController.position.maxScrollExtent);
- scrollController.jumpTo(offset);
- setState(() {
- selectedEntries.add(isLocal, searchResult.first);
- debugPrint("focused on ${searchResult.first.name}");
- });
- }
-
- void _onSelectedChanged(SelectedItems selectedItems, List entries,
- Entry entry, bool isLocal) {
- final isCtrlDown = RawKeyboard.instance.keysPressed
- .contains(LogicalKeyboardKey.controlLeft);
- final isShiftDown =
- RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft);
- if (isCtrlDown) {
- if (selectedItems.contains(entry)) {
- selectedItems.remove(entry);
- } else {
- selectedItems.add(isLocal, entry);
- }
- } else if (isShiftDown) {
- final List indexGroup = [];
- for (var selected in selectedItems.items) {
- indexGroup.add(entries.indexOf(selected));
- }
- indexGroup.add(entries.indexOf(entry));
- indexGroup.removeWhere((e) => e == -1);
- final maxIndex = indexGroup.reduce(max);
- final minIndex = indexGroup.reduce(min);
- selectedItems.clear();
- entries
- .getRange(minIndex, maxIndex + 1)
- .forEach((e) => selectedItems.add(isLocal, e));
- } else {
- selectedItems.clear();
- selectedItems.add(isLocal, entry);
- }
- setState(() {});
- }
-
- bool _checkDoubleClick(Entry entry) {
- final current = DateTime.now().millisecondsSinceEpoch;
- final elapsed = current - _lastClickTime;
- _lastClickTime = current;
- if (_lastClickEntry == entry) {
- if (elapsed < bind.getDoubleClickTime()) {
- return true;
- }
- } else {
- _lastClickEntry = entry;
- }
- return false;
- }
-
/// transfer status list
/// watch transfer status
Widget statusList() {
- return PreferredSize(
- preferredSize: const Size(200, double.infinity),
- child: model.jobTable.isEmpty
- ? Center(child: Text(translate("Empty")))
- : Container(
- margin:
- const EdgeInsets.only(top: 16.0, bottom: 16.0, right: 16.0),
- padding: const EdgeInsets.all(8.0),
- child: Obx(
- () => ListView.builder(
- controller: ScrollController(),
- itemBuilder: (BuildContext context, int index) {
- final item = model.jobTable[index];
- return Padding(
- padding: const EdgeInsets.only(bottom: 5),
- child: Container(
- decoration: BoxDecoration(
- color: Theme.of(context).cardColor,
- borderRadius: BorderRadius.all(
- Radius.circular(15.0),
- ),
+ statusListView(List jobs) => ListView.builder(
+ controller: ScrollController(),
+ itemBuilder: (BuildContext context, int index) {
+ final item = jobs[index];
+ return Padding(
+ padding: const EdgeInsets.only(bottom: 5),
+ child: generateCard(
+ Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Transform.rotate(
+ angle: item.isRemoteToLocal ? pi : 0,
+ child: SvgPicture.asset(
+ "assets/arrow.svg",
+ color: Theme.of(context).tabBarTheme.labelColor,
),
+ ).paddingOnly(left: 15),
+ const SizedBox(
+ width: 16.0,
+ ),
+ Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Transform.rotate(
- angle: item.isRemote ? pi : 0,
- child: SvgPicture.asset(
- "assets/arrow.svg",
- color: Theme.of(context)
- .tabBarTheme
- .labelColor,
- ),
- ).paddingOnly(left: 15),
- const SizedBox(
- width: 16.0,
+ Tooltip(
+ waitDuration: Duration(milliseconds: 500),
+ message: item.jobName,
+ child: Text(
+ item.fileName,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ).paddingSymmetric(vertical: 10),
+ ),
+ Text(
+ '${translate("Total")} ${readableFileSize(item.totalSize.toDouble())}',
+ style: TextStyle(
+ fontSize: 12,
+ color: MyTheme.darkGray,
+ ),
+ ),
+ Offstage(
+ offstage: item.state != JobState.inProgress,
+ child: Text(
+ '${translate("Speed")} ${readableFileSize(item.speed)}/s',
+ style: TextStyle(
+ fontSize: 12,
+ color: MyTheme.darkGray,
),
- Expanded(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment:
- CrossAxisAlignment.start,
- children: [
- Tooltip(
- waitDuration:
- Duration(milliseconds: 500),
- message: item.jobName,
- child: Text(
- item.jobName,
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ).paddingSymmetric(vertical: 10),
- ),
- Text(
- '${translate("Total")} ${readableFileSize(item.totalSize.toDouble())}',
- style: TextStyle(
- fontSize: 12,
- color: MyTheme.darkGray,
- ),
- ),
- Offstage(
- offstage:
- item.state != JobState.inProgress,
- child: Text(
- '${translate("Speed")} ${readableFileSize(item.speed)}/s',
- style: TextStyle(
- fontSize: 12,
- color: MyTheme.darkGray,
- ),
- ),
- ),
- Offstage(
- offstage:
- item.state == JobState.inProgress,
- child: Text(
- translate(
- item.display(),
- ),
- style: TextStyle(
- fontSize: 12,
- color: MyTheme.darkGray,
- ),
- ),
- ),
- Offstage(
- offstage:
- item.state != JobState.inProgress,
- child: LinearPercentIndicator(
- padding: EdgeInsets.only(right: 15),
- animateFromLastPercent: true,
- center: Text(
- '${(item.finishedSize / item.totalSize * 100).toStringAsFixed(0)}%',
- ),
- barRadius: Radius.circular(15),
- percent: item.finishedSize /
- item.totalSize,
- progressColor: MyTheme.accent,
- backgroundColor:
- Theme.of(context).hoverColor,
- lineHeight:
- kDesktopFileTransferRowHeight,
- ).paddingSymmetric(vertical: 15),
- ),
- ],
- ),
+ ),
+ ),
+ Offstage(
+ offstage: item.state == JobState.inProgress,
+ child: Text(
+ translate(
+ item.display(),
),
- Row(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- Offstage(
- offstage: item.state != JobState.paused,
- child: MenuButton(
- onPressed: () {
- model.resumeJob(item.id);
- },
- child: SvgPicture.asset(
- "assets/refresh.svg",
- color: Colors.white,
- ),
- color: MyTheme.accent,
- hoverColor: MyTheme.accent80,
- ),
- ),
- MenuButton(
- padding: EdgeInsets.only(right: 15),
- child: SvgPicture.asset(
- "assets/close.svg",
- color: Colors.white,
- ),
- onPressed: () {
- model.jobTable.removeAt(index);
- model.cancelJob(item.id);
- },
- color: MyTheme.accent,
- hoverColor: MyTheme.accent80,
- ),
- ],
+ style: TextStyle(
+ fontSize: 12,
+ color: MyTheme.darkGray,
),
- ],
+ ),
+ ),
+ Offstage(
+ offstage: item.state != JobState.inProgress,
+ child: LinearPercentIndicator(
+ padding: EdgeInsets.only(right: 15),
+ animateFromLastPercent: true,
+ center: Text(
+ '${(item.finishedSize / item.totalSize * 100).toStringAsFixed(0)}%',
+ ),
+ barRadius: Radius.circular(15),
+ percent: item.finishedSize / item.totalSize,
+ progressColor: MyTheme.accent,
+ backgroundColor: Theme.of(context).hoverColor,
+ lineHeight: kDesktopFileTransferRowHeight,
+ ).paddingSymmetric(vertical: 15),
),
],
- ).paddingSymmetric(vertical: 10),
+ ),
),
- );
- },
- itemCount: model.jobTable.length,
- ),
- ),
- ));
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Offstage(
+ offstage: item.state != JobState.paused,
+ child: MenuButton(
+ onPressed: () {
+ jobController.resumeJob(item.id);
+ },
+ child: SvgPicture.asset(
+ "assets/refresh.svg",
+ color: Colors.white,
+ ),
+ color: MyTheme.accent,
+ hoverColor: MyTheme.accent80,
+ ),
+ ),
+ MenuButton(
+ padding: EdgeInsets.only(right: 15),
+ child: SvgPicture.asset(
+ "assets/close.svg",
+ color: Colors.white,
+ ),
+ onPressed: () {
+ jobController.jobTable.removeAt(index);
+ jobController.cancelJob(item.id);
+ },
+ color: MyTheme.accent,
+ hoverColor: MyTheme.accent80,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ).paddingSymmetric(vertical: 10),
+ ),
+ );
+ },
+ itemCount: jobController.jobTable.length,
+ );
+
+ return PreferredSize(
+ preferredSize: const Size(200, double.infinity),
+ child: Container(
+ margin: const EdgeInsets.only(top: 16.0, bottom: 16.0, right: 16.0),
+ padding: const EdgeInsets.all(8.0),
+ child: Obx(
+ () => jobController.jobTable.isEmpty
+ ? generateCard(
+ Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset(
+ "assets/transfer.svg",
+ color: Theme.of(context).tabBarTheme.labelColor,
+ height: 40,
+ ).paddingOnly(bottom: 10),
+ Text(
+ translate("No transfers in progress"),
+ textAlign: TextAlign.center,
+ textScaleFactor: 1.20,
+ style: TextStyle(
+ color:
+ Theme.of(context).tabBarTheme.labelColor),
+ ),
+ ],
+ ),
+ ),
+ )
+ : statusListView(jobController.jobTable),
+ )),
+ );
}
- Widget headTools(bool isLocal) {
- final locationStatus =
- isLocal ? _locationStatusLocal : _locationStatusRemote;
- final locationFocus = isLocal ? _locationNodeLocal : _locationNodeRemote;
- final selectedItems = getSelectedItems(isLocal);
+ void handleDragDone(DropDoneDetails details, bool isLocal) {
+ if (isLocal) {
+ // ignore local
+ return;
+ }
+ final items = SelectedItems(isLocal: false);
+ for (var file in details.files) {
+ final f = File(file.path);
+ items.add(Entry()
+ ..path = file.path
+ ..name = file.name
+ ..size = FileSystemEntity.isDirectorySync(f.path) ? 0 : f.lengthSync());
+ }
+ final otherSideData = model.localController.directoryData();
+ model.remoteController.sendFiles(items, otherSideData);
+ }
+}
+
+class FileManagerView extends StatefulWidget {
+ final FileController controller;
+ final FFI _ffi;
+ final Rx _mouseFocusScope;
+
+ FileManagerView(this.controller, this._ffi, this._mouseFocusScope);
+
+ @override
+ State createState() => _FileManagerViewState();
+}
+
+class _FileManagerViewState extends State {
+ final _locationStatus = LocationStatus.bread.obs;
+ final _locationNode = FocusNode();
+ final _locationBarKey = GlobalKey();
+ final _searchText = "".obs;
+ final _breadCrumbScroller = ScrollController();
+ final _keyboardNode = FocusNode();
+ final _listSearchBuffer = TimeoutStringBuffer();
+ final _nameColWidth = kDesktopFileTransferNameColWidth.obs;
+ final _modifiedColWidth = kDesktopFileTransferModifiedColWidth.obs;
+ final _fileListScrollController = ScrollController();
+
+ /// [_lastClickTime], [_lastClickEntry] help to handle double click
+ var _lastClickTime =
+ DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000;
+ Entry? _lastClickEntry;
+
+ FileController get controller => widget.controller;
+ bool get isLocal => widget.controller.isLocal;
+ FFI get _ffi => widget._ffi;
+ SelectedItems get selectedItems => controller.selectedItems;
+
+ @override
+ void initState() {
+ super.initState();
+ // register location listener
+ _locationNode.addListener(onLocationFocusChanged);
+ controller.directory.listen((e) => breadCrumbScrollToEnd());
+ }
+
+ @override
+ void dispose() {
+ _locationNode.removeListener(onLocationFocusChanged);
+ _locationNode.dispose();
+ _keyboardNode.dispose();
+ _breadCrumbScroller.dispose();
+ _fileListScrollController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: const EdgeInsets.all(16.0),
+ padding: const EdgeInsets.all(8.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ headTools(),
+ Expanded(
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Expanded(
+ child: MouseRegion(
+ onEnter: (evt) {
+ widget._mouseFocusScope.value = isLocal
+ ? MouseFocusScope.local
+ : MouseFocusScope.remote;
+ _keyboardNode.requestFocus();
+ },
+ onExit: (evt) =>
+ widget._mouseFocusScope.value = MouseFocusScope.none,
+ child: _buildFileList(context, _fileListScrollController),
+ ))
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ void onLocationFocusChanged() {
+ debugPrint("focus changed on local");
+ if (_locationNode.hasFocus) {
+ // ignore
+ } else {
+ // lost focus, change to bread
+ if (_locationStatus.value != LocationStatus.fileSearchBar) {
+ _locationStatus.value = LocationStatus.bread;
+ }
+ }
+ }
+
+ Widget headTools() {
return Container(
child: Column(
children: [
@@ -788,7 +490,7 @@ class _FileManagerPageState extends State
hoverColor: Theme.of(context).hoverColor,
onPressed: () {
selectedItems.clear();
- model.goBack(isLocal: isLocal);
+ controller.goBack();
},
),
MenuButton(
@@ -803,7 +505,7 @@ class _FileManagerPageState extends State
hoverColor: Theme.of(context).hoverColor,
onPressed: () {
selectedItems.clear();
- model.goToParentDirectory(isLocal: isLocal);
+ controller.goToParentDirectory();
},
),
],
@@ -822,14 +524,14 @@ class _FileManagerPageState extends State
padding: EdgeInsets.symmetric(vertical: 2.5),
child: GestureDetector(
onTap: () {
- locationStatus.value =
- locationStatus.value == LocationStatus.bread
+ _locationStatus.value =
+ _locationStatus.value == LocationStatus.bread
? LocationStatus.pathLocation
: LocationStatus.bread;
Future.delayed(Duration.zero, () {
- if (locationStatus.value ==
+ if (_locationStatus.value ==
LocationStatus.pathLocation) {
- locationFocus.requestFocus();
+ _locationNode.requestFocus();
}
});
},
@@ -838,10 +540,10 @@ class _FileManagerPageState extends State
child: Row(
children: [
Expanded(
- child: locationStatus.value ==
+ child: _locationStatus.value ==
LocationStatus.bread
- ? buildBread(isLocal)
- : buildPathLocation(isLocal)),
+ ? buildBread()
+ : buildPathLocation()),
],
),
),
@@ -852,15 +554,13 @@ class _FileManagerPageState extends State
),
),
Obx(() {
- switch (locationStatus.value) {
+ switch (_locationStatus.value) {
case LocationStatus.bread:
return MenuButton(
onPressed: () {
- locationStatus.value = LocationStatus.fileSearchBar;
- final focusNode =
- isLocal ? _locationNodeLocal : _locationNodeRemote;
+ _locationStatus.value = LocationStatus.fileSearchBar;
Future.delayed(
- Duration.zero, () => focusNode.requestFocus());
+ Duration.zero, () => _locationNode.requestFocus());
},
child: SvgPicture.asset(
"assets/search.svg",
@@ -883,7 +583,7 @@ class _FileManagerPageState extends State
return MenuButton(
onPressed: () {
onSearchText("", isLocal);
- locationStatus.value = LocationStatus.bread;
+ _locationStatus.value = LocationStatus.bread;
},
child: SvgPicture.asset(
"assets/close.svg",
@@ -899,7 +599,7 @@ class _FileManagerPageState extends State
left: 3,
),
onPressed: () {
- model.refresh(isLocal: isLocal);
+ controller.refresh();
},
child: SvgPicture.asset(
"assets/refresh.svg",
@@ -923,7 +623,7 @@ class _FileManagerPageState extends State
right: 3,
),
onPressed: () {
- model.goHome(isLocal: isLocal);
+ controller.goToHomeDirectory();
},
child: SvgPicture.asset(
"assets/home.svg",
@@ -938,26 +638,37 @@ class _FileManagerPageState extends State
_ffi.dialogManager.show((setState, close) {
submit() {
if (name.value.text.isNotEmpty) {
- model.createDir(
- PathUtil.join(
- model.getCurrentDir(isLocal).path,
- name.value.text,
- model.getCurrentIsWindows(isLocal)),
- isLocal: isLocal);
+ controller.createDir(PathUtil.join(
+ controller.directory.value.path,
+ name.value.text,
+ controller.options.value.isWindows,
+ ));
close();
}
}
cancel() => close(false);
return CustomAlertDialog(
- title: Text(translate("Create Folder")),
+ title: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset("assets/folder_new.svg",
+ color: MyTheme.accent),
+ Text(
+ translate("Create Folder"),
+ ).paddingOnly(
+ left: 10,
+ ),
+ ],
+ ),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
decoration: InputDecoration(
labelText: translate(
- "Please enter the folder name"),
+ "Please enter the folder name",
+ ),
),
controller: name,
autofocus: true,
@@ -965,9 +676,17 @@ class _FileManagerPageState extends State
],
),
actions: [
- dialogButton("Cancel",
- onPressed: cancel, isOutline: true),
- dialogButton("OK", onPressed: submit)
+ dialogButton(
+ "Cancel",
+ icon: Icon(Icons.close_rounded),
+ onPressed: cancel,
+ isOutline: true,
+ ),
+ dialogButton(
+ "Ok",
+ icon: Icon(Icons.done_rounded),
+ onPressed: submit,
+ ),
],
onSubmit: submit,
onCancel: cancel,
@@ -981,83 +700,93 @@ class _FileManagerPageState extends State
color: Theme.of(context).cardColor,
hoverColor: Theme.of(context).hoverColor,
),
- MenuButton(
- onPressed: validItems(selectedItems)
- ? () async {
- await (model.removeAction(selectedItems,
- isLocal: isLocal));
- selectedItems.clear();
- }
- : null,
- child: SvgPicture.asset(
- "assets/trash.svg",
- color: Theme.of(context).tabBarTheme.labelColor,
- ),
- color: Theme.of(context).cardColor,
- hoverColor: Theme.of(context).hoverColor,
- ),
+ Obx(() => MenuButton(
+ onPressed: SelectedItems.valid(selectedItems.items)
+ ? () async {
+ await (controller
+ .removeAction(selectedItems));
+ selectedItems.clear();
+ }
+ : null,
+ child: SvgPicture.asset(
+ "assets/trash.svg",
+ color: Theme.of(context).tabBarTheme.labelColor,
+ ),
+ color: Theme.of(context).cardColor,
+ hoverColor: Theme.of(context).hoverColor,
+ )),
menu(isLocal: isLocal),
],
),
),
- ElevatedButton.icon(
- style: ButtonStyle(
- padding: MaterialStateProperty.all(isLocal
- ? EdgeInsets.only(left: 10)
- : EdgeInsets.only(right: 10)),
- backgroundColor: MaterialStateProperty.all(
- selectedItems.length == 0
- ? MyTheme.accent80
- : MyTheme.accent,
- ),
- shape: MaterialStateProperty.all(
- RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(18.0),
+ Obx(() => ElevatedButton.icon(
+ style: ButtonStyle(
+ padding: MaterialStateProperty.all(
+ isLocal
+ ? EdgeInsets.only(left: 10)
+ : EdgeInsets.only(right: 10)),
+ backgroundColor: MaterialStateProperty.all(
+ selectedItems.items.isEmpty
+ ? MyTheme.accent80
+ : MyTheme.accent,
+ ),
),
- ),
- ),
- onPressed: validItems(selectedItems)
- ? () {
- model.sendFiles(selectedItems, isRemote: !isLocal);
- selectedItems.clear();
- }
- : null,
- icon: isLocal
- ? Text(
- translate('Send'),
- textAlign: TextAlign.right,
- style: TextStyle(
- color: selectedItems.length == 0
- ? MyTheme.darkGray
- : Colors.white,
- ),
- )
- : RotatedBox(
- quarterTurns: 2,
- child: SvgPicture.asset(
- "assets/arrow.svg",
- color: selectedItems.length == 0
- ? MyTheme.darkGray
- : Colors.white,
- alignment: Alignment.bottomRight,
- ),
- ),
- label: isLocal
- ? SvgPicture.asset(
- "assets/arrow.svg",
- color: selectedItems.length == 0
- ? MyTheme.darkGray
- : Colors.white,
- )
- : Text(
- translate('Receive'),
- style: TextStyle(
- color: selectedItems.length == 0
- ? MyTheme.darkGray
- : Colors.white,
- ),
- ),
- ),
+ onPressed: SelectedItems.valid(selectedItems.items)
+ ? () {
+ final otherSideData =
+ controller.getOtherSideDirectoryData();
+ controller.sendFiles(selectedItems, otherSideData);
+ selectedItems.clear();
+ }
+ : null,
+ icon: isLocal
+ ? Text(
+ translate('Send'),
+ textAlign: TextAlign.right,
+ style: TextStyle(
+ color: selectedItems.items.isEmpty
+ ? Theme.of(context).brightness ==
+ Brightness.light
+ ? MyTheme.grayBg
+ : MyTheme.darkGray
+ : Colors.white,
+ ),
+ )
+ : RotatedBox(
+ quarterTurns: 2,
+ child: SvgPicture.asset(
+ "assets/arrow.svg",
+ color: selectedItems.items.isEmpty
+ ? Theme.of(context).brightness ==
+ Brightness.light
+ ? MyTheme.grayBg
+ : MyTheme.darkGray
+ : Colors.white,
+ alignment: Alignment.bottomRight,
+ ),
+ ),
+ label: isLocal
+ ? SvgPicture.asset(
+ "assets/arrow.svg",
+ color: selectedItems.items.isEmpty
+ ? Theme.of(context).brightness ==
+ Brightness.light
+ ? MyTheme.grayBg
+ : MyTheme.darkGray
+ : Colors.white,
+ )
+ : Text(
+ translate('Receive'),
+ style: TextStyle(
+ color: selectedItems.items.isEmpty
+ ? Theme.of(context).brightness ==
+ Brightness.light
+ ? MyTheme.grayBg
+ : MyTheme.darkGray
+ : Colors.white,
+ ),
+ ),
+ )),
],
).marginOnly(top: 8.0)
],
@@ -1065,55 +794,443 @@ class _FileManagerPageState extends State
);
}
- bool validItems(SelectedItems items) {
- if (items.length > 0) {
- // exclude DirDrive type
- return items.items.any((item) => !item.isDrive);
+ Widget menu({bool isLocal = false}) {
+ var menuPos = RelativeRect.fill;
+
+ final List> items = [
+ MenuEntrySwitch(
+ switchType: SwitchType.scheckbox,
+ text: translate("Show Hidden Files"),
+ getter: () async {
+ return controller.options.value.isWindows;
+ },
+ setter: (bool v) async {
+ controller.toggleShowHidden();
+ },
+ padding: kDesktopMenuPadding,
+ dismissOnClicked: true,
+ ),
+ MenuEntryButton(
+ childBuilder: (style) => Text(translate("Select All"), style: style),
+ proc: () => setState(() =>
+ selectedItems.selectAll(controller.directory.value.entries)),
+ padding: kDesktopMenuPadding,
+ dismissOnClicked: true),
+ MenuEntryButton(
+ childBuilder: (style) =>
+ Text(translate("Unselect All"), style: style),
+ proc: () => selectedItems.clear(),
+ padding: kDesktopMenuPadding,
+ dismissOnClicked: true)
+ ];
+
+ return Listener(
+ onPointerDown: (e) {
+ final x = e.position.dx;
+ final y = e.position.dy;
+ menuPos = RelativeRect.fromLTRB(x, y, x, y);
+ },
+ child: MenuButton(
+ onPressed: () => mod_menu.showMenu(
+ context: context,
+ position: menuPos,
+ items: items
+ .map(
+ (e) => e.build(
+ context,
+ MenuConfig(
+ commonColor: CustomPopupMenuTheme.commonColor,
+ height: CustomPopupMenuTheme.height,
+ dividerHeight: CustomPopupMenuTheme.dividerHeight),
+ ),
+ )
+ .expand((i) => i)
+ .toList(),
+ elevation: 8,
+ ),
+ child: SvgPicture.asset(
+ "assets/dots.svg",
+ color: Theme.of(context).tabBarTheme.labelColor,
+ ),
+ color: Theme.of(context).cardColor,
+ hoverColor: Theme.of(context).hoverColor,
+ ),
+ );
+ }
+
+ Widget _buildFileList(
+ BuildContext context, ScrollController scrollController) {
+ final fd = controller.directory.value;
+ final entries = fd.entries;
+
+ return ListSearchActionListener(
+ node: _keyboardNode,
+ buffer: _listSearchBuffer,
+ onNext: (buffer) {
+ debugPrint("searching next for $buffer");
+ assert(buffer.length == 1);
+ assert(selectedItems.items.length <= 1);
+ var skipCount = 0;
+ if (selectedItems.items.isNotEmpty) {
+ final index = entries.indexOf(selectedItems.items.first);
+ if (index < 0) {
+ return;
+ }
+ skipCount = index + 1;
+ }
+ var searchResult = entries
+ .skip(skipCount)
+ .where((element) => element.name.toLowerCase().startsWith(buffer));
+ if (searchResult.isEmpty) {
+ // cannot find next, lets restart search from head
+ debugPrint("restart search from head");
+ searchResult = entries.where(
+ (element) => element.name.toLowerCase().startsWith(buffer));
+ }
+ if (searchResult.isEmpty) {
+ selectedItems.clear();
+ return;
+ }
+ _jumpToEntry(isLocal, searchResult.first, scrollController,
+ kDesktopFileTransferRowHeight);
+ },
+ onSearch: (buffer) {
+ debugPrint("searching for $buffer");
+ final selectedEntries = selectedItems;
+ final searchResult = entries
+ .where((element) => element.name.toLowerCase().startsWith(buffer));
+ selectedEntries.clear();
+ if (searchResult.isEmpty) {
+ selectedItems.clear();
+ return;
+ }
+ _jumpToEntry(isLocal, searchResult.first, scrollController,
+ kDesktopFileTransferRowHeight);
+ },
+ child: Obx(() {
+ final entries = controller.directory.value.entries;
+ final filteredEntries = _searchText.isNotEmpty
+ ? entries.where((element) {
+ return element.name.contains(_searchText.value);
+ }).toList(growable: false)
+ : entries;
+ final rows = filteredEntries.map((entry) {
+ final sizeStr =
+ entry.isFile ? readableFileSize(entry.size.toDouble()) : "";
+ final lastModifiedStr = entry.isDrive
+ ? " "
+ : "${entry.lastModified().toString().replaceAll(".000", "")} ";
+ return Padding(
+ padding: EdgeInsets.symmetric(vertical: 1),
+ child: Obx(() => Container(
+ decoration: BoxDecoration(
+ color: selectedItems.items.contains(entry)
+ ? Theme.of(context).hoverColor
+ : Theme.of(context).cardColor,
+ borderRadius: BorderRadius.all(
+ Radius.circular(5.0),
+ ),
+ ),
+ key: ValueKey(entry.name),
+ height: kDesktopFileTransferRowHeight,
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.spaceAround,
+ children: [
+ Expanded(
+ child: InkWell(
+ child: Row(
+ children: [
+ GestureDetector(
+ child: Obx(
+ () => Container(
+ width: _nameColWidth.value,
+ child: Tooltip(
+ waitDuration: Duration(milliseconds: 500),
+ message: entry.name,
+ child: Row(children: [
+ entry.isDrive
+ ? Image(
+ image: iconHardDrive,
+ fit: BoxFit.scaleDown,
+ color: Theme.of(context)
+ .iconTheme
+ .color
+ ?.withOpacity(0.7))
+ .paddingAll(4)
+ : SvgPicture.asset(
+ entry.isFile
+ ? "assets/file.svg"
+ : "assets/folder.svg",
+ color: Theme.of(context)
+ .tabBarTheme
+ .labelColor,
+ ),
+ Expanded(
+ child: Text(entry.name.nonBreaking,
+ overflow:
+ TextOverflow.ellipsis))
+ ]),
+ )),
+ ),
+ onTap: () {
+ final items = selectedItems;
+ // handle double click
+ if (_checkDoubleClick(entry)) {
+ controller.openDirectory(entry.path);
+ items.clear();
+ return;
+ }
+ _onSelectedChanged(
+ items, filteredEntries, entry, isLocal);
+ },
+ ),
+ SizedBox(
+ width: 2.0,
+ ),
+ GestureDetector(
+ child: Obx(
+ () => SizedBox(
+ width: _modifiedColWidth.value,
+ child: Tooltip(
+ waitDuration: Duration(milliseconds: 500),
+ message: lastModifiedStr,
+ child: Text(
+ lastModifiedStr,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ fontSize: 12,
+ color: MyTheme.darkGray,
+ ),
+ )),
+ ),
+ ),
+ ),
+ // Divider from header.
+ SizedBox(
+ width: 2.0,
+ ),
+ Expanded(
+ // width: 100,
+ child: GestureDetector(
+ child: Tooltip(
+ waitDuration: Duration(milliseconds: 500),
+ message: sizeStr,
+ child: Text(
+ sizeStr,
+ overflow: TextOverflow.ellipsis,
+ style: TextStyle(
+ fontSize: 10, color: MyTheme.darkGray),
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ))),
+ );
+ }).toList(growable: false);
+
+ return Column(
+ children: [
+ // Header
+ Row(
+ children: [
+ Expanded(child: _buildFileBrowserHeader(context)),
+ ],
+ ),
+ // Body
+ Expanded(
+ child: ListView.builder(
+ controller: scrollController,
+ itemExtent: kDesktopFileTransferRowHeight,
+ itemBuilder: (context, index) {
+ return rows[index];
+ },
+ itemCount: rows.length,
+ ),
+ ),
+ ],
+ );
+ }),
+ );
+ }
+
+ onSearchText(String searchText, bool isLocal) {
+ selectedItems.clear();
+ _searchText.value = searchText;
+ }
+
+ void _jumpToEntry(bool isLocal, Entry entry,
+ ScrollController scrollController, double rowHeight) {
+ final entries = controller.directory.value.entries;
+ final index = entries.indexOf(entry);
+ if (index == -1) {
+ debugPrint("entry is not valid: ${entry.path}");
+ }
+ final selectedEntries = selectedItems;
+ final searchResult = entries.where((element) => element == entry);
+ selectedEntries.clear();
+ if (searchResult.isEmpty) {
+ return;
+ }
+ final offset = min(
+ max(scrollController.position.minScrollExtent,
+ entries.indexOf(searchResult.first) * rowHeight),
+ scrollController.position.maxScrollExtent);
+ scrollController.jumpTo(offset);
+ selectedEntries.add(searchResult.first);
+ debugPrint("focused on ${searchResult.first.name}");
+ }
+
+ void _onSelectedChanged(SelectedItems selectedItems, List entries,
+ Entry entry, bool isLocal) {
+ final isCtrlDown = RawKeyboard.instance.keysPressed
+ .contains(LogicalKeyboardKey.controlLeft);
+ final isShiftDown =
+ RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft);
+ if (isCtrlDown) {
+ if (selectedItems.items.contains(entry)) {
+ selectedItems.remove(entry);
+ } else {
+ selectedItems.add(entry);
+ }
+ } else if (isShiftDown) {
+ final List indexGroup = [];
+ for (var selected in selectedItems.items) {
+ indexGroup.add(entries.indexOf(selected));
+ }
+ indexGroup.add(entries.indexOf(entry));
+ indexGroup.removeWhere((e) => e == -1);
+ final maxIndex = indexGroup.reduce(max);
+ final minIndex = indexGroup.reduce(min);
+ selectedItems.clear();
+ entries
+ .getRange(minIndex, maxIndex + 1)
+ .forEach((e) => selectedItems.add(e));
+ } else {
+ selectedItems.clear();
+ selectedItems.add(entry);
+ }
+ setState(() {});
+ }
+
+ bool _checkDoubleClick(Entry entry) {
+ final current = DateTime.now().millisecondsSinceEpoch;
+ final elapsed = current - _lastClickTime;
+ _lastClickTime = current;
+ if (_lastClickEntry == entry) {
+ if (elapsed < bind.getDoubleClickTime()) {
+ return true;
+ }
+ } else {
+ _lastClickEntry = entry;
}
return false;
}
- @override
- bool get wantKeepAlive => true;
-
- void onLocalLocationFocusChanged() {
- debugPrint("focus changed on local");
- if (_locationNodeLocal.hasFocus) {
- // ignore
- } else {
- // lost focus, change to bread
- if (_locationStatusLocal.value != LocationStatus.fileSearchBar) {
- _locationStatusLocal.value = LocationStatus.bread;
- }
- }
+ Widget _buildFileBrowserHeader(BuildContext context) {
+ final padding = EdgeInsets.all(1.0);
+ return SizedBox(
+ height: kDesktopFileTransferHeaderHeight,
+ child: Row(
+ children: [
+ Obx(
+ () => headerItemFunc(
+ _nameColWidth.value, SortBy.name, translate("Name")),
+ ),
+ DraggableDivider(
+ axis: Axis.vertical,
+ onPointerMove: (dx) {
+ _nameColWidth.value += dx;
+ _nameColWidth.value = min(kDesktopFileTransferMaximumWidth,
+ max(kDesktopFileTransferMinimumWidth, _nameColWidth.value));
+ },
+ padding: padding,
+ ),
+ Obx(
+ () => headerItemFunc(_modifiedColWidth.value, SortBy.modified,
+ translate("Modified")),
+ ),
+ DraggableDivider(
+ axis: Axis.vertical,
+ onPointerMove: (dx) {
+ _modifiedColWidth.value += dx;
+ _modifiedColWidth.value = min(
+ kDesktopFileTransferMaximumWidth,
+ max(kDesktopFileTransferMinimumWidth,
+ _modifiedColWidth.value));
+ },
+ padding: padding),
+ Expanded(child: headerItemFunc(null, SortBy.size, translate("Size")))
+ ],
+ ),
+ );
}
- void onRemoteLocationFocusChanged() {
- debugPrint("focus changed on remote");
- if (_locationNodeRemote.hasFocus) {
- // ignore
- } else {
- // lost focus, change to bread
- if (_locationStatusRemote.value != LocationStatus.fileSearchBar) {
- _locationStatusRemote.value = LocationStatus.bread;
+ Widget headerItemFunc(double? width, SortBy sortBy, String name) {
+ final headerTextStyle =
+ Theme.of(context).dataTableTheme.headingTextStyle ?? TextStyle();
+ return ObxValue>(
+ (ascending) => InkWell(
+ onTap: () {
+ if (ascending.value == null) {
+ ascending.value = true;
+ } else {
+ ascending.value = !ascending.value!;
+ }
+ controller.changeSortStyle(sortBy,
+ isLocal: isLocal, ascending: ascending.value!);
+ },
+ child: SizedBox(
+ width: width,
+ height: kDesktopFileTransferHeaderHeight,
+ child: Row(
+ children: [
+ Flexible(
+ flex: 2,
+ child: Text(
+ name,
+ style: headerTextStyle,
+ overflow: TextOverflow.ellipsis,
+ ).marginSymmetric(horizontal: 4),
+ ),
+ Flexible(
+ flex: 1,
+ child: ascending.value != null
+ ? Icon(
+ ascending.value!
+ ? Icons.keyboard_arrow_up_rounded
+ : Icons.keyboard_arrow_down_rounded,
+ )
+ : const Offstage())
+ ],
+ ),
+ ),
+ ), () {
+ if (controller.sortBy.value == sortBy) {
+ return controller.sortAscending.obs;
+ } else {
+ return Rx(null);
}
- }
+ }());
}
- Widget buildBread(bool isLocal) {
+ Widget buildBread() {
final items = getPathBreadCrumbItems(isLocal, (list) {
var path = "";
for (var item in list) {
- path = PathUtil.join(path, item, model.getCurrentIsWindows(isLocal));
+ path = PathUtil.join(path, item, controller.options.value.isWindows);
}
- openDirectory(path, isLocal: isLocal);
+ controller.openDirectory(path);
});
- final locationBarKey = getLocationBarKey(isLocal);
return items.isEmpty
? Offstage()
: Row(
- key: locationBarKey,
+ key: _locationBarKey,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
@@ -1121,7 +1238,7 @@ class _FileManagerPageState extends State
// handle mouse wheel
onPointerSignal: (e) {
if (e is PointerScrollEvent) {
- final sc = getBreadCrumbScrollController(isLocal);
+ final sc = _breadCrumbScroller;
final scale = Platform.isWindows ? 2 : 4;
sc.jumpTo(sc.offset + e.scrollDelta.dy / scale);
}
@@ -1130,7 +1247,7 @@ class _FileManagerPageState extends State
items: items,
divider: const Icon(Icons.keyboard_arrow_right_rounded),
overflow: ScrollableOverflow(
- controller: getBreadCrumbScrollController(isLocal),
+ controller: _breadCrumbScroller,
),
),
),
@@ -1139,9 +1256,9 @@ class _FileManagerPageState extends State
message: "",
icon: Icons.keyboard_arrow_down_rounded,
onTap: () async {
- final renderBox = locationBarKey.currentContext
+ final renderBox = _locationBarKey.currentContext
?.findRenderObject() as RenderBox;
- locationBarKey.currentContext?.size;
+ _locationBarKey.currentContext?.size;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
@@ -1149,17 +1266,17 @@ class _FileManagerPageState extends State
final x = offset.dx;
final y = offset.dy + size.height + 1;
- final isPeerWindows = model.getCurrentIsWindows(isLocal);
+ final isPeerWindows = controller.options.value.isWindows;
final List menuItems = [
MenuEntryButton(
childBuilder: (TextStyle? style) => isPeerWindows
- ? buildWindowsThisPC(style)
+ ? buildWindowsThisPC(context, style)
: Text(
'/',
style: style,
),
proc: () {
- openDirectory('/', isLocal: isLocal);
+ controller.openDirectory('/');
},
dismissOnClicked: true),
MenuEntryDivider()
@@ -1170,8 +1287,9 @@ class _FileManagerPageState extends State
loadingTag = _ffi.dialogManager.showLoading("Waiting");
}
try {
- final fd =
- await model.fetchDirectory("/", isLocal, isLocal);
+ final showHidden = controller.options.value.showHidden;
+ final fd = await controller.fileFetcher
+ .fetchDirectory("/", isLocal, showHidden);
for (var entry in fd.entries) {
menuItems.add(MenuEntryButton(
childBuilder: (TextStyle? style) =>
@@ -1190,8 +1308,7 @@ class _FileManagerPageState extends State
)
]),
proc: () {
- openDirectory('${entry.name}\\',
- isLocal: isLocal);
+ controller.openDirectory('${entry.name}\\');
},
dismissOnClicked: true));
}
@@ -1226,24 +1343,15 @@ class _FileManagerPageState extends State
]);
}
- Widget buildWindowsThisPC([TextStyle? textStyle]) {
- final color = Theme.of(context).iconTheme.color?.withOpacity(0.7);
- return Row(children: [
- Icon(Icons.computer, size: 20, color: color),
- SizedBox(width: 10),
- Text(translate('This PC'), style: textStyle)
- ]);
- }
-
List getPathBreadCrumbItems(
bool isLocal, void Function(List) onPressed) {
- final path = model.getCurrentDir(isLocal).path;
+ final path = controller.directory.value.path;
final breadCrumbList = List.empty(growable: true);
- final isWindows = model.getCurrentIsWindows(isLocal);
+ final isWindows = controller.options.value.isWindows;
if (isWindows && path == '/') {
breadCrumbList.add(BreadCrumbItem(
content: TextButton(
- child: buildWindowsThisPC(),
+ child: buildWindowsThisPC(context),
style: ButtonStyle(
minimumSize: MaterialStateProperty.all(Size(0, 0))),
onPressed: () => onPressed(['/']))
@@ -1271,39 +1379,34 @@ class _FileManagerPageState extends State
return breadCrumbList;
}
- breadCrumbScrollToEnd(bool isLocal) {
+ breadCrumbScrollToEnd() {
Future.delayed(Duration(milliseconds: 200), () {
- final breadCrumbScroller = getBreadCrumbScrollController(isLocal);
- if (breadCrumbScroller.hasClients) {
- breadCrumbScroller.animateTo(
- breadCrumbScroller.position.maxScrollExtent,
+ if (_breadCrumbScroller.hasClients) {
+ _breadCrumbScroller.animateTo(
+ _breadCrumbScroller.position.maxScrollExtent,
duration: Duration(milliseconds: 200),
curve: Curves.fastLinearToSlowEaseIn);
}
});
}
- Widget buildPathLocation(bool isLocal) {
- final searchTextObs = isLocal ? _searchTextLocal : _searchTextRemote;
- final locationStatus =
- isLocal ? _locationStatusLocal : _locationStatusRemote;
- final focusNode = isLocal ? _locationNodeLocal : _locationNodeRemote;
- final text = locationStatus.value == LocationStatus.pathLocation
- ? model.getCurrentDir(isLocal).path
- : searchTextObs.value;
+ Widget buildPathLocation() {
+ final text = _locationStatus.value == LocationStatus.pathLocation
+ ? controller.directory.value.path
+ : _searchText.value;
final textController = TextEditingController(text: text)
..selection = TextSelection.collapsed(offset: text.length);
return Row(
children: [
SvgPicture.asset(
- locationStatus.value == LocationStatus.pathLocation
+ _locationStatus.value == LocationStatus.pathLocation
? "assets/folder.svg"
: "assets/search.svg",
color: Theme.of(context).tabBarTheme.labelColor,
),
Expanded(
child: TextField(
- focusNode: focusNode,
+ focusNode: _locationNode,
decoration: InputDecoration(
border: InputBorder.none,
isDense: true,
@@ -1313,9 +1416,9 @@ class _FileManagerPageState extends State
),
controller: textController,
onSubmitted: (path) {
- openDirectory(path, isLocal: isLocal);
+ controller.openDirectory(path);
},
- onChanged: locationStatus.value == LocationStatus.fileSearchBar
+ onChanged: _locationStatus.value == LocationStatus.fileSearchBar
? (searchText) => onSearchText(searchText, isLocal)
: null,
),
@@ -1324,141 +1427,16 @@ class _FileManagerPageState extends State
);
}
- onSearchText(String searchText, bool isLocal) {
- if (isLocal) {
- _localSelectedItems.clear();
- _searchTextLocal.value = searchText;
- } else {
- _remoteSelectedItems.clear();
- _searchTextRemote.value = searchText;
- }
- }
-
- openDirectory(String path, {bool isLocal = false}) {
- model.openDirectory(path, isLocal: isLocal);
- }
-
- void handleDragDone(DropDoneDetails details, bool isLocal) {
- if (isLocal) {
- // ignore local
- return;
- }
- var items = SelectedItems();
- for (var file in details.files) {
- final f = File(file.path);
- items.add(
- true,
- Entry()
- ..path = file.path
- ..name = file.name
- ..size =
- FileSystemEntity.isDirectorySync(f.path) ? 0 : f.lengthSync());
- }
- model.sendFiles(items, isRemote: false);
- }
-
- void refocusKeyboardListener(bool isLocal) {
- Future.delayed(Duration.zero, () {
- if (isLocal) {
- _keyboardNodeLocal.requestFocus();
- } else {
- _keyboardNodeRemote.requestFocus();
- }
- });
- }
-
- Widget headerItemFunc(
- double? width, SortBy sortBy, String name, bool isLocal) {
- final headerTextStyle =
- Theme.of(context).dataTableTheme.headingTextStyle ?? TextStyle();
- return ObxValue>(
- (ascending) => InkWell(
- onTap: () {
- if (ascending.value == null) {
- ascending.value = true;
- } else {
- ascending.value = !ascending.value!;
- }
- model.changeSortStyle(sortBy,
- isLocal: isLocal, ascending: ascending.value!);
- },
- child: SizedBox(
- width: width,
- height: kDesktopFileTransferHeaderHeight,
- child: Row(
- children: [
- Flexible(
- flex: 2,
- child: Text(
- name,
- style: headerTextStyle,
- overflow: TextOverflow.ellipsis,
- ).marginSymmetric(horizontal: 4),
- ),
- Flexible(
- flex: 1,
- child: ascending.value != null
- ? Icon(
- ascending.value!
- ? Icons.keyboard_arrow_up_rounded
- : Icons.keyboard_arrow_down_rounded,
- )
- : const Offstage())
- ],
- ),
- ),
- ), () {
- if (model.getSortStyle(isLocal) == sortBy) {
- return model.getSortAscending(isLocal).obs;
- } else {
- return Rx(null);
- }
- }());
- }
-
- Widget _buildFileBrowserHeader(BuildContext context, bool isLocal) {
- final nameColWidth = isLocal ? _nameColWidthLocal : _nameColWidthRemote;
- final modifiedColWidth =
- isLocal ? _modifiedColWidthLocal : _modifiedColWidthRemote;
- final padding = EdgeInsets.all(1.0);
- return SizedBox(
- height: kDesktopFileTransferHeaderHeight,
- child: Row(
- children: [
- Obx(
- () => headerItemFunc(
- nameColWidth.value, SortBy.name, translate("Name"), isLocal),
- ),
- DraggableDivider(
- axis: Axis.vertical,
- onPointerMove: (dx) {
- nameColWidth.value += dx;
- nameColWidth.value = min(
- kDesktopFileTransferMaximumWidth,
- max(kDesktopFileTransferMinimumWidth,
- nameColWidth.value));
- },
- padding: padding,
- ),
- Obx(
- () => headerItemFunc(modifiedColWidth.value, SortBy.modified,
- translate("Modified"), isLocal),
- ),
- DraggableDivider(
- axis: Axis.vertical,
- onPointerMove: (dx) {
- modifiedColWidth.value += dx;
- modifiedColWidth.value = min(
- kDesktopFileTransferMaximumWidth,
- max(kDesktopFileTransferMinimumWidth,
- modifiedColWidth.value));
- },
- padding: padding),
- Expanded(
- child:
- headerItemFunc(null, SortBy.size, translate("Size"), isLocal))
- ],
- ),
- );
- }
+ // openDirectory(String path, {bool isLocal = false}) {
+ // model.openDirectory(path, isLocal: isLocal);
+ // }
+}
+
+Widget buildWindowsThisPC(BuildContext context, [TextStyle? textStyle]) {
+ final color = Theme.of(context).iconTheme.color?.withOpacity(0.7);
+ return Row(children: [
+ Icon(Icons.computer, size: 20, color: color),
+ SizedBox(width: 10),
+ Text(translate('This PC'), style: textStyle)
+ ]);
}
diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart
index 148d928d9..39958e88e 100644
--- a/flutter/lib/desktop/pages/file_manager_tab_page.dart
+++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart
@@ -98,7 +98,7 @@ class _FileManagerTabPageState extends State {
labelGetter: DesktopTab.labelGetterAlias,
)),
);
- return Platform.isMacOS
+ return Platform.isMacOS || kUseCompatibleUiMode
? tabWidget
: SubWindowDragToResizeArea(
child: tabWidget,
diff --git a/flutter/lib/desktop/pages/install_page.dart b/flutter/lib/desktop/pages/install_page.dart
index e7bb28813..adc0df138 100644
--- a/flutter/lib/desktop/pages/install_page.dart
+++ b/flutter/lib/desktop/pages/install_page.dart
@@ -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 createState() => _InstallPageState();
}
-class _InstallPageState extends State with WindowListener {
+class _InstallPageState extends State {
+ final tabController = DesktopTabController(tabType: DesktopTabType.main);
+
+ @override
+ void initState() {
+ super.initState();
+ Get.put(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();
+ }
+
+ @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 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 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 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 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 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 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 {
diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart
index f2d75d00f..32f02c9b7 100644
--- a/flutter/lib/desktop/pages/port_forward_tab_page.dart
+++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart
@@ -107,13 +107,15 @@ class _PortForwardTabPageState extends State {
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) {
diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart
index ab0daece7..2099f2e95 100644
--- a/flutter/lib/desktop/pages/remote_page.dart
+++ b/flutter/lib/desktop/pages/remote_page.dart
@@ -308,6 +308,10 @@ class _RemotePageState extends State
}
void leaveView(PointerExitEvent evt) {
+ if (_ffi.ffiModel.keyboard()) {
+ _ffi.inputModel.tryMoveEdgeOnExit(evt.position);
+ }
+
_cursorOverImage.value = false;
_firstEnterImage.value = false;
if (_onEnterOrLeaveImage4Menubar != null) {
@@ -329,8 +333,8 @@ class _RemotePageState extends State
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.
diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart
index 0deb646c0..d810650fd 100644
--- a/flutter/lib/desktop/pages/remote_tab_page.dart
+++ b/flutter/lib/desktop/pages/remote_tab_page.dart
@@ -205,11 +205,13 @@ class _ConnectionTabPageState extends State {
),
),
);
- 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,
));
diff --git a/flutter/lib/desktop/screen/desktop_remote_screen.dart b/flutter/lib/desktop/screen/desktop_remote_screen.dart
index bb6bc431b..64af41401 100644
--- a/flutter/lib/desktop/screen/desktop_remote_screen.dart
+++ b/flutter/lib/desktop/screen/desktop_remote_screen.dart
@@ -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,
),
diff --git a/flutter/lib/desktop/widgets/menu_button.dart b/flutter/lib/desktop/widgets/menu_button.dart
index df2c48ab4..17b160fed 100644
--- a/flutter/lib/desktop/widgets/menu_button.dart
+++ b/flutter/lib/desktop/widgets/menu_button.dart
@@ -37,7 +37,7 @@ class _MenuButtonState extends State {
message: widget.tooltip,
child: Material(
type: MaterialType.transparency,
- child: Ink(
+ child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_borderRadius),
color: _isHover ? widget.hoverColor : widget.color,
diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart
index 37bbbd66a..d1189abe4 100644
--- a/flutter/lib/desktop/widgets/remote_menubar.dart
+++ b/flutter/lib/desktop/widgets/remote_menubar.dart
@@ -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();
@@ -365,10 +370,13 @@ class _RemoteMenubarState extends State {
alignment: FractionalOffset(_fractionX.value, 0),
child: Offstage(
offstage: _dragging.isTrue,
- child: _DraggableShowHide(
- dragging: _dragging,
- fractionX: _fractionX,
- show: show,
+ child: Material(
+ elevation: _MenubarTheme.elevation,
+ child: _DraggableShowHide(
+ dragging: _dragging,
+ fractionX: _fractionX,
+ show: show,
+ ),
),
),
);
@@ -402,22 +410,27 @@ class _RemoteMenubarState extends State {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
- Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.all(Radius.circular(10)),
- ),
+ Material(
+ elevation: _MenubarTheme.elevation,
+ 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),
+ ...menubarItems,
+ SizedBox(width: _MenubarTheme.buttonHMargin * 2)
+ ],
+ ),
+ ),
+ ),
),
_buildDraggableShowHide(context),
],
@@ -427,11 +440,22 @@ class _RemoteMenubarState extends State {
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 +525,12 @@ class _MonitorMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
- if (stateGlobal.displaysCount.value < 2) return Offstage();
+ if (PrivacyModeState.find(id).isTrue ||
+ stateGlobal.displaysCount.value < 2) {
+ return Offstage();
+ }
return _IconSubmenuButton(
+ tooltip: 'Select Monitor',
icon: icon(),
ffi: ffi,
color: _MenubarTheme.blueColor,
@@ -541,6 +569,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: "",
@@ -593,6 +622,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 +681,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,
@@ -896,6 +944,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 +965,7 @@ class _DisplayMenuState extends State<_DisplayMenu> {
disableClipboard(),
lockAfterSessionEnd(),
privacyMode(),
+ swapKey(),
]);
}
@@ -949,12 +999,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 +1074,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);
}
@@ -1400,6 +1451,9 @@ class _DisplayMenuState extends State<_DisplayMenu> {
}
showRemoteCursor() {
+ if (widget.ffi.ffiModel.pi.platform == kPeerPlatformAndroid) {
+ return Offstage();
+ }
final visible = !widget.ffi.canvasModel.cursorEmbedded;
if (!visible) return Offstage();
final state = ShowRemoteCursorState.find(widget.id);
@@ -1417,6 +1471,9 @@ class _DisplayMenuState extends State<_DisplayMenu> {
}
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';
@@ -1518,11 +1575,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,12 +1624,17 @@ class _KeyboardMenu extends StatelessWidget {
Widget build(BuildContext context) {
var ffiModel = Provider.of(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,
@@ -1555,13 +1644,13 @@ class _KeyboardMenu extends StatelessWidget {
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 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 = [];
onChanged(String? value) async {
@@ -1571,13 +1660,13 @@ class _KeyboardMenu extends StatelessWidget {
for (KeyboardModeMenu mode in modes) {
if (bind.sessionIsKeyboardModeSupported(id: id, mode: mode.key)) {
- if (mode.key == 'translate') {
+ if (mode.key == _kKeyTranslateMode) {
if (Platform.isLinux || pi.platform == kPeerPlatformLinux) {
continue;
}
}
var text = translate(mode.menu);
- if (mode.key == 'translate') {
+ if (mode.key == _kKeyTranslateMode) {
text = '$text beta';
}
list.add(_RadioMenuButton(
@@ -1633,6 +1722,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 +1841,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 +1878,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 +1924,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 +1949,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 +2116,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 +2168,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),
),
diff --git a/flutter/lib/desktop/widgets/scroll_wrapper.dart b/flutter/lib/desktop/widgets/scroll_wrapper.dart
index 32ed149e5..c5bc3394b 100644
--- a/flutter/lib/desktop/widgets/scroll_wrapper.dart
+++ b/flutter/lib/desktop/widgets/scroll_wrapper.dart
@@ -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,
diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart
index ee3aaaf2c..4211911ff 100644
--- a/flutter/lib/desktop/widgets/tabbar_widget.dart
+++ b/flutter/lib/desktop/widgets/tabbar_widget.dart
@@ -53,6 +53,7 @@ enum DesktopTabType {
remoteScreen,
fileTransfer,
portForward,
+ install,
}
class DesktopTabState {
@@ -249,8 +250,9 @@ class DesktopTab extends StatelessWidget {
this.unSelectedTabBackgroundColor,
}) : super(key: key) {
tabType = controller.tabType;
- isMainWindow =
- tabType == DesktopTabType.main || tabType == DesktopTabType.cm;
+ isMainWindow = tabType == DesktopTabType.main ||
+ tabType == DesktopTabType.cm ||
+ tabType == DesktopTabType.install;
}
static RxString labelGetterAlias(String peerId) {
@@ -278,7 +280,6 @@ class DesktopTab extends StatelessWidget {
),
const Divider(
height: 1,
- thickness: 1,
),
],
),
@@ -361,7 +362,8 @@ class DesktopTab extends StatelessWidget {
/// - hide single item when only has one item (home) on [DesktopTabPage].
bool isHideSingleItem() {
return state.value.tabs.length == 1 &&
- controller.tabType == DesktopTabType.main;
+ (controller.tabType == DesktopTabType.main ||
+ controller.tabType == DesktopTabType.install);
}
Widget _buildBar() {
@@ -523,12 +525,18 @@ class WindowActionPanelState extends State
super.dispose();
}
+ void _setMaximize(bool maximize) {
+ stateGlobal.setMaximize(maximize);
+ setState(() {});
+ }
+
@override
void onWindowMaximize() {
// catch maximize from system
if (!widget.isMaximized.value) {
widget.isMaximized.value = true;
}
+ _setMaximize(true);
super.onWindowMaximize();
}
@@ -538,6 +546,7 @@ class WindowActionPanelState extends State
if (widget.isMaximized.value) {
widget.isMaximized.value = false;
}
+ _setMaximize(false);
super.onWindowUnmaximize();
}
@@ -599,7 +608,7 @@ class WindowActionPanelState extends State
offstage: !widget.showMaximize || Platform.isMacOS,
child: Obx(() => ActionIcon(
message:
- widget.isMaximized.value ? "Restore" : "Maximize",
+ widget.isMaximized.value ? 'Restore' : 'Maximize',
icon: widget.isMaximized.value
? IconFont.restore
: IconFont.max,
@@ -752,7 +761,8 @@ class _ListView extends StatelessWidget {
/// - hide single item when only has one item (home) on [DesktopTabPage].
bool isHideSingleItem() {
return state.value.tabs.length == 1 &&
- controller.tabType == DesktopTabType.main;
+ controller.tabType == DesktopTabType.main ||
+ controller.tabType == DesktopTabType.install;
}
@override
@@ -946,7 +956,6 @@ class _TabState extends State<_Tab> with RestorationMixin {
indent: _kDividerIndent,
endIndent: _kDividerIndent,
color: MyTheme.tabbar(context).dividerColor,
- thickness: 1,
),
)
],
diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart
index c61287d4f..f0a9a938f 100644
--- a/flutter/lib/main.dart
+++ b/flutter/lib/main.dart
@@ -153,6 +153,7 @@ void runMainApp(bool startService) async {
void runMobileApp() async {
await initEnv(kAppTypeMain);
if (isAndroid) androidChannelInit();
+ platformFFI.syncAndroidServiceAppDirConfigPath();
runApp(App());
}
@@ -216,7 +217,6 @@ void runMultiWindow(
void runConnectionManagerScreen(bool hide) async {
await initEnv(kAppTypeConnectionManager);
- await bind.cmStartListenIpcThread();
_runApp(
'',
const DesktopServerPage(),
@@ -291,17 +291,20 @@ void _runApp(
void runInstallPage() async {
await windowManager.ensureInitialized();
await initEnv(kAppTypeMain);
- _runApp('', const InstallPage(), ThemeMode.light);
- windowManager.waitUntilReadyToShow(
- WindowOptions(size: Size(800, 600), center: true), () async {
+ _runApp('', const InstallPage(), MyTheme.currentThemeMode());
+ WindowOptions windowOptions =
+ getHiddenTitleBarWindowOptions(size: Size(800, 600), center: true);
+ windowManager.waitUntilReadyToShow(windowOptions, () async {
windowManager.show();
windowManager.focus();
windowManager.setOpacity(1);
windowManager.setAlignment(Alignment.center); // ensure
+ windowManager.setTitle(getWindowName());
});
}
-WindowOptions getHiddenTitleBarWindowOptions({Size? size}) {
+WindowOptions getHiddenTitleBarWindowOptions(
+ {Size? size, bool center = false}) {
var defaultTitleBarStyle = TitleBarStyle.hidden;
// we do not hide titlebar on win7 because of the frame overflow.
if (kUseCompatibleUiMode) {
@@ -309,7 +312,7 @@ WindowOptions getHiddenTitleBarWindowOptions({Size? size}) {
}
return WindowOptions(
size: size,
- center: false,
+ center: center,
backgroundColor: Colors.transparent,
skipTaskbar: false,
titleBarStyle: defaultTitleBarStyle,
diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart
index 7aa9a0005..c6ba42d31 100644
--- a/flutter/lib/mobile/pages/file_manager_page.dart
+++ b/flutter/lib/mobile/pages/file_manager_page.dart
@@ -3,7 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:flutter_hbb/models/file_model.dart';
-import 'package:provider/provider.dart';
+import 'package:get/get.dart';
import 'package:toggle_switch/toggle_switch.dart';
import 'package:wakelock/wakelock.dart';
@@ -18,10 +18,51 @@ class FileManagerPage extends StatefulWidget {
State createState() => _FileManagerPageState();
}
+enum SelectMode { local, remote, none }
+
+extension SelectModeEq on SelectMode {
+ bool eq(bool? currentIsLocal) {
+ if (currentIsLocal == null) {
+ return false;
+ }
+ if (currentIsLocal) {
+ return this == SelectMode.local;
+ } else {
+ return this == SelectMode.remote;
+ }
+ }
+}
+
+extension SelectModeExt on Rx {
+ void toggle(bool currentIsLocal) {
+ switch (value) {
+ case SelectMode.local:
+ value = SelectMode.none;
+ break;
+ case SelectMode.remote:
+ value = SelectMode.none;
+ break;
+ case SelectMode.none:
+ if (currentIsLocal) {
+ value = SelectMode.local;
+ } else {
+ value = SelectMode.remote;
+ }
+ break;
+ }
+ }
+}
+
class _FileManagerPageState extends State {
final model = gFFI.fileModel;
- final _selectedItems = SelectedItems();
- final _breadCrumbScroller = ScrollController();
+ final selectMode = SelectMode.none.obs;
+
+ var showLocal = true;
+
+ FileController get currentFileController =>
+ showLocal ? model.localController : model.remoteController;
+ FileDirectory get currentDir => currentFileController.directory.value;
+ DirectoryOptions get currentOptions => currentFileController.options.value;
@override
void initState() {
@@ -32,13 +73,12 @@ class _FileManagerPageState extends State {
.showLoading(translate('Connecting...'), onCancel: closeConnection);
});
gFFI.ffiModel.updateEventListener(widget.id);
- model.onDirChanged = (_) => breadCrumbScrollToEnd();
Wakelock.enable();
}
@override
void dispose() {
- model.onClose().whenComplete(() {
+ model.close().whenComplete(() {
gFFI.close();
gFFI.dialogManager.dismissAll();
Wakelock.disable();
@@ -47,288 +87,455 @@ class _FileManagerPageState extends State {
}
@override
- Widget build(BuildContext context) => ChangeNotifierProvider.value(
- value: model,
- child: Consumer(builder: (_context, _model, _child) {
- return WillPopScope(
- onWillPop: () async {
- if (model.selectMode) {
- model.toggleSelectMode();
- } else {
- model.goBack();
+ Widget build(BuildContext context) => WillPopScope(
+ onWillPop: () async {
+ if (selectMode.value != SelectMode.none) {
+ selectMode.value = SelectMode.none;
+ setState(() {});
+ } else {
+ currentFileController.goBack();
+ }
+ return false;
+ },
+ child: Scaffold(
+ // backgroundColor: MyTheme.grayBg,
+ appBar: AppBar(
+ leading: Row(children: [
+ IconButton(
+ icon: Icon(Icons.close),
+ onPressed: () => clientClose(widget.id, gFFI.dialogManager)),
+ ]),
+ centerTitle: true,
+ title: ToggleSwitch(
+ initialLabelIndex: showLocal ? 0 : 1,
+ activeBgColor: [MyTheme.idColor],
+ inactiveBgColor: Theme.of(context).brightness == Brightness.light
+ ? MyTheme.grayBg
+ : null,
+ inactiveFgColor: Theme.of(context).brightness == Brightness.light
+ ? Colors.black54
+ : null,
+ totalSwitches: 2,
+ minWidth: 100,
+ fontSize: 15,
+ iconSize: 18,
+ labels: [translate("Local"), translate("Remote")],
+ icons: [Icons.phone_android_sharp, Icons.screen_share],
+ onToggle: (index) {
+ final current = showLocal ? 0 : 1;
+ if (index != current) {
+ setState(() => showLocal = !showLocal);
}
- return false;
},
- child: Scaffold(
- // backgroundColor: MyTheme.grayBg,
- appBar: AppBar(
- leading: Row(children: [
- IconButton(
- icon: Icon(Icons.close),
- onPressed: () =>
- clientClose(widget.id, gFFI.dialogManager)),
- ]),
- centerTitle: true,
- title: ToggleSwitch(
- initialLabelIndex: model.isLocal ? 0 : 1,
- activeBgColor: [MyTheme.idColor],
- inactiveBgColor:
- Theme.of(context).brightness == Brightness.light
- ? MyTheme.grayBg
- : null,
- inactiveFgColor:
- Theme.of(context).brightness == Brightness.light
- ? Colors.black54
- : null,
- totalSwitches: 2,
- minWidth: 100,
- fontSize: 15,
- iconSize: 18,
- labels: [translate("Local"), translate("Remote")],
- icons: [Icons.phone_android_sharp, Icons.screen_share],
- onToggle: (index) {
- final current = model.isLocal ? 0 : 1;
- if (index != current) {
- model.togglePage();
- }
- },
- ),
- actions: [
- PopupMenuButton(
- icon: Icon(Icons.more_vert),
- itemBuilder: (context) {
- return [
- PopupMenuItem(
- child: Row(
- children: [
- Icon(Icons.refresh,
- color: Theme.of(context).iconTheme.color),
- SizedBox(width: 5),
- Text(translate("Refresh File"))
- ],
- ),
- value: "refresh",
- ),
- PopupMenuItem(
- enabled: model.currentDir.path != "/",
- child: Row(
- children: [
- Icon(Icons.check,
- color: Theme.of(context).iconTheme.color),
- SizedBox(width: 5),
- Text(translate("Multi Select"))
- ],
- ),
- value: "select",
- ),
- PopupMenuItem(
- enabled: model.currentDir.path != "/",
- child: Row(
- children: [
- Icon(Icons.folder_outlined,
- color: Theme.of(context).iconTheme.color),
- SizedBox(width: 5),
- Text(translate("Create Folder"))
- ],
- ),
- value: "folder",
- ),
- PopupMenuItem(
- enabled: model.currentDir.path != "/",
- child: Row(
- children: [
- Icon(
- model.getCurrentShowHidden()
- ? Icons.check_box_outlined
- : Icons.check_box_outline_blank,
- color: Theme.of(context).iconTheme.color),
- SizedBox(width: 5),
- Text(translate("Show Hidden Files"))
- ],
- ),
- value: "hidden",
- )
- ];
- },
- onSelected: (v) {
- if (v == "refresh") {
- model.refresh();
- } else if (v == "select") {
- _selectedItems.clear();
- model.toggleSelectMode();
- } else if (v == "folder") {
- final name = TextEditingController();
- gFFI.dialogManager
- .show((setState, close) => CustomAlertDialog(
- title: Text(translate("Create Folder")),
- content: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- TextFormField(
- decoration: InputDecoration(
- labelText: translate(
- "Please enter the folder name"),
- ),
- controller: name,
- ),
- ],
+ ),
+ actions: [
+ PopupMenuButton(
+ icon: Icon(Icons.more_vert),
+ itemBuilder: (context) {
+ return [
+ PopupMenuItem(
+ child: Row(
+ children: [
+ Icon(Icons.refresh,
+ color: Theme.of(context).iconTheme.color),
+ SizedBox(width: 5),
+ Text(translate("Refresh File"))
+ ],
+ ),
+ value: "refresh",
+ ),
+ PopupMenuItem(
+ enabled: currentDir.path != "/",
+ child: Row(
+ children: [
+ Icon(Icons.check,
+ color: Theme.of(context).iconTheme.color),
+ SizedBox(width: 5),
+ Text(translate("Multi Select"))
+ ],
+ ),
+ value: "select",
+ ),
+ PopupMenuItem(
+ enabled: currentDir.path != "/",
+ child: Row(
+ children: [
+ Icon(Icons.folder_outlined,
+ color: Theme.of(context).iconTheme.color),
+ SizedBox(width: 5),
+ Text(translate("Create Folder"))
+ ],
+ ),
+ value: "folder",
+ ),
+ PopupMenuItem(
+ enabled: currentDir.path != "/",
+ child: Row(
+ children: [
+ Icon(
+ currentOptions.showHidden
+ ? Icons.check_box_outlined
+ : Icons.check_box_outline_blank,
+ color: Theme.of(context).iconTheme.color),
+ SizedBox(width: 5),
+ Text(translate("Show Hidden Files"))
+ ],
+ ),
+ value: "hidden",
+ )
+ ];
+ },
+ onSelected: (v) {
+ if (v == "refresh") {
+ currentFileController.refresh();
+ } else if (v == "select") {
+ model.localController.selectedItems.clear();
+ model.remoteController.selectedItems.clear();
+ selectMode.toggle(showLocal);
+ setState(() {});
+ } else if (v == "folder") {
+ final name = TextEditingController();
+ gFFI.dialogManager
+ .show((setState, close) => CustomAlertDialog(
+ title: Text(translate("Create Folder")),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ TextFormField(
+ decoration: InputDecoration(
+ labelText: translate(
+ "Please enter the folder name"),
),
- actions: [
- dialogButton("Cancel",
- onPressed: () => close(false),
- isOutline: true),
- dialogButton("OK", onPressed: () {
- if (name.value.text.isNotEmpty) {
- model.createDir(PathUtil.join(
- model.currentDir.path,
- name.value.text,
- model.getCurrentIsWindows()));
- close();
- }
- })
- ]));
- } else if (v == "hidden") {
- model.toggleShowHidden();
- }
- }),
- ],
+ controller: name,
+ ),
+ ],
+ ),
+ actions: [
+ dialogButton("Cancel",
+ onPressed: () => close(false),
+ isOutline: true),
+ dialogButton("OK", onPressed: () {
+ if (name.value.text.isNotEmpty) {
+ currentFileController.createDir(
+ PathUtil.join(
+ currentDir.path,
+ name.value.text,
+ currentOptions.isWindows));
+ close();
+ }
+ })
+ ]));
+ } else if (v == "hidden") {
+ currentFileController.toggleShowHidden();
+ }
+ }),
+ ],
+ ),
+ body: showLocal
+ ? FileManagerView(
+ controller: model.localController,
+ selectMode: selectMode,
+ )
+ : FileManagerView(
+ controller: model.remoteController,
+ selectMode: selectMode,
),
- body: body(),
- bottomSheet: bottomSheet(),
- ));
- }));
+ bottomSheet: bottomSheet(),
+ ));
- bool showCheckBox() {
- if (!model.selectMode) {
- return false;
- }
- return !_selectedItems.isOtherPage(model.isLocal);
+ Widget? bottomSheet() {
+ return Obx(() {
+ final selectedItems = getActiveSelectedItems();
+ final jobTable = model.jobController.jobTable;
+
+ final localLabel = selectedItems?.isLocal == null
+ ? ""
+ : " [${selectedItems!.isLocal ? translate("Local") : translate("Remote")}]";
+ if (!(selectMode.value == SelectMode.none)) {
+ final selectedItemsLen =
+ "${selectedItems?.items.length ?? 0} ${translate("items")}";
+ if (selectedItems == null ||
+ selectedItems.items.isEmpty ||
+ selectMode.value.eq(showLocal)) {
+ return BottomSheetBody(
+ leading: Icon(Icons.check),
+ title: translate("Selected"),
+ text: selectedItemsLen + localLabel,
+ onCanceled: () {
+ selectedItems?.items.clear();
+ selectMode.value = SelectMode.none;
+ setState(() {});
+ },
+ actions: [
+ IconButton(
+ icon: Icon(Icons.compare_arrows),
+ onPressed: () => setState(() => showLocal = !showLocal),
+ ),
+ IconButton(
+ icon: Icon(Icons.delete_forever),
+ onPressed: selectedItems != null
+ ? () async {
+ if (selectedItems.items.isNotEmpty) {
+ await currentFileController
+ .removeAction(selectedItems);
+ selectedItems.items.clear();
+ selectMode.value = SelectMode.none;
+ }
+ }
+ : null,
+ )
+ ]);
+ } else {
+ return BottomSheetBody(
+ leading: Icon(Icons.input),
+ title: translate("Paste here?"),
+ text: selectedItemsLen + localLabel,
+ onCanceled: () {
+ selectedItems.items.clear();
+ selectMode.value = SelectMode.none;
+ setState(() {});
+ },
+ actions: [
+ IconButton(
+ icon: Icon(Icons.compare_arrows),
+ onPressed: () => setState(() => showLocal = !showLocal),
+ ),
+ IconButton(
+ icon: Icon(Icons.paste),
+ onPressed: () {
+ selectMode.value = SelectMode.none;
+ final otherSide = showLocal
+ ? model.remoteController
+ : model.localController;
+ final thisSideData =
+ DirectoryData(currentDir, currentOptions);
+ otherSide.sendFiles(selectedItems, thisSideData);
+ selectedItems.items.clear();
+ selectMode.value = SelectMode.none;
+ },
+ )
+ ]);
+ }
+ }
+
+ if (jobTable.isEmpty) {
+ return Offstage();
+ }
+
+ switch (jobTable.last.state) {
+ case JobState.inProgress:
+ return BottomSheetBody(
+ leading: CircularProgressIndicator(),
+ title: translate("Waiting"),
+ text:
+ "${translate("Speed")}: ${readableFileSize(jobTable.last.speed)}/s",
+ onCanceled: () {
+ model.jobController.cancelJob(jobTable.last.id);
+ jobTable.clear();
+ },
+ );
+ case JobState.done:
+ return BottomSheetBody(
+ leading: Icon(Icons.check),
+ title: "${translate("Successful")}!",
+ text: jobTable.last.display(),
+ onCanceled: () => jobTable.clear(),
+ );
+ case JobState.error:
+ return BottomSheetBody(
+ leading: Icon(Icons.error),
+ title: "${translate("Error")}!",
+ text: "",
+ onCanceled: () => jobTable.clear(),
+ );
+ case JobState.none:
+ break;
+ case JobState.paused:
+ // TODO: Handle this case.
+ break;
+ }
+ return Offstage();
+ });
}
- Widget body() {
- final isLocal = model.isLocal;
- final fd = model.currentDir;
- final entries = fd.entries;
+ SelectedItems? getActiveSelectedItems() {
+ final localSelectedItems = model.localController.selectedItems;
+ final remoteSelectedItems = model.remoteController.selectedItems;
+
+ if (localSelectedItems.items.isNotEmpty &&
+ remoteSelectedItems.items.isNotEmpty) {
+ // assert unreachable
+ debugPrint("Wrong SelectedItems state, reset");
+ localSelectedItems.clear();
+ remoteSelectedItems.clear();
+ }
+
+ if (localSelectedItems.items.isEmpty && remoteSelectedItems.items.isEmpty) {
+ return null;
+ }
+
+ if (localSelectedItems.items.length > remoteSelectedItems.items.length) {
+ return localSelectedItems;
+ } else {
+ return remoteSelectedItems;
+ }
+ }
+}
+
+class FileManagerView extends StatefulWidget {
+ final FileController controller;
+ final Rx selectMode;
+
+ FileManagerView({required this.controller, required this.selectMode});
+
+ @override
+ State createState() => _FileManagerViewState();
+}
+
+class _FileManagerViewState extends State {
+ final _listScrollController = ScrollController();
+ final _breadCrumbScroller = ScrollController();
+
+ bool get isLocal => widget.controller.isLocal;
+ FileController get controller => widget.controller;
+ SelectedItems get _selectedItems => widget.controller.selectedItems;
+
+ @override
+ void initState() {
+ super.initState();
+ controller.directory.listen((e) => breadCrumbScrollToEnd());
+ }
+
+ @override
+ Widget build(BuildContext context) {
return Column(children: [
headTools(),
- Expanded(
- child: ListView.builder(
- controller: ScrollController(),
- itemCount: entries.length + 1,
- itemBuilder: (context, index) {
- if (index >= entries.length) {
- return listTail();
- }
- var selected = false;
- if (model.selectMode) {
- selected = _selectedItems.contains(entries[index]);
- }
+ Expanded(child: Obx(() {
+ final entries = controller.directory.value.entries;
+ return ListView.builder(
+ controller: _listScrollController,
+ itemCount: entries.length + 1,
+ itemBuilder: (context, index) {
+ if (index >= entries.length) {
+ return listTail();
+ }
+ var selected = false;
+ if (widget.selectMode.value != SelectMode.none) {
+ selected = _selectedItems.items.contains(entries[index]);
+ }
- final sizeStr = entries[index].isFile
- ? readableFileSize(entries[index].size.toDouble())
- : "";
- return Card(
- child: ListTile(
- leading: entries[index].isDrive
- ? Padding(
- padding: EdgeInsets.symmetric(vertical: 8),
- child: Image(
- image: iconHardDrive,
- fit: BoxFit.scaleDown,
- color: Theme.of(context)
- .iconTheme
- .color
- ?.withOpacity(0.7)))
- : Icon(
- entries[index].isFile
- ? Icons.feed_outlined
- : Icons.folder,
- size: 40),
- title: Text(entries[index].name),
- selected: selected,
- subtitle: entries[index].isDrive
- ? null
- : Text(
- "${entries[index].lastModified().toString().replaceAll(".000", "")} $sizeStr",
- style: TextStyle(fontSize: 12, color: MyTheme.darkGray),
- ),
- trailing: entries[index].isDrive
- ? null
- : showCheckBox()
- ? Checkbox(
- value: selected,
- onChanged: (v) {
- if (v == null) return;
- if (v && !selected) {
- _selectedItems.add(isLocal, entries[index]);
- } else if (!v && selected) {
- _selectedItems.remove(entries[index]);
- }
- setState(() {});
- })
- : PopupMenuButton(
- icon: Icon(Icons.more_vert),
- itemBuilder: (context) {
- return [
- PopupMenuItem(
- child: Text(translate("Delete")),
- value: "delete",
- ),
- PopupMenuItem(
- child: Text(translate("Multi Select")),
- value: "multi_select",
- ),
- PopupMenuItem(
- child: Text(translate("Properties")),
- value: "properties",
- enabled: false,
- )
- ];
- },
- onSelected: (v) {
- if (v == "delete") {
- final items = SelectedItems();
- items.add(isLocal, entries[index]);
- model.removeAction(items);
- } else if (v == "multi_select") {
- _selectedItems.clear();
- model.toggleSelectMode();
- }
- }),
- onTap: () {
- if (model.selectMode && !_selectedItems.isOtherPage(isLocal)) {
- if (selected) {
- _selectedItems.remove(entries[index]);
- } else {
- _selectedItems.add(isLocal, entries[index]);
+ final sizeStr = entries[index].isFile
+ ? readableFileSize(entries[index].size.toDouble())
+ : "";
+
+ final showCheckBox = () {
+ return widget.selectMode.value != SelectMode.none &&
+ widget.selectMode.value.eq(controller.selectedItems.isLocal);
+ }();
+ return Card(
+ child: ListTile(
+ leading: entries[index].isDrive
+ ? Padding(
+ padding: EdgeInsets.symmetric(vertical: 8),
+ child: Image(
+ image: iconHardDrive,
+ fit: BoxFit.scaleDown,
+ color: Theme.of(context)
+ .iconTheme
+ .color
+ ?.withOpacity(0.7)))
+ : Icon(
+ entries[index].isFile
+ ? Icons.feed_outlined
+ : Icons.folder,
+ size: 40),
+ title: Text(entries[index].name),
+ selected: selected,
+ subtitle: entries[index].isDrive
+ ? null
+ : Text(
+ "${entries[index].lastModified().toString().replaceAll(".000", "")} $sizeStr",
+ style: TextStyle(fontSize: 12, color: MyTheme.darkGray),
+ ),
+ trailing: entries[index].isDrive
+ ? null
+ : showCheckBox
+ ? Checkbox(
+ value: selected,
+ onChanged: (v) {
+ if (v == null) return;
+ if (v && !selected) {
+ _selectedItems.add(entries[index]);
+ } else if (!v && selected) {
+ _selectedItems.remove(entries[index]);
+ }
+ setState(() {});
+ })
+ : PopupMenuButton(
+ icon: Icon(Icons.more_vert),
+ itemBuilder: (context) {
+ return [
+ PopupMenuItem(
+ child: Text(translate("Delete")),
+ value: "delete",
+ ),
+ PopupMenuItem(
+ child: Text(translate("Multi Select")),
+ value: "multi_select",
+ ),
+ PopupMenuItem(
+ child: Text(translate("Properties")),
+ value: "properties",
+ enabled: false,
+ )
+ ];
+ },
+ onSelected: (v) {
+ if (v == "delete") {
+ final items = SelectedItems(isLocal: isLocal);
+ items.add(entries[index]);
+ controller.removeAction(items);
+ } else if (v == "multi_select") {
+ _selectedItems.clear();
+ widget.selectMode.toggle(isLocal);
+ setState(() {});
+ }
+ }),
+ onTap: () {
+ if (showCheckBox) {
+ if (selected) {
+ _selectedItems.remove(entries[index]);
+ } else {
+ _selectedItems.add(entries[index]);
+ }
+ setState(() {});
+ return;
}
- setState(() {});
- return;
- }
- if (entries[index].isDirectory || entries[index].isDrive) {
- model.openDirectory(entries[index].path);
- } else {
- // Perform file-related tasks.
- }
- },
- onLongPress: entries[index].isDrive
- ? null
- : () {
- _selectedItems.clear();
- model.toggleSelectMode();
- if (model.selectMode) {
- _selectedItems.add(isLocal, entries[index]);
- }
- setState(() {});
- },
- ),
- );
- },
- ))
+ if (entries[index].isDirectory || entries[index].isDrive) {
+ controller.openDirectory(entries[index].path);
+ } else {
+ // Perform file-related tasks.
+ }
+ },
+ onLongPress: entries[index].isDrive
+ ? null
+ : () {
+ _selectedItems.clear();
+ widget.selectMode.toggle(isLocal);
+ if (widget.selectMode.value != SelectMode.none) {
+ _selectedItems.add(entries[index]);
+ }
+ setState(() {});
+ },
+ ),
+ );
+ },
+ );
+ }))
]);
}
- breadCrumbScrollToEnd() {
+ void breadCrumbScrollToEnd() {
Future.delayed(Duration(milliseconds: 200), () {
if (_breadCrumbScroller.hasClients) {
_breadCrumbScroller.animateTo(
@@ -342,35 +549,39 @@ class _FileManagerPageState extends State {
Widget headTools() => Container(
child: Row(
children: [
- Expanded(
- child: BreadCrumb(
- items: getPathBreadCrumbItems(() => model.goHome(), (list) {
- var path = "";
- if (model.currentHome.startsWith(list[0])) {
- // absolute path
- for (var item in list) {
- path = PathUtil.join(path, item, model.getCurrentIsWindows());
+ Expanded(child: Obx(() {
+ final home = controller.options.value.home;
+ final isWindows = controller.options.value.isWindows;
+ return BreadCrumb(
+ items: getPathBreadCrumbItems(controller.shortPath, isWindows,
+ () => controller.goToHomeDirectory(), (list) {
+ var path = "";
+ if (home.startsWith(list[0])) {
+ // absolute path
+ for (var item in list) {
+ path = PathUtil.join(path, item, isWindows);
+ }
+ } else {
+ path += home;
+ for (var item in list) {
+ path = PathUtil.join(path, item, isWindows);
+ }
}
- } else {
- path += model.currentHome;
- for (var item in list) {
- path = PathUtil.join(path, item, model.getCurrentIsWindows());
- }
- }
- model.openDirectory(path);
- }),
- divider: Icon(Icons.chevron_right),
- overflow: ScrollableOverflow(controller: _breadCrumbScroller),
- )),
+ controller.openDirectory(path);
+ }),
+ divider: Icon(Icons.chevron_right),
+ overflow: ScrollableOverflow(controller: _breadCrumbScroller),
+ );
+ })),
Row(
children: [
IconButton(
icon: Icon(Icons.arrow_back),
- onPressed: model.goBack,
+ onPressed: controller.goBack,
),
IconButton(
icon: Icon(Icons.arrow_upward),
- onPressed: model.goToParentDirectory,
+ onPressed: controller.goToParentDirectory,
),
PopupMenuButton(
icon: Icon(Icons.sort),
@@ -382,123 +593,37 @@ class _FileManagerPageState extends State {
))
.toList();
},
- onSelected: model.changeSortStyle),
+ onSelected: controller.changeSortStyle),
],
)
],
));
- Widget listTail() {
- return Container(
- height: 100,
- child: Column(
- children: [
- Padding(
- padding: EdgeInsets.fromLTRB(30, 5, 30, 0),
- child: Text(
- model.currentDir.path,
- style: TextStyle(color: MyTheme.darkGray),
- ),
- ),
- Padding(
- padding: EdgeInsets.all(2),
- child: Text(
- "${translate("Total")}: ${model.currentDir.entries.length} ${translate("items")}",
- style: TextStyle(color: MyTheme.darkGray),
- ),
- )
- ],
- ),
- );
- }
-
- Widget? bottomSheet() {
- final state = model.jobState;
- final isOtherPage = _selectedItems.isOtherPage(model.isLocal);
- final selectedItemsLen = "${_selectedItems.length} ${translate("items")}";
- final local = _selectedItems.isLocal == null
- ? ""
- : " [${_selectedItems.isLocal! ? translate("Local") : translate("Remote")}]";
-
- if (model.selectMode) {
- if (_selectedItems.length == 0 || !isOtherPage) {
- return BottomSheetBody(
- leading: Icon(Icons.check),
- title: translate("Selected"),
- text: selectedItemsLen + local,
- onCanceled: () => model.toggleSelectMode(),
- actions: [
- IconButton(
- icon: Icon(Icons.compare_arrows),
- onPressed: model.togglePage,
+ Widget listTail() => Obx(() => Container(
+ height: 100,
+ child: Column(
+ children: [
+ Padding(
+ padding: EdgeInsets.fromLTRB(30, 5, 30, 0),
+ child: Text(
+ controller.directory.value.path,
+ style: TextStyle(color: MyTheme.darkGray),
),
- IconButton(
- icon: Icon(Icons.delete_forever),
- onPressed: () {
- if (_selectedItems.length > 0) {
- model.removeAction(_selectedItems);
- }
- },
- )
- ]);
- } else {
- return BottomSheetBody(
- leading: Icon(Icons.input),
- title: translate("Paste here?"),
- text: selectedItemsLen + local,
- onCanceled: () => model.toggleSelectMode(),
- actions: [
- IconButton(
- icon: Icon(Icons.compare_arrows),
- onPressed: model.togglePage,
+ ),
+ Padding(
+ padding: EdgeInsets.all(2),
+ child: Text(
+ "${translate("Total")}: ${controller.directory.value.entries.length} ${translate("items")}",
+ style: TextStyle(color: MyTheme.darkGray),
),
- IconButton(
- icon: Icon(Icons.paste),
- onPressed: () {
- model.toggleSelectMode();
- model.sendFiles(_selectedItems);
- },
- )
- ]);
- }
- }
+ )
+ ],
+ ),
+ ));
- switch (state) {
- case JobState.inProgress:
- return BottomSheetBody(
- leading: CircularProgressIndicator(),
- title: translate("Waiting"),
- text:
- "${translate("Speed")}: ${readableFileSize(model.jobProgress.speed)}/s",
- onCanceled: () => model.cancelJob(model.jobProgress.id),
- );
- case JobState.done:
- return BottomSheetBody(
- leading: Icon(Icons.check),
- title: "${translate("Successful")}!",
- text: model.jobProgress.display(),
- onCanceled: () => model.jobReset(),
- );
- case JobState.error:
- return BottomSheetBody(
- leading: Icon(Icons.error),
- title: "${translate("Error")}!",
- text: "",
- onCanceled: () => model.jobReset(),
- );
- case JobState.none:
- break;
- case JobState.paused:
- // TODO: Handle this case.
- break;
- }
- return null;
- }
-
- List getPathBreadCrumbItems(
+ List getPathBreadCrumbItems(String shortPath, bool isWindows,
void Function() onHome, void Function(List) onPressed) {
- final path = model.currentShortPath;
- final list = PathUtil.split(path, model.getCurrentIsWindows());
+ final list = PathUtil.split(shortPath, isWindows);
final breadCrumbList = [
BreadCrumbItem(
content: IconButton(
diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart
index abccdf683..ae61c91a7 100644
--- a/flutter/lib/mobile/pages/server_page.dart
+++ b/flutter/lib/mobile/pages/server_page.dart
@@ -1,11 +1,14 @@
import 'dart:async';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_hbb/mobile/widgets/dialog.dart';
+import 'package:get/get.dart';
import 'package:provider/provider.dart';
import '../../common.dart';
import '../../common/widgets/dialog.dart';
+import '../../consts.dart';
import '../../models/platform_model.dart';
import '../../models/server_model.dart';
import 'home_page.dart';
@@ -40,14 +43,14 @@ class ServerPage extends StatefulWidget implements PageShape {
value: "setTemporaryPasswordLength",
enabled:
gFFI.serverModel.verificationMethod != kUsePermanentPassword,
- child: Text(translate("Set temporary password length")),
+ child: Text(translate("One-time password length")),
),
const PopupMenuDivider(),
PopupMenuItem(
padding: const EdgeInsets.symmetric(horizontal: 0.0),
value: kUseTemporaryPassword,
child: ListTile(
- title: Text(translate("Use temporary password")),
+ title: Text(translate("Use one-time password")),
trailing: Icon(
Icons.check,
color: gFFI.serverModel.verificationMethod ==
@@ -138,9 +141,11 @@ class _ServerPageState extends State {
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
- ServerInfo(),
- const PermissionChecker(),
+ gFFI.serverModel.isStart
+ ? ServerInfo()
+ : ServiceNotRunningNotification(),
const ConnectionManager(),
+ const PermissionChecker(),
SizedBox.fromSize(size: const Size(0, 15.0)),
],
),
@@ -150,14 +155,42 @@ class _ServerPageState extends State {
}
void checkService() async {
- gFFI.invokeMethod("check_service"); // jvm
- // for Android 10/11,MANAGE_EXTERNAL_STORAGE permission from a system setting page
- if (PermissionManager.isWaitingFile() && !gFFI.serverModel.fileOk) {
- PermissionManager.complete("file", await PermissionManager.check("file"));
+ gFFI.invokeMethod("check_service");
+ // for Android 10/11, request MANAGE_EXTERNAL_STORAGE permission from system setting page
+ if (AndroidPermissionManager.isWaitingFile() && !gFFI.serverModel.fileOk) {
+ AndroidPermissionManager.complete(kManageExternalStorage,
+ await AndroidPermissionManager.check(kManageExternalStorage));
debugPrint("file permission finished");
}
}
+class ServiceNotRunningNotification extends StatelessWidget {
+ ServiceNotRunningNotification({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ final serverModel = Provider.of(context);
+
+ return PaddingCard(
+ title: translate("Service is not running"),
+ titleIcon:
+ const Icon(Icons.warning_amber_sharp, color: Colors.redAccent),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(translate("android_start_service_tip"),
+ style:
+ const TextStyle(fontSize: 12, color: MyTheme.darkGray))
+ .marginOnly(bottom: 8),
+ ElevatedButton.icon(
+ icon: const Icon(Icons.play_arrow),
+ onPressed: serverModel.toggleService,
+ label: Text(translate("Start Service")))
+ ],
+ ));
+ }
+}
+
class ServerInfo extends StatelessWidget {
final model = gFFI.serverModel;
final emptyController = TextEditingController(text: "-");
@@ -167,78 +200,104 @@ class ServerInfo extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isPermanent = model.verificationMethod == kUsePermanentPassword;
- return model.isStart
- ? PaddingCard(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- TextFormField(
- readOnly: true,
- style: const TextStyle(
- fontSize: 25.0,
- fontWeight: FontWeight.bold,
- color: MyTheme.accent),
- controller: model.serverId,
- decoration: InputDecoration(
- icon: const Icon(Icons.perm_identity),
- labelText: translate("ID"),
- labelStyle: const TextStyle(
- fontWeight: FontWeight.bold, color: MyTheme.accent80),
- ),
- onSaved: (String? value) {},
+ final serverModel = Provider.of(context);
+
+ const Color colorPositive = Colors.green;
+ const Color colorNegative = Colors.red;
+ const double iconMarginRight = 15;
+ const double iconSize = 24;
+ const TextStyle textStyleHeading = TextStyle(
+ fontSize: 16.0, fontWeight: FontWeight.bold, color: Colors.grey);
+ const TextStyle textStyleValue =
+ TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold);
+
+ void copyToClipboard(String value) {
+ Clipboard.setData(ClipboardData(text: value));
+ showToast(translate('Copied'));
+ }
+
+ Widget ConnectionStateNotification() {
+ if (serverModel.connectStatus == -1) {
+ return Row(children: [
+ const Icon(Icons.warning_amber_sharp,
+ color: colorNegative, size: iconSize)
+ .marginOnly(right: iconMarginRight),
+ Expanded(child: Text(translate('not_ready_status')))
+ ]);
+ } else if (serverModel.connectStatus == 0) {
+ return Row(children: [
+ SizedBox(width: 20, height: 20, child: CircularProgressIndicator())
+ .marginOnly(left: 4, right: iconMarginRight),
+ Expanded(child: Text(translate('connecting_status')))
+ ]);
+ } else {
+ return Row(children: [
+ const Icon(Icons.check, color: colorPositive, size: iconSize)
+ .marginOnly(right: iconMarginRight),
+ Expanded(child: Text(translate('Ready')))
+ ]);
+ }
+ }
+
+ return PaddingCard(
+ title: translate('Your Device'),
+ child: Column(
+ // ID
+ children: [
+ Row(children: [
+ const Icon(Icons.perm_identity,
+ color: Colors.grey, size: iconSize)
+ .marginOnly(right: iconMarginRight),
+ Text(
+ translate('ID'),
+ style: textStyleHeading,
+ )
+ ]),
+ Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
+ Text(
+ model.serverId.value.text,
+ style: textStyleValue,
),
- TextFormField(
- readOnly: true,
- style: const TextStyle(
- fontSize: 25.0,
- fontWeight: FontWeight.bold,
- color: MyTheme.accent),
- controller: isPermanent ? emptyController : model.serverPasswd,
- decoration: InputDecoration(
- icon: const Icon(Icons.lock),
- labelText: translate("Password"),
- labelStyle: const TextStyle(
- fontWeight: FontWeight.bold, color: MyTheme.accent80),
- suffix: isPermanent
- ? null
- : IconButton(
- icon: const Icon(Icons.refresh),
- onPressed: () =>
- bind.mainUpdateTemporaryPassword())),
- onSaved: (String? value) {},
+ IconButton(
+ visualDensity: VisualDensity.compact,
+ icon: Icon(Icons.copy_outlined),
+ onPressed: () {
+ copyToClipboard(model.serverId.value.text.trim());
+ })
+ ]).marginOnly(left: 39, bottom: 10),
+ // Password
+ Row(children: [
+ const Icon(Icons.lock_outline, color: Colors.grey, size: iconSize)
+ .marginOnly(right: iconMarginRight),
+ Text(
+ translate('One-time Password'),
+ style: textStyleHeading,
+ )
+ ]),
+ Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
+ Text(
+ isPermanent ? '-' : model.serverPasswd.value.text,
+ style: textStyleValue,
),
- ],
- ))
- : PaddingCard(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Center(
- child: Row(
- children: [
- const Icon(Icons.warning_amber_sharp,
- color: Colors.redAccent, size: 24),
- const SizedBox(width: 10),
- Expanded(
- child: Text(
- translate("Service is not running"),
- style: const TextStyle(
- fontFamily: 'WorkSans',
- fontWeight: FontWeight.bold,
- fontSize: 18,
- color: MyTheme.accent,
- ),
- ))
- ],
- )),
- const SizedBox(height: 5),
- Center(
- child: Text(
- translate("android_start_service_tip"),
- style: const TextStyle(fontSize: 12, color: MyTheme.darkGray),
- ))
- ],
- ));
+ isPermanent
+ ? SizedBox.shrink()
+ : Row(children: [
+ IconButton(
+ visualDensity: VisualDensity.compact,
+ icon: const Icon(Icons.refresh),
+ onPressed: () => bind.mainUpdateTemporaryPassword()),
+ IconButton(
+ visualDensity: VisualDensity.compact,
+ icon: Icon(Icons.copy_outlined),
+ onPressed: () {
+ copyToClipboard(
+ model.serverPasswd.value.text.trim());
+ })
+ ])
+ ]).marginOnly(left: 40, bottom: 15),
+ ConnectionStateNotification()
+ ],
+ ));
}
}
@@ -254,78 +313,37 @@ class _PermissionCheckerState extends State {
Widget build(BuildContext context) {
final serverModel = Provider.of(context);
final hasAudioPermission = androidVersion >= 30;
- final String status;
- if (serverModel.connectStatus == -1) {
- status = 'not_ready_status';
- } else if (serverModel.connectStatus == 0) {
- status = 'connecting_status';
- } else {
- status = 'Ready';
- }
return PaddingCard(
title: translate("Permissions"),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- PermissionRow(translate("Screen Capture"), serverModel.mediaOk,
- serverModel.toggleService),
- PermissionRow(translate("Input Control"), serverModel.inputOk,
- serverModel.toggleInput),
- PermissionRow(translate("Transfer File"), serverModel.fileOk,
- serverModel.toggleFile),
- hasAudioPermission
- ? PermissionRow(translate("Audio Capture"), serverModel.audioOk,
- serverModel.toggleAudio)
- : Text(
- "* ${translate("android_version_audio_tip")}",
+ child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ serverModel.mediaOk
+ ? ElevatedButton.icon(
+ style: ButtonStyle(
+ backgroundColor:
+ MaterialStateProperty.all(Colors.red)),
+ icon: const Icon(Icons.stop),
+ onPressed: serverModel.toggleService,
+ label: Text(translate("Stop service")))
+ .marginOnly(bottom: 8)
+ : SizedBox.shrink(),
+ PermissionRow(translate("Screen Capture"), serverModel.mediaOk,
+ serverModel.toggleService),
+ PermissionRow(translate("Input Control"), serverModel.inputOk,
+ serverModel.toggleInput),
+ PermissionRow(translate("Transfer File"), serverModel.fileOk,
+ serverModel.toggleFile),
+ hasAudioPermission
+ ? PermissionRow(translate("Audio Capture"), serverModel.audioOk,
+ serverModel.toggleAudio)
+ : Row(children: [
+ Icon(Icons.info_outline).marginOnly(right: 15),
+ Expanded(
+ child: Text(
+ translate("android_version_audio_tip"),
style: const TextStyle(color: MyTheme.darkGray),
- ),
- const SizedBox(height: 8),
- Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Expanded(
- flex: 0,
- child: serverModel.mediaOk
- ? ElevatedButton.icon(
- style: ButtonStyle(
- backgroundColor:
- MaterialStateProperty.all(Colors.red)),
- icon: const Icon(Icons.stop),
- onPressed: serverModel.toggleService,
- label: Text(translate("Stop service")))
- : ElevatedButton.icon(
- icon: const Icon(Icons.play_arrow),
- onPressed: serverModel.toggleService,
- label: Text(translate("Start Service")))),
- Expanded(
- child: serverModel.mediaOk
- ? Row(
- children: [
- Expanded(
- flex: 0,
- child: Padding(
- padding: const EdgeInsets.only(
- left: 20, right: 5),
- child: Icon(Icons.circle,
- color: serverModel.connectStatus > 0
- ? Colors.greenAccent
- : Colors.deepOrangeAccent,
- size: 10))),
- Expanded(
- child: Text(translate(status),
- softWrap: true,
- style: const TextStyle(
- fontSize: 14.0,
- fontWeight: FontWeight.w500,
- color: MyTheme.accent80)))
- ],
- )
- : const SizedBox.shrink())
- ],
- ),
- ],
- ));
+ ))
+ ])
+ ]));
}
}
@@ -339,37 +357,14 @@ class PermissionRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return Row(
- children: [
- Expanded(
- flex: 5,
- child: FittedBox(
- fit: BoxFit.scaleDown,
- alignment: Alignment.centerLeft,
- child:
- Text(name, style: Theme.of(context).textTheme.labelLarge))),
- Expanded(
- flex: 2,
- child: FittedBox(
- fit: BoxFit.scaleDown,
- child: Text(isOk ? translate("ON") : translate("OFF"),
- style: TextStyle(
- fontSize: 16.0,
- color: isOk ? Colors.green : Colors.grey))),
- ),
- Expanded(
- flex: 3,
- child: FittedBox(
- fit: BoxFit.scaleDown,
- alignment: Alignment.centerRight,
- child: TextButton(
- onPressed: onPressed,
- child: Text(
- translate(isOk ? "CLOSE" : "OPEN"),
- style: const TextStyle(fontWeight: FontWeight.bold),
- )))),
- ],
- );
+ return SwitchListTile(
+ visualDensity: VisualDensity.compact,
+ contentPadding: EdgeInsets.all(0),
+ title: Text(name),
+ value: isOk,
+ onChanged: (bool value) {
+ onPressed();
+ });
}
}
@@ -386,70 +381,66 @@ class ConnectionManager extends StatelessWidget {
? "File Connection"
: "Screen Connection"),
titleIcon: client.isFileTransfer
- ? Icons.folder_outlined
- : Icons.mobile_screen_share,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Expanded(child: ClientInfo(client)),
- Expanded(
- flex: -1,
- child: client.isFileTransfer || !client.authorized
- ? const SizedBox.shrink()
- : IconButton(
- onPressed: () {
- gFFI.chatModel.changeCurrentID(client.id);
- final bar =
- navigationBarKey.currentWidget;
- if (bar != null) {
- bar as BottomNavigationBar;
- bar.onTap!(1);
- }
- },
- icon: const Icon(
- Icons.chat,
- color: MyTheme.accent,
- )))
- ],
- ),
- client.authorized
- ? const SizedBox.shrink()
- : Text(
- translate("android_new_connection_tip"),
- style: Theme.of(globalKey.currentContext!)
- .textTheme
- .bodyMedium,
- ),
- client.authorized
- ? ElevatedButton.icon(
- style: ButtonStyle(
- backgroundColor:
- MaterialStateProperty.all(Colors.red)),
- icon: const Icon(Icons.close),
- onPressed: () {
- bind.cmCloseConnection(connId: client.id);
- gFFI.invokeMethod(
- "cancel_notification", client.id);
- },
- label: Text(translate("Close")))
- : Row(children: [
- TextButton(
- child: Text(translate("Dismiss")),
- onPressed: () {
- serverModel.sendLoginResponse(client, false);
- }),
- const SizedBox(width: 20),
- ElevatedButton(
- child: Text(translate("Accept")),
- onPressed: () {
- serverModel.sendLoginResponse(client, true);
- }),
- ]),
- ],
- )))
+ ? Icon(Icons.folder_outlined)
+ : Icon(Icons.mobile_screen_share),
+ child: Column(children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(child: ClientInfo(client)),
+ Expanded(
+ flex: -1,
+ child: client.isFileTransfer || !client.authorized
+ ? const SizedBox.shrink()
+ : IconButton(
+ onPressed: () {
+ gFFI.chatModel.changeCurrentID(client.id);
+ final bar = navigationBarKey.currentWidget;
+ if (bar != null) {
+ bar as BottomNavigationBar;
+ bar.onTap!(1);
+ }
+ },
+ icon: const Icon(Icons.chat)))
+ ],
+ ),
+ client.authorized
+ ? const SizedBox.shrink()
+ : Text(
+ translate("android_new_connection_tip"),
+ style: Theme.of(context).textTheme.bodyMedium,
+ ).marginOnly(bottom: 5),
+ client.authorized
+ ? Container(
+ alignment: Alignment.centerRight,
+ child: ElevatedButton.icon(
+ style: ButtonStyle(
+ backgroundColor:
+ MaterialStatePropertyAll(Colors.red)),
+ icon: const Icon(Icons.close),
+ onPressed: () {
+ bind.cmCloseConnection(connId: client.id);
+ gFFI.invokeMethod(
+ "cancel_notification", client.id);
+ },
+ label: Text(translate("Disconnect"))))
+ : Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ TextButton(
+ child: Text(translate("Dismiss")),
+ onPressed: () {
+ serverModel.sendLoginResponse(
+ client, false);
+ }).marginOnly(right: 15),
+ ElevatedButton.icon(
+ icon: const Icon(Icons.check),
+ label: Text(translate("Accept")),
+ onPressed: () {
+ serverModel.sendLoginResponse(client, true);
+ }),
+ ]),
+ ])))
.toList());
}
}
@@ -459,7 +450,7 @@ class PaddingCard extends StatelessWidget {
: super(key: key);
final String? title;
- final IconData? titleIcon;
+ final Icon? titleIcon;
final Widget child;
@override
@@ -469,23 +460,16 @@ class PaddingCard extends StatelessWidget {
children.insert(
0,
Padding(
- padding: const EdgeInsets.symmetric(vertical: 5.0),
+ padding: const EdgeInsets.fromLTRB(0, 5, 0, 8),
child: Row(
children: [
- titleIcon != null
- ? Padding(
- padding: const EdgeInsets.only(right: 10),
- child:
- Icon(titleIcon, color: MyTheme.accent, size: 30))
- : const SizedBox.shrink(),
- Text(
- title!,
- style: const TextStyle(
- fontFamily: 'WorkSans',
- fontWeight: FontWeight.bold,
- fontSize: 20,
- color: MyTheme.accent,
- ),
+ titleIcon?.marginOnly(right: 10) ?? const SizedBox.shrink(),
+ Expanded(
+ child: Text(title!,
+ style: Theme.of(context)
+ .textTheme
+ .titleLarge
+ ?.merge(TextStyle(fontWeight: FontWeight.bold))),
)
],
)));
@@ -493,12 +477,14 @@ class PaddingCard extends StatelessWidget {
return SizedBox(
width: double.maxFinite,
child: Card(
- margin: const EdgeInsets.fromLTRB(15.0, 15.0, 15.0, 0),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(13),
+ ),
+ margin: const EdgeInsets.fromLTRB(12.0, 10.0, 12.0, 0),
child: Padding(
padding:
- const EdgeInsets.symmetric(vertical: 15.0, horizontal: 30.0),
+ const EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0),
child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
children: children,
),
),
@@ -514,7 +500,7 @@ class ClientInfo extends StatelessWidget {
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
- child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ child: Column(children: [
Row(
children: [
Expanded(
@@ -522,21 +508,19 @@ class ClientInfo extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.only(right: 12),
child: CircleAvatar(
- backgroundColor:
- str2color(client.name).withOpacity(0.7),
+ backgroundColor: str2color(
+ client.name,
+ Theme.of(context).brightness == Brightness.light
+ ? 255
+ : 150),
child: Text(client.name[0])))),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisAlignment: MainAxisAlignment.center,
children: [
- Text(client.name,
- style: const TextStyle(
- color: MyTheme.idColor, fontSize: 18)),
+ Text(client.name, style: const TextStyle(fontSize: 18)),
const SizedBox(width: 8),
- Text(client.peerId,
- style: const TextStyle(
- color: MyTheme.idColor, fontSize: 10))
+ Text(client.peerId, style: const TextStyle(fontSize: 10))
]))
],
),
@@ -567,7 +551,7 @@ void androidChannelInit() {
{
var type = arguments["type"] as String;
var result = arguments["result"] as bool;
- PermissionManager.complete(type, result);
+ AndroidPermissionManager.complete(type, result);
break;
}
case "on_media_projection_canceled":
diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart
index c5f3b6935..c19601956 100644
--- a/flutter/lib/mobile/pages/settings_page.dart
+++ b/flutter/lib/mobile/pages/settings_page.dart
@@ -10,6 +10,7 @@ import 'package:url_launcher/url_launcher.dart';
import '../../common.dart';
import '../../common/widgets/dialog.dart';
import '../../common/widgets/login.dart';
+import '../../consts.dart';
import '../../models/model.dart';
import '../../models/platform_model.dart';
import '../widgets/dialog.dart';
@@ -31,18 +32,20 @@ class SettingsPage extends StatefulWidget implements PageShape {
}
const url = 'https://rustdesk.com/';
-final _hasIgnoreBattery = androidVersion >= 26;
-var _ignoreBatteryOpt = false;
-var _enableAbr = false;
-var _denyLANDiscovery = false;
-var _onlyWhiteList = false;
-var _enableDirectIPAccess = false;
-var _enableRecordSession = false;
-var _autoRecordIncomingSession = false;
-var _localIP = "";
-var _directAccessPort = "";
class _SettingsState extends State with WidgetsBindingObserver {
+ final _hasIgnoreBattery = androidVersion >= 26;
+ var _ignoreBatteryOpt = false;
+ var _enableStartOnBoot = false;
+ var _enableAbr = false;
+ var _denyLANDiscovery = false;
+ var _onlyWhiteList = false;
+ var _enableDirectIPAccess = false;
+ var _enableRecordSession = false;
+ var _autoRecordIncomingSession = false;
+ var _localIP = "";
+ var _directAccessPort = "";
+
@override
void initState() {
super.initState();
@@ -50,11 +53,34 @@ class _SettingsState extends State with WidgetsBindingObserver {
() async {
var update = false;
+
if (_hasIgnoreBattery) {
- update = await updateIgnoreBatteryStatus();
+ if (await checkAndUpdateIgnoreBatteryStatus()) {
+ update = true;
+ }
}
- final enableAbrRes = await bind.mainGetOption(key: "enable-abr") != "N";
+ if (await checkAndUpdateStartOnBoot()) {
+ update = true;
+ }
+
+ // start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW
+ var enableStartOnBoot =
+ await gFFI.invokeMethod(AndroidChannel.kGetStartOnBootOpt);
+ if (enableStartOnBoot) {
+ if (!await canStartOnBoot()) {
+ enableStartOnBoot = false;
+ gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, false);
+ }
+ }
+
+ if (enableStartOnBoot != _enableStartOnBoot) {
+ update = true;
+ _enableStartOnBoot = enableStartOnBoot;
+ }
+
+ final enableAbrRes = option2bool(
+ "enable-abr", await bind.mainGetOption(key: "enable-abr"));
if (enableAbrRes != _enableAbr) {
update = true;
_enableAbr = enableAbrRes;
@@ -125,15 +151,18 @@ class _SettingsState extends State with WidgetsBindingObserver {
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
() async {
- if (await updateIgnoreBatteryStatus()) {
+ final ibs = await checkAndUpdateIgnoreBatteryStatus();
+ final sob = await checkAndUpdateStartOnBoot();
+ if (ibs || sob) {
setState(() {});
}
}();
}
}
- Future updateIgnoreBatteryStatus() async {
- final res = await PermissionManager.check("ignore_battery_optimizations");
+ Future checkAndUpdateIgnoreBatteryStatus() async {
+ final res = await AndroidPermissionManager.check(
+ kRequestIgnoreBatteryOptimizations);
if (_ignoreBatteryOpt != res) {
_ignoreBatteryOpt = res;
return true;
@@ -142,6 +171,18 @@ class _SettingsState extends State with WidgetsBindingObserver {
}
}
+ Future checkAndUpdateStartOnBoot() async {
+ if (!await canStartOnBoot() && _enableStartOnBoot) {
+ _enableStartOnBoot = false;
+ debugPrint(
+ "checkAndUpdateStartOnBoot and set _enableStartOnBoot -> false");
+ gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, false);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
@override
Widget build(BuildContext context) {
Provider.of(context);
@@ -265,7 +306,8 @@ class _SettingsState extends State with WidgetsBindingObserver {
]),
onToggle: (v) async {
if (v) {
- PermissionManager.request("ignore_battery_optimizations");
+ await AndroidPermissionManager.request(
+ kRequestIgnoreBatteryOptimizations);
} else {
final res = await gFFI.dialogManager
.show((setState, close) => CustomAlertDialog(
@@ -282,11 +324,44 @@ class _SettingsState extends State with WidgetsBindingObserver {
],
));
if (res == true) {
- PermissionManager.request("application_details_settings");
+ AndroidPermissionManager.startAction(
+ kActionApplicationDetailsSettings);
}
}
}));
}
+ enhancementsTiles.add(SettingsTile.switchTile(
+ initialValue: _enableStartOnBoot,
+ title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
+ Text("${translate('Start on Boot')} (beta)"),
+ Text(
+ '* ${translate('Start the screen sharing service on boot, requires special permissions')}',
+ style: Theme.of(context).textTheme.bodySmall),
+ ]),
+ onToggle: (toValue) async {
+ if (toValue) {
+ // 1. request kIgnoreBatteryOptimizations
+ if (!await AndroidPermissionManager.check(
+ kRequestIgnoreBatteryOptimizations)) {
+ if (!await AndroidPermissionManager.request(
+ kRequestIgnoreBatteryOptimizations)) {
+ return;
+ }
+ }
+
+ // 2. request kSystemAlertWindow
+ if (!await AndroidPermissionManager.check(kSystemAlertWindow)) {
+ if (!await AndroidPermissionManager.request(kSystemAlertWindow)) {
+ return;
+ }
+ }
+
+ // (Optional) 3. request input permission
+ }
+ setState(() => _enableStartOnBoot = toValue);
+
+ gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, toValue);
+ }));
return SettingsList(
sections: [
@@ -322,8 +397,13 @@ class _SettingsState extends State with WidgetsBindingObserver {
showLanguageSettings(gFFI.dialogManager);
}),
SettingsTile.navigation(
- title: Text(translate('Dark Theme')),
- leading: Icon(Icons.dark_mode),
+ title: Text(translate(
+ Theme.of(context).brightness == Brightness.light
+ ? 'Dark Theme'
+ : 'Light Theme')),
+ leading: Icon(Theme.of(context).brightness == Brightness.light
+ ? Icons.dark_mode
+ : Icons.light_mode),
onPressed: (context) {
showThemeSettings(gFFI.dialogManager);
},
@@ -387,6 +467,17 @@ class _SettingsState extends State with WidgetsBindingObserver {
],
);
}
+
+ Future canStartOnBoot() async {
+ // start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW
+ if (_hasIgnoreBattery && !_ignoreBatteryOpt) {
+ return false;
+ }
+ if (!await AndroidPermissionManager.check(kSystemAlertWindow)) {
+ return false;
+ }
+ return true;
+ }
}
void showServerSettings(OverlayDialogManager dialogManager) async {
diff --git a/flutter/lib/mobile/widgets/dialog.dart b/flutter/lib/mobile/widgets/dialog.dart
index 931999382..b14401795 100644
--- a/flutter/lib/mobile/widgets/dialog.dart
+++ b/flutter/lib/mobile/widgets/dialog.dart
@@ -25,19 +25,26 @@ void showRestartRemoteDevice(
final res =
await dialogManager.show((setState, close) => CustomAlertDialog(
title: Row(children: [
- Icon(Icons.warning_amber_sharp,
- color: Colors.redAccent, size: 28),
- SizedBox(width: 10),
- Text(translate("Restart Remote Device")),
+ Icon(Icons.warning_rounded, color: Colors.redAccent, size: 28),
+ Text(translate("Restart Remote Device")).paddingOnly(left: 10),
]),
content: Text(
"${translate('Are you sure you want to restart')} \n${pi.username}@${pi.hostname}($id) ?"),
+ actions: [
+ dialogButton(
+ "Cancel",
+ icon: Icon(Icons.close_rounded),
+ onPressed: close,
+ isOutline: true,
+ ),
+ dialogButton(
+ "OK",
+ icon: Icon(Icons.done_rounded),
+ onPressed: () => close(true),
+ ),
+ ],
onCancel: close,
onSubmit: () => close(true),
- actions: [
- dialogButton("Cancel", onPressed: close, isOutline: true),
- dialogButton("OK", onPressed: () => close(true)),
- ],
));
if (res == true) bind.sessionRestartRemoteDevice(id: id);
}
@@ -62,7 +69,13 @@ void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async {
}
return CustomAlertDialog(
- title: Text(translate('Set your own password')),
+ title: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(Icons.password_rounded, color: MyTheme.accent),
+ Text(translate('Set your own password')).paddingOnly(left: 10),
+ ],
+ ),
content: Form(
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(mainAxisSize: MainAxisSize.min, children: [
@@ -112,11 +125,13 @@ void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async {
actions: [
dialogButton(
'Cancel',
+ icon: Icon(Icons.close_rounded),
onPressed: close,
isOutline: true,
),
dialogButton(
'OK',
+ icon: Icon(Icons.done_rounded),
onPressed: (validateLength && validateSame) ? submit : null,
),
],
@@ -147,7 +162,7 @@ void setTemporaryPasswordLengthDialog(
}
return CustomAlertDialog(
- title: Text(translate("Set temporary password length")),
+ title: Text(translate("Set one-time password length")),
content: Column(
mainAxisSize: MainAxisSize.min,
children:
@@ -178,7 +193,13 @@ void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async {
}
return CustomAlertDialog(
- title: Text(translate('Password Required')),
+ title: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Icon(Icons.password_rounded, color: MyTheme.accent),
+ Text(translate('Password Required')).paddingOnly(left: 10),
+ ],
+ ),
content: Column(mainAxisSize: MainAxisSize.min, children: [
PasswordWidget(controller: controller),
CheckboxListTile(
@@ -197,8 +218,17 @@ void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async {
),
]),
actions: [
- dialogButton('Cancel', onPressed: cancel, isOutline: true),
- dialogButton('OK', onPressed: submit),
+ dialogButton(
+ 'Cancel',
+ icon: Icon(Icons.close_rounded),
+ onPressed: cancel,
+ isOutline: true,
+ ),
+ dialogButton(
+ 'OK',
+ icon: Icon(Icons.done_rounded),
+ onPressed: submit,
+ ),
],
onSubmit: submit,
onCancel: cancel,
@@ -437,7 +467,7 @@ void showRequestElevationDialog(String id, OverlayDialogManager dialogManager) {
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.symmetric(vertical: 15),
- hintText: 'eg: admin',
+ hintText: translate('eg: admin'),
errorText: errUser.isEmpty ? null : errUser.value),
onChanged: (s) {
if (s.isNotEmpty) {
diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart
index 8666e13e4..9db5a1571 100644
--- a/flutter/lib/models/chat_model.dart
+++ b/flutter/lib/models/chat_model.dart
@@ -43,7 +43,7 @@ class ChatModel with ChangeNotifier {
final ChatUser me = ChatUser(
id: "",
- firstName: "Me",
+ firstName: translate("Me"),
);
late final Map _messages = {}..[clientModeID] =
diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart
index 5817e54fe..4170a5461 100644
--- a/flutter/lib/models/file_model.dart
+++ b/flutter/lib/models/file_model.dart
@@ -24,298 +24,83 @@ enum SortBy {
}
}
-class FileModel extends ChangeNotifier {
- /// mobile, current selected page show on mobile screen
- var _isSelectedLocal = false;
-
- /// mobile, select mode state
- var _selectMode = false;
-
- final _localOption = DirectoryOption();
- final _remoteOption = DirectoryOption();
-
- List localHistory = [];
- List remoteHistory = [];
-
- var _jobId = 0;
-
- final _jobProgress = JobProgress(); // from rust update
-
- /// JobTable
- final _jobTable = List.empty(growable: true).obs;
-
- /// `isLocal` bool
- Function(bool)? onDirChanged;
-
- RxList get jobTable => _jobTable;
-
- bool get isLocal => _isSelectedLocal;
-
- bool get selectMode => _selectMode;
-
- JobProgress get jobProgress => _jobProgress;
-
- JobState get jobState => _jobProgress.state;
-
- SortBy _sortStyle = SortBy.name;
-
- SortBy get sortStyle => _sortStyle;
-
- SortBy _localSortStyle = SortBy.name;
-
- bool _localSortAscending = true;
-
- bool _remoteSortAscending = true;
-
- SortBy _remoteSortStyle = SortBy.name;
-
- bool get localSortAscending => _localSortAscending;
-
- SortBy getSortStyle(bool isLocal) {
- return isLocal ? _localSortStyle : _remoteSortStyle;
+class JobID {
+ int _count = 0;
+ int next() {
+ _count++;
+ return _count;
}
+}
- bool getSortAscending(bool isLocal) {
- return isLocal ? _localSortAscending : _remoteSortAscending;
- }
-
- FileDirectory _currentLocalDir = FileDirectory();
-
- FileDirectory get currentLocalDir => _currentLocalDir;
-
- FileDirectory _currentRemoteDir = FileDirectory();
-
- FileDirectory get currentRemoteDir => _currentRemoteDir;
-
- FileDirectory get currentDir =>
- _isSelectedLocal ? currentLocalDir : currentRemoteDir;
-
- FileDirectory getCurrentDir(bool isLocal) {
- return isLocal ? currentLocalDir : currentRemoteDir;
- }
-
- String getCurrentShortPath(bool isLocal) {
- final currentDir = getCurrentDir(isLocal);
- final currentHome = getCurrentHome(isLocal);
- if (currentDir.path.startsWith(currentHome)) {
- var path = currentDir.path.replaceFirst(currentHome, "");
- if (path.isEmpty) return "";
- if (path[0] == "/" || path[0] == "\\") {
- // remove more '/' or '\'
- path = path.replaceFirst(path[0], "");
- }
- return path;
- } else {
- return currentDir.path.replaceFirst(currentHome, "");
- }
- }
-
- String get currentHome =>
- _isSelectedLocal ? _localOption.home : _remoteOption.home;
-
- String getCurrentHome(bool isLocal) {
- return isLocal ? _localOption.home : _remoteOption.home;
- }
-
- int getJob(int id) {
- return jobTable.indexWhere((element) => element.id == id);
- }
-
- String get currentShortPath {
- if (currentDir.path.startsWith(currentHome)) {
- var path = currentDir.path.replaceFirst(currentHome, "");
- if (path.isEmpty) return "";
- if (path[0] == "/" || path[0] == "\\") {
- // remove more '/' or '\'
- path = path.replaceFirst(path[0], "");
- }
- return path;
- } else {
- return currentDir.path.replaceFirst(currentHome, "");
- }
- }
-
- String shortPath(bool isLocal) {
- final dir = isLocal ? currentLocalDir : currentRemoteDir;
- if (dir.path.startsWith(currentHome)) {
- var path = dir.path.replaceFirst(currentHome, "");
- if (path.isEmpty) return "";
- if (path[0] == "/" || path[0] == "\\") {
- // remove more '/' or '\'
- path = path.replaceFirst(path[0], "");
- }
- return path;
- } else {
- return dir.path.replaceFirst(currentHome, "");
- }
- }
-
- bool getCurrentShowHidden([bool? isLocal]) {
- final isLocal_ = isLocal ?? _isSelectedLocal;
- return isLocal_ ? _localOption.showHidden : _remoteOption.showHidden;
- }
-
- bool getCurrentIsWindows([bool? isLocal]) {
- final isLocal_ = isLocal ?? _isSelectedLocal;
- return isLocal_ ? _localOption.isWindows : _remoteOption.isWindows;
- }
-
- final _fileFetcher = FileFetcher();
-
- final _jobResultListener = JobResultListener