diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index 5cc4bb78809c..c50d550257db 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -6,7 +6,7 @@ on: workflow_call: env: - FLUTTER_VERSION: "3.13.9" + FLUTTER_VERSION: "3.16.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" jobs: diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 7027dbb45ce0..6b115c2affe9 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -13,11 +13,11 @@ on: env: CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.13.9" + FLUTTER_VERSION: "3.16.9" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work - FLUTTER_ELINUX_VERSION: "3.13.9" - FLUTTER_ELINUX_COMMIT_ID: "f4d4205893c16b0aa9cb6ba46b9f32b639d3b057" + FLUTTER_ELINUX_VERSION: "3.16.9" + FLUTTER_ELINUX_COMMIT_ID: "c02bd16e1630f5bd690b85c5c2456ac1920e25af" TAG_NAME: "${{ inputs.upload-tag }}" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # vcpkg version: 2023.10.19 @@ -73,7 +73,7 @@ jobs: run: | flutter doctor -v flutter precache --windows - Invoke-WebRequest -Uri https://github.com/fufesou/flutter-engine/releases/download/bugfix-crash-windows-angle-context-frameBuffer/windows-x64-release.zip -OutFile windows-x64-flutter-release.zip + Invoke-WebRequest -Uri https://github.com/fufesou/flutter-engine/releases/download/bugfix-subwindow-crash-3.16.9-apply-pull-47787/windows-x64-release.zip -OutFile windows-x64-flutter-release.zip Expand-Archive windows-x64-flutter-release.zip -DestinationPath . mv -Force windows-x64-release/* C:/hostedtoolcache/windows/flutter/stable-${{ env.FLUTTER_VERSION }}-x64/bin/cache/artifacts/engine/windows-x64-release/ @@ -141,7 +141,7 @@ jobs: if: env.UPLOAD_ARTIFACT == 'true' run: | pushd ./libs/portable - python3 ./generate.py -f ../../flutter/build/windows/runner/Release/ -o . -e ../../flutter/build/windows/runner/Release/rustdesk.exe + python3 ./generate.py -f ../../flutter/build/windows/x64/runner/Release/ -o . -e ../../flutter/build/windows/x64/runner/Release/rustdesk.exe popd mkdir -p ./SignOutput mv ./target/release/rustdesk-portable-packer.exe ./SignOutput/rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.exe diff --git a/.github/workflows/history.yml b/.github/workflows/history.yml index 5d7842fd4c44..f016494a8eee 100644 --- a/.github/workflows/history.yml +++ b/.github/workflows/history.yml @@ -4,7 +4,7 @@ on: [workflow_dispatch] env: LLVM_VERSION: "10.0" - FLUTTER_VERSION: "3.13.9" + FLUTTER_VERSION: "3.16.9" TAG_NAME: "tmp" FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" diff --git a/Cargo.lock b/Cargo.lock index b1e66b8f5cf9..8d560f16d1b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,24 +443,23 @@ dependencies = [ [[package]] name = "atk" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" +checksum = "b4af014b17dd80e8af9fa689b2d4a211ddba6eb583c1622f35d0cb543f6b17e4" dependencies = [ "atk-sys", - "bitflags 1.3.2", - "glib 0.16.9", + "glib 0.18.5", "libc", ] [[package]] name = "atk-sys" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ad703eb64dc058024f0e57ccfa069e15a413b98dbd50a1a950e743b7f11148" +checksum = "251e0b7d90e33e0ba930891a505a9a35ece37b2dd37a14f3ffc306c13b980009" dependencies = [ - "glib-sys 0.16.3", - "gobject-sys 0.16.3", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "system-deps 6.1.2", ] @@ -641,6 +640,9 @@ name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde 1.0.190", +] [[package]] name = "bitmask-enum" @@ -803,7 +805,7 @@ dependencies = [ "bitmask-enum", "block2", "core-foundation 0.9.3 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", - "core-graphics 0.23.1", + "core-graphics 0.23.1 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", "dispatch", "lazy_static", "libc", @@ -815,13 +817,13 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.16.7" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cairo-sys-rs", - "glib 0.16.9", + "glib 0.18.5", "libc", "once_cell", "thiserror", @@ -829,11 +831,11 @@ dependencies = [ [[package]] name = "cairo-sys-rs" -version = "0.16.3" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" dependencies = [ - "glib-sys 0.16.3", + "glib-sys 0.18.1", "libc", "system-deps 6.1.2", ] @@ -1043,6 +1045,22 @@ dependencies = [ "objc", ] +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.5.0", + "libc", + "objc", +] + [[package]] name = "cocoa-foundation" version = "0.1.2" @@ -1199,6 +1217,19 @@ dependencies = [ "libc", ] +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics-types 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.5.0", + "libc", +] + [[package]] name = "core-graphics" version = "0.23.1" @@ -1376,23 +1407,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "dark-light" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62007a65515b3cd88c733dd3464431f05d2ad066999a824259d8edc3cf6f645" -dependencies = [ - "dconf_rs", - "detect-desktop-environment", - "dirs 4.0.0", - "objc", - "rust-ini", - "web-sys", - "winreg 0.10.1", - "zbus", - "zvariant", -] - [[package]] name = "dart-sys" version = "4.0.2" @@ -1554,12 +1568,6 @@ dependencies = [ "dbus", ] -[[package]] -name = "dconf_rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" - [[package]] name = "debug-helper" version = "0.3.13" @@ -1627,12 +1635,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "detect-desktop-environment" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" - [[package]] name = "digest" version = "0.10.7" @@ -1664,15 +1666,6 @@ dependencies = [ "dirs-sys 0.3.7", ] -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys 0.3.7", -] - [[package]] name = "dirs" version = "5.0.1" @@ -2371,57 +2364,56 @@ dependencies = [ [[package]] name = "gdk" -version = "0.16.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" +checksum = "f5ba081bdef3b75ebcdbfc953699ed2d7417d6bd853347a42a37d76406a33646" dependencies = [ - "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", "gio", - "glib 0.16.9", + "glib 0.18.5", "libc", "pango", ] [[package]] name = "gdk-pixbuf" -version = "0.16.7" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ - "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", - "glib 0.16.9", + "glib 0.18.5", "libc", + "once_cell", ] [[package]] name = "gdk-pixbuf-sys" -version = "0.16.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ "gio-sys", - "glib-sys 0.16.3", - "gobject-sys 0.16.3", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "system-deps 6.1.2", ] [[package]] name = "gdk-sys" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" +checksum = "31ff856cb3386dae1703a920f803abafcc580e9b5f711ca62ed1620c25b51ff2" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gio-sys", - "glib-sys 0.16.3", - "gobject-sys 0.16.3", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "pango-sys", "pkg-config", @@ -2430,13 +2422,13 @@ dependencies = [ [[package]] name = "gdkwayland-sys" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4511710212ed3020b61a8622a37aa6f0dd2a84516575da92e9b96928dcbe83ba" +checksum = "a90fbf5c033c65d93792192a49a8efb5bb1e640c419682a58bb96f5ae77f3d4a" dependencies = [ "gdk-sys", - "glib-sys 0.16.3", - "gobject-sys 0.16.3", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "pkg-config", "system-deps 6.1.2", @@ -2444,12 +2436,12 @@ dependencies = [ [[package]] name = "gdkx11-sys" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa2bf8b5b8c414bc5d05e48b271896d0fd3ddb57464a3108438082da61de6af" +checksum = "fee8f00f4ee46cad2939b8990f5c70c94ff882c3028f3cc5abf950fa4ab53043" dependencies = [ "gdk-sys", - "glib-sys 0.16.3", + "glib-sys 0.18.1", "libc", "system-deps 6.1.2", "x11 2.21.0", @@ -2504,17 +2496,16 @@ checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "gio" -version = "0.16.7" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" dependencies = [ - "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", "futures-util", "gio-sys", - "glib 0.16.9", + "glib 0.18.5", "libc", "once_cell", "pin-project-lite", @@ -2524,12 +2515,12 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.16.3" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" dependencies = [ - "glib-sys 0.16.3", - "gobject-sys 0.16.3", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "system-deps 6.1.2", "winapi 0.3.9", @@ -2569,21 +2560,22 @@ dependencies = [ [[package]] name = "glib" -version = "0.16.9" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16aa2475c9debed5a32832cb5ff2af5a3f9e1ab9e69df58eaadc1ab2004d6eba" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "futures-channel", "futures-core", "futures-executor", "futures-task", "futures-util", "gio-sys", - "glib-macros 0.16.8", - "glib-sys 0.16.3", - "gobject-sys 0.16.3", + "glib-macros 0.18.5", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", + "memchr", "once_cell", "smallvec", "thiserror", @@ -2607,17 +2599,16 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.16.8" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ - "anyhow", "heck 0.4.1", - "proc-macro-crate 1.3.1", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2 1.0.69", "quote 1.0.33", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] @@ -2632,9 +2623,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.16.3" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" dependencies = [ "libc", "system-deps 6.1.2", @@ -2659,11 +2650,11 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.16.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" dependencies = [ - "glib-sys 0.16.3", + "glib-sys 0.18.1", "libc", "system-deps 6.1.2", ] @@ -2833,40 +2824,38 @@ dependencies = [ [[package]] name = "gtk" -version = "0.16.2" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" +checksum = "93c4f5e0e20b60e10631a5f06da7fe3dda744b05ad0ea71fee2f47adf865890c" dependencies = [ "atk", - "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", "gdk", "gdk-pixbuf", "gio", - "glib 0.16.9", + "glib 0.18.5", "gtk-sys", "gtk3-macros", "libc", - "once_cell", "pango", "pkg-config", ] [[package]] name = "gtk-sys" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b5f8946685d5fe44497007786600c2f368ff6b1e61a16251c89f72a97520a3" +checksum = "771437bf1de2c1c0b496c11505bdf748e26066bbe942dfc8f614c9460f6d7722" dependencies = [ "atk-sys", "cairo-sys-rs", "gdk-pixbuf-sys", "gdk-sys", "gio-sys", - "glib-sys 0.16.3", - "gobject-sys 0.16.3", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "pango-sys", "system-deps 6.1.2", @@ -2874,16 +2863,15 @@ dependencies = [ [[package]] name = "gtk3-macros" -version = "0.16.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "096eb63c6fedf03bafe65e5924595785eaf1bcb7200dac0f2cbe9c9738f05ad8" +checksum = "c6063efb63db582968fb7df72e1ae68aa6360dcfb0a75143f34fc7d616bad75e" dependencies = [ - "anyhow", "proc-macro-crate 1.3.1", "proc-macro-error", "proc-macro2 1.0.69", "quote 1.0.33", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] @@ -3160,7 +3148,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.51.1", ] [[package]] @@ -3445,11 +3433,11 @@ dependencies = [ [[package]] name = "keyboard-types" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "serde 1.0.190", "unicode-segmentation", ] @@ -3474,11 +3462,11 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libappindicator" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e1edfdc9b0853358306c6dfb4b77c79c779174256fe93d80c0b5ebca451a2f" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" dependencies = [ - "glib 0.16.9", + "glib 0.18.5", "gtk", "gtk-sys", "libappindicator-sys", @@ -3487,9 +3475,9 @@ dependencies = [ [[package]] name = "libappindicator-sys" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08fcb2bea89cee9613982501ec83eaa2d09256b24540ae463c52a28906163918" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" dependencies = [ "gtk-sys", "libloading 0.7.4", @@ -3718,7 +3706,7 @@ dependencies = [ [[package]] name = "magnum-opus" version = "0.4.0" -source = "git+https://github.com/rustdesk/magnum-opus#5cd2bf989c148662fa3a2d9d539a71d71fd1d256" +source = "git+https://github.com/rustdesk-org/magnum-opus#5cd2bf989c148662fa3a2d9d539a71d71fd1d256" dependencies = [ "bindgen 0.59.2", "pkg-config", @@ -3815,11 +3803,11 @@ dependencies = [ [[package]] name = "miow" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123" +checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -3832,14 +3820,12 @@ dependencies = [ [[package]] name = "muda" -version = "0.5.0" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c820db003e601413e835a33b10cf51452b6415ef34ff1d862401826431c675" +checksum = "e406691fa7749604bbc7964bde28a300572d52621bb84540f6907c0f8fe08737" dependencies = [ - "cocoa", + "cocoa 0.25.0", "crossbeam-channel", - "gdk", - "gdk-pixbuf", "gtk", "keyboard-types", "libxdo", @@ -3847,7 +3833,7 @@ dependencies = [ "once_cell", "png", "thiserror", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3884,7 +3870,7 @@ dependencies = [ "jni-sys", "ndk-sys", "num_enum", - "raw-window-handle", + "raw-window-handle 0.5.2", "thiserror", ] @@ -4430,13 +4416,12 @@ dependencies = [ [[package]] name = "pango" -version = "0.16.5" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" dependencies = [ - "bitflags 1.3.2", "gio", - "glib 0.16.9", + "glib 0.18.5", "libc", "once_cell", "pango-sys", @@ -4444,20 +4429,20 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.16.3" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" dependencies = [ - "glib-sys 0.16.3", - "gobject-sys 0.16.3", + "glib-sys 0.18.1", + "gobject-sys 0.18.0", "libc", "system-deps 6.1.2", ] [[package]] name = "parity-tokio-ipc" -version = "0.7.3-2" -source = "git+https://github.com/open-trade/parity-tokio-ipc#a5b7861249107cbacc856cd43507cb95f40aef6e" +version = "0.7.3-3" +source = "git+https://github.com/rustdesk-org/parity-tokio-ipc#f2d1fcf8fb002335d9a62bec308559d40698694d" dependencies = [ "futures", "libc", @@ -4734,6 +4719,15 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5092,6 +5086,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + [[package]] name = "rayon" version = "1.8.0" @@ -5117,7 +5117,7 @@ name = "rdev" version = "0.5.0-2" source = "git+https://github.com/fufesou/rdev#b3434caee84c92412b45a2f655a15ac5dad33488" dependencies = [ - "cocoa", + "cocoa 0.24.1", "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.8.4", "core-graphics 0.22.3", @@ -5424,13 +5424,12 @@ dependencies = [ "cidr-utils", "clap 4.4.7", "clipboard", - "cocoa", + "cocoa 0.24.1", "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.22.3", "cpal", "crossbeam-queue", "ctrlc", - "dark-light", "dasp", "dbus", "dbus-crossroads", @@ -5957,6 +5956,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "sodiumoxide" version = "0.2.7" @@ -6180,25 +6189,18 @@ dependencies = [ [[package]] name = "tao" -version = "0.22.2" -source = "git+https://github.com/rustdesk-org/tao?branch=dev#1e5b97258cf42a30f80f85a6aa0b1a4aece1977e" +version = "0.25.0" +source = "git+https://github.com/rustdesk-org/tao?branch=dev#1a813dc8788735ff0ad427ffa71394aa02d16709" dependencies = [ "bitflags 1.3.2", - "cairo-rs", "cc", - "cocoa", + "cocoa 0.25.0", "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.22.3", + "core-graphics 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel", "dispatch", - "gdk", - "gdk-pixbuf", - "gdk-sys", "gdkwayland-sys", "gdkx11-sys", - "gio", - "glib 0.16.9", - "glib-sys 0.16.3", "gtk", "image", "instant", @@ -6213,14 +6215,14 @@ dependencies = [ "once_cell", "parking_lot", "png", - "raw-window-handle", + "raw-window-handle 0.6.0", "scopeguard", "tao-macros", "unicode-segmentation", "url", - "uuid", - "windows 0.48.0", + "windows 0.52.0", "windows-implement", + "windows-version", "x11-dl", "zbus", ] @@ -6228,7 +6230,7 @@ dependencies = [ [[package]] name = "tao-macros" version = "0.1.2" -source = "git+https://github.com/rustdesk-org/tao?branch=dev#1e5b97258cf42a30f80f85a6aa0b1a4aece1977e" +source = "git+https://github.com/rustdesk-org/tao?branch=dev#1a813dc8788735ff0ad427ffa71394aa02d16709" dependencies = [ "proc-macro2 1.0.69", "quote 1.0.33", @@ -6419,11 +6421,11 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ - "autocfg 1.1.0", + "backtrace", "bytes", "libc", "mio", @@ -6431,16 +6433,16 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.4.10", + "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2 1.0.69", "quote 1.0.33", @@ -6668,11 +6670,11 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.5.1" -source = "git+https://github.com/rustdesk-org/tray-icon#ef98e7b98abed2e3da614277eced12a85bfb717c" +version = "0.11.3" +source = "git+https://github.com/tauri-apps/tray-icon#b8dbd42c6f94a29f34b0a0daa619486277185512" dependencies = [ - "cocoa", - "core-graphics 0.22.3", + "cocoa 0.25.0", + "core-graphics 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel", "dirs-next", "libappindicator", @@ -6681,7 +6683,7 @@ dependencies = [ "once_cell", "png", "thiserror", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7318,8 +7320,6 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-implement", - "windows-interface", "windows-targets 0.48.5", ] @@ -7329,10 +7329,22 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ - "windows-core", + "windows-core 0.51.1", "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-implement", + "windows-interface", + "windows-targets 0.52.0", +] + [[package]] name = "windows-core" version = "0.51.1" @@ -7342,26 +7354,35 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-implement" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" +checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946" dependencies = [ "proc-macro2 1.0.69", "quote 1.0.33", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] name = "windows-interface" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" +checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1" dependencies = [ "proc-macro2 1.0.69", "quote 1.0.33", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] @@ -7408,6 +7429,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -7438,6 +7468,30 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows-version" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -7450,6 +7504,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.32.0" @@ -7474,6 +7534,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.32.0" @@ -7498,6 +7564,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.32.0" @@ -7522,6 +7594,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.32.0" @@ -7546,6 +7624,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -7558,6 +7642,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.32.0" @@ -7582,6 +7672,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.17" @@ -7591,15 +7687,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "winreg" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index b1368b8375df..d37f694a3c76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,9 +56,9 @@ cfg-if = "1.0" lazy_static = "1.4" sha2 = "0.10" repng = "0.2" -parity-tokio-ipc = { git = "https://github.com/open-trade/parity-tokio-ipc" } +parity-tokio-ipc = { git = "https://github.com/rustdesk-org/parity-tokio-ipc" } runas = "=1.0" # https://github.com/mitsuhiko/rust-runas/issues/13 -magnum-opus = { git = "https://github.com/rustdesk/magnum-opus" } +magnum-opus = { git = "https://github.com/rustdesk-org/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 } @@ -100,7 +100,7 @@ system_shutdown = "4.0" qrcode-generator = "4.1" [target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi", "pdh", "synchapi", "memoryapi"] } +winapi = { version = "0.3", features = ["winuser", "wincrypt", "shellscalingapi", "pdh", "synchapi", "memoryapi", "shellapi"] } winreg = "0.11" windows-service = "0.6" virtual_display = { path = "libs/virtual_display", optional = true } @@ -115,12 +115,11 @@ dispatch = "0.2" core-foundation = "0.9" core-graphics = "0.22" include_dir = "0.7" -dark-light = "1.0" fruitbasket = "0.10" objc_id = "0.1" [target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies] -tray-icon = { git = "https://github.com/rustdesk-org/tray-icon" } +tray-icon = { git = "https://github.com/tauri-apps/tray-icon" } tao = { git = "https://github.com/rustdesk-org/tao", branch = "dev" } image = "0.24" diff --git a/build.py b/build.py index 58ea83464f54..e47835dee7e6 100755 --- a/build.py +++ b/build.py @@ -16,7 +16,7 @@ hbb_name = 'rustdesk' + ('.exe' if windows else '') exe_path = 'target/release/' + hbb_name if windows: - flutter_build_dir = 'build/windows/runner/Release/' + flutter_build_dir = 'build/windows/x64/runner/Release/' elif osx: flutter_build_dir = 'build/macos/Build/Products/Release/' else: diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 49c74cadb42d..44d197e763eb 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -326,6 +326,8 @@ class MyTheme { ); static ThemeData lightTheme = ThemeData( + // https://stackoverflow.com/questions/77537315/after-upgrading-to-flutter-3-16-the-app-bar-background-color-button-size-and + useMaterial3: false, brightness: Brightness.light, hoverColor: Color.fromARGB(255, 224, 224, 224), scaffoldBackgroundColor: Colors.white, @@ -418,6 +420,7 @@ class MyTheme { ], ); static ThemeData darkTheme = ThemeData( + useMaterial3: false, brightness: Brightness.dark, hoverColor: Color.fromARGB(255, 45, 46, 53), scaffoldBackgroundColor: Color(0xFF18191E), @@ -2552,7 +2555,7 @@ Future start_service(bool is_start) async { } } -typedef Future WhetherUseRemoteBlock(); +typedef WhetherUseRemoteBlock = Future Function(); Widget buildRemoteBlock({required Widget child, WhetherUseRemoteBlock? use}) { var block = false.obs; return Obx(() => MouseRegion( @@ -2979,3 +2982,11 @@ Future setServerConfig( } return true; } + +ColorFilter? svgColor(Color? color) { + if (color == null) { + return null; + } else { + return ColorFilter.mode(color, BlendMode.srcIn); + } +} diff --git a/flutter/lib/common/widgets/autocomplete.dart b/flutter/lib/common/widgets/autocomplete.dart index 966cb099fea1..d4b185706b2c 100644 --- a/flutter/lib/common/widgets/autocomplete.dart +++ b/flutter/lib/common/widgets/autocomplete.dart @@ -7,15 +7,14 @@ import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common/widgets/peer_card.dart'; Future> getAllPeers() async { - Map recentPeers = - jsonDecode(await bind.mainLoadRecentPeersSync()); - Map lanPeers = jsonDecode(await bind.mainLoadLanPeersSync()); - Map abPeers = jsonDecode(await bind.mainLoadAbSync()); - Map groupPeers = jsonDecode(await bind.mainLoadGroupSync()); + Map recentPeers = jsonDecode(bind.mainLoadRecentPeersSync()); + Map lanPeers = jsonDecode(bind.mainLoadLanPeersSync()); + Map abPeers = jsonDecode(bind.mainLoadAbSync()); + Map groupPeers = jsonDecode(bind.mainLoadGroupSync()); Map combinedPeers = {}; - void _mergePeers(Map peers) { + void mergePeers(Map peers) { if (peers.containsKey("peers")) { dynamic peerData = peers["peers"]; @@ -41,10 +40,10 @@ Future> getAllPeers() async { } } - _mergePeers(recentPeers); - _mergePeers(lanPeers); - _mergePeers(abPeers); - _mergePeers(groupPeers); + mergePeers(recentPeers); + mergePeers(lanPeers); + mergePeers(abPeers); + mergePeers(groupPeers); List parsedPeers = []; @@ -65,10 +64,10 @@ class AutocompletePeerTile extends StatefulWidget { }) : super(key: key); @override - _AutocompletePeerTileState createState() => _AutocompletePeerTileState(); + AutocompletePeerTileState createState() => AutocompletePeerTileState(); } -class _AutocompletePeerTileState extends State { +class AutocompletePeerTileState extends State { List _frontN(List list, int n) { if (list.length <= n) { return list; @@ -79,7 +78,7 @@ class _AutocompletePeerTileState extends State { @override Widget build(BuildContext context) { - final double _tileRadius = 5; + final double tileRadius = 5; final name = '${widget.peer.username}${widget.peer.username.isNotEmpty && widget.peer.hostname.isNotEmpty ? '@' : ''}${widget.peer.hostname}'; final greyStyle = TextStyle( @@ -100,8 +99,8 @@ class _AutocompletePeerTileState extends State { color: str2color( '${widget.peer.id}${widget.peer.platform}', 0x7f), borderRadius: BorderRadius.only( - topLeft: Radius.circular(_tileRadius), - bottomLeft: Radius.circular(_tileRadius), + topLeft: Radius.circular(tileRadius), + bottomLeft: Radius.circular(tileRadius), ), ), alignment: Alignment.center, @@ -117,8 +116,8 @@ class _AutocompletePeerTileState extends State { decoration: BoxDecoration( color: Theme.of(context).colorScheme.background, borderRadius: BorderRadius.only( - topRight: Radius.circular(_tileRadius), - bottomRight: Radius.circular(_tileRadius), + topRight: Radius.circular(tileRadius), + bottomRight: Radius.circular(tileRadius), ), ), child: Row( @@ -148,7 +147,7 @@ class _AutocompletePeerTileState extends State { .textTheme .titleSmall, )), - !widget.peer.alias.isEmpty + widget.peer.alias.isNotEmpty ? Padding( padding: const EdgeInsets diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index 78487eb86313..e7a720c8a052 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -1861,3 +1861,106 @@ void enter2FaDialog( onCancel: cancel); }); } + +void showWindowsSessionsDialog( + String type, + String title, + String text, + OverlayDialogManager dialogManager, + SessionID sessionId, + String peerId, + String sessions) { + List sessionsList = sessions.split(','); + Map sessionMap = {}; + for (var session in sessionsList) { + var sessionInfo = session.split('-'); + if (sessionInfo.isNotEmpty) { + sessionMap[sessionInfo[0]] = sessionInfo[1]; + } + } + String selectedUserValue = sessionMap.keys.first; + dialogManager.dismissAll(); + dialogManager.show((setState, close, context) { + onConnect() { + bind.sessionReconnect( + sessionId: sessionId, + forceRelay: false, + userSessionId: selectedUserValue); + dialogManager.dismissAll(); + dialogManager.showLoading(translate('Connecting...'), + onCancel: closeConnection); + } + + return CustomAlertDialog( + title: null, + content: msgboxContent(type, title, text), + actions: [ + SessionsDropdown(peerId, sessionId, sessionMap, (value) { + setState(() { + selectedUserValue = value; + }); + }), + dialogButton('Connect', onPressed: onConnect, isOutline: false), + ], + ); + }); +} + +class SessionsDropdown extends StatefulWidget { + final String peerId; + final SessionID sessionId; + final Map sessions; + final Function(String) onValueChanged; + + SessionsDropdown( + this.peerId, this.sessionId, this.sessions, this.onValueChanged); + + @override + _SessionsDropdownState createState() => _SessionsDropdownState(); +} + +class _SessionsDropdownState extends State { + late String selectedValue; + @override + void initState() { + super.initState(); + selectedValue = widget.sessions.keys.first; + } + + @override + Widget build(BuildContext context) { + return Container( + width: 300, + child: DropdownButton( + value: selectedValue, + isExpanded: true, + borderRadius: BorderRadius.circular(8), + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + items: widget.sessions.entries.map((entry) { + return DropdownMenuItem( + value: entry.key, + child: Text( + entry.value, + style: TextStyle( + color: MyTheme.currentThemeMode() == ThemeMode.dark + ? Colors.white + : MyTheme.dark, + ), + ), + ); + }).toList(), + onChanged: (value) { + if (value != null) { + setState(() { + selectedValue = value; + }); + widget.onValueChanged(value); + } + }, + style: TextStyle( + fontSize: 16.0, + ), + ), + ); + } +} diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index cfe2e1927354..c90709826df4 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -369,10 +369,10 @@ class IOSDraggable extends StatefulWidget { final Widget Function(BuildContext) builder; @override - _IOSDraggableState createState() => _IOSDraggableState(); + IOSDraggableState createState() => IOSDraggableState(); } -class _IOSDraggableState extends State { +class IOSDraggableState extends State { late Offset _position; late ChatModel? _chatModel; late double _width; diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 406bab66c5da..7c186c919d5c 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -602,6 +602,7 @@ abstract class BasePeerCard extends StatelessWidget { _openInWindowsAction(String id) async => await _openNewConnInAction( id, 'Open in new window', kOptionOpenInWindows); + // ignore: unused_element _openNewConnInOptAction(String id) async => mainGetLocalBoolOptionSync(kOptionOpenNewConnInTabs) ? await _openInWindowsAction(id) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 7430e71c1696..d3f2c01cdb51 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -88,6 +88,9 @@ class _PeerTabPageState extends State Future handleTabSelection(int tabIndex) async { if (tabIndex < entries.length) { + if (tabIndex != gFFI.peerTabModel.currentTab) { + gFFI.peerTabModel.setCurrentTabCachedPeers([]); + } gFFI.peerTabModel.setCurrentTab(tabIndex); entries[tabIndex].load(hint: false); } @@ -237,7 +240,7 @@ class _PeerTabPageState extends State "assets/checkbox-outline.svg", width: 18, height: 18, - color: textColor, + colorFilter: svgColor(textColor), )), ); } @@ -333,15 +336,26 @@ class _PeerTabPageState extends State Widget createMultiSelectionBar() { final model = Provider.of(context); return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - deleteSelection(), - addSelectionToFav(), - addSelectionToAb(), - editSelectionTags(), - Expanded(child: Container()), - selectionCount(model.selectedPeers.length), - selectAll(), - closeSelection(), + Offstage( + offstage: model.selectedPeers.isEmpty, + child: Row( + children: [ + deleteSelection(), + addSelectionToFav(), + addSelectionToAb(), + editSelectionTags(), + ], + ), + ), + Row( + children: [ + selectionCount(model.selectedPeers.length), + selectAll(), + closeSelection(), + ], + ) ], ); } @@ -585,7 +599,7 @@ class _PeerTabPageState extends State "assets/chevron_up_chevron_down.svg", width: 18, height: 18, - color: textColor, + colorFilter: svgColor(textColor), )), onTap: showMenu, ); diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 4793feecfe87..c1f8fab0dfdf 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -43,13 +43,6 @@ class _ConnectionPageState extends State bool isWindowMinimized = false; List peers = []; - List _frontN(List list, int n) { - if (list.length <= n) { - return list; - } else { - return list.sublist(0, n); - } - } bool isPeersLoading = false; bool isPeersLoaded = false; @@ -470,7 +463,7 @@ class _ConnectionPageState extends State } void onUsePublicServerGuide() { - const url = "https://rustdesk.com/blog/id-relay-set/"; + const url = "https://rustdesk.com/pricing.html"; canLaunchUrlString(url).then((can) { if (can) { launchUrlString(url); diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 25670e628c40..55cbfe110a8d 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -408,13 +408,13 @@ class _DesktopHomePageState extends State "Warning", "wayland_experiment_tip", "", () async {}, marginTop: LinuxCards.isEmpty ? 20.0 : 5.0, help: 'Help', - link: 'https://rustdesk.com/docs/en/manual/linux/#x11-required')); + link: 'https://rustdesk.com/docs/en/client/linux/#x11-required')); } else if (bind.mainIsLoginWayland()) { LinuxCards.add(buildInstallCard("Warning", "Login screen using Wayland is not supported", "", () async {}, marginTop: LinuxCards.isEmpty ? 20.0 : 5.0, help: 'Help', - link: 'https://rustdesk.com/docs/en/manual/linux/#login-screen')); + link: 'https://rustdesk.com/docs/en/client/linux/#login-screen')); } if (LinuxCards.isNotEmpty) { return Column( diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index b8a897a65c1f..712ed0c5b7fc 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -182,10 +182,9 @@ class _FileManagerPageState extends State children: [ Transform.rotate( angle: item.isRemoteToLocal ? pi : 0, - child: SvgPicture.asset( - "assets/arrow.svg", - color: Theme.of(context).tabBarTheme.labelColor, - ), + child: SvgPicture.asset("assets/arrow.svg", + colorFilter: svgColor( + Theme.of(context).tabBarTheme.labelColor)), ).paddingOnly(left: 15), const SizedBox( width: 16.0, @@ -262,7 +261,7 @@ class _FileManagerPageState extends State }, child: SvgPicture.asset( "assets/refresh.svg", - color: Colors.white, + colorFilter: svgColor(Colors.white), ), color: MyTheme.accent, hoverColor: MyTheme.accent80, @@ -272,7 +271,7 @@ class _FileManagerPageState extends State padding: EdgeInsets.only(right: 15), child: SvgPicture.asset( "assets/close.svg", - color: Colors.white, + colorFilter: svgColor(Colors.white), ), onPressed: () { jobController.jobTable.removeAt(index); @@ -307,13 +306,14 @@ class _FileManagerPageState extends State children: [ SvgPicture.asset( "assets/transfer.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: svgColor( + Theme.of(context).tabBarTheme.labelColor), height: 40, ).paddingOnly(bottom: 10), Text( translate("No transfers in progress"), textAlign: TextAlign.center, - textScaleFactor: 1.20, + textScaler: TextScaler.linear(1.20), style: TextStyle( color: Theme.of(context).tabBarTheme.labelColor), @@ -522,7 +522,8 @@ class _FileManagerViewState extends State { quarterTurns: 2, child: SvgPicture.asset( "assets/arrow.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: + svgColor(Theme.of(context).tabBarTheme.labelColor), ), ), color: Theme.of(context).cardColor, @@ -537,7 +538,8 @@ class _FileManagerViewState extends State { quarterTurns: 3, child: SvgPicture.asset( "assets/arrow.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: + svgColor(Theme.of(context).tabBarTheme.labelColor), ), ), color: Theme.of(context).cardColor, @@ -603,7 +605,8 @@ class _FileManagerViewState extends State { }, child: SvgPicture.asset( "assets/search.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: + svgColor(Theme.of(context).tabBarTheme.labelColor), ), color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, @@ -613,7 +616,8 @@ class _FileManagerViewState extends State { onPressed: null, child: SvgPicture.asset( "assets/close.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: + svgColor(Theme.of(context).tabBarTheme.labelColor), ), color: Theme.of(context).disabledColor, hoverColor: Theme.of(context).hoverColor, @@ -626,7 +630,8 @@ class _FileManagerViewState extends State { }, child: SvgPicture.asset( "assets/close.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: + svgColor(Theme.of(context).tabBarTheme.labelColor), ), color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, @@ -642,7 +647,8 @@ class _FileManagerViewState extends State { }, child: SvgPicture.asset( "assets/refresh.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: + svgColor(Theme.of(context).tabBarTheme.labelColor), ), color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, @@ -666,7 +672,8 @@ class _FileManagerViewState extends State { }, child: SvgPicture.asset( "assets/home.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: + svgColor(Theme.of(context).tabBarTheme.labelColor), ), color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, @@ -692,7 +699,7 @@ class _FileManagerViewState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ SvgPicture.asset("assets/folder_new.svg", - color: MyTheme.accent), + colorFilter: svgColor(MyTheme.accent)), Text( translate("Create Folder"), ).paddingOnly( @@ -734,7 +741,8 @@ class _FileManagerViewState extends State { }, child: SvgPicture.asset( "assets/folder_new.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: + svgColor(Theme.of(context).tabBarTheme.labelColor), ), color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, @@ -749,7 +757,8 @@ class _FileManagerViewState extends State { : null, child: SvgPicture.asset( "assets/trash.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: svgColor( + Theme.of(context).tabBarTheme.labelColor), ), color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, @@ -795,24 +804,24 @@ class _FileManagerViewState extends State { quarterTurns: 2, child: SvgPicture.asset( "assets/arrow.svg", - color: selectedItems.items.isEmpty + colorFilter: svgColor(selectedItems.items.isEmpty ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray - : Colors.white, + : Colors.white), alignment: Alignment.bottomRight, ), ), label: isLocal ? SvgPicture.asset( "assets/arrow.svg", - color: selectedItems.items.isEmpty + colorFilter: svgColor(selectedItems.items.isEmpty ? Theme.of(context).brightness == Brightness.light ? MyTheme.grayBg : MyTheme.darkGray - : Colors.white, + : Colors.white), ) : Text( translate('Receive'), @@ -889,7 +898,7 @@ class _FileManagerViewState extends State { ), child: SvgPicture.asset( "assets/dots.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: svgColor(Theme.of(context).tabBarTheme.labelColor), ), color: Theme.of(context).cardColor, hoverColor: Theme.of(context).hoverColor, @@ -1000,9 +1009,10 @@ class _FileManagerViewState extends State { entry.isFile ? "assets/file.svg" : "assets/folder.svg", - color: Theme.of(context) - .tabBarTheme - .labelColor, + colorFilter: svgColor( + Theme.of(context) + .tabBarTheme + .labelColor), ), Expanded( child: Text(entry.name.nonBreaking, @@ -1126,11 +1136,14 @@ class _FileManagerViewState extends State { void _onSelectedChanged(SelectedItems selectedItems, List entries, Entry entry, bool isLocal) { - final isCtrlDown = RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.controlLeft) || - RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.controlRight); - final isShiftDown = - RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft) || - RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftRight); + final isCtrlDown = RawKeyboard.instance.keysPressed + .contains(LogicalKeyboardKey.controlLeft) || + RawKeyboard.instance.keysPressed + .contains(LogicalKeyboardKey.controlRight); + final isShiftDown = RawKeyboard.instance.keysPressed + .contains(LogicalKeyboardKey.shiftLeft) || + RawKeyboard.instance.keysPressed + .contains(LogicalKeyboardKey.shiftRight); if (isCtrlDown) { if (selectedItems.items.contains(entry)) { selectedItems.remove(entry); @@ -1445,7 +1458,7 @@ class _FileManagerViewState extends State { _locationStatus.value == LocationStatus.pathLocation ? "assets/folder.svg" : "assets/search.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: svgColor(Theme.of(context).tabBarTheme.labelColor), ), Expanded( child: TextField( diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 1fbd52c45401..75c8eb78ccbc 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -1006,7 +1006,7 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> { angle: item.action == CmFileAction.remoteToLocal ? 0 : pi, child: SvgPicture.asset( "assets/arrow.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: svgColor(Theme.of(context).tabBarTheme.labelColor), ), ), Text(item.action == CmFileAction.remoteToLocal @@ -1154,13 +1154,14 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> { children: [ SvgPicture.asset( "assets/transfer.svg", - color: Theme.of(context).tabBarTheme.labelColor, + colorFilter: svgColor( + Theme.of(context).tabBarTheme.labelColor), height: 40, ).paddingOnly(bottom: 10), Text( translate("No transfers in progress"), textAlign: TextAlign.center, - textScaleFactor: 1.20, + textScaler: TextScaler.linear(1.20), style: TextStyle( color: Theme.of(context).tabBarTheme.labelColor), diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index ab937787ca62..e035578e6dca 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1327,7 +1327,8 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } } - _onChanged(BuildContext context, String? value) async { + // This widget has been unmounted, so the State no longer has a context + _onChanged(String? value) async { if (pi.currentDisplay == kAllDisplayValue) { return; } @@ -1350,12 +1351,12 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { if (w != null && h != null) { if (w != rect?.width.toInt() || h != rect?.height.toInt()) { - await _changeResolution(context, w, h); + await _changeResolution(w, h); } } } - _changeResolution(BuildContext context, int w, int h) async { + _changeResolution(int w, int h) async { if (pi.currentDisplay == kAllDisplayValue) { return; } @@ -1387,8 +1388,8 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { return Offstage( offstage: !showOriginalBtn, child: MenuButton( - onPressed: () => _changeResolution( - context, display.originalWidth, display.originalHeight), + onPressed: () => + _changeResolution(display.originalWidth, display.originalHeight), ffi: widget.ffi, child: Text( '${translate('resolution_original_tip')} ${display.originalWidth}x${display.originalHeight}'), @@ -1404,7 +1405,7 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { onPressed: () { final resolution = _getBestFitResolution(); if (resolution != null) { - _changeResolution(context, resolution.width, resolution.height); + _changeResolution(resolution.width, resolution.height); } }, ffi: widget.ffi, @@ -1420,7 +1421,7 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { child: RdoMenuButton( value: _kCustomResolutionValue, groupValue: _groupValue, - onChanged: (String? value) => _onChanged(context, value), + onChanged: (String? value) => _onChanged(value), ffi: widget.ffi, child: Row( children: [ @@ -1461,7 +1462,7 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { .map((e) => RdoMenuButton( value: '${e.width}x${e.height}', groupValue: _groupValue, - onChanged: (String? value) => _onChanged(context, value), + onChanged: (String? value) => _onChanged(value), ffi: widget.ffi, child: Text('${e.width}x${e.height}'))) .toList(); diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 7e793a7762e3..a53dd14db03c 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -430,7 +430,7 @@ class _AppState extends State { ? (context, child) => AccessibilityListener( child: MediaQuery( data: MediaQuery.of(context).copyWith( - textScaleFactor: 1.0, + textScaler: TextScaler.linear(1.0), ), child: child ?? Container(), ), @@ -452,7 +452,7 @@ class _AppState extends State { Widget _keepScaleBuilder(BuildContext context, Widget? child) { return MediaQuery( data: MediaQuery.of(context).copyWith( - textScaleFactor: 1.0, + textScaler: TextScaler.linear(1.0), ), child: child ?? Container(), ); diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index dfd7132fbb91..f2755a14369e 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -45,13 +45,6 @@ class _ConnectionPageState extends State { /// Update url. If it's not null, means an update is available. var _updateUrl = ''; List peers = []; - List _frontN(List list, int n) { - if (list.length <= n) { - return list; - } else { - return list.sublist(0, n); - } - } bool isPeersLoading = false; bool isPeersLoaded = false; diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart index 5344c5da3037..1e9a070fecfa 100644 --- a/flutter/lib/mobile/pages/file_manager_page.dart +++ b/flutter/lib/mobile/pages/file_manager_page.dart @@ -3,6 +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:flutter_hbb/models/platform_model.dart'; import 'package:get/get.dart'; import 'package:toggle_switch/toggle_switch.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; @@ -661,6 +662,7 @@ class BottomSheetBody extends StatelessWidget { @override BottomSheet build(BuildContext context) { + // ignore: no_leading_underscores_for_local_identifiers final _actions = actions ?? []; return BottomSheet( builder: (BuildContext context) { diff --git a/flutter/lib/mobile/pages/home_page.dart b/flutter/lib/mobile/pages/home_page.dart index c04e03244f8a..f19541c8c734 100644 --- a/flutter/lib/mobile/pages/home_page.dart +++ b/flutter/lib/mobile/pages/home_page.dart @@ -13,15 +13,15 @@ abstract class PageShape extends Widget { } class HomePage extends StatefulWidget { - static final homeKey = GlobalKey<_HomePageState>(); + static final homeKey = GlobalKey(); HomePage() : super(key: homeKey); @override - _HomePageState createState() => _HomePageState(); + HomePageState createState() => HomePageState(); } -class _HomePageState extends State { +class HomePageState extends State { var _selectedIndex = 0; int get selectedIndex => _selectedIndex; final List _pages = []; @@ -154,7 +154,7 @@ class WebHomePage extends StatelessWidget { // backgroundColor: MyTheme.grayBg, appBar: AppBar( centerTitle: true, - title: Text("RustDesk" + (isWeb ? " (Beta) " : "")), + title: Text("RustDesk${isWeb ? " (Beta) " : ""}"), actions: connectionPage.appBarActions, ), body: connectionPage, diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index 3aae8fe01192..3bf7ca4499b6 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -211,7 +211,9 @@ class ServiceNotRunningNotification extends StatelessWidget { ElevatedButton.icon( icon: const Icon(Icons.play_arrow), onPressed: () { - if (gFFI.userModel.userName.value.isEmpty && bind.mainGetLocalOption(key: "show-scam-warning") != "N") { + if (gFFI.userModel.userName.value.isEmpty && + bind.mainGetLocalOption(key: "show-scam-warning") != + "N") { showScamWarning(context, serverModel); } else { serverModel.toggleService(); @@ -229,10 +231,10 @@ class ScamWarningDialog extends StatefulWidget { ScamWarningDialog({required this.serverModel}); @override - _ScamWarningDialogState createState() => _ScamWarningDialogState(); + ScamWarningDialogState createState() => ScamWarningDialogState(); } -class _ScamWarningDialogState extends State { +class ScamWarningDialogState extends State { int _countdown = 12; bool show_warning = false; late Timer _timer; @@ -323,10 +325,7 @@ class _ScamWarningDialogState extends State { ), SizedBox(height: 18), Text( - translate("scam_text1") + - "\n\n" + - translate("scam_text2") + - "\n", + "${translate("scam_text1")}\n\n${translate("scam_text2")}\n", style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, @@ -370,11 +369,11 @@ class _ScamWarningDialogState extends State { } }, style: ElevatedButton.styleFrom( - primary: Colors.blueAccent, + backgroundColor: Colors.blueAccent, ), child: Text( isButtonLocked - ? translate("I Agree") + " (${_countdown}s)" + ? "${translate("I Agree")} (${_countdown}s)" : translate("I Agree"), style: TextStyle( fontWeight: FontWeight.bold, diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index d0dbc325cc06..f6b217533244 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -227,6 +227,7 @@ class _SettingsState extends State with WidgetsBindingObserver { update() async { setState(() {}); } + change2fa(callback: update); }, ), @@ -718,7 +719,7 @@ class ScanButton extends StatelessWidget { } class _DisplayPage extends StatefulWidget { - const _DisplayPage({super.key}); + const _DisplayPage(); @override State<_DisplayPage> createState() => __DisplayPageState(); diff --git a/flutter/lib/mobile/widgets/gesture_help.dart b/flutter/lib/mobile/widgets/gesture_help.dart index bc31ae2c43c9..f4c5a35a4d0e 100644 --- a/flutter/lib/mobile/widgets/gesture_help.dart +++ b/flutter/lib/mobile/widgets/gesture_help.dart @@ -7,30 +7,27 @@ class GestureIcons { GestureIcons._(); - static const IconData icon_mouse = IconData(0xe65c, fontFamily: _family); - static const IconData icon_Tablet_Touch = - IconData(0xe9ce, fontFamily: _family); - static const IconData icon_gesture_f_drag = + static const IconData iconMouse = IconData(0xe65c, fontFamily: _family); + static const IconData iconTabletTouch = IconData(0xe9ce, fontFamily: _family); + static const IconData iconGestureFDrag = IconData(0xe686, fontFamily: _family); - static const IconData icon_Mobile_Touch = - IconData(0xe9cd, fontFamily: _family); - static const IconData icon_gesture_press = + static const IconData iconMobileTouch = IconData(0xe9cd, fontFamily: _family); + static const IconData iconGesturePress = IconData(0xe66c, fontFamily: _family); - static const IconData icon_gesture_tap = - IconData(0xe66f, fontFamily: _family); - static const IconData icon_gesture_pinch = + static const IconData iconGestureTap = IconData(0xe66f, fontFamily: _family); + static const IconData iconGesturePinch = IconData(0xe66a, fontFamily: _family); - static const IconData icon_gesture_press_hold = + static const IconData iconGesturePressHold = IconData(0xe66b, fontFamily: _family); - static const IconData icon_gesture_f_drag_up_down_ = + static const IconData iconGestureFDragUpDown_ = IconData(0xe685, fontFamily: _family); - static const IconData icon_gesture_f_tap_ = + static const IconData iconGestureFTap_ = IconData(0xe68e, fontFamily: _family); - static const IconData icon_gesture_f_swipe_right = + static const IconData iconGestureFSwipeRight = IconData(0xe68f, fontFamily: _family); - static const IconData icon_gesture_f_double_tap = + static const IconData iconGestureFdoubleTap = IconData(0xe691, fontFamily: _family); - static const IconData icon_gesture_f_three_fingers = + static const IconData iconGestureFThreeFingers = IconData(0xe687, fontFamily: _family); } @@ -106,64 +103,64 @@ class _GestureHelpState extends State { ? [ GestureInfo( width, - GestureIcons.icon_Mobile_Touch, + GestureIcons.iconMobileTouch, translate("One-Finger Tap"), translate("Left Mouse")), GestureInfo( width, - GestureIcons.icon_gesture_press_hold, + GestureIcons.iconGesturePressHold, translate("One-Long Tap"), translate("Right Mouse")), GestureInfo( width, - GestureIcons.icon_gesture_f_swipe_right, + GestureIcons.iconGestureFSwipeRight, translate("One-Finger Move"), translate("Mouse Drag")), GestureInfo( width, - GestureIcons.icon_gesture_f_three_fingers, + GestureIcons.iconGestureFThreeFingers, translate("Three-Finger vertically"), translate("Mouse Wheel")), GestureInfo( width, - GestureIcons.icon_gesture_f_drag, + GestureIcons.iconGestureFDrag, translate("Two-Finger Move"), translate("Canvas Move")), GestureInfo( width, - GestureIcons.icon_gesture_pinch, + GestureIcons.iconGesturePinch, translate("Pinch to Zoom"), translate("Canvas Zoom")), ] : [ GestureInfo( width, - GestureIcons.icon_Mobile_Touch, + GestureIcons.iconMobileTouch, translate("One-Finger Tap"), translate("Left Mouse")), GestureInfo( width, - GestureIcons.icon_gesture_press_hold, + GestureIcons.iconGesturePressHold, translate("One-Long Tap"), translate("Right Mouse")), GestureInfo( width, - GestureIcons.icon_gesture_f_swipe_right, + GestureIcons.iconGestureFSwipeRight, translate("Double Tap & Move"), translate("Mouse Drag")), GestureInfo( width, - GestureIcons.icon_gesture_f_three_fingers, + GestureIcons.iconGestureFThreeFingers, translate("Three-Finger vertically"), translate("Mouse Wheel")), GestureInfo( width, - GestureIcons.icon_gesture_f_drag, + GestureIcons.iconGestureFDrag, translate("Two-Finger Move"), translate("Canvas Move")), GestureInfo( width, - GestureIcons.icon_gesture_pinch, + GestureIcons.iconGesturePinch, translate("Pinch to Zoom"), translate("Canvas Zoom")), ], diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 5817e187a04f..6a0c8d6d31e0 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -245,6 +245,8 @@ class FfiModel with ChangeNotifier { var name = evt['name']; if (name == 'msgbox') { handleMsgBox(evt, sessionId, peerId); + } else if (name == 'set_multiple_user_session') { + handleMultipleUserSession(evt, sessionId, peerId); } else if (name == 'peer_info') { handlePeerInfo(evt, peerId, false); } else if (name == 'sync_peer_info') { @@ -488,6 +490,19 @@ class FfiModel with ChangeNotifier { dialogManager.dismissByTag(tag); } + handleMultipleUserSession( + Map evt, SessionID sessionId, String peerId) { + if (parent.target == null) return; + final dialogManager = parent.target!.dialogManager; + final sessions = evt['user_sessions']; + final title = translate('Multiple active user sessions found'); + final text = translate('Please select the user you want to connect to'); + final type = ""; + + showWindowsSessionsDialog( + type, title, text, dialogManager, sessionId, peerId, sessions); + } + /// Handle the message box event based on [evt] and [id]. handleMsgBox(Map evt, SessionID sessionId, String peerId) { if (parent.target == null) return; @@ -549,7 +564,8 @@ class FfiModel with ChangeNotifier { void reconnect(OverlayDialogManager dialogManager, SessionID sessionId, bool forceRelay) { - bind.sessionReconnect(sessionId: sessionId, forceRelay: forceRelay); + bind.sessionReconnect( + sessionId: sessionId, forceRelay: forceRelay, userSessionId: ""); clearPermissions(); dialogManager.dismissAll(); dialogManager.showLoading(translate('Connecting...'), diff --git a/flutter/lib/models/web_model.dart b/flutter/lib/models/web_model.dart index 291e21a9c2a0..3c7de06c22b7 100644 --- a/flutter/lib/models/web_model.dart +++ b/flutter/lib/models/web_model.dart @@ -25,7 +25,7 @@ class PlatformFFI { static get localeName => window.navigator.language; - static Future init(String _appType) async { + static Future init(String appType) async { isWeb = true; isWebDesktop = !context.callMethod('isMobile'); context.callMethod('init'); diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index b8edeb3e4c00..9ad8270e4f37 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -9,6 +9,7 @@ import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/common.dart'; /// must keep the order +// ignore: constant_identifier_names enum WindowType { Main, RemoteDesktop, FileTransfer, PortForward, Unknown } extension Index on int { diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index 6e1ecb334840..2335323acce2 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -29,18 +29,18 @@ packages: dependency: transitive description: name: animations - sha256: ef57563eed3620bd5d75ad96189846aca1e033c0c45fc9a7d26e80ab02b88a70 + sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb url: "https://pub.dev" source: hosted - version: "2.0.8" + version: "2.0.11" archive: dependency: transitive description: name: archive - sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.4.6" + version: "3.4.10" args: dependency: transitive description: @@ -125,34 +125,34 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.8" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" url: "https://pub.dev" source: hosted - version: "7.2.11" + version: "7.3.0" built_collection: dependency: transitive description: @@ -165,34 +165,34 @@ packages: dependency: transitive description: name: built_value - sha256: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e" + sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6 url: "https://pub.dev" source: hosted - version: "8.7.0" + version: "8.9.0" cached_network_image: dependency: transitive description: name: cached_network_image - sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.3.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613" + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257" + sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" characters: dependency: transitive description: @@ -221,10 +221,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: transitive description: @@ -237,18 +237,18 @@ packages: dependency: transitive description: name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.10.0" collection: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" contextmenu: dependency: "direct main" description: @@ -269,10 +269,10 @@ packages: dependency: transitive description: name: cross_file - sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c" + sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e url: "https://pub.dev" source: hosted - version: "0.3.3+6" + version: "0.3.3+8" crypto: dependency: transitive description: @@ -293,10 +293,10 @@ packages: dependency: transitive description: name: dart_style - sha256: abd7625e16f51f554ea244d090292945ec4d4be7bfbaf2ec8cccea568919d334 + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" dash_chat_2: dependency: "direct main" description: @@ -310,10 +310,10 @@ packages: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" debounce_throttle: dependency: "direct main" description: @@ -343,10 +343,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419" + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.2" device_info_plus_platform_interface: dependency: transitive description: @@ -440,10 +440,10 @@ packages: dependency: transitive description: name: file_selector_platform_interface - sha256: "0aa47a725c346825a2bd396343ce63ac00bda6eff2fbc43eabe99737dede8262" + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b url: "https://pub.dev" source: hosted - version: "2.6.1" + version: "2.6.2" file_selector_windows: dependency: transitive description: @@ -464,10 +464,10 @@ packages: dependency: "direct main" description: name: flex_color_picker - sha256: f37476ab3e80dcaca94e428e159944d465dd16312fda9ff41e07e86f04bfa51c + sha256: "0871edc170153cfc3de316d30625f40a85daecfa76ce541641f3cc0ec7757cbf" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.3.1" flex_seed_scheme: dependency: transitive description: @@ -620,10 +620,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: bfc7cc3c75fe1282e8ce2e056d8fd1533f1a6848b65c379b4a5e7a9b623d3371 + sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c url: "https://pub.dev" source: hosted - version: "2.0.8" + version: "2.0.9" flutter_web_plugins: dependency: transitive description: flutter @@ -633,10 +633,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "21bf2825311de65501d22e563e3d7605dff57fb5e6da982db785ae5372ff018a" + sha256: "57247f692f35f068cae297549a46a9a097100685c6780fe67177503eea5ed4e5" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "2.4.7" freezed_annotation: dependency: "direct main" description: @@ -689,10 +689,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" http_multi_server: dependency: transitive description: @@ -713,18 +713,18 @@ packages: dependency: "direct dev" description: name: icons_launcher - sha256: "69de6373013966ea033f4cefbbbae258ccbfe790a6cfc69796cb33fda996298a" + sha256: "9b514ffed6ed69b232fd2bf34c44878c8526be71fc74129a658f35c04c9d4a9d" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.7" image: dependency: "direct main" description: name: image - sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" + sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" url: "https://pub.dev" source: hosted - version: "4.1.3" + version: "4.1.7" image_picker: dependency: "direct main" description: @@ -737,10 +737,10 @@ packages: dependency: transitive description: name: image_picker_android - sha256: d6a6e78821086b0b737009b09363018309bbc6de3fd88cc5c26bc2bb44a4957f + sha256: "39f2bfe497e495450c81abcd44b62f56c2a36a37a175da7d137b4454977b51b1" url: "https://pub.dev" source: hosted - version: "0.8.8+2" + version: "0.8.9+3" image_picker_for_web: dependency: transitive description: @@ -753,10 +753,10 @@ packages: dependency: transitive description: name: image_picker_ios - sha256: c5538cacefacac733c724be7484377923b476216ad1ead35a0d2eadcdc0fc497 + sha256: fadafce49e8569257a0cad56d24438a6fa1f0cbd7ee0af9b631f7492818a4ca3 url: "https://pub.dev" source: hosted - version: "0.8.8+2" + version: "0.8.9+1" image_picker_linux: dependency: transitive description: @@ -777,10 +777,10 @@ packages: dependency: transitive description: name: image_picker_platform_interface - sha256: ed9b00e63977c93b0d2d2b343685bed9c324534ba5abafbb3dfbd6a780b1b514 + sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b url: "https://pub.dev" source: hosted - version: "2.9.1" + version: "2.9.3" image_picker_windows: dependency: transitive description: @@ -841,10 +841,10 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -857,18 +857,18 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mime: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" nested: dependency: transitive description: @@ -937,26 +937,26 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: e595b98692943b4881b219f0a9e3945118d3c16bd7e2813f98ec6e532d905f72 + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -969,10 +969,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -1001,34 +1001,34 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" platform: dependency: transitive description: name: platform - sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.4" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.8" pointycastle: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.7.4" pool: dependency: transitive description: @@ -1041,10 +1041,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: transitive description: @@ -1073,10 +1073,10 @@ packages: dependency: transitive description: name: puppeteer - sha256: "59e723cc5b69537159a7c34efd645dc08a6a1ac4647d7d7823606802c0f93cdb" + sha256: eedeaae6ec5d2e54f9ae22ab4d6b3dda2e8791c356cc783046d06c287ffe11d8 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.6.0" qr: dependency: transitive description: @@ -1182,10 +1182,10 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" source_span: dependency: transitive description: @@ -1198,18 +1198,18 @@ packages: dependency: transitive description: name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.2" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.5.3" stack_trace: dependency: transitive description: @@ -1246,10 +1246,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.0+1" term_glyph: dependency: transitive description: @@ -1262,10 +1262,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" texture_rgba_renderer: dependency: "direct main" description: @@ -1351,34 +1351,34 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba + sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.4" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + sha256: "507dc655b1d9cb5ebc756032eb785f114e415f91557b73bf60b7e201dfedeb2f" url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.2.2" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a" + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.2.4" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" url_launcher_macos: dependency: transitive description: @@ -1391,26 +1391,26 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + sha256: a932c3a8082e118f80a475ce692fde89dc20fddb24c57360b96bc56f7035de1f url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.1" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.3" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.1" uuid: dependency: "direct main" description: @@ -1423,26 +1423,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" + sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.10+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" + sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33 url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.10+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 + sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.10+1" vector_math: dependency: transitive description: @@ -1455,42 +1455,42 @@ packages: dependency: transitive description: name: video_player - sha256: "74b86e63529cf5885130c639d74cd2f9232e7c8a66cbecbddd1dcb9dbd060d1e" + sha256: fbf28ce8bcfe709ad91b5789166c832cb7a684d14f571a81891858fefb5bb1c2 url: "https://pub.dev" source: hosted - version: "2.7.2" + version: "2.8.2" video_player_android: dependency: transitive description: name: video_player_android - sha256: "3fe89ab07fdbce786e7eb25b58532d6eaf189ceddc091cb66cba712f8d9e8e55" + sha256: "7f8f25d7ad56819a82b2948357f3c3af071f6a678db33833b26ec36bbc221316" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.11" video_player_avfoundation: dependency: transitive description: name: video_player_avfoundation - sha256: "6387c2de77763b45104256b3b00b660089be4f909ded8631457dc11bf635e38f" + sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.5.6" video_player_platform_interface: dependency: transitive description: name: video_player_platform_interface - sha256: be72301bf2c0150ab35a8c34d66e5a99de525f6de1e8d27c0672b836fe48f73a + sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.2" video_player_web: dependency: transitive description: name: video_player_web - sha256: "2dd24f7ba46bfb5d070e9c795001db95e0ca5f2a3d025e98f287c10c9f0fd62f" + sha256: "34beb3a07d4331a24f7e7b2f75b8e2b103289038e07e65529699a671b6a6e2cb" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.3" visibility_detector: dependency: "direct main" description: @@ -1503,10 +1503,10 @@ packages: dependency: "direct main" description: name: wakelock_plus - sha256: f45a6c03aa3f8322e0a9d7f4a0482721c8789cb41d555407367650b8f9c26018 + sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.4" wakelock_plus_platform_interface: dependency: transitive description: @@ -1527,10 +1527,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1543,10 +1543,10 @@ packages: dependency: "direct main" description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.2.0" win32_registry: dependency: transitive description: @@ -1577,18 +1577,18 @@ packages: dependency: transitive description: name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" xml: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.5.0" yaml: dependency: transitive description: @@ -1609,10 +1609,10 @@ packages: dependency: "direct main" description: name: zxing2 - sha256: "1e141568c9646bc262fa75aacf739bc151ef6ad0226997c0016cc3da358a1bbc" + sha256: a042961441bd400f59595f9125ef5fca4c888daf0ea59c17f41e0e151f8a12b5 url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.2.1" sdks: - dart: ">=3.1.0 <4.0.0" - flutter: ">=3.13.0" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" diff --git a/flutter/windows/flutter/CMakeLists.txt b/flutter/windows/flutter/CMakeLists.txt index b5655b2fa3bc..27ecd3108b25 100644 --- a/flutter/windows/flutter/CMakeLists.txt +++ b/flutter/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index fb5969c1705c..01274c4fca84 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" [dependencies] flexi_logger = { version = "0.27", features = ["async"] } -protobuf = { version = "3.2", features = ["with-bytes"] } -tokio = { version = "=1.28.1", features = ["full"] } +protobuf = { version = "3.3", features = ["with-bytes"] } +tokio = { version = "1.36", features = ["full"] } tokio-util = { version = "0.7", features = ["full"] } futures = "0.3" bytes = { version = "1.4", features = ["serde"] } @@ -50,7 +50,7 @@ quic = [] flatpak = [] [build-dependencies] -protobuf-codegen = { version = "3.2" } +protobuf-codegen = { version = "3.3" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winuser", "synchapi", "pdh", "memoryapi"] } diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 22750c1ecf70..66df3a656da3 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -124,6 +124,11 @@ message PeerInfo { string platform_additions = 12; } +message RdpUserSession { + string user_session_id = 1; + string user_name = 2; +} + message LoginResponse { oneof union { string error = 1; @@ -589,6 +594,7 @@ message OptionMessage { BoolOption disable_keyboard = 12; // Position 13 is used for Resolution. Remove later. // Resolution custom_resolution = 13; + string user_session = 14; } message TestDelay { @@ -703,6 +709,10 @@ message PluginFailure { string msg = 3; } +message RdpUserSessions { + repeated RdpUserSession rdp_user_sessions = 1; +} + message Misc { oneof union { ChatMessage chat_message = 4; @@ -734,6 +744,7 @@ message Misc { ToggleVirtualDisplay toggle_virtual_display = 32; TogglePrivacyMode toggle_privacy_mode = 33; SupportedEncoding supported_encoding = 34; + RdpUserSessions rdp_user_sessions = 35; } } diff --git a/src/auth_2fa.rs b/src/auth_2fa.rs index dc3095b875a5..bfbecd4e9e2b 100644 --- a/src/auth_2fa.rs +++ b/src/auth_2fa.rs @@ -73,7 +73,11 @@ impl TOTPInfo { } pub fn generate2fa() -> String { - if let Ok(info) = TOTPInfo::gen_totp_info(crate::ipc::get_id(), 6) { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + let id = crate::ipc::get_id(); + #[cfg(any(target_os = "android", target_os = "ios"))] + let id = Config::get_id(); + if let Ok(info) = TOTPInfo::gen_totp_info(id, 6) { if let Ok(totp) = info.new_totp() { let code = totp.get_url(); *CURRENT_2FA.lock().unwrap() = Some((info, totp)); @@ -89,7 +93,10 @@ pub fn verify2fa(code: String) -> bool { let res = code == cur; if res { if let Ok(v) = info.into_string() { + #[cfg(not(any(target_os = "android", target_os = "ios")))] crate::ipc::set_option("2fa", &v); + #[cfg(any(target_os = "android", target_os = "ios"))] + Config::set_option("2fa".to_owned(), v); return res; } } diff --git a/src/client.rs b/src/client.rs index 2db3328b45f4..2f7e2bc70668 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1149,6 +1149,7 @@ pub struct LoginConfigHandler { pub custom_fps: Arc>>, pub adapter_luid: Option, pub mark_unsupported: Vec, + pub selected_user_session_id: String, } impl Deref for LoginConfigHandler { @@ -1235,6 +1236,7 @@ impl LoginConfigHandler { self.received = false; self.switch_uuid = switch_uuid; self.adapter_luid = adapter_luid; + self.selected_user_session_id = "".to_owned(); } /// Check if the client should auto login. @@ -1511,14 +1513,17 @@ impl LoginConfigHandler { /// /// * `ignore_default` - If `true`, ignore the default value of the option. fn get_option_message(&self, ignore_default: bool) -> Option { - if self.conn_type.eq(&ConnType::FILE_TRANSFER) - || self.conn_type.eq(&ConnType::PORT_FORWARD) - || self.conn_type.eq(&ConnType::RDP) - { + if self.conn_type.eq(&ConnType::PORT_FORWARD) || self.conn_type.eq(&ConnType::RDP) { return None; } let mut n = 0; let mut msg = OptionMessage::new(); + msg.user_session = self.selected_user_session_id.clone(); + n += 1; + + if self.conn_type.eq(&ConnType::FILE_TRANSFER) { + return Some(msg); + } let q = self.image_quality.clone(); if let Some(q) = self.get_image_quality_enum(&q, ignore_default) { msg.image_quality = q.into(); @@ -1526,7 +1531,7 @@ impl LoginConfigHandler { } else if q == "custom" { let config = self.load_config(); let allow_more = - !crate::ui_interface::using_public_server() || self.direct == Some(true); + !crate::using_public_server() || self.direct == Some(true); let quality = if config.custom_image_quality.is_empty() { 50 } else { @@ -1581,7 +1586,6 @@ impl LoginConfigHandler { &self.mark_unsupported, )); n += 1; - if n > 0 { Some(msg) } else { @@ -2740,6 +2744,7 @@ pub trait Interface: Send + Clone + 'static + Sized { fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str); fn handle_login_error(&self, err: &str) -> bool; fn handle_peer_info(&self, pi: PeerInfo); + fn set_multiple_user_sessions(&self, sessions: Vec); fn on_error(&self, err: &str) { self.msgbox("error", "Error", err, ""); } diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 7496a4bd2ff3..2dbf4f7dc6e1 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -1314,6 +1314,13 @@ impl Remote { } } Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::RdpUserSessions(sessions)) => { + if !sessions.rdp_user_sessions.is_empty() { + self.handler + .set_multiple_user_session(sessions.rdp_user_sessions); + return false; + } + } Some(misc::Union::AudioFormat(f)) => { self.audio_sender.send(MediaData::AudioFormat(f)).ok(); } diff --git a/src/common.rs b/src/common.rs index 2eea02f934a1..e6ff54cdf628 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1328,3 +1328,10 @@ pub fn create_symmetric_key_msg(their_pk_b: [u8; 32]) -> (Bytes, Bytes, secretbo let sealed_key = box_::seal(&key.0, &nonce, &their_pk_b, &out_sk_b); (Vec::from(our_pk_b.0).into(), sealed_key.into(), key) } + +#[inline] +pub fn using_public_server() -> bool { + option_env!("RENDEZVOUS_SERVER").unwrap_or("").is_empty() + && crate::get_custom_rendezvous_server(get_option("custom-rendezvous-server")).is_empty() +} + diff --git a/src/core_main.rs b/src/core_main.rs index 632558baf5ea..caab46867b1f 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -348,6 +348,7 @@ pub fn core_main() -> Option> { lic.host, ); crate::ui_interface::set_option("api-server".into(), lic.api); + crate::ui_interface::set_option("relay-server".into(), lic.relay); } } } else { diff --git a/src/flutter.rs b/src/flutter.rs index c4e81e342f9c..160e40c3b27b 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -826,6 +826,18 @@ impl InvokeUiSession for FlutterHandler { ) } + fn set_multiple_user_session(&self, sessions: Vec) { + let formatted_sessions: Vec = sessions + .iter() + .map(|session| format!("{}-{}", session.user_session_id, session.user_name)) + .collect(); + let sessions = formatted_sessions.join(","); + self.push_event( + "set_multiple_user_session", + vec![("user_sessions", &sessions)], + ); + } + fn on_connected(&self, _conn_type: ConnType) {} fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index e8e9b953a21f..cb68a8480e5c 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -217,9 +217,9 @@ pub fn session_record_status(session_id: SessionID, status: bool) { } } -pub fn session_reconnect(session_id: SessionID, force_relay: bool) { +pub fn session_reconnect(session_id: SessionID, force_relay: bool, user_session_id: String) { if let Some(session) = sessions::get_session_by_session_id(&session_id) { - session.reconnect(force_relay); + session.reconnect(force_relay, user_session_id); } session_on_waiting_for_image_dialog_show(session_id); } @@ -841,7 +841,7 @@ pub fn main_check_connect_status() { } pub fn main_is_using_public_server() -> bool { - using_public_server() + crate::using_public_server() } pub fn main_discover() { diff --git a/src/ipc.rs b/src/ipc.rs index 8b496dd9bcb1..83533c447c3c 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -187,6 +187,7 @@ pub enum Data { Authorize, Close, SAS, + UserSid(Option), OnlineStatus(Option<(i64, bool)>), Config((String, Option)), Options(Option>), @@ -917,6 +918,13 @@ pub fn close_all_instances() -> ResultType { } } +#[tokio::main(flavor = "current_thread")] +pub async fn connect_to_user_session(usid: Option) -> ResultType<()> { + let mut stream = crate::ipc::connect(1000, crate::POSTFIX_SERVICE).await?; + timeout(1000, stream.send(&crate::ipc::Data::UserSid(usid))).await??; + Ok(()) +} + #[cfg(test)] mod test { use super::*; diff --git a/src/lang.rs b/src/lang.rs index 8b88ff41f110..bfba44701182 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -2,6 +2,7 @@ use hbb_common::regex::Regex; use std::ops::Deref; mod ar; +mod bg; mod ca; mod cn; mod cs; @@ -52,6 +53,7 @@ pub const LANGS: &[(&str, &str)] = &[ ("es", "Español"), ("et", "Eesti keel"), ("hu", "Magyar"), + ("bg", "Български"), ("ru", "Русский"), ("sk", "Slovenčina"), ("id", "Indonesia"), @@ -145,6 +147,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "lt" => lt::T.deref(), "lv" => lv::T.deref(), "ar" => ar::T.deref(), + "bg" => bg::T.deref(), _ => en::T.deref(), }; let (name, placeholder_value) = extract_placeholder(&name); diff --git a/src/lang/ar.rs b/src/lang/ar.rs index cd57ef865cf3..b797ade458d8 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/bg.rs b/src/lang/bg.rs new file mode 100644 index 000000000000..7166e448d675 --- /dev/null +++ b/src/lang/bg.rs @@ -0,0 +1,219 @@ +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("desk_tip", "Вашият работен плот може да бъде достъпен с този идентификационен код и парола."), + ("connecting_status", "Свързване с RustDesk мрежата..."), + ("not_ready_status", "Не е в готовност. Моля проверете мрежова връзка"), + ("ID/Relay Server", "ID/Релейн сървър"), + ("id_change_tip", "Само a-z, A-Z, 0-9 и _ (долна черта) символи са позволени. Първата буква трябва да е a-z, A-Z. С дължина мержу 6 и 16."), + ("Slogan_tip", "Направено от сърце в този хаотичен свят!"), + ("Build Date", "Дата на изграждане"), + ("Audio Input", "Аудио вход"), + ("Hardware Codec", "Хардуерен кодек"), + ("ID Server", "ID сървър"), + ("Relay Server", "Релейн сървър"), + ("API Server", "API сървър"), + ("invalid_http", "трябва да започва с http:// или https://"), + ("server_not_support", "Все още не се поддържа от сървъра"), + ("Password Required", "Изисква се парола"), + ("Wrong Password", "Грешна парола"), + ("Connection Error", "Грешка при свързване"), + ("Login Error", "Грешка при вписване"), + ("Show Hidden Files", "Показване на скрити файлове"), + ("Refresh File", "Опресняване на файла"), + ("Remote Computer", "Отдалечен компютър"), + ("Local Computer", "Локален компютър"), + ("Confirm Delete", "Потвърдете изтриването"), + ("Multi Select", "Множествен избор"), + ("Select All", "Избери всички"), + ("Unselect All", "Деселектирай всички"), + ("Empty Directory", "Празна директория"), + ("Custom Image Quality", "Персонализирано качество на изображението"), + ("Adjust Window", "Регулирай прозореца"), + ("Insert Lock", "Поставете ключ"), + ("Set Password", "Задайте парола"), + ("OS Password", "Парола на Операционната система"), + ("install_tip", "Поради UAC, RustDesk в някои случай не може да работи правилно като отдалечена достъп. За да заобиколите UAC, моля, щракнете върху бутона по-долу, за да инсталирате RustDesk в системата."), + ("config_acc", "За да управлявате вашия работен плот дистанционно, трябва да предоставите на RustDesk разрешения \"Достъпност\"."), + ("config_screen", "In order to access your Desktop remotely, you need to grant RustDesk \"Screen Recording\" permissions."), + ("Installation Path", "Инсталационен път"), + ("agreement_tip", "Стартирайки инсталацията, вие приемате лицензионното споразумение."), + ("Accept and Install", "Приемете и инсталирайте"), + ("not_close_tcp_tip", "Не затваряйте този прозорец, докато използвате тунела"), + ("Remote Host", "Отдалечен хост"), + ("Remote Port", "Отдалечен порт"), + ("Local Port", "Локален порт"), + ("Local Address", "Локален адрес"), + ("Change Local Port", "Промяна на локалният порт"), + ("setup_server_tip", "За по-бърза връзка, моля направете свой собствен сървър"), + ("Enter Remote ID", "Въведете дистанционно ID"), + ("Auto Login", "Автоматично вписване (Валидно само ако зададете \"Заключване след края на сесията\")"), + ("Change Path", "Промяна на пътя"), + ("Create Folder", "Създай папка"), + ("whitelist_tip", "Само IP от белия списък има достъп до мен"), + ("verification_tip", "На регистрирания имейл адрес е изпратен код за потвърждение, въведете кода за потвърждение, за да продължите да влизате."), + ("whitelist_sep", "Разделени със запетая, точка и запетая, интервали или нов ред"), + ("Add Tag", "Добавяне на етикет"), + ("Wrong credentials", "Wrong username or password"), + ("Edit Tag", "Edit tag"), + ("Forget Password", "Забравена парола"), + ("Add to Favorites", "Добави към любими"), + ("Remove from Favorites", "Премахване от любими"), + ("Socks5 Proxy", "Socks5 прокси"), + ("install_daemon_tip", "За стартиране с компютъра трябва да инсталирате системна услуга."), + ("Are you sure to close the connection?", "Сигурни ли сте, че искате да затворите връзката?"), + ("One-Finger Tap", "Докосване с един пръст"), + ("Left Mouse", "Ляв бутон на мишката"), + ("One-Long Tap", "Едно дълго докосване"), + ("Two-Finger Tap", "Докосване с два пръста"), + ("Right Mouse", "Десен бутон на мишката"), + ("One-Finger Move", "Преместване с един пръст"), + ("Double Tap & Move", "Докоснете два пъти и преместете"), + ("Mouse Drag", "Плъзгане с мишката"), + ("Three-Finger vertically", "Три пръста вертикално"), + ("Mouse Wheel", "Колело на мишката"), + ("Two-Finger Move", "Движение с два пръста"), + ("Canvas Move", "Преместване на платното"), + ("Pinch to Zoom", "Щипнете, за да увеличите"), + ("Canvas Zoom", "Увеличение на платното"), + ("Share Screen", "Сподели екран"), + ("Screen Capture", "Заснемане на екрана"), + ("Input Control", "Контрол на въвеждане"), + ("Audio Capture", "Аудио записване"), + ("File Connection", "Файлова връзка"), + ("Screen Connection", "Свързване на екрана"), + ("Open System Setting", "Отворете системната настройка"), + ("android_input_permission_tip1", "За да може отдалечено устройство да управлява вашето Android устройство чрез мишка или докосване, трябва да разрешите на RustDesk да използва услугата \"Достъпност\"."), + ("android_input_permission_tip2", "Моля, отидете на следващата страница с системни настройки, намерете и въведете [Installed Services], включете услугата [RustDesk Input]."), + ("android_new_connection_tip", "Получена е нова заявка за контрол, която иска да контролира вашето текущо устройство."), + ("android_service_will_start_tip", "Включването на \"Заснемане на екрана\" автоматично ще стартира услугата, позволявайки на други устройства да поискат връзка с вашето устройство."), + ("android_stop_service_tip", "Затварянето на услугата автоматично ще затвори всички установени връзки."), + ("android_version_audio_tip", "Текущата версия на Android не поддържа аудио заснемане, моля, актуализирайте устройството с Android 10 или по-нова версия."), + ("android_start_service_tip", "Докоснете [Start service] или активирайте разрешение [Screen Capture], за да стартирате услугата за споделяне на екрана."), + ("android_permission_may_not_change_tip", "Разрешенията за установени връзки може да не се променят незабавно, докато не се свържете отново."), + ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), + ("Ignore Battery Optimizations", "Игнорирай оптимизациите на батерията"), + ("android_open_battery_optimizations_tip", "Ако искате да деактивирате тази функция, моля, отидете на следващата страница с настройки на приложението RustDesk, намерете и въведете [Battery], премахнете отметката от [Unrestricted]"), + ("remote_restarting_tip", "Отдалеченото устройство се рестартира, моля, затворете това съобщение и се свържете отново с постоянна парола след известно време"), + ("Exit Fullscreen", "Изход от цял екран"), + ("Mobile Actions", "Мобилни действия"), + ("Select Monitor", "Изберете монитор"), + ("Control Actions", "Контролни действия"), + ("Display Settings", "Настройки на дисплея"), + ("Image Quality", "Качество на изображението"), + ("Scroll Style", "Стил на превъртане"), + ("Show Toolbar", "Показване на лентата с инструменти"), + ("Hide Toolbar", "Скриване на лентата с инструменти"), + ("Direct Connection", "Директна връзка"), + ("Relay Connection", "Релейна връзка"), + ("Secure Connection", "Защитена връзка"), + ("Insecure Connection", "Незащитена връзка"), + ("Dark Theme", "Тъмна тема"), + ("Light Theme", "Светла тема"), + ("Follow System", "Следвай системата"), + ("Unlock Security Settings", "Отключи настройките за сигурност"), + ("Unlock Network Settings", "Отключи мрежовите настройки"), + ("Direct IP Access", "Директен IP достъп"), + ("Audio Input Device", "Аудио входно устройство"), + ("Use IP Whitelisting", "Използвайте бял списък с IP адреси"), + ("Pin Toolbar", "Фиксиране на лентата с инструменти"), + ("Unpin Toolbar", "Откачване на лентата с инструменти"), + ("elevated_foreground_window_tip", "Текущият прозорец на отдалечения работен плот изисква по-високи привилегии за работа, така че временно не може да използва мишката и клавиатурата. Можете да поискате от отдалечения потребител да минимизира текущия прозорец или да щракнете върху бутона за повдигане в прозореца за управление на връзката. За да избегнете този проблем, се препоръчва да инсталирате софтуера на отдалеченото устройство."), + ("Keyboard Settings", "Настройки на клавиатурата"), + ("Full Access", "Пълен достъп"), + ("Screen Share", "Споделяне на екрана"), + ("JumpLink", "Преглед"), + ("Please Select the screen to be shared(Operate on the peer side).", "Моля, изберете екрана, който да бъде споделен (Работете от страна на партньора)."), + ("One-time Password", "Еднократна парола"), + ("hide_cm_tip", "Разрешете скриването само ако приемате сесии чрез парола и използвате постоянна парола"), + ("wayland_experiment_tip", "Wayland support is in experimental stage, please use X11 if you require unattended access."), + ("software_render_tip", "Ако използвате графична карта Nvidia под Linux и отдалеченият прозорец се затваря веднага след свързване, превключването към драйвера Nouveau с отворен код и изборът да използвате софтуерно изобразяване може да помогне. Изисква се рестартиране на софтуера."), + ("config_input", "За да контролирате отдалечен работен плот с клавиатура, трябва да предоставите на RustDesk разрешения \"Input Monitoring\"."), + ("config_microphone", "За да говорите дистанционно, трябва да предоставите на RustDesk разрешения \"Запис на звук\"."), + ("request_elevation_tip", "Можете също така да поискате повишаване на привилегии, ако има някой от отдалечената страна."), + ("Elevation Error", "Грешка при повишаване на привилегии"), + ("still_click_uac_tip", "Все още изисква отдалеченият потребител да щракне върху OK в прозореца на UAC при стартиранят RustDesk."), + ("Request Elevation", "Поискайте повишаване на привилегии"), + ("wait_accept_uac_tip", "Моля, изчакайте отдалеченият потребител да приеме диалоговия прозорец на UAC."), + ("Switch Sides", "Сменете страните"), + ("Default View Style", "Стил на изглед по подразбиране"), + ("Default Scroll Style", "Стил на превъртане по подразбиране"), + ("Default Image Quality", "Качество на изображението по подразбиране"), + ("Default Codec", "Кодек по подразбиране"), + ("Other Default Options", "Други опции по подразбиране"), + ("relay_hint_tip", "Може да не е възможно да се свържете директно; можете да опитате да се свържете чрез реле. Освен това, ако искате да използвате реле при първия си опит, добавете наставка \"/r\" към идентификатора или да изберете опцията \"Винаги свързване чрез реле\" в картата на последните сесии, ако съществува."), + ("install_cert_tip", "Инсталирайте сертификат на RustDesk"), + ("confirm_install_cert_tip", "Това е сертификат за тестване на RustDesk, на който може да се вярва. Сертификатът ще се използва за доверие и инсталиране на драйвери на RustDesk, когато е необходимо."), + ("RDP Settings", "RDP настройки"), + ("New Connection", "Ново свързване"), + ("Your Device", "Вашето устройство"), + ("empty_recent_tip", "Ами сега, няма скорошни сесии!\nВреме е да планирате нова."), + ("empty_favorite_tip", "Все още нямате любими връстници?\nНека намерим някой, с когото да се свържете, и да го добавим към вашите любими!"), + ("empty_lan_tip", "О, не, изглежда, че все още не сме открили връстници."), + ("empty_address_book_tip", "Изглежда, че в момента няма изброени връстници във вашата адресна книга."), + ("Empty Username", "Празно потребителско име"), + ("Empty Password", "Празна парола"), + ("identical_file_tip", "Този файл е идентичен с този на партньора."), + ("show_monitors_tip", "Показване на мониторите в лентата с инструменти"), + ("View Mode", "Режим на преглед"), + ("login_linux_tip", "Трябва да влезете в отдалечен Linux акаунт, за да активирате X сесия на работния плот"), + ("verify_rustdesk_password_tip", "Проверете RustDesk паролата"), + ("remember_account_tip", "Запомнете този акаунт"), + ("os_account_desk_tip", "Този акаунт се използва за влизане в отдалечената операционна система и позволява на десктоп сесията без глава"), + ("OS Account", "Операционната система акаунт"), + ("another_user_login_title_tip", "Друг потребител вече е влязъл"), + ("another_user_login_text_tip", "Прекъснете връзката"), + ("xorg_not_found_title_tip", "Xorg не е намерен"), + ("xorg_not_found_text_tip", "Моля, инсталирайте Xorg"), + ("no_desktop_title_tip", "Няма наличен работен плот"), + ("no_desktop_text_tip", "Моля, инсталирайте работен плот GNOME"), + ("System Sound", "Системен звук"), + ("Copy Fingerprint", "Копиране на пръстов отпечатък"), + ("no fingerprints", "Няма пръстови отпечатъци"), + ("resolution_original_tip", "Оригинална резолюция"), + ("resolution_fit_local_tip", "Напасване към локална разделителна способност"), + ("resolution_custom_tip", "Персонализирана разделителна способност"), + ("Accept and Elevate", "Приемете и повишаване на привилегии"), + ("accept_and_elevate_btn_tooltip", "Приемете връзката и повишете UAC разрешенията."), + ("clipboard_wait_response_timeout_tip", "Времето за изчакване на отговор за копиране изтече."), + ("logout_tip", "Сигурни ли сте, че искате да излезете?"), + ("exceed_max_devices", "Достигнахте максималния брой управлявани устройства."), + ("Change Password", "Промяна на паролата"), + ("Refresh Password", "Обнови паролата"), + ("Grid View", "Мрежов изглед"), + ("List View", "Списъчен изглед"), + ("Toggle Tags", "Превключване на етикети"), + ("pull_ab_failed_tip", "Неуспешно опресняване на адресната книга"), + ("push_ab_failed_tip", "Неуспешно синхронизиране на адресната книга със сървъра"), + ("synced_peer_readded_tip", "Устройствата, които са присъствали в последните сесии, ще бъдат синхронизирани обратно към адресната книга."), + ("Change Color", "Промяна на цвета"), + ("Primary Color", "Основен цвят"), + ("HSV Color", "HSV цвят"), + ("Installation Successful!", "Успешна инсталация!"), + ("scam_title", "Възможно е да сте ИЗМАМЕНИ!"), + ("scam_text1", "Ако разговаряте по телефона с някой, когото НЕ ПОЗНАВАТЕ и НЯМАТЕ ДОВЕРИЕ, който ви е помолил да използвате RustDesk и да стартирате услугата, не продължавайте и затворете незабавно."), + ("scam_text2", "Те вероятно са измамник, който се опитва да открадне вашите пари или друга лична информация."), + ("auto_disconnect_option_tip", "Автоматично затваряне на входящите сесии при неактивност на потребителя"), + ("Connection failed due to inactivity", "Автоматично прекъсване на връзката поради неактивност"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "Моля обновете RustDesk Server Pro на версия {} или по-нова!"), + ("pull_group_failed_tip", "Неуспешно опресняване на групата"), + ("doc_fix_wayland", "https://rustdesk.com/docs/en/manual/linux/#x11-required"), + ("display_is_plugged_out_msg", "Дисплеят е изключен, превключете на първия монитор."), + ("elevated_switch_display_msg", "Превключете към основния монитор, защото множество монитори не се поддържат в потребителски режим с повишени права."), + ("selinux_tip", "SELinux е активиран на вашето устройство, което може да попречи на RustDesk да работи правилно като контролирана страна."), + ("id_input_tip", "Можете да въведете ID, директен IP адрес или домейн с порт (:).\nАко искате да получите достъп до устройство на друг сървър, моля, добавете адреса на сървъра (@?key=), например\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nАко искате да получите достъп до устройство на обществен сървър, моля, въведете \"@public\" , ключът не е необходим за публичен сървър"), + ("privacy_mode_impl_mag_tip", "Режим 1"), + ("privacy_mode_impl_virtual_display_tip", "Режим 2"), + ("idd_not_support_under_win10_2004_tip", "Индиректен драйвер за дисплей не се поддържа. Изисква се Windows 10, версия 2004 или по-нова."), + ("switch_display_elevated_connections_tip", "Превключването към неосновен дисплей не се поддържа в режим на потребител с повишени права, когато има множество връзки. Моля, опитайте отново след инсталация, ако искате да контролирате няколко дисплея."), + ("input_source_1_tip", "Входен източник 1"), + ("input_source_2_tip", "Входен източник 2"), + ("capture_display_elevated_connections_tip", "Заснемането на множество дисплеи не се поддържа в потребителския режим с повишени права. Моля, опитайте отново след инсталация, ако искате да контролирате няколко дисплея."), + ("swap-left-right-mouse", "Разменете левия и десния бутон на мишката"), + ("2FA code", "Код за Двуфакторна удостоверяване"), + ("enable-2fa-title", "Активиране на двуфакторно удостоверяване"), + ("enable-2fa-desc", "Моля, настройте вашия удостоверител сега. Можете да използвате приложение за удостоверяване като Authy, Microsoft или Google Authenticator на вашия телефон или настолен компютър.\n\nСканирайте QR кода с вашето приложение и въведете кода, който приложението ви показва, за да активирате двуфакторно удостоверяване."), + ("wrong-2fa-code", "е може да се потвърди кодът. Проверете дали настройките за код и локалното време са правилни"), + ("enter-2fa-title", "Двуфакторно удостоверяване"), + ].iter().cloned().collect(); +} diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 246825d18328..367ea0368f97 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 7103de5bdcd6..3f7b85bd43d6 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "双重认证"), ("Email verification code must be 6 characters.", "Email 验证码必须是 6 个字符。"), ("2FA code must be 6 digits.", "双重认证代码必须是 6 位数字。"), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 5cc8c00c0a3b..5155a0b1f6dd 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "Dvoufaktorová autentizace"), ("Email verification code must be 6 characters.", "E-mailový ověřovací kód musí mít 6 znaků."), ("2FA code must be 6 digits.", "Kód 2FA musí mít 6 číslic."), + ("Multiple active user sessions found", "Bylo nalezeno několik aktivních uživatelských relací"), + ("Please select the user you want to connect to", "Vyberte prosím uživatele, ke kterému se chcete připojit"), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 52f423b2449a..abbfb7ef8027 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index eaf326d4f184..9d8234d78fb6 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "Zwei-Faktor-Authentifizierung"), ("Email verification code must be 6 characters.", "Der E-Mail-Verifizierungscode muss aus 6 Zeichen bestehen."), ("2FA code must be 6 digits.", "Der 2FA-Code muss 6 Ziffern haben."), + ("Multiple active user sessions found", "Mehrere aktive Benutzersitzungen gefunden"), + ("Please select the user you want to connect to", "Bitte wählen Sie den Benutzer, mit dem Sie sich verbinden möchten"), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 7070eb2aa1f5..6bdfe3ff5569 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 5e091d137979..e7f9f32bbba4 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -91,7 +91,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_version_audio_tip", "The current Android version does not support audio capture, please upgrade to Android 10 or higher."), ("android_start_service_tip", "Tap [Start service] or enable [Screen Capture] permission to start the screen sharing service."), ("android_permission_may_not_change_tip", "Permissions for established connections may not be changed instantly until reconnected."), - ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), + ("doc_mac_permission", "https://rustdesk.com/docs/en/client/mac/#enable-permissions"), ("Ignore Battery Optimizations", "Ignore battery optimizations"), ("android_open_battery_optimizations_tip", "If you want to disable this feature, please go to the next RustDesk application settings page, find and enter [Battery], Uncheck [Unrestricted]"), ("remote_restarting_tip", "Remote device is restarting, please close this message box and reconnect with permanent password after a while"), @@ -197,7 +197,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Connection failed due to inactivity", "Automatically disconnected due to inactivity"), ("upgrade_rustdesk_server_pro_to_{}_tip", "Please upgrade RustDesk Server Pro to version {} or newer!"), ("pull_group_failed_tip", "Failed to refresh group"), - ("doc_fix_wayland", "https://rustdesk.com/docs/en/manual/linux/#x11-required"), + ("doc_fix_wayland", "https://rustdesk.com/docs/en/client/linux/#x11-required"), ("display_is_plugged_out_msg", "The display is plugged out, switch to the first display."), ("elevated_switch_display_msg", "Switch to the primary display because multiple displays are not supported in elevated user mode."), ("selinux_tip", "SELinux is enabled on your device, which may prevent RustDesk from running properly as controlled side."), diff --git a/src/lang/eo.rs b/src/lang/eo.rs index ba73d48bd39a..b61e88c1b618 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 6af061323a75..2798a1a1de79 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "Autenticación en dos pasos"), ("Email verification code must be 6 characters.", "El código de verificación por mail debe tener 6 caracteres"), ("2FA code must be 6 digits.", "El cóidigo 2FA debe tener 6 dígitos"), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/et.rs b/src/lang/et.rs index 651189aa66c1..b12b48dbe2a6 100644 --- a/src/lang/et.rs +++ b/src/lang/et.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 44af6543188f..6c6851e83c7e 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -445,7 +445,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Voice call", "تماس صوتی"), ("Text chat", "گفتگو متنی (چت متنی)"), ("Stop voice call", "توقف تماس صوتی"), - ("relay_hint_tip", " را به شناسه اضافه کنید یا گزینه \"همیشه از طریق رله متصل شوید\" را در کارت همتا انتخاب کنید. همچنین، اگر می‌خواهید فوراً از سرور رله استفاده کنید، می‌توانید پسوند \"/r\".\n اتصال مستقیم ممکن است امکان پذیر نباشد. در این صورت می توانید سعی کنید از طریق سرور رله متصل شوید"), + ("relay_hint_tip", " را به شناسه اضافه کنید یا گزینه \" همیشه از طریق رله متصل شوید\" را در کارت همتا انتخاب کنید. همچنین، اگر می‌خواهید فوراً از سرور رله استفاده کنید، می‌توانید پسوند \"/r\".\n اتصال مستقیم ممکن است امکان پذیر نباشد. در این صورت می توانید سعی کنید از طریق سرور رله متصل شوید"), ("Reconnect", "اتصال مجدد"), ("Codec", "کدک"), ("Resolution", "وضوح"), @@ -566,7 +566,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Plug out all", "همه را خارج کنید"), ("True color (4:4:4)", "رنگ واقعی (4:4:4)"), ("Enable blocking user input", "مسدود کردن ورودی کاربر را فعال کنید"), - ("id_input_tip", "\"@public\" :برای دسترسی به سرورهای عمومی نیازی به کلید نیست ، مثل \n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\n برای مثال (@?key=) :اگر می خواهید به دستگاه دیگری در سروری دسترسی پیدا کنید ، ادرس سرور را اضافه نمایید ماتتد \n (domain:port)یا یک دامنه با پورت را وارد کنید IP شما می توانید یک شناسه یا یک"), + ("id_input_tip", " \"@public\" :برای دسترسی به سرورهای عمومی نیازی به کلید نیست ، مثل \n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\n برای مثال (@?key=) :اگر می خواهید به دستگاه دیگری در سروری دسترسی پیدا کنید ، ادرس سرور را اضافه نمایید ماتتد \n (domain:port)یا یک دامنه با پورت را وارد کنید IP شما می توانید یک شناسه یا یک"), ("privacy_mode_impl_mag_tip", "حالت 1"), ("privacy_mode_impl_virtual_display_tip", "حالت 2"), ("Enter privacy mode", "ورود به حالت حریم خصوصی"), @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "احراز هویت دو مرحله ای"), ("Email verification code must be 6 characters.", "کد تأیید ایمیل باید 6 کاراکتر باشد"), ("2FA code must be 6 digits.", "کد احراز هویت دو مرحله ای باید 6 رقم باشد"), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 757f0a3999fb..333049225ff8 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 623ab273f01b..e1a207cb7bf3 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 31e1018e2ec3..f628fa97c3ac 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index e2fed1085e50..bc250149aa38 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "Autenticazione a due fattori"), ("Email verification code must be 6 characters.", "Il codice di verifica email deve contenere 6 caratteri."), ("2FA code must be 6 digits.", "Il codice 2FA deve essere composto da 6 cifre."), + ("Multiple active user sessions found", "Rilevate sessioni utente attive multiple"), + ("Please select the user you want to connect to", "Seleziona l'utente a cui connetterti"), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index f2328a8b20f1..3b2bc1cb2de8 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index b8174a44c4d1..bdf3b694cda8 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 61f3ba5eed44..45dbb9b395ae 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 7588830f8650..ca9e289fbec1 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index e2177b96410d..cfe01c832c30 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "Divu faktoru autentifikācija"), ("Email verification code must be 6 characters.", "E-pasta verifikācijas kodam jābūt ar 6 rakstzīmēm."), ("2FA code must be 6 digits.", "2FA kodam ir jābūt ar 6 cipariem."), + ("Multiple active user sessions found", "Atrastas vairākas aktīvas lietotāju sesijas"), + ("Please select the user you want to connect to", "Lūdzu, atlasiet lietotāju, ar kuru vēlaties izveidot savienojumu"), ].iter().cloned().collect(); } diff --git a/src/lang/nb.rs b/src/lang/nb.rs index 6c091665b46f..03bc346a6cea 100644 --- a/src/lang/nb.rs +++ b/src/lang/nb.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index a02ac423660c..6dc613096638 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -292,7 +292,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Overwrite", "Overschrijven"), ("This file exists, skip or overwrite this file?", "Dit bestand bestaat reeds, overslaan of overschrijven?"), ("Quit", "Afsluiten"), - ("Help", "https://rustdesk.com/docs/en/manual/linux/#x11-required"), ("Failed", "Mislukt"), ("Succeeded", "Geslaagd"), ("Someone turns on privacy mode, exit", "Iemand schakelt privacymodus in, afsluiten"), @@ -586,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "geef-2fa-titel in"), ("Email verification code must be 6 characters.", "E-mailverificatiecode moet 6 tekens lang zijn."), ("2FA code must be 6 digits.", "2FA-code moet 6 cijfers lang zijn."), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index b4567c9ab45b..3de86ffc3cff 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "Autoryzacja dwuskładnikowa"), ("Email verification code must be 6 characters.", "Kod weryfikacyjny wysłany e-mailem musi mieć 6 znaków."), ("2FA code must be 6 digits.", "Kod 2FA musi zawierać 6 cyfr."), + ("Multiple active user sessions found", "Znaleziono wiele aktywnych sesji użytkownika"), + ("Please select the user you want to connect to", "Wybierz użytkownika, z którym chcesz się połączyć"), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index d3775aef7aa6..32fa059a6645 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index e52612bc1592..a8462305e401 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index a7cf40490e29..5ed9a4a5d4e1 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 3048e0304103..b7164d3c84d9 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "Двухфакторная аутентификация"), ("Email verification code must be 6 characters.", "Код подтверждения электронной почты должен состоять из 6 символов."), ("2FA code must be 6 digits.", "Код двухфакторной аутентификации должен состоять из 6 цифр."), + ("Multiple active user sessions found", "Обнаружено несколько активных пользовательских сеансов"), + ("Please select the user you want to connect to", "Выберите пользователя, к которому хотите подключиться"), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index dbfea23d3bdb..d3722c9a31c7 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "Dvojfaktorové overenie"), ("Email verification code must be 6 characters.", "Overovací kód e-mailu musí mať 6 znakov."), ("2FA code must be 6 digits.", "Kód 2FA musí obsahovať 6 číslic."), + ("Multiple active user sessions found", "Našlo sa viacero aktívnych používateľských relácií"), + ("Please select the user you want to connect to", "Vyberte používateľa ku ktorému sa chcete pripojiť"), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index c29a8884e4b9..49c94b7202d0 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 34d45fe26c75..9c13940a4647 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 80712b544b07..76c1a1150af5 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 33340d42326e..26acc87eff84 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 762f98ec735a..4702ac2eda95 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 547fb096a3b1..613ff24a8074 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index a6fd869d804c..95143e982062 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 350a2a138452..0e05e6a330ca 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -452,7 +452,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("No transfers in progress", "沒有正在進行的傳輸"), ("Set one-time password length", "設定一次性密碼的長度"), ("install_cert_tip", "安裝 RustDesk 憑證"), - ("confirm_install_cert_tip", "這是 RustDesk 的測試證書,這是可以信任的。 這個證書會被用來在需要的時候信任和安裝 RustDes 驅動。"), + ("confirm_install_cert_tip", "這是 RustDesk 的測試證書,這是可以信任的。 這個證書會被用來在需要的時候信任和安裝 RustDesk 驅動。"), ("RDP Settings", "RDP 設定"), ("Sort by", "排序方式"), ("New Connection", "新連線"), @@ -537,8 +537,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Reverse mouse wheel", "滑鼠滾輪反向"), ("{} sessions", "{}個工作階段"), ("scam_title", "您可能遭遇詐騙!"), - ("scam_text1", "如果您正在和素不相識的人通話,而對方要求您使用 RustDesk 啟用服務,請勿繼續操作並立即掛斷電話。 "), - ("scam_text2", "他們很可能是很可能是詐騙,試圖竊取您的金錢或其他個人資料。"), + ("scam_text1", "如果您正在和素不相識的人通話,而對方要求您使用 RustDesk 啟用服務,請勿繼續操作並立即掛斷電話。"), + ("scam_text2", "他們很可能是詐騙,試圖竊取您的金錢或其他個人資料。"), ("Don't show again", "下次不再顯示"), ("I Agree", "同意"), ("Decline", "拒絕"), @@ -547,7 +547,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Connection failed due to inactivity", "由於長時間沒有操作,已自動關閉工作階段"), ("Check for software update on startup", "啟動時檢查更新"), ("upgrade_rustdesk_server_pro_to_{}_tip", "請升級專業版伺服器到{}或更高版本!"), - ("pull_group_failed_tip", "獲取群組群組訊息失敗"), + ("pull_group_failed_tip", "獲取群組訊息失敗"), ("Filter by intersection", "按照交集篩選"), ("Remove wallpaper during incoming sessions", "在接受連入連線時移除桌布"), ("Test", "測試"), @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", "二步驟驗證"), ("Email verification code must be 6 characters.", "Email 驗證碼必須是 6 個字元。"), ("2FA code must be 6 digits.", "二步驟驗證碼必須是 6 位數字。"), + ("Multiple active user sessions found", "發現多個使用者工作階段"), + ("Please select the user you want to connect to", "請選擇您想要連接的使用者"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 3755207cbe82..53ff61144761 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index c6e71d2e76c1..c403456ba78e 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("enter-2fa-title", ""), ("Email verification code must be 6 characters.", ""), ("2FA code must be 6 digits.", ""), + ("Multiple active user sessions found", ""), + ("Please select the user you want to connect to", ""), ].iter().cloned().collect(); } diff --git a/src/license.rs b/src/license.rs index 029611345e0d..cb9e72dd541a 100644 --- a/src/license.rs +++ b/src/license.rs @@ -10,6 +10,8 @@ pub struct License { pub host: String, #[serde(default)] pub api: String, + #[serde(default)] + pub relay: String, } fn get_license_from_string_(s: &str) -> ResultType { @@ -56,6 +58,7 @@ pub fn get_license_from_string(s: &str) -> ResultType { let mut host = ""; let mut key = ""; let mut api = ""; + let mut relay = ""; let strs_iter = strs.iter(); for el in strs_iter { if el.starts_with("host=") { @@ -67,11 +70,15 @@ pub fn get_license_from_string(s: &str) -> ResultType { if el.starts_with("api=") { api = &el[4..el.len()]; } + if el.starts_with("relay=") { + relay = &el[4..el.len()]; + } } return Ok(License { host: host.to_owned(), key: key.to_owned(), api: api.to_owned(), + relay: relay.to_owned(), }); } else { let strs = if s.contains("-licensed-") { @@ -82,6 +89,12 @@ pub fn get_license_from_string(s: &str) -> ResultType { for s in strs { if let Ok(lic) = get_license_from_string_(s) { return Ok(lic); + } else if s.contains("(") { // https://github.com/rustdesk/rustdesk/issues/4162 + for s in s.split("(") { + if let Ok(lic) = get_license_from_string_(s) { + return Ok(lic); + } + } } } } diff --git a/src/naming.rs b/src/naming.rs index 1b70af923abb..18d30782546c 100644 --- a/src/naming.rs +++ b/src/naming.rs @@ -11,11 +11,13 @@ fn gen_name(lic: &License) -> ResultType { fn main() { let args: Vec<_> = std::env::args().skip(1).collect(); let api = args.get(2).cloned().unwrap_or_default(); + let relay = args.get(3).cloned().unwrap_or_default(); if args.len() >= 2 { match gen_name(&License { key: args[0].clone(), host: args[1].clone(), api, + relay, }) { Ok(name) => println!("rustdesk-licensed-{}.exe", name), Err(e) => println!("{:?}", e), diff --git a/src/platform/macos.mm b/src/platform/macos.mm index 4a19973fc1fb..d487098b617f 100644 --- a/src/platform/macos.mm +++ b/src/platform/macos.mm @@ -13,6 +13,11 @@ #endif } +extern "C" uint32_t majorVersion() { + NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion]; + return version.majorVersion; +} + extern "C" bool IsCanScreenRecording(bool prompt) { #ifdef NO_InputMonitoringAuthStatus return false; @@ -113,7 +118,7 @@ // https://github.com/jhford/screenresolution/blob/master/cg_utils.c // https://github.com/jdoupe/screenres/blob/master/setgetscreen.m -size_t bitDepth(CGDisplayModeRef mode) { +size_t bitDepth(CGDisplayModeRef mode) { size_t depth = 0; // Deprecated, same display same bpp? // https://stackoverflow.com/questions/8210824/how-to-avoid-cgdisplaymodecopypixelencoding-to-get-bpp diff --git a/src/platform/macos.rs b/src/platform/macos.rs index f14e520926a0..e9d553b7fd3b 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -45,10 +45,15 @@ extern "C" { max: u32, numModes: *mut u32, ) -> BOOL; + fn majorVersion() -> u32; fn MacGetMode(display: u32, width: *mut u32, height: *mut u32) -> BOOL; fn MacSetMode(display: u32, width: u32, height: u32) -> BOOL; } +pub fn major_version() -> u32 { + unsafe { majorVersion() } +} + pub fn is_process_trusted(prompt: bool) -> bool { unsafe { let value = if prompt { YES } else { NO }; diff --git a/src/platform/windows.cc b/src/platform/windows.cc index 8e2f73fbecf9..68d194c5028e 100644 --- a/src/platform/windows.cc +++ b/src/platform/windows.cc @@ -9,6 +9,7 @@ #include // NOLINT(build/include_order) #include #include +#include void flog(char const *fmt, ...) { @@ -433,6 +434,74 @@ extern "C" return nout; } + uint32_t get_current_process_session_id() + { + DWORD sessionId = 0; + HANDLE hProcess = GetCurrentProcess(); + if (hProcess) { + ProcessIdToSessionId(GetCurrentProcessId(), &sessionId); + CloseHandle(hProcess); + } + return sessionId; + } + + uint32_t get_session_user_info(PWSTR bufin, uint32_t nin, BOOL rdp, uint32_t id) + { + uint32_t nout = 0; + PWSTR buf = NULL; + DWORD n = 0; + if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, id, WTSUserName, &buf, &n)) + { + if (buf) + { + nout = min(nin, n); + memcpy(bufin, buf, nout); + WTSFreeMemory(buf); + } + } + return nout; + } + + void get_available_session_ids(PWSTR buf, uint32_t bufSize, BOOL include_rdp) { + std::vector sessionIds; + PWTS_SESSION_INFOA pInfos = NULL; + DWORD count; + + if (WTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pInfos, &count)) { + for (DWORD i = 0; i < count; i++) { + auto info = pInfos[i]; + auto rdp = "rdp"; + auto nrdp = strlen(rdp); + if (info.State == WTSActive) { + if (info.pWinStationName == NULL) + continue; + if (info.SessionId == 65536 || info.SessionId == 655) + continue; + + if (!stricmp(info.pWinStationName, "console")){ + sessionIds.push_back(std::wstring(L"Console:") + std::to_wstring(info.SessionId)); + } + else if (include_rdp && !strnicmp(info.pWinStationName, rdp, nrdp)) { + sessionIds.push_back(std::wstring(L"RDP:") + std::to_wstring(info.SessionId)); + } + } + } + WTSFreeMemory(pInfos); + } + + std::wstring tmpStr; + for (size_t i = 0; i < sessionIds.size(); i++) { + if (i > 0) { + tmpStr += L","; + } + tmpStr += sessionIds[i]; + } + + if (buf && !tmpStr.empty() && tmpStr.size() < bufSize) { + wcsncpy_s(buf, bufSize, tmpStr.c_str(), tmpStr.size()); + } + } + BOOL has_rdp_service() { PWTS_SESSION_INFOA pInfos; diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 84ca39e1ebeb..59e155dbc0d5 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -5,6 +5,7 @@ use crate::{ license::*, privacy_mode::win_topmost_window::{self, WIN_TOPMOST_INJECTED_PROCESS_EXE}, }; +use hbb_common::libc::{c_int, wchar_t}; use hbb_common::{ allow_err, anyhow::anyhow, @@ -508,7 +509,16 @@ async fn run_service(_arguments: Vec) -> ResultType<()> { log::info!("session id {}", session_id); let mut h_process = launch_server(session_id, true).await.unwrap_or(NULL); let mut incoming = ipc::new_listener(crate::POSTFIX_SERVICE).await?; + let mut stored_usid = None; loop { + let sids = get_all_active_session_ids(); + if !sids.contains(&format!("{}", session_id)) || !is_share_rdp() { + let current_active_session = unsafe { get_current_session(share_rdp()) }; + if session_id != current_active_session { + session_id = current_active_session; + h_process = launch_server(session_id, true).await.unwrap_or(NULL); + } + } let res = timeout(super::SERVICE_INTERVAL, incoming.next()).await; match res { Ok(res) => match res { @@ -523,6 +533,21 @@ async fn run_service(_arguments: Vec) -> ResultType<()> { ipc::Data::SAS => { send_sas(); } + ipc::Data::UserSid(usid) => { + if let Some(usid) = usid { + if session_id != usid { + log::info!( + "session changed from {} to {}", + session_id, + usid + ); + session_id = usid; + stored_usid = Some(session_id); + h_process = + launch_server(session_id, true).await.unwrap_or(NULL); + } + } + } _ => {} } } @@ -537,7 +562,7 @@ async fn run_service(_arguments: Vec) -> ResultType<()> { continue; } let mut close_sent = false; - if tmp != session_id { + if tmp != session_id && stored_usid != Some(session_id) { log::info!("session changed from {} to {}", session_id, tmp); session_id = tmp; send_close_async("").await.ok(); @@ -603,13 +628,16 @@ async fn launch_server(session_id: DWORD, close_first: bool) -> ResultType) -> ResultType> { +pub fn run_as_user(arg: Vec<&str>, usid: Option) -> ResultType> { let cmd = format!( "\"{}\" {}", std::env::current_exe()?.to_str().unwrap_or(""), arg.join(" "), ); - let session_id = unsafe { get_current_session(share_rdp()) }; + let mut session_id = get_current_process_session_id(); + if let Some(usid) = usid { + session_id = usid; + } use std::os::windows::ffi::OsStrExt; let wstr: Vec = std::ffi::OsStr::new(&cmd) .encode_wide() @@ -684,10 +712,10 @@ pub fn try_change_desktop() -> bool { } fn share_rdp() -> BOOL { - if get_reg("share_rdp") != "true" { - FALSE - } else { + if get_reg("share_rdp") != "false" { TRUE + } else { + FALSE } } @@ -705,6 +733,13 @@ pub fn set_share_rdp(enable: bool) { run_cmds(cmd, false, "share_rdp").ok(); } +pub fn get_current_process_session_id() -> u32 { + extern "C" { + fn get_current_process_session_id() -> u32; + } + unsafe { get_current_process_session_id() } +} + pub fn get_active_username() -> String { if !is_root() { return crate::username(); @@ -727,6 +762,76 @@ pub fn get_active_username() -> String { .to_owned() } +pub fn get_all_active_sessions() -> Vec> { + let sids = get_all_active_session_ids_with_station(); + let mut out = Vec::new(); + for sid in sids.split(',') { + let username = get_session_username(sid.to_owned()); + if !username.is_empty() { + let sid_split = sid.split(':').collect::>()[1]; + let v = vec![sid_split.to_owned(), username]; + out.push(v); + } + } + out +} + +pub fn get_session_username(session_id_with_station_name: String) -> String { + let mut session_id = session_id_with_station_name.split(':'); + let station = session_id.next().unwrap_or(""); + let session_id = session_id.next().unwrap_or(""); + if session_id == "" { + return "".to_owned(); + } + + extern "C" { + fn get_session_user_info(path: *mut u16, n: u32, rdp: bool, session_id: u32) -> u32; + } + let buff_size = 256; + let mut buff: Vec = Vec::with_capacity(buff_size); + buff.resize(buff_size, 0); + let n = unsafe { + get_session_user_info( + buff.as_mut_ptr(), + buff_size as _, + true, + session_id.parse::().unwrap(), + ) + }; + if n == 0 { + return "".to_owned(); + } + let sl = unsafe { std::slice::from_raw_parts(buff.as_ptr(), n as _) }; + let out = String::from_utf16(sl) + .unwrap_or("".to_owned()) + .trim_end_matches('\0') + .to_owned(); + station.to_owned() + ": " + &out +} + +pub fn get_all_active_session_ids_with_station() -> String { + extern "C" { + fn get_available_session_ids(buf: *mut wchar_t, buf_size: c_int, include_rdp: bool); + } + const BUF_SIZE: c_int = 1024; + let mut buf: Vec = vec![0; BUF_SIZE as usize]; + + unsafe { + get_available_session_ids(buf.as_mut_ptr(), BUF_SIZE, true); + let session_ids = String::from_utf16_lossy(&buf); + session_ids.trim_matches(char::from(0)).trim().to_string() + } +} + +pub fn get_all_active_session_ids() -> String { + let out = get_all_active_session_ids_with_station() + .split(',') + .map(|x| x.split(':').nth(1).unwrap_or("")) + .collect::>() + .join(","); + out.trim_matches(char::from(0)).trim().to_string() +} + pub fn get_active_user_home() -> Option { let username = get_active_username(); if !username.is_empty() { diff --git a/src/server/connection.rs b/src/server/connection.rs index e05559f9c896..4e8a86a50ab5 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -237,6 +237,8 @@ pub struct Connection { file_remove_log_control: FileRemoveLogControl, #[cfg(feature = "gpucodec")] supported_encoding_flag: (bool, Option), + user_session_id: Option, + checked_multiple_session: bool, } impl ConnInner { @@ -384,6 +386,8 @@ impl Connection { file_remove_log_control: FileRemoveLogControl::new(id), #[cfg(feature = "gpucodec")] supported_encoding_flag: (false, None), + user_session_id: None, + checked_multiple_session: false, }; let addr = hbb_common::try_into_v4(addr); if !conn.on_open(addr).await { @@ -1491,8 +1495,50 @@ impl Connection { self.video_ack_required = lr.video_ack_required; } + #[cfg(target_os = "windows")] + async fn handle_multiple_user_sessions(&mut self, usid: Option) -> bool { + if self.port_forward_socket.is_some() { + return true; + } else { + let active_sessions = crate::platform::get_all_active_sessions(); + if active_sessions.len() <= 1 { + return true; + } + let current_process_usid = crate::platform::get_current_process_session_id(); + if usid.is_none() { + let mut res = Misc::new(); + let mut rdp = Vec::new(); + for session in active_sessions { + let u_sid = &session[0]; + let u_name = &session[1]; + let mut rdp_session = RdpUserSession::new(); + rdp_session.user_session_id = u_sid.clone(); + rdp_session.user_name = u_name.clone(); + rdp.push(rdp_session); + } + res.set_rdp_user_sessions(RdpUserSessions { + rdp_user_sessions: rdp, + ..Default::default() + }); + let mut msg_out = Message::new(); + msg_out.set_misc(res); + self.send(msg_out).await; + return true; + } + if usid != Some(current_process_usid) { + self.on_close("Reconnecting...", false).await; + std::thread::spawn(move || { + let _ = ipc::connect_to_user_session(usid); + }); + return false; + } + true + } + } + #[cfg(not(any(target_os = "android", target_os = "ios")))] fn try_start_cm_ipc(&mut self) { + let usid = self.user_session_id; if let Some(p) = self.start_cm_ipc_para.take() { tokio::spawn(async move { #[cfg(windows)] @@ -1502,6 +1548,7 @@ impl Connection { p.tx_from_cm, p.rx_desktop_ready, p.tx_cm_stream_ready, + usid.clone(), ) .await { @@ -1513,9 +1560,9 @@ impl Connection { } }); #[cfg(all(windows, feature = "flutter"))] - std::thread::spawn(|| { + std::thread::spawn(move || { if crate::is_server() && !crate::check_process("--tray", false) { - crate::platform::run_as_user(vec!["--tray"]).ok(); + crate::platform::run_as_user(vec!["--tray"], usid).ok(); } }); } @@ -1523,6 +1570,19 @@ impl Connection { async fn on_message(&mut self, msg: Message) -> bool { if let Some(message::Union::LoginRequest(lr)) = msg.union { + #[cfg(target_os = "windows")] + { + if !self.checked_multiple_session { + let usid; + match lr.option.user_session.parse::() { + Ok(n) => usid = Some(n), + Err(..) => usid = None, + } + if usid.is_some() { + self.user_session_id = usid; + } + } + } self.handle_login_request_without_validation(&lr).await; if self.authorized { return true; @@ -1761,6 +1821,22 @@ impl Connection { } } } else if self.authorized { + #[cfg(target_os = "windows")] + if !self.checked_multiple_session { + self.checked_multiple_session = true; + if crate::platform::is_installed() + && crate::platform::is_share_rdp() + && Self::alive_conns().len() == 1 + && get_version_number(&self.lr.version) >= get_version_number("1.2.4") + { + if !self + .handle_multiple_user_sessions(self.user_session_id) + .await + { + return false; + } + } + } match msg.union { Some(message::Union::MouseEvent(me)) => { #[cfg(any(target_os = "android", target_os = "ios"))] @@ -3010,6 +3086,7 @@ async fn start_ipc( tx_from_cm: mpsc::UnboundedSender, mut _rx_desktop_ready: mpsc::Receiver<()>, tx_stream_ready: mpsc::Sender<()>, + user_session_id: Option, ) -> ResultType<()> { use hbb_common::anyhow::anyhow; @@ -3057,7 +3134,7 @@ async fn start_ipc( if crate::platform::is_root() { let mut res = Ok(None); for _ in 0..10 { - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "windows")))] { log::debug!("Start cm"); res = crate::platform::run_as_user(args.clone()); @@ -3071,6 +3148,11 @@ async fn start_ipc( None::<(&str, &str)>, ); } + #[cfg(target_os = "windows")] + { + log::debug!("Start cm"); + res = crate::platform::run_as_user(args.clone(), user_session_id); + } if res.is_ok() { break; } @@ -3405,6 +3487,10 @@ extern "C" fn connection_shutdown_hook() { } mod raii { + // CONN_COUNT: remote connection count in fact + // ALIVE_CONNS: all connections, including unauthorized connections + // AUTHED_CONNS: all authorized connections + use super::*; pub struct ConnectionID(i32); diff --git a/src/tray.rs b/src/tray.rs index 68d77f80ffdf..b6772cafdc31 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -18,32 +18,12 @@ pub fn make_tray() -> hbb_common::ResultType<()> { use tao::event_loop::{ControlFlow, EventLoopBuilder}; use tray_icon::{ menu::{Menu, MenuEvent, MenuItem}, - TrayEvent, TrayIconBuilder, + TrayIconBuilder, TrayIconEvent as TrayEvent, }; let icon; #[cfg(target_os = "macos")] { - const DARK: &[u8] = include_bytes!("../res/mac-tray-dark-x2.png"); - const LIGHT: &[u8] = include_bytes!("../res/mac-tray-light-x2.png"); - let output = std::process::Command::new("sw_vers") - .args(&["-productVersion"]) - .output() - .map(|x| x.stdout) - .unwrap_or_default(); - let version: f64 = String::from_utf8_lossy(output.as_slice()) - .trim() - .parse() - .unwrap_or_default(); - icon = if version >= 14. { - // workaround for Sonoma, always light menubar - DARK - } else { - let mode = dark_light::detect(); - match mode { - dark_light::Mode::Dark => LIGHT, - _ => DARK, - } - }; + icon = include_bytes!("../res/mac-tray-dark-x2.png"); // use as template, so color is not important } #[cfg(not(target_os = "macos"))] { @@ -57,7 +37,7 @@ pub fn make_tray() -> hbb_common::ResultType<()> { let rgba = image.into_raw(); (rgba, width, height) }; - let icon = tray_icon::icon::Icon::from_rgba(icon_rgba, icon_width, icon_height) + let icon = tray_icon::Icon::from_rgba(icon_rgba, icon_width, icon_height) .context("Failed to open icon")?; let event_loop = EventLoopBuilder::new().build(); @@ -65,7 +45,7 @@ pub fn make_tray() -> hbb_common::ResultType<()> { let tray_menu = Menu::new(); let quit_i = MenuItem::new(translate("Exit".to_owned()), true, None); let open_i = MenuItem::new(translate("Open".to_owned()), true, None); - tray_menu.append_items(&[&open_i, &quit_i]); + tray_menu.append_items(&[&open_i, &quit_i]).ok(); let tooltip = |count: usize| { if count == 0 { format!( @@ -87,6 +67,7 @@ pub fn make_tray() -> hbb_common::ResultType<()> { .with_menu(Box::new(tray_menu)) .with_tooltip(tooltip(0)) .with_icon(icon) + .with_icon_as_template(true) // mac only .build()?, ); let _tray_icon = Arc::new(Mutex::new(_tray_icon)); @@ -129,6 +110,8 @@ pub fn make_tray() -> hbb_common::ResultType<()> { std::thread::spawn(move || { start_query_session_count(ipc_sender.clone()); }); + #[cfg(windows)] + let mut last_click = std::time::Instant::now(); event_loop.run(move |_event, _, control_flow| { if !docker_hiden { #[cfg(target_os = "macos")] @@ -157,8 +140,14 @@ pub fn make_tray() -> hbb_common::ResultType<()> { if let Ok(_event) = tray_channel.try_recv() { #[cfg(target_os = "windows")] - if _event.event == tray_icon::ClickEvent::Left { + if _event.click_type == tray_icon::ClickType::Left + || _event.click_type == tray_icon::ClickType::Double + { + if last_click.elapsed() < std::time::Duration::from_secs(1) { + return; + } open_func(); + last_click = std::time::Instant::now(); } } diff --git a/src/ui.rs b/src/ui.rs index 8c4f4b3d9408..c6916e6c8e9b 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -259,7 +259,7 @@ impl UI { } fn using_public_server(&self) -> bool { - using_public_server() + crate::using_public_server() } fn get_options(&self) -> Value { diff --git a/src/ui/common.css b/src/ui/common.css index 7ee744463bf0..ff2f83883e56 100644 --- a/src/ui/common.css +++ b/src/ui/common.css @@ -461,3 +461,18 @@ div#msgbox div.set-password input { div#msgbox #error { color: red; } + +div.user-session .title { + font-size: 1.2em; + margin-bottom: 2em; +} + +div.user-session select { + width: 98%; + height: 2em; + border-radius: 0.5em; + border: color(border) solid 1px; + background: color(bg); + color: color(text); + padding-left: 0.5em; +} \ No newline at end of file diff --git a/src/ui/common.tis b/src/ui/common.tis index 6ecfb4342475..e3ed83a6ee2f 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -304,7 +304,21 @@ function msgbox(type, title, content, link="", callback=null, height=180, width= return; } }; - } + } else if (type === "multiple-sessions") { + var parts = content.split("-"); + var ids = parts[0].split(","); + var names = parts[1].split(","); + var sessionData = []; + for (var i = 0; i < ids.length; i++) { + sessionData.push({ id: ids[i], name: names[i] }); + } + content = ; + callback = function () { + retryConnect(); + return; + }; + height += 50; + } last_msgbox_tag = type + "-" + title + "-" + content + "-" + link; $(#msgbox).content(); } @@ -339,7 +353,7 @@ handler.msgbox_retry = function(type, title, text, link, hasRetry) { function retryConnect(cancelTimer=false) { if (cancelTimer) self.timer(0, retryConnect); if (!is_port_forward) connecting(); - handler.reconnect(false); + handler.reconnect(false, ""); } /******************** end of msgbox ****************************************/ @@ -458,3 +472,37 @@ function awake() { view.focus = self; } +class MultipleSessionComponent extends Reactor.Component { + this var sessions = []; + this var selectedSessionId = null; + this var sessionlength = 0; + this var messageText = translate("Please select the user you want to connect to"); + + function this(params) { + if (params && params.sessions) { + this.sessions = params.sessions; + this.selectedSessionId = params.sessions[0].id; + this.sessions.map(session => { + this.sessionlength += session.name.length; + }); + } + handler.set_selected_user_session_id(this.selectedSessionId); + } + + function render() { + return
+
{this.messageText}
+ +
; + } + + event change { + var selectedSessionName = this.value.substr(this.messageText.length + this.sessionlength); + this.selectedSessionId = this.sessions.find(session => session.name == selectedSessionName).id; + handler.set_selected_user_session_id(this.selectedSessionId); + } +} \ No newline at end of file diff --git a/src/ui/header.tis b/src/ui/header.tis index 76f82be2b03a..69be084b67a9 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -527,6 +527,10 @@ handler.updateDisplays = function(v) { } } +handler.setMultipleUserSession = function(usid,uname) { + msgbox("multiple-sessions", translate("Multiple active user sessions found"), usid+"-"+uname, "", function(res) {}); +} + function updatePrivacyMode() { var el = $(li#privacy-mode); if (el) { diff --git a/src/ui/remote.rs b/src/ui/remote.rs index edd58ef61769..cf3c951839d4 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -259,6 +259,15 @@ impl InvokeUiSession for SciterHandler { // Ignore for sciter version. } + fn set_multiple_user_session(&self, sessions: Vec) { + let formatted_sessions: Vec = sessions.iter() + .map(|session| format!("{}-{}", session.user_session_id, session.user_name)) + .collect(); + let u_sids: String = formatted_sessions.iter().map(|s| s.split("-").next().unwrap().to_string()).collect::>().join(","); + let u_names:String = formatted_sessions.iter().map(|s| s.split("-").nth(1).unwrap().to_string()).collect::>().join(","); + self.call("setMultipleUserSession", &make_args!(u_sids, u_names)); + } + fn on_connected(&self, conn_type: ConnType) { match conn_type { ConnType::RDP => {} @@ -346,6 +355,7 @@ impl sciter::EventHandler for SciterSession { } fn detached(&mut self, _root: HELEMENT) { + self.set_selected_user_session_id("".to_string()); *self.element.lock().unwrap() = None; self.sender.write().unwrap().take().map(|sender| { sender.send(Data::Close).ok(); @@ -376,7 +386,7 @@ impl sciter::EventHandler for SciterSession { let site = AssetPtr::adopt(ptr as *mut video_destination); log::debug!("[video] start video"); *VIDEO.lock().unwrap() = Some(site); - self.reconnect(false); + self.reconnect(false, "".to_string()); } } BEHAVIOR_EVENTS::VIDEO_INITIALIZED => { @@ -426,7 +436,7 @@ impl sciter::EventHandler for SciterSession { fn transfer_file(); fn tunnel(); fn lock_screen(); - fn reconnect(bool); + fn reconnect(bool, String); fn get_chatbox(); fn get_icon(); fn get_home_dir(); @@ -477,6 +487,7 @@ impl sciter::EventHandler for SciterSession { fn request_voice_call(); fn close_voice_call(); fn version_cmp(String, String); + fn set_selected_user_session_id(String); } } @@ -580,6 +591,10 @@ impl SciterSession { log::info!("size saved"); } + fn set_selected_user_session_id(&mut self, u_sid: String) { + self.lc.write().unwrap().selected_user_session_id = u_sid; + } + fn get_port_forwards(&mut self) -> Value { let port_forwards = self.lc.read().unwrap().port_forwards.clone(); let mut v = Value::array(0); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 83d5ed119058..dbcf525db748 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -248,12 +248,6 @@ pub fn set_peer_option(id: String, name: String, value: String) { c.store(&id); } -#[inline] -pub fn using_public_server() -> bool { - option_env!("RENDEZVOUS_SERVER").unwrap_or("").is_empty() - && crate::get_custom_rendezvous_server(get_option("custom-rendezvous-server")).is_empty() -} - #[inline] pub fn get_options() -> String { let options = { @@ -1044,7 +1038,9 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver { @@ -1112,6 +1108,11 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver { + if last_timer.elapsed() < TIMER_OUT { + continue; + } + last_timer = time::Instant::now(); + c.send(&ipc::Data::OnlineStatus(None)).await.ok(); c.send(&ipc::Data::Options(None)).await.ok(); c.send(&ipc::Data::Config(("id".to_owned(), None))).await.ok(); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 069fd319e3b5..32fd26a38b81 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1003,7 +1003,7 @@ impl Session { } } - pub fn reconnect(&self, force_relay: bool) { + pub fn reconnect(&self, force_relay: bool, user_session_id: String) { // 1. If current session is connecting, do not reconnect. // 2. If the connection is established, send `Data::Close`. // 3. If the connection is disconnected, do nothing. @@ -1023,6 +1023,9 @@ impl Session { if true == force_relay { self.lc.write().unwrap().force_relay = true; } + if !user_session_id.is_empty() { + self.lc.write().unwrap().selected_user_session_id = user_session_id; + } let mut lock = self.thread.lock().unwrap(); // No need to join the previous thread, because it will exit automatically. // And the previous thread will not change important states. @@ -1310,6 +1313,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { fn next_rgba(&self, display: usize); #[cfg(all(feature = "gpucodec", feature = "flutter"))] fn on_texture(&self, display: usize, texture: *mut c_void); + fn set_multiple_user_session(&self, sessions: Vec); } impl Deref for Session { @@ -1351,6 +1355,10 @@ impl Interface for Session { handle_login_error(self.lc.clone(), err, self) } + fn set_multiple_user_sessions(&self, sessions: Vec) { + self.ui_handler.set_multiple_user_session(sessions); + } + fn handle_peer_info(&self, mut pi: PeerInfo) { log::debug!("handle_peer_info :{:?}", pi); pi.username = self.lc.read().unwrap().get_username(&pi);