diff --git a/.github/workflows/bridge.yml b/.github/workflows/bridge.yml index b9285a87a..c94322102 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/bridge.yml @@ -6,8 +6,8 @@ on: workflow_call: env: - FLUTTER_VERSION: "3.10.6" - FLUTTER_RUST_BRIDGE_VERSION: "1.75.3" + FLUTTER_VERSION: "3.13.9" + FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" jobs: generate_bridge: diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 2aa6e564a..94a08fc37 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -13,8 +13,8 @@ on: env: CARGO_NDK_VERSION: "3.1.2" LLVM_VERSION: "15.0.6" - FLUTTER_VERSION: "3.10.6" - FLUTTER_RUST_BRIDGE_VERSION: "1.75.3" + FLUTTER_VERSION: "3.13.9" + FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux FLUTTER_ELINUX_VERSION: "3.10.6" FLUTTER_ELINUX_COMMIT_ID: "410b3ca42f2cd0c485edf517a1666652bab442d4" diff --git a/.github/workflows/history.yml b/.github/workflows/history.yml index 4c23eaa66..c40641fcd 100644 --- a/.github/workflows/history.yml +++ b/.github/workflows/history.yml @@ -4,9 +4,9 @@ on: [workflow_dispatch] env: LLVM_VERSION: "10.0" - FLUTTER_VERSION: "3.10.6" + FLUTTER_VERSION: "3.13.9" TAG_NAME: "tmp" - FLUTTER_RUST_BRIDGE_VERSION: "1.75.3" + FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # vcpkg version: 2022.05.10 # for multiarch gcc compatibility VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1" diff --git a/Cargo.lock b/Cargo.lock index 337a4a695..9a79c58e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -19,21 +19,20 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if 1.0.0", "cipher", "cpufeatures", - "opaque-debug", ] [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ "getrandom", "once_cell", @@ -41,19 +40,31 @@ dependencies = [ ] [[package]] -name = "aho-corasick" -version = "1.0.1" +name = "ahash" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy 0.7.18", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "allo-isolate" -version = "0.1.14" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed55848be9f41d44c79df6045b680a74a78bc579e0813f7f196cd7928e22fb1" +checksum = "f56b7997817c178b853573e8bdfb6c3afe02810b43f17d766d6703560074b0c3" dependencies = [ "anyhow", "atomic", @@ -77,13 +88,19 @@ dependencies = [ ] [[package]] -name = "alsa" -version = "0.7.0" +name = "allocator-api2" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8512c9117059663fb5606788fbca3619e2a91dac0e3fe516242eab1fa6be5e44" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "alsa" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47" dependencies = [ "alsa-sys", - "bitflags", + "bitflags 1.3.2", "libc", "nix 0.24.3", ] @@ -99,16 +116,22 @@ dependencies = [ ] [[package]] -name = "android_log-sys" -version = "0.3.0" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f0fc03f560e1aebde41c2398b691cb98b5ea5996a6184a7a67bbbb77448969" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_log-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" [[package]] name = "android_logger" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa490e751f3878eb9accb9f18988eca52c2337ce000a8bf31ef50d4c723ca9e" +checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" dependencies = [ "android_log-sys", "env_logger 0.10.0", @@ -136,30 +159,29 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -175,9 +197,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -185,9 +207,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "apple-bindgen" @@ -199,7 +221,7 @@ dependencies = [ "bindgen 0.63.0", "derive_more", "regex", - "serde 1.0.163", + "serde 1.0.190", "thiserror", "toml 0.6.0", ] @@ -211,8 +233,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a04f192a700686ee70008ff4e4eb76fe7d11814ab93b7ee9d48c36b9a9f0bd2a" dependencies = [ "plist", - "serde 1.0.163", - "serde_json 1.0.96", + "serde 1.0.190", + "serde_json 1.0.107", ] [[package]] @@ -228,22 +250,21 @@ dependencies = [ [[package]] name = "arboard" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854" +checksum = "ac57f2b058a76363e357c056e4f74f1945bf734d37b8b3ef49066c4787dde0fc" dependencies = [ "clipboard-win", - "core-graphics", + "core-graphics 0.22.3", "image", "log", "objc", "objc-foundation", "objc_id", - "once_cell", "parking_lot", "thiserror", "winapi 0.3.9", - "x11rb", + "x11rb 0.10.1", ] [[package]] @@ -252,31 +273,31 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" dependencies = [ - "event-listener", + "event-listener 2.5.3", "futures-core", ] [[package]] name = "async-channel" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", - "event-listener", + "event-listener 2.5.3", "futures-core", ] [[package]] name = "async-executor" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" dependencies = [ "async-lock", "async-task", "concurrent-queue", - "fastrand", + "fastrand 2.0.1", "futures-lite", "slab", ] @@ -307,65 +328,82 @@ dependencies = [ "log", "parking", "polling", - "rustix", + "rustix 0.37.27", "slab", - "socket2 0.4.9", + "socket2 0.4.10", "waker-fn", ] [[package]] name = "async-lock" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", ] [[package]] name = "async-process" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" dependencies = [ "async-io", "async-lock", - "autocfg 1.1.0", + "async-signal", "blocking", "cfg-if 1.0.0", - "event-listener", + "event-listener 3.0.0", "futures-lite", - "rustix", - "signal-hook", + "rustix 0.38.21", "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "async-signal" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2a5415b7abcdc9cd7d63d6badba5288b2ca017e3fbd4173b8f405449f1a2399" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if 1.0.0", + "futures-core", + "futures-io", + "rustix 0.38.21", + "signal-hook-registry", + "slab", + "windows-sys 0.48.0", ] [[package]] name = "async-task" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] @@ -375,8 +413,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" dependencies = [ "atk-sys", - "bitflags", - "glib 0.16.7", + "bitflags 1.3.2", + "glib 0.16.9", "libc", ] @@ -389,23 +427,20 @@ dependencies = [ "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", - "system-deps 6.1.0", + "system-deps 6.1.2", ] [[package]] name = "atomic" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" -dependencies = [ - "autocfg 1.1.0", -] +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" [[package]] name = "atomic-waker" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atty" @@ -435,24 +470,24 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide 0.6.2", + "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "base64" -version = "0.21.0" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -466,7 +501,7 @@ version = "0.59.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "clap 2.34.0", @@ -475,8 +510,8 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "regex", "rustc-hash", "shlex", @@ -489,15 +524,15 @@ version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", "lazycell", "log", "peeking_take_while", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "regex", "rustc-hash", "shlex", @@ -505,33 +540,13 @@ dependencies = [ "which", ] -[[package]] -name = "bindgen" -version = "0.64.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "proc-macro2 1.0.63", - "quote 1.0.27", - "regex", - "rustc-hash", - "shlex", - "syn 1.0.109", -] - [[package]] name = "bindgen" version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -539,15 +554,35 @@ dependencies = [ "log", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "regex", "rustc-hash", "shlex", - "syn 2.0.15", + "syn 2.0.38", "which", ] +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2 1.0.69", + "quote 1.0.33", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.38", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -560,6 +595,22 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bitmask-enum" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49fb8528abca6895a5ada33d62aedd538a5c33e77068256483b44a3230270163" +dependencies = [ + "quote 1.0.33", + "syn 2.0.38", +] + [[package]] name = "bitvec" version = "1.0.1" @@ -588,25 +639,45 @@ dependencies = [ ] [[package]] -name = "blocking" -version = "1.3.1" +name = "block-sys" +version = "0.1.0-beta.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +dependencies = [ + "block-sys", + "objc2-encode", +] + +[[package]] +name = "blocking" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" dependencies = [ "async-channel", "async-lock", "async-task", - "atomic-waker", - "fastrand", + "fastrand 2.0.1", + "futures-io", "futures-lite", - "log", + "piper", + "tracing", ] [[package]] name = "brotli" -version = "3.3.4" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -615,9 +686,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.4" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -631,29 +702,29 @@ checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" [[package]] name = "bumpalo" -version = "3.12.2" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", ] [[package]] @@ -677,15 +748,33 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "cacao" +version = "0.4.0-beta2" +source = "git+https://github.com/clslaid/cacao?branch=feat/set-file-urls#05e1536b0b43aaae308ec72c0eed703e875b7b95" +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", + "dispatch", + "lazy_static", + "libc", + "objc2", + "os_info", + "percent-encoding", + "url", +] + [[package]] name = "cairo-rs" version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", - "glib 0.16.7", + "glib 0.16.9", "libc", "once_cell", "thiserror", @@ -699,66 +788,17 @@ checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" dependencies = [ "glib-sys 0.16.3", "libc", - "system-deps 6.1.0", -] - -[[package]] -name = "camino" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" -dependencies = [ - "serde 1.0.163", -] - -[[package]] -name = "cargo-platform" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" -dependencies = [ - "serde 1.0.163", -] - -[[package]] -name = "cargo_metadata" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde 1.0.163", - "serde_json 1.0.96", -] - -[[package]] -name = "cbindgen" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" -dependencies = [ - "clap 3.2.25", - "heck 0.4.1", - "indexmap", - "log", - "proc-macro2 1.0.63", - "quote 1.0.27", - "serde 1.0.163", - "serde_json 1.0.96", - "syn 1.0.109", - "tempfile", - "toml 0.5.11", + "system-deps 6.1.2", ] [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -778,9 +818,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8790cf1286da485c72cf5fc7aeba308438800036ec67d89425924c4807268c9" +checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" dependencies = [ "smallvec", "target-lexicon", @@ -800,39 +840,39 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ + "android-tzdata", "iana-time-zone", "js-sys", - "num-integer", - "num-traits 0.2.15", - "time 0.1.45", + "num-traits 0.2.17", "wasm-bindgen", - "winapi 0.3.9", + "windows-targets 0.48.5", ] [[package]] name = "cidr-utils" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdfa36f04861d39453affe1cf084ce2d6554021a84eb6f31ebdeafb6fb92a01c" +checksum = "2315f7119b7146d6a883de6acd63ddf96071b5f79d9d98d2adaa84d749f6abf1" dependencies = [ "debug-helper", "num-bigint", - "num-traits 0.2.15", + "num-traits 0.2.17", "once_cell", "regex", ] [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array", + "crypto-common", + "inout", ] [[package]] @@ -854,89 +894,61 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "bitflags 1.3.2", "strsim 0.8.0", - "textwrap 0.11.0", + "textwrap", "unicode-width", "vec_map", ] [[package]] name = "clap" -version = "3.2.25" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "atty", - "bitflags", - "clap_lex 0.2.4", - "indexmap", - "strsim 0.10.0", - "termcolor", - "textwrap 0.16.0", -] - -[[package]] -name = "clap" -version = "4.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d21f9bf1b425d2968943631ec91202fe5e837264063503708b83013f8fc938" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", - "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.2.7" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914c8c79fb560f238ef6429439a30023c862f7a28e688c58f7203f12b29970bd" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", - "bitflags", - "clap_lex 0.4.1", + "clap_lex", "strsim 0.10.0", ] -[[package]] -name = "clap_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" -dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", -] - [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "clap_lex" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clipboard" version = "0.1.0" dependencies = [ + "cacao", "cc", + "dashmap", + "fuser", "hbb_common", "lazy_static", - "serde 1.0.163", + "libc", + "once_cell", + "parking_lot", + "percent-encoding", + "rand 0.8.5", + "serde 1.0.190", "serde_derive", "thiserror", + "utf16string", + "x11-clipboard", + "x11rb 0.12.0", ] [[package]] @@ -956,7 +968,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -974,41 +986,30 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "cocoa-foundation", - "core-foundation", - "core-graphics", - "foreign-types", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.22.3", + "foreign-types 0.3.2", "libc", "objc", ] [[package]] name = "cocoa-foundation" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", - "core-foundation", - "core-graphics-types", - "foreign-types", + "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)", "libc", "objc", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -1021,17 +1022,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" -[[package]] -name = "colored" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59" -dependencies = [ - "atty", - "lazy_static", - "winapi 0.3.9", -] - [[package]] name = "combine" version = "4.6.6" @@ -1044,9 +1034,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ "crossbeam-utils", ] @@ -1057,7 +1047,7 @@ version = "0.4.0-2" source = "git+https://github.com/open-trade/confy#7855cd3c32b1a60b44e5076ee8f6b4131da10350" dependencies = [ "directories-next", - "serde 1.0.163", + "serde 1.0.190", "thiserror", "toml 0.5.11", ] @@ -1080,21 +1070,21 @@ checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" [[package]] name = "const_format" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "unicode-xid 0.2.4", ] @@ -1110,12 +1100,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "convert_case" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" - [[package]] name = "core-foundation" version = "0.9.3" @@ -1127,10 +1111,13 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +name = "core-foundation" +version = "0.9.3" +source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" +dependencies = [ + "core-foundation-sys 0.8.6", + "libc", +] [[package]] name = "core-foundation-sys" @@ -1138,49 +1125,80 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" +dependencies = [ + "objc2-encode", +] + [[package]] name = "core-graphics" version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ - "bitflags", - "core-foundation", - "core-graphics-types", - "foreign-types", + "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.3.2", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.23.1" +source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.3 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", + "core-graphics-types 0.1.2 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", + "foreign-types 0.5.0", + "libc", + "objc2-encode", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc", ] [[package]] name = "core-graphics-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +version = "0.1.2" +source = "git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd#7d593d016175755e492a92ef89edca68ac3bd5cd" dependencies = [ - "bitflags", - "core-foundation", - "foreign-types", + "bitflags 1.3.2", + "core-foundation 0.9.3 (git+https://github.com/madsmtm/core-foundation-rs.git?rev=7d593d016175755e492a92ef89edca68ac3bd5cd)", "libc", + "objc2-encode", ] [[package]] name = "coreaudio-rs" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb17e2d1795b1996419648915df94bc7103c28f7b48062d7acf4652fc371b2ff" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" dependencies = [ - "bitflags", - "core-foundation-sys 0.6.2", + "bitflags 1.3.2", + "core-foundation-sys 0.8.4", "coreaudio-sys", ] [[package]] name = "coreaudio-sys" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f034b2258e6c4ade2f73bf87b21047567fb913ee9550837c2316d139b0262b24" +checksum = "d8478e5bdad14dce236b9898ea002eabfa87cbe14f0aa538dbe3b6a4bec4332d" dependencies = [ - "bindgen 0.64.0", + "bindgen 0.68.1", ] [[package]] @@ -1210,9 +1228,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -1249,14 +1267,14 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", "crossbeam-utils", - "memoffset 0.8.0", + "memoffset 0.9.0", "scopeguard", ] @@ -1272,9 +1290,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if 1.0.0", ] @@ -1297,56 +1315,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.2.5" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639" +checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" dependencies = [ - "nix 0.26.2", - "windows-sys 0.45.0", -] - -[[package]] -name = "cxx" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2 1.0.63", - "quote 1.0.27", - "scratch", - "syn 2.0.15", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" -dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "nix 0.27.1", + "windows-sys 0.48.0", ] [[package]] @@ -1375,6 +1349,19 @@ dependencies = [ "cc", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown 0.14.2", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "dasp" version = "0.11.0" @@ -1544,14 +1531,12 @@ dependencies = [ ] [[package]] -name = "delegate" -version = "0.8.0" +name = "deranged" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "082a24a9967533dc5d743c602157637116fc1b52806d694a5a45e6f32567fcdd" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 1.0.109", + "powerfmt", ] [[package]] @@ -1560,8 +1545,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -1571,9 +1556,9 @@ version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case 0.4.0", - "proc-macro2 1.0.63", - "quote 1.0.27", + "convert_case", + "proc-macro2 1.0.69", + "quote 1.0.33", "rustc_version", "syn 1.0.109", ] @@ -1586,9 +1571,9 @@ checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -1713,8 +1698,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a09ac8bb8c16a282264c379dffba707b9c998afc7506009137f3c6136888078" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -1743,7 +1728,7 @@ checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f" dependencies = [ "lazy_static", "regex", - "serde 1.0.163", + "serde 1.0.190", "strsim 0.10.0", ] @@ -1760,7 +1745,7 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.163", + "serde 1.0.190", "serde_derive", "thiserror", ] @@ -1776,28 +1761,28 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "embed-resource" -version = "2.1.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80663502655af01a2902dff3f06869330782267924bf1788410b74edcd93770a" +checksum = "f54cc3e827ee1c3812239a9a41dede7b4d7d5d5464faa32d71bd7cba28ce2cb2" dependencies = [ "cc", "rustc_version", - "toml 0.7.3", + "toml 0.8.6", "vswhom", - "winreg 0.11.0", + "winreg 0.51.0", ] [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if 1.0.0", ] @@ -1806,13 +1791,13 @@ dependencies = [ name = "enigo" version = "0.0.14" dependencies = [ - "core-graphics", + "core-graphics 0.22.3", "hbb_common", "log", "objc", "pkg-config", "rdev 0.5.0-2 (git+https://github.com/fufesou/rdev)", - "serde 1.0.163", + "serde 1.0.190", "serde_derive", "tfc", "unicode-segmentation", @@ -1828,77 +1813,45 @@ dependencies = [ "thiserror", ] -[[package]] -name = "enum-iterator" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7add3873b5dd076766ee79c8e406ad1a472c385476b9e38849f8eec24f1be689" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb" -dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", -] - [[package]] name = "enum-map" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "988f0d17a0fa38291e5f41f71ea8d46a5d5497b9054d5a759fae2cbb819f2356" +checksum = "53337c2dbf26a3c31eccc73a37b10c1614e8d4ae99b6a50d553e8936423c1f16" dependencies = [ "enum-map-derive", ] [[package]] name = "enum-map-derive" -version = "0.11.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2" +checksum = "04d0b288e3bb1d861c4403c1774a6f7a798781dfc519b3647df2a3dd4ae95f25" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 1.0.109", -] - -[[package]] -name = "enum_dispatch" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" -dependencies = [ - "once_cell", - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 1.0.109", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] name = "enumflags2" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" +checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939" dependencies = [ "enumflags2_derive", - "serde 1.0.163", + "serde 1.0.190", ] [[package]] name = "enumflags2_derive" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" +checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] @@ -1929,35 +1882,30 @@ dependencies = [ [[package]] name = "epoll" -version = "4.3.1" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20df693c700404f7e19d4d6fae6b15215d2913c27955d2b9d6f2c0f537511cd0" +checksum = "74351c3392ea1ff6cd2628e0042d268ac2371cb613252ff383b6dfa50d22fa79" dependencies = [ - "bitflags", + "bitflags 2.4.1", "libc", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" -version = "0.3.1" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "2.3.1" @@ -1985,16 +1933,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] -name = "exr" -version = "1.6.3" +name = "event-listener" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" +checksum = "29e56284f00d94c1bc7fd3c77027b4623c88c1f53d8d2394c6199f2921dea325" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" dependencies = [ "bit_field", "flume", "half", "lebe", - "miniz_oxide 0.6.2", + "miniz_oxide", "rayon-core", "smallvec", "zune-inflate", @@ -2009,6 +1968,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fdeflate" version = "0.3.0" @@ -2018,54 +1983,43 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "fern" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee" -dependencies = [ - "chrono", - "colored", - "log", -] - [[package]] name = "field-offset" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.8.0", + "memoffset 0.9.0", "rustc_version", ] [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "windows-sys 0.48.0", ] [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", - "miniz_oxide 0.7.1", + "miniz_oxide", ] [[package]] name = "flexi_logger" -version = "0.25.4" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611de758a8869ffffa7524aafdb48658c64dae95cdce49654d68a8442e500d89" +checksum = "84075a94fd76ea9b7a0d2c257444ae30356e58fdac6cd0fe68af33f5279981ae" dependencies = [ "chrono", "crossbeam-channel", @@ -2081,22 +2035,18 @@ dependencies = [ [[package]] name = "flume" -version = "0.10.14" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "pin-project", "spin 0.9.8", ] [[package]] name = "flutter_rust_bridge" -version = "1.75.3" +version = "1.80.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907f6624c3c6a5a1421b1c1eb9c2881104607730d5f67ce7ae50051efa7a2cf3" +checksum = "fd0305ebc9f097d9826530a55fc2acd63222e912c663f7adce3ab641ecc0f346" dependencies = [ "allo-isolate", "anyhow", @@ -2118,44 +2068,11 @@ dependencies = [ "web-sys", ] -[[package]] -name = "flutter_rust_bridge_codegen" -version = "1.75.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49fdb2728cabeb86d1271ed4398ee1518b4a3d9f659756cf5021e5c5a91e3f25" -dependencies = [ - "anyhow", - "atty", - "cargo_metadata", - "cbindgen", - "chrono", - "clap 4.2.7", - "convert_case 0.5.0", - "delegate", - "enum-iterator", - "enum_dispatch", - "fern", - "itertools 0.10.5", - "lazy_static", - "log", - "pathdiff", - "quote 1.0.27", - "regex", - "serde 1.0.163", - "serde_yaml", - "strum_macros 0.24.3", - "syn 1.0.109", - "tempfile", - "thiserror", - "toml 0.5.11", - "topological-sort", -] - [[package]] name = "flutter_rust_bridge_macros" -version = "1.75.3" +version = "1.82.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de66560e226cb4a5da7ab042a0872c0be4e165288f5bcde809a08f3f5ca22962" +checksum = "d1ab3d175f0a09c1adb55fd98d7b6460b00af72c4e889b9eec2c5aee88273996" [[package]] name = "fnv" @@ -2178,7 +2095,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] @@ -2188,10 +2126,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] -name = "form_urlencoded" -version = "1.1.0" +name = "foreign-types-shared" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -2222,10 +2166,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] -name = "futures" -version = "0.3.28" +name = "fuser" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "21370f84640642c8ea36dfb2a6bfc4c55941f476fcf431f6fef25a5ddcf0169b" +dependencies = [ + "libc", + "log", + "memchr", + "page_size", + "pkg-config", + "smallvec", + "zerocopy 0.6.5", +] + +[[package]] +name = "futures" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -2238,9 +2197,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -2248,15 +2207,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -2265,9 +2224,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-lite" @@ -2275,7 +2234,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.9.0", "futures-core", "futures-io", "memchr", @@ -2286,32 +2245,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -2331,12 +2290,12 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", "gio", - "glib 0.16.7", + "glib 0.16.9", "libc", "pango", ] @@ -2347,10 +2306,10 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", - "glib 0.16.7", + "glib 0.16.9", "libc", ] @@ -2364,7 +2323,7 @@ dependencies = [ "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", - "system-deps 6.1.0", + "system-deps 6.1.2", ] [[package]] @@ -2381,7 +2340,7 @@ dependencies = [ "libc", "pango-sys", "pkg-config", - "system-deps 6.1.0", + "system-deps 6.1.2", ] [[package]] @@ -2395,7 +2354,7 @@ dependencies = [ "gobject-sys 0.16.3", "libc", "pkg-config", - "system-deps 6.1.0", + "system-deps 6.1.2", ] [[package]] @@ -2407,7 +2366,7 @@ dependencies = [ "gdk-sys", "glib-sys 0.16.3", "libc", - "system-deps 6.1.0", + "system-deps 6.1.2", "x11 2.21.0", ] @@ -2432,16 +2391,24 @@ dependencies = [ ] [[package]] -name = "getrandom" -version = "0.2.9" +name = "gethostname" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if 1.0.0", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", ] [[package]] @@ -2456,9 +2423,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "gio" @@ -2466,13 +2433,13 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", "futures-util", "gio-sys", - "glib 0.16.7", + "glib 0.16.9", "libc", "once_cell", "pin-project-lite", @@ -2489,7 +2456,7 @@ dependencies = [ "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", - "system-deps 6.1.0", + "system-deps 6.1.2", "winapi 0.3.9", ] @@ -2499,7 +2466,7 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf7f68c2995f392c49fffb4f95ae2c873297830eb25c6bc4c114ce8f4562acc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libgit2-sys", "log", @@ -2512,7 +2479,7 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c685013b7515e668f1b57a165b009d4d28cb139a8a989bbd699c10dad29d0c5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -2527,11 +2494,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.16.7" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f" +checksum = "16aa2475c9debed5a32832cb5ff2af5a3f9e1ab9e69df58eaadc1ab2004d6eba" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -2555,11 +2522,11 @@ checksum = "41486a26d1366a8032b160b59065a59fb528530a46a49f627e7048fb8c064039" dependencies = [ "anyhow", "heck 0.3.3", - "itertools 0.9.0", + "itertools", "proc-macro-crate 0.1.5", "proc-macro-error", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -2573,8 +2540,8 @@ dependencies = [ "heck 0.4.1", "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -2595,7 +2562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" dependencies = [ "libc", - "system-deps 6.1.0", + "system-deps 6.1.2", ] [[package]] @@ -2623,7 +2590,7 @@ checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" dependencies = [ "glib-sys 0.16.3", "libc", - "system-deps 6.1.0", + "system-deps 6.1.2", ] [[package]] @@ -2632,7 +2599,7 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ff5d0f7ff308ae37e6eb47b6ded17785bdea06e438a708cd09e0288c1862f33" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "futures-channel", "futures-core", @@ -2656,7 +2623,7 @@ version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc80888271338c3ede875d8cafc452eb207476ff5539dcbe0018a8f5b827af0e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-core", "futures-sink", "glib 0.10.3", @@ -2689,7 +2656,7 @@ version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bafd01c56f59cb10f4b5a10f97bb4bdf8c2b2784ae5b04da7e2d400cf6e6afcf" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glib 0.10.3", "glib-sys 0.10.1", "gobject-sys 0.10.0", @@ -2730,7 +2697,7 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7bbb1485d87469849ec45c08e03c2f280d3ea20ff3c439d03185be54e3ce98e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-util", "glib 0.10.3", @@ -2766,14 +2733,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" dependencies = [ "atk", - "bitflags", + "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", "gdk", "gdk-pixbuf", "gio", - "glib 0.16.7", + "glib 0.16.9", "gtk-sys", "gtk3-macros", "libc", @@ -2797,7 +2764,7 @@ dependencies = [ "gobject-sys 0.16.3", "libc", "pango-sys", - "system-deps 6.1.0", + "system-deps 6.1.2", ] [[package]] @@ -2809,16 +2776,16 @@ dependencies = [ "anyhow", "proc-macro-crate 1.3.1", "proc-macro-error", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] [[package]] name = "h2" -version = "0.3.18" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -2826,7 +2793,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -2848,7 +2815,17 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.7", +] + +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +dependencies = [ + "ahash 0.8.6", + "allocator-api2", ] [[package]] @@ -2879,19 +2856,19 @@ dependencies = [ "quinn", "rand 0.8.5", "regex", - "serde 1.0.163", + "serde 1.0.190", "serde_derive", - "serde_json 1.0.96", + "serde_json 1.0.107", "socket2 0.3.19", "sodiumoxide", "sysinfo", "tokio", "tokio-socks", "tokio-util", - "toml 0.7.3", + "toml 0.7.8", "uuid", "winapi 0.3.9", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] @@ -2920,18 +2897,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -2949,10 +2917,19 @@ dependencies = [ ] [[package]] -name = "hound" -version = "3.5.0" +name = "home" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" [[package]] name = "http" @@ -2962,7 +2939,7 @@ checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", - "itoa 1.0.6", + "itoa 1.0.9", ] [[package]] @@ -2984,9 +2961,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -2996,22 +2973,22 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" -version = "0.1.1" -source = "git+https://github.com/21pages/hwcodec?branch=stable#82cdc15457e42feaf14e1b38622506b2d54baf76" +version = "0.1.3" +source = "git+https://github.com/21pages/hwcodec?branch=stable#83300549075158e5a3fa6c59ea527af3330e48ff" dependencies = [ "bindgen 0.59.2", "cc", "log", - "serde 1.0.163", + "serde 1.0.190", "serde_derive", - "serde_json 1.0.96", + "serde_json 1.0.107", ] [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -3022,9 +2999,9 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa 1.0.6", + "itoa 1.0.9", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -3033,46 +3010,46 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0646026eb1b3eea4cd9ba47912ea5ce9cc07713d105b1a14698f4e6433d348b7" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ + "futures-util", "http", "hyper", - "rustls 0.21.2", + "rustls 0.21.8", "tokio", "tokio-rustls", ] [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys 0.8.4", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows 0.48.0", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -3080,9 +3057,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", @@ -3091,7 +3068,7 @@ dependencies = [ "gif", "jpeg-decoder", "num-rational 0.4.1", - "num-traits 0.2.15", + "num-traits 0.2.17", "png", "qoi", "tiff", @@ -3100,7 +3077,7 @@ dependencies = [ [[package]] name = "impersonate_system" version = "0.1.0" -source = "git+https://github.com/21pages/impersonate-system#84b401893d5b6628c8b33b295328d13fbbe2674b" +source = "git+https://github.com/21pages/impersonate-system#2f429010a5a10b1fe5eceb553c6672fd53d20167" dependencies = [ "cc", ] @@ -3120,8 +3097,8 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", ] [[package]] @@ -3131,16 +3108,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg 1.1.0", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +dependencies = [ + "equivalent", + "hashbrown 0.14.2", ] [[package]] name = "inotify" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf888f9575c290197b2c948dc9e9ff10bd1a39ad1ea8585f734585fa6b9d3f9" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "inotify-sys", "libc", ] @@ -3154,6 +3141,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -3165,30 +3161,29 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi 0.3.1", + "hermit-abi 0.3.3", "libc", "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.7.2" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", + "hermit-abi 0.3.3", + "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -3207,15 +3202,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "0.3.4" @@ -3224,9 +3210,9 @@ checksum = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jni" @@ -3280,9 +3266,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] @@ -3298,9 +3284,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -3313,7 +3299,7 @@ dependencies = [ "anyhow", "apple-sys", "cfg-if 1.0.0", - "core-foundation", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "shadow-rs", "windows 0.48.0", "winres", @@ -3336,8 +3322,8 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68" dependencies = [ - "bitflags", - "serde 1.0.163", + "bitflags 1.3.2", + "serde 1.0.190", "unicode-segmentation", ] @@ -3365,7 +3351,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e1edfdc9b0853358306c6dfb4b77c79c779174256fe93d80c0b5ebca451a2f" dependencies = [ - "glib 0.16.7", + "glib 0.16.9", "gtk", "gtk-sys", "libappindicator-sys", @@ -3385,9 +3371,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.144" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libdbus-sys" @@ -3422,9 +3408,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if 1.0.0", "windows-sys 0.48.0", @@ -3432,29 +3418,29 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libpulse-binding" -version = "2.27.1" +version = "2.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1745b20bfc194ac12ef828f144f0ec2d4a7fe993281fa3567a0bd4969aee6890" +checksum = "ed3557a2dfc380c8f061189a01c6ae7348354e0c9886038dc6c171219c08eaff" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "libpulse-sys", "num-derive", - "num-traits 0.2.15", + "num-traits 0.2.17", "winapi 0.3.9", ] [[package]] name = "libpulse-simple-binding" -version = "2.27.1" +version = "2.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ced94199e6e44133431374e4043f34e1f0697ebfb7b7d6c244a65bfaedf0e31" +checksum = "05fd6b68f33f6a251265e6ed1212dc3107caad7c5c6fdcd847b2e65ef58c308d" dependencies = [ "libpulse-binding", "libpulse-simple-sys", @@ -3463,9 +3449,9 @@ dependencies = [ [[package]] name = "libpulse-simple-sys" -version = "1.20.1" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e423d9c619c908ce9b4916080e65ab586ca55b8c4939379f15e6e72fb43842" +checksum = "ea6613b4199d8b9f0edcfb623e020cb17bbd0bee8dd21f3c7cc938de561c4152" dependencies = [ "libpulse-sys", "pkg-config", @@ -3473,13 +3459,13 @@ dependencies = [ [[package]] name = "libpulse-sys" -version = "1.20.1" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2191e6880818d1df4cf72eac8e91dce7a5a52ba0da4b2a5cdafabc22b937eadb" +checksum = "bc19e110fbf42c17260d30f6d3dc545f58491c7830d38ecb9aaca96e26067a9b" dependencies = [ "libc", "num-derive", - "num-traits 0.2.15", + "num-traits 0.2.17", "pkg-config", "winapi 0.3.9", ] @@ -3526,9 +3512,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.9" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" dependencies = [ "cc", "libc", @@ -3546,31 +3532,22 @@ dependencies = [ ] [[package]] -name = "link-cplusplus" -version = "1.0.8" +name = "linux-raw-sys" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.3.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg 1.1.0", "scopeguard", @@ -3578,18 +3555,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "mac_address" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b238e3235c8382b7653c6408ed1b08dd379bdb9fdf990fb0bbae3db2cc0ae963" +checksum = "4863ee94f19ed315bf3bc00299338d857d4b5bc856af375cc97d237382ad3856" dependencies = [ "nix 0.23.2", "winapi 0.3.9", @@ -3647,9 +3621,9 @@ checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -3671,9 +3645,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg 1.1.0", ] @@ -3690,15 +3664,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.7.1" @@ -3711,14 +3676,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3764,22 +3729,13 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom", -] - [[package]] name = "ndk" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "jni-sys", "ndk-sys", "num_enum", @@ -3821,7 +3777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea993e32c77d87f01236c38f572ecb6c311d592e56a06262a007fd2a6e31253c" dependencies = [ "anyhow", - "bitflags", + "bitflags 1.3.2", "byteorder", "libc", "netlink-packet-core", @@ -3857,7 +3813,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", "cfg-if 1.0.0", "libc", @@ -3870,7 +3826,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "libc", "memoffset 0.6.5", @@ -3878,15 +3834,25 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "libc", "memoffset 0.7.1", - "static_assertions", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if 1.0.0", + "libc", ] [[package]] @@ -3919,32 +3885,31 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" dependencies = [ - "overload", - "winapi 0.3.9", + "windows-sys 0.48.0", ] [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg 1.1.0", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.17", ] [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" dependencies = [ - "num-traits 0.2.15", + "num-traits 0.2.17", ] [[package]] @@ -3953,8 +3918,8 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -3965,7 +3930,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg 1.1.0", - "num-traits 0.2.15", + "num-traits 0.2.17", ] [[package]] @@ -3976,7 +3941,7 @@ checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ "autocfg 1.1.0", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.17", ] [[package]] @@ -3987,7 +3952,7 @@ checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg 1.1.0", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.17", ] [[package]] @@ -3996,25 +3961,25 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.15", + "num-traits 0.2.17", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg 1.1.0", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.3", "libc", ] @@ -4034,8 +3999,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -4069,6 +4034,32 @@ dependencies = [ "objc_id", ] +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc2" +version = "0.3.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49f420f16c8814efdcd6b4258664de9d9920cbc26b6f95d034a1ca9850ccc2c" +dependencies = [ + "block2", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -4089,9 +4080,9 @@ dependencies = [ [[package]] name = "object" -version = "0.30.3" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -4106,7 +4097,7 @@ dependencies = [ "ndk", "ndk-context", "num-derive", - "num-traits 0.2.15", + "num-traits 0.2.17", "oboe-sys", ] @@ -4125,12 +4116,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "openssl-probe" version = "0.1.5" @@ -4150,7 +4135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ "dlv-list", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -4176,10 +4161,15 @@ dependencies = [ ] [[package]] -name = "os_str_bytes" -version = "6.5.0" +name = "os_info" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde 1.0.190", + "winapi 0.3.9", +] [[package]] name = "osascript" @@ -4187,16 +4177,20 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38731fa859ef679f1aec66ca9562165926b442f298467f76f5990f431efe87dc" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", "serde_derive", - "serde_json 1.0.96", + "serde_json 1.0.107", ] [[package]] -name = "overload" -version = "0.1.1" +name = "page_size" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "1b7663cbd190cfd818d08efa8497f6cd383076688c49a391ef7c0d03cd12b561" +dependencies = [ + "libc", + "winapi 0.3.9", +] [[package]] name = "pam" @@ -4215,8 +4209,8 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c94f3b9b97df3c6d4e51a14916639b24e02c7d15d1dba686ce9b1118277cb811" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -4236,9 +4230,9 @@ version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio", - "glib 0.16.7", + "glib 0.16.9", "libc", "once_cell", "pango-sys", @@ -4253,7 +4247,7 @@ dependencies = [ "glib-sys 0.16.3", "gobject-sys 0.16.3", "libc", - "system-deps 6.1.0", + "system-deps 6.1.2", ] [[package]] @@ -4272,9 +4266,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" @@ -4288,15 +4282,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.4.1", "smallvec", - "windows-sys 0.45.0", + "windows-targets 0.48.5", ] [[package]] @@ -4312,15 +4306,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pbkdf2" @@ -4342,9 +4330,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "phf" @@ -4386,29 +4374,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 1.0.109", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -4416,6 +4404,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.27" @@ -4424,29 +4423,29 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plist" -version = "1.4.3" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd9647b268a3d3e14ff09c23201133a62589c658db02bb7388c7246aafe0590" +checksum = "9a4a0cfc5fb21a09dc6af4bf834cf10d4a32fccd9e2ea468c4b1751a097487aa" dependencies = [ "base64", - "indexmap", + "indexmap 1.9.3", "line-wrap", - "quick-xml 0.28.2", - "serde 1.0.163", - "time 0.3.21", + "quick-xml", + "serde 1.0.190", + "time 0.3.30", ] [[package]] name = "png" -version = "0.17.8" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.1", + "miniz_oxide", ] [[package]] @@ -4456,7 +4455,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg 1.1.0", - "bitflags", + "bitflags 1.3.2", "cfg-if 1.0.0", "concurrent-queue", "libc", @@ -4465,6 +4464,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -4479,12 +4484,12 @@ checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" [[package]] name = "prettyplease" -version = "0.2.4" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ - "proc-macro2 1.0.63", - "syn 2.0.15", + "proc-macro2 1.0.69", + "syn 2.0.38", ] [[package]] @@ -4512,7 +4517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit 0.19.8", + "toml_edit 0.19.15", ] [[package]] @@ -4522,8 +4527,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", "version_check", ] @@ -4534,8 +4539,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "version_check", ] @@ -4550,18 +4555,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "protobuf" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +checksum = "b65f4a8ec18723a734e5dc09c173e0abf9690432da5340285d536edcb4dac190" dependencies = [ "bytes", "once_cell", @@ -4571,9 +4576,9 @@ dependencies = [ [[package]] name = "protobuf-codegen" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901" +checksum = "6e85514a216b1c73111d9032e26cc7a5ecb1bb3d4d9539e91fb72a4395060f78" dependencies = [ "anyhow", "once_cell", @@ -4586,12 +4591,12 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49" +checksum = "77d6fbd6697c9e531873e81cec565a85e226b99a0f10e1acc079be057fe2fcba" dependencies = [ "anyhow", - "indexmap", + "indexmap 1.9.3", "log", "protobuf", "protobuf-support", @@ -4602,9 +4607,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +checksum = "6872f4d4f4b98303239a2b5838f5bbbb77b01ffc892d627957f37a22d7cfe69c" dependencies = [ "thiserror", ] @@ -4633,34 +4638,25 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.23.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" dependencies = [ "memchr", ] [[package]] name = "quinn" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445cbfe2382fa023c4f2f3c7e1c95c03dcc1df2bf23cebcb2b13e1402c4394d1" +checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.20.8", + "rustls 0.20.9", "thiserror", "tokio", "tracing", @@ -4669,15 +4665,15 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" +checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" dependencies = [ "bytes", "rand 0.8.5", - "ring", + "ring 0.16.20", "rustc-hash", - "rustls 0.20.8", + "rustls 0.20.9", "rustls-native-certs", "slab", "thiserror", @@ -4694,7 +4690,7 @@ checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" dependencies = [ "libc", "quinn-proto", - "socket2 0.4.9", + "socket2 0.4.10", "tracing", "windows-sys 0.42.0", ] @@ -4710,11 +4706,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.27" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.63", + "proc-macro2 1.0.69", ] [[package]] @@ -4867,9 +4863,9 @@ checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -4877,14 +4873,12 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] @@ -4893,9 +4887,9 @@ version = "0.5.0-2" source = "git+https://github.com/fufesou/rdev?branch=master#339b2a334ba273afebb7e27fb76984e620fc76e5" dependencies = [ "cocoa", - "core-foundation", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.8.4", - "core-graphics", + "core-graphics 0.22.3", "dispatch", "enum-map", "epoll", @@ -4914,12 +4908,12 @@ dependencies = [ [[package]] name = "rdev" version = "0.5.0-2" -source = "git+https://github.com/fufesou/rdev#2e8221d653f4995c831ad52966e79a514516b1fa" +source = "git+https://github.com/fufesou/rdev#339b2a334ba273afebb7e27fb76984e620fc76e5" dependencies = [ "cocoa", - "core-foundation", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.8.4", - "core-graphics", + "core-graphics 0.22.3", "dispatch", "enum-map", "epoll", @@ -4946,9 +4940,9 @@ dependencies = [ [[package]] name = "realfft" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6b8e8f0c6d2234aa58048d7290c60bf92cd36fd2888cd8331c66ad4f2e1d2" +checksum = "953d9f7e5cdd80963547b456251296efc2626ed4e3cbf36c869d9564e0220571" dependencies = [ "rustfft", ] @@ -4959,7 +4953,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -4968,7 +4962,16 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -4984,9 +4987,21 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -4995,9 +5010,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "repng" @@ -5031,10 +5046,10 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.2", + "rustls 0.21.8", "rustls-pemfile", - "serde 1.0.163", - "serde_json 1.0.96", + "serde 1.0.190", + "serde_json 1.0.107", "serde_urlencoded", "tokio", "tokio-rustls", @@ -5057,11 +5072,25 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi 0.3.9", ] +[[package]] +name = "ring" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "ringbuf" version = "0.3.3" @@ -5111,7 +5140,7 @@ checksum = "cd70209c27d5b08f5528bdc779ea3ffb418954e28987f9f9775c6eac41003f9c" dependencies = [ "num-complex", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.17", "realfft", ] @@ -5178,11 +5207,11 @@ dependencies = [ "cfg-if 1.0.0", "chrono", "cidr-utils", - "clap 4.2.7", + "clap 4.4.7", "clipboard", "cocoa", - "core-foundation", - "core-graphics", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.22.3", "cpal", "crossbeam-queue", "ctrlc", @@ -5196,7 +5225,6 @@ dependencies = [ "errno", "evdev", "flutter_rust_bridge", - "flutter_rust_bridge_codegen", "fon", "fruitbasket", "hbb_common", @@ -5208,7 +5236,7 @@ dependencies = [ "jni 0.21.1", "keepawake", "lazy_static", - "libloading 0.8.0", + "libloading 0.8.1", "libpulse-binding", "libpulse-simple-binding", "mac_address", @@ -5217,9 +5245,11 @@ dependencies = [ "num_cpus", "objc", "objc_id", + "once_cell", "os-version", "pam", "parity-tokio-ipc", + "percent-encoding", "rdev 0.5.0-2 (git+https://github.com/fufesou/rdev?branch=master)", "repng", "reqwest", @@ -5231,9 +5261,9 @@ dependencies = [ "samplerate", "sciter-rs", "scrap", - "serde 1.0.163", + "serde 1.0.190", "serde_derive", - "serde_json 1.0.96", + "serde_json 1.0.107", "serde_repr", "sha2", "shared_memory", @@ -5254,6 +5284,8 @@ dependencies = [ "winreg 0.11.0", "winres", "wol-rs", + "x11-clipboard", + "x11rb 0.12.0", "zip", ] @@ -5276,7 +5308,7 @@ checksum = "e17d4f6cbdb180c9f4b2a26bbf01c4e647f1e1dea22fe8eb9db54198b32f9434" dependencies = [ "num-complex", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.17", "primal-check", "strength_reduce", "transpose", @@ -5285,46 +5317,59 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.19" +version = "0.37.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys 0.4.10", "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" dependencies = [ - "ring", + "ring 0.16.20", "sct", "webpki", ] [[package]] name = "rustls" -version = "0.21.2" +version = "0.21.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f" +checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" dependencies = [ "log", - "ring", - "rustls-webpki", + "ring 0.17.5", + "rustls-webpki 0.101.7", "sct", ] [[package]] name = "rustls-native-certs" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", "rustls-pemfile", @@ -5334,34 +5379,44 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64", ] [[package]] name = "rustls-webpki" -version = "0.100.1" +version = "0.100.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +checksum = "5f6a5fc258f1c1276dfe3016516945546e2d5383911efc0fc4f1cdc5df3a4ae3" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "safemem" @@ -5389,11 +5444,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -5409,9 +5464,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scrap" @@ -5436,38 +5491,32 @@ dependencies = [ "pkg-config", "quest", "repng", - "serde 1.0.163", - "serde_json 1.0.96", + "serde 1.0.190", + "serde_json 1.0.107", "target_build_utils", "tracing", "webm", "winapi 0.3.9", ] -[[package]] -name = "scratch" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" - [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", - "core-foundation", + "bitflags 1.3.2", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.8.4", "libc", "security-framework-sys", @@ -5475,9 +5524,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys 0.8.4", "libc", @@ -5485,12 +5534,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" -dependencies = [ - "serde 1.0.163", -] +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" @@ -5500,22 +5546,22 @@ checksum = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" [[package]] name = "serde" -version = "1.0.163" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.163" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] @@ -5532,33 +5578,33 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ - "itoa 1.0.6", + "itoa 1.0.9", "ryu", - "serde 1.0.163", + "serde 1.0.190", ] [[package]] name = "serde_repr" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] name = "serde_spanned" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", ] [[package]] @@ -5568,28 +5614,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.6", + "itoa 1.0.9", "ryu", - "serde 1.0.163", -] - -[[package]] -name = "serde_yaml" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" -dependencies = [ - "indexmap", - "ryu", - "serde 1.0.163", - "yaml-rust", + "serde 1.0.190", ] [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -5598,9 +5632,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if 1.0.0", "cpufeatures", @@ -5616,7 +5650,7 @@ dependencies = [ "const_format", "git2", "is_debug", - "time 0.3.21", + "time 0.3.30", "tzdb", ] @@ -5635,9 +5669,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "shutdown_hooks" @@ -5645,16 +5679,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6057adedbec913419c92996f395ba69931acbd50b7d56955394cd3f7bedbfa45" -[[package]] -name = "signal-hook" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -5672,9 +5696,9 @@ checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[package]] name = "simd-adler32" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "siphasher" @@ -5684,18 +5708,18 @@ checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg 1.1.0", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" @@ -5710,9 +5734,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi 0.3.9", @@ -5727,7 +5751,7 @@ dependencies = [ "ed25519", "libc", "libsodium-sys", - "serde 1.0.163", + "serde 1.0.190", ] [[package]] @@ -5794,8 +5818,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -5806,17 +5830,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "rustversion", "syn 1.0.109", ] [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -5835,37 +5859,36 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.15" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "unicode-ident", ] [[package]] name = "sys-locale" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0b9eefabb91675082b41eb94c3ecd91af7656caee3fb4961a07c0ec8c7ca6f" +checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" dependencies = [ "libc", - "windows-sys 0.45.0", ] [[package]] name = "sysinfo" -version = "0.29.6" +version = "0.29.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7cb97a5a85a136d84e75d5c3cf89655090602efb1be0d8d5337b7e386af2908" +checksum = "0a18d114d420ada3a891e6bc8e96a2023402203296a47cdd65083377dad18ba5" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys 0.8.4", @@ -5878,12 +5901,12 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags", - "core-foundation", + "bitflags 1.3.2", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", "system-configuration-sys", ] @@ -5914,14 +5937,14 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.1.0" +version = "6.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2" +checksum = "94af52f9402f94aac4948a2518b43359be8d9ce6cd9efc1c4de3b2f7b7e897d6" dependencies = [ "cfg-expr", "heck 0.4.1", "pkg-config", - "toml 0.7.3", + "toml 0.8.6", "version-compare 0.1.1", ] @@ -5940,12 +5963,12 @@ name = "tao" version = "0.22.2" source = "git+https://github.com/rustdesk-org/tao?branch=dev#1e5b97258cf42a30f80f85a6aa0b1a4aece1977e" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "cc", "cocoa", - "core-foundation", - "core-graphics", + "core-foundation 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.22.3", "crossbeam-channel", "dispatch", "gdk", @@ -5954,7 +5977,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gio", - "glib 0.16.7", + "glib 0.16.9", "glib-sys 0.16.3", "gtk", "image", @@ -5987,8 +6010,8 @@ name = "tao-macros" version = "0.1.2" source = "git+https://github.com/rustdesk-org/tao?branch=dev#1e5b97258cf42a30f80f85a6aa0b1a4aece1977e" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -6000,9 +6023,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.7" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "target_build_utils" @@ -6017,32 +6040,32 @@ dependencies = [ [[package]] name = "tauri-winrt-notification" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5bff1d532fead7c43324a0fa33643b8621a47ce2944a633be4cb6c0240898f" +checksum = "006851c9ccefa3c38a7646b8cec804bb429def3da10497bfa977179869c3e8e2" dependencies = [ - "quick-xml 0.23.1", - "windows 0.39.0", + "quick-xml", + "windows 0.51.1", ] [[package]] name = "tempfile" -version = "3.5.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if 1.0.0", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", + "fastrand 2.0.1", + "redox_syscall 0.4.1", + "rustix 0.38.21", + "windows-sys 0.48.0", ] [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] @@ -6065,19 +6088,13 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "tfc" version = "0.6.1" source = "git+https://github.com/fufesou/The-Fat-Controller#9dd86151525fd010dc93f6bc9b6aedd1a75cc342" dependencies = [ "anyhow", - "core-graphics", + "core-graphics 0.22.3", "unicode-segmentation", "winapi 0.3.9", "x11 2.19.0", @@ -6085,22 +6102,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] @@ -6114,9 +6131,9 @@ dependencies = [ [[package]] name = "tiff" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" dependencies = [ "flate2", "jpeg-decoder", @@ -6136,29 +6153,31 @@ dependencies = [ [[package]] name = "time" -version = "0.3.21" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ - "itoa 1.0.6", + "deranged", + "itoa 1.0.9", "libc", "num_threads", - "serde 1.0.163", + "powerfmt", + "serde 1.0.190", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -6192,7 +6211,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.4.9", + "socket2 0.4.10", "tokio-macros", "windows-sys 0.48.0", ] @@ -6203,9 +6222,9 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] @@ -6214,7 +6233,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.2", + "rustls 0.21.8", "tokio", ] @@ -6236,16 +6255,16 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", "futures-util", - "hashbrown", + "hashbrown 0.14.2", "pin-project-lite", "slab", "tokio", @@ -6258,7 +6277,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", ] [[package]] @@ -6267,7 +6286,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", "serde_spanned", "toml_datetime 0.5.1", "toml_edit 0.18.1", @@ -6275,14 +6294,26 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.3" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", "serde_spanned", - "toml_datetime 0.6.1", - "toml_edit 0.19.8", + "toml_datetime 0.6.5", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc" +dependencies = [ + "serde 1.0.190", + "serde_spanned", + "toml_datetime 0.6.5", + "toml_edit 0.20.7", ] [[package]] @@ -6291,16 +6322,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", ] [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", ] [[package]] @@ -6309,31 +6340,38 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" dependencies = [ - "indexmap", + "indexmap 1.9.3", "nom8", - "serde 1.0.163", + "serde 1.0.190", "serde_spanned", "toml_datetime 0.5.1", ] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap", - "serde 1.0.163", + "indexmap 2.0.2", + "serde 1.0.190", "serde_spanned", - "toml_datetime 0.6.1", + "toml_datetime 0.6.5", "winnow", ] [[package]] -name = "topological-sort" -version = "0.2.2" +name = "toml_edit" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.0.2", + "serde 1.0.190", + "serde_spanned", + "toml_datetime 0.6.5", + "winnow", +] [[package]] name = "tower-service" @@ -6343,11 +6381,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if 1.0.0", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -6355,20 +6392,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] @@ -6389,7 +6426,7 @@ version = "0.5.1" source = "git+https://github.com/rustdesk-org/tray-icon#ef98e7b98abed2e3da614277eced12a85bfb717c" dependencies = [ "cocoa", - "core-graphics", + "core-graphics 0.22.3", "crossbeam-channel", "dirs-next", "libappindicator", @@ -6409,9 +6446,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "tz-rs" @@ -6459,9 +6496,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -6480,9 +6517,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unicode-xid" @@ -6503,15 +6540,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] -name = "url" -version = "2.3.1" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde 1.0.163", + "serde 1.0.190", ] [[package]] @@ -6534,6 +6577,15 @@ dependencies = [ "log", ] +[[package]] +name = "utf16string" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b62a1e85e12d5d712bf47a85f426b73d303e2d00a90de5f3004df3596e9d216" +dependencies = [ + "byteorder", +] + [[package]] name = "utf8parse" version = "0.2.1" @@ -6542,9 +6594,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.3.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dad5567ad0cf5b760e5665964bec1b47dfd077ba8a2544b513f3556d3d239a2" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" dependencies = [ "getrandom", ] @@ -6609,15 +6661,15 @@ dependencies = [ [[package]] name = "waker-fn" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -6638,11 +6690,10 @@ dependencies = [ [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -6660,9 +6711,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -6670,24 +6721,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.35" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -6697,38 +6748,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ - "quote 1.0.27", + "quote 1.0.33", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", - "syn 2.0.15", + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.85" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.62" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -6752,12 +6803,12 @@ dependencies = [ [[package]] name = "webpki" -version = "0.22.0" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring", - "untrusted", + "ring 0.17.5", + "untrusted 0.9.0", ] [[package]] @@ -6766,7 +6817,7 @@ version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" dependencies = [ - "rustls-webpki", + "rustls-webpki 0.100.3", ] [[package]] @@ -6777,20 +6828,21 @@ checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix 0.38.21", ] [[package]] name = "whoami" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" dependencies = [ "wasm-bindgen", "web-sys", @@ -6841,9 +6893,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi 0.3.9", ] @@ -6889,19 +6941,6 @@ dependencies = [ "windows_x86_64_msvc 0.34.0", ] -[[package]] -name = "windows" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c4bd0a50ac6020f65184721f758dba47bb9fbc2133df715ec74a237b26794a" -dependencies = [ - "windows_aarch64_msvc 0.39.0", - "windows_i686_gnu 0.39.0", - "windows_i686_msvc 0.39.0", - "windows_x86_64_gnu 0.39.0", - "windows_x86_64_msvc 0.39.0", -] - [[package]] name = "windows" version = "0.44.0" @@ -6928,7 +6967,26 @@ checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ "windows-implement", "windows-interface", - "windows-targets 0.48.0", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +dependencies = [ + "windows-core", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -6937,8 +6995,8 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -6948,8 +7006,8 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] @@ -6959,7 +7017,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd9db37ecb5b13762d95468a2fc6009d4b2c62801243223aabd44fca13ad13c8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "widestring", "windows-sys 0.45.0", ] @@ -6994,7 +7052,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.48.5", ] [[package]] @@ -7014,17 +7072,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -7035,9 +7093,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -7051,12 +7109,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" -[[package]] -name = "windows_aarch64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2" - [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -7065,9 +7117,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -7081,12 +7133,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" -[[package]] -name = "windows_i686_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b" - [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -7095,9 +7141,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -7111,12 +7157,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" -[[package]] -name = "windows_i686_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106" - [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -7125,9 +7165,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -7141,12 +7181,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" -[[package]] -name = "windows_x86_64_gnu" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65" - [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -7155,9 +7189,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -7167,9 +7201,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -7183,12 +7217,6 @@ version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" -[[package]] -name = "windows_x86_64_msvc" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809" - [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -7197,15 +7225,15 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" dependencies = [ "memchr", ] @@ -7239,6 +7267,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +dependencies = [ + "cfg-if 1.0.0", + "windows-sys 0.48.0", +] + [[package]] name = "winres" version = "0.1.12" @@ -7250,9 +7288,9 @@ dependencies = [ [[package]] name = "wol-rs" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48dc5e486e34a31515518d370cdd8bf59ec696323fe8f92b858e43942e84a765" +checksum = "3c5a8a033ef9b208ec8b5946761958ed2b2693ac49b04f647fdc013000870b8f" [[package]] name = "wyz" @@ -7282,6 +7320,14 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x11-clipboard" +version = "0.8.1" +source = "git+https://github.com/clslaid/x11-clipboard?branch=feat/store-batch#5fc2e73bc01ada3681159b34cf3ea8f0d14cd904" +dependencies = [ + "x11rb 0.12.0", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -7299,11 +7345,24 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" dependencies = [ - "gethostname", + "gethostname 0.2.3", "nix 0.24.3", "winapi 0.3.9", "winapi-wsapoll", - "x11rb-protocol", + "x11rb-protocol 0.10.0", +] + +[[package]] +name = "x11rb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" +dependencies = [ + "gethostname 0.3.0", + "nix 0.26.4", + "winapi 0.3.9", + "winapi-wsapoll", + "x11rb-protocol 0.12.0", ] [[package]] @@ -7315,52 +7374,54 @@ dependencies = [ "nix 0.24.3", ] +[[package]] +name = "x11rb-protocol" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc" +dependencies = [ + "nix 0.26.4", +] + [[package]] name = "xdg-home" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2769203cd13a0c6015d515be729c526d041e9cf2c0cc478d57faee85f40c6dcd" dependencies = [ - "nix 0.26.2", + "nix 0.26.4", "winapi 0.3.9", ] -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "zbus" -version = "3.12.0" +version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29242fa5ec5693629ae74d6eb1f69622a9511f600986d6d9779bccf36ac316e3" +checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" dependencies = [ "async-broadcast", "async-executor", "async-fs", "async-io", "async-lock", + "async-process", "async-recursion", "async-task", "async-trait", + "blocking", "byteorder", "derivative", "enumflags2", - "event-listener", + "event-listener 2.5.3", "futures-core", "futures-sink", "futures-util", "hex", - "nix 0.26.2", + "nix 0.26.4", "once_cell", "ordered-stream", "rand 0.8.5", - "serde 1.0.163", + "serde 1.0.190", "serde_repr", "sha1", "static_assertions", @@ -7375,13 +7436,13 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.12.0" +version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537793e26e9af85f774801dc52c6f6292352b2b517c5cf0449ffd3735732a53a" +checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "regex", "syn 1.0.109", "zvariant_utils", @@ -7389,20 +7450,61 @@ dependencies = [ [[package]] name = "zbus_names" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" +checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9" dependencies = [ - "serde 1.0.163", + "serde 1.0.190", "static_assertions", "zvariant", ] [[package]] -name = "zip" +name = "zerocopy" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e92305c174683d78035cbf1b70e18db6329cc0f1b9cae0a52ca90bf5bfe7125" +checksum = "96f8f25c15a0edc9b07eb66e7e6e97d124c0505435c382fde1ab7ceb188aa956" +dependencies = [ + "byteorder", + "zerocopy-derive 0.6.5", +] + +[[package]] +name = "zerocopy" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7d7c7970ca2215b8c1ccf4d4f354c4733201dfaaba72d44ae5b37472e4901" +dependencies = [ + "zerocopy-derive 0.7.18", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "855e0f6af9cd72b87d8a6c586f3cb583f5cdcc62c2c80869d8cd7e96fdf7ee20" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b27b1bb92570f989aac0ab7e9cbfbacdd65973f7ee920d9f0e71ebac878fd0b" +dependencies = [ + "proc-macro2 1.0.69", + "quote 1.0.33", + "syn 2.0.38", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "aes", "byteorder", @@ -7414,7 +7516,7 @@ dependencies = [ "hmac", "pbkdf2", "sha1", - "time 0.3.21", + "time 0.3.30", "zstd 0.11.2+zstd.1.5.2", ] @@ -7429,11 +7531,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ - "zstd-safe 6.0.5+zstd.1.5.4", + "zstd-safe 6.0.6", ] [[package]] @@ -7448,9 +7550,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", @@ -7458,12 +7560,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] @@ -7478,38 +7579,38 @@ dependencies = [ [[package]] name = "zvariant" -version = "3.12.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe4914a985446d6fd287019b5fceccce38303d71407d9e6e711d44954a05d8" +checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c" dependencies = [ "byteorder", "enumflags2", "libc", - "serde 1.0.163", + "serde 1.0.190", "static_assertions", "zvariant_derive", ] [[package]] name = "zvariant_derive" -version = "3.12.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34c20260af4b28b3275d6676c7e2a6be0d4332e8e0aba4616d34007fd84e462a" +checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b" +checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" dependencies = [ - "proc-macro2 1.0.63", - "quote 1.0.27", + "proc-macro2 1.0.69", + "quote 1.0.33", "syn 1.0.109", ] diff --git a/Cargo.toml b/Cargo.toml index 3f1037f5b..a18a3c984 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,13 @@ linux_headless = ["pam" ] virtual_display_driver = ["virtual_display"] plugin_framework = [] linux-pkg-config = ["magnum-opus/linux-pkg-config", "scrap/linux-pkg-config"] +unix-file-copy-paste = [ + "dep:x11-clipboard", + "dep:x11rb", + "dep:percent-encoding", + "dep:once_cell", + "clipboard/unix-file-copy-paste", +] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -62,7 +69,7 @@ num_cpus = "1.15" bytes = { version = "1.4", features = ["serde"] } default-net = "0.14" wol-rs = "1.0" -flutter_rust_bridge = { version = "1.75", features = ["uuid"], optional = true} +flutter_rust_bridge = { version = "=1.80", features = ["uuid"], optional = true} errno = "0.3" rdev = { git = "https://github.com/fufesou/rdev", branch = "master" } url = { version = "2.3", features = ["serde"] } @@ -132,6 +139,10 @@ dbus = "0.9" dbus-crossroads = "0.5" pam = { git="https://github.com/fufesou/pam", optional = true } users = { version = "0.11" } +x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true} +x11rb = {version = "0.12", features = ["all-extensions"], optional = true} +percent-encoding = {version = "2.3", optional = true} +once_cell = {version = "1.18", optional = true} [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.13" @@ -153,7 +164,6 @@ winapi = { version = "0.3", features = [ "winnt" ] } [build-dependencies] cc = "1.0" hbb_common = { path = "libs/hbb_common" } -flutter_rust_bridge_codegen = "1.75" os-version = "0.2" [dev-dependencies] diff --git a/README.md b/README.md index 924cd1786..a805c2ca8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) -[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) +[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) [![Open Bounties](https://img.shields.io/endpoint?url=https%3A%2F%2Fconsole.algora.io%2Fapi%2Fshields%2Frustdesk%2Fbounties%3Fstatus%3Dopen)](https://console.algora.io/org/rustdesk/bounties?status=open) @@ -80,11 +80,12 @@ sudo apt install -y zip g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxc libclang-dev ninja-build libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev ``` -### openSUSE Tumbleweed +### openSUSE Tumbleweed ```sh sudo zypper install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libXfixes-devel cmake alsa-lib-devel gstreamer-devel gstreamer-plugins-base-devel xdotool-devel ``` + ### Fedora 28 (CentOS 8) ```sh @@ -170,6 +171,7 @@ Please ensure that you are running these commands from the root of the RustDesk - **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions - **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: screen capture - **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: platform specific keyboard/mouse control +- **[libs/clipboard](https://github.com/rustdesk/rustdesk/tree/master/libs/clipboard)**: file copy and paste implemention for Windows, Linux, OSX. - **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: obsolete Sciter UI (deprecated) - **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: audio/clipboard/input/video services, and network connections - **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: start a peer connection diff --git a/build.py b/build.py index 42dc8d6de..8cff351e2 100755 --- a/build.py +++ b/build.py @@ -24,18 +24,21 @@ else: flutter_build_dir_2 = f'flutter/{flutter_build_dir}' skip_cargo = False + def get_arch() -> str: custom_arch = os.environ.get("ARCH") if custom_arch is None: return "amd64" return custom_arch + def system2(cmd): err = os.system(cmd) if err != 0: print(f"Error occurred when executing: {cmd}. Exiting.") sys.exit(-1) + def get_version(): with open("Cargo.toml", encoding="utf-8") as fh: for line in fh: @@ -123,6 +126,11 @@ def make_parser(): action='store_true', help='Build windows portable' ) + parser.add_argument( + '--unix-file-copy-paste', + action='store_true', + help='Build with unix file copy paste feature' + ) parser.add_argument( '--flatpak', action='store_true', @@ -185,6 +193,7 @@ def download_extract_features(features, res_dir): import re proxy = '' + def req(url): if not proxy: return url @@ -196,9 +205,9 @@ def download_extract_features(features, res_dir): for (feat, feat_info) in features.items(): includes = feat_info['include'] if 'include' in feat_info and feat_info['include'] else [] - includes = [ re.compile(p) for p in includes ] + includes = [re.compile(p) for p in includes] excludes = feat_info['exclude'] if 'exclude' in feat_info and feat_info['exclude'] else [] - excludes = [ re.compile(p) for p in excludes ] + excludes = [re.compile(p) for p in excludes] print(f'{feat} download begin') download_filename = feat_info['zip_url'].split('/')[-1] @@ -272,6 +281,8 @@ def get_features(args): features.append('flatpak') if args.appimage: features.append('appimage') + if args.unix_file_copy_paste: + features.append('unix-file-copy-paste') print("features:", features) return features @@ -350,6 +361,7 @@ def build_flutter_deb(version, features): os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version) os.chdir("..") + def build_deb_from_folder(version, binary_folder): os.chdir('flutter') system2('mkdir -p tmpdeb/usr/bin/') @@ -388,10 +400,12 @@ def build_deb_from_folder(version, binary_folder): os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version) os.chdir("..") + def build_flutter_dmg(version, features): if not skip_cargo: # set minimum osx build target, now is 10.14, which is the same as the flutter xcode project - system2(f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release') + system2( + f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --lib --release') # copy dylib system2( "cp target/release/liblibrustdesk.dylib target/release/librustdesk.dylib") @@ -557,7 +571,8 @@ def main(): codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/* codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app '''.format(pa)) - system2('create-dmg "RustDesk %s.dmg" "target/release/bundle/osx/RustDesk.app"' % version) + system2( + 'create-dmg "RustDesk %s.dmg" "target/release/bundle/osx/RustDesk.app"' % version) os.rename('RustDesk %s.dmg' % version, 'rustdesk-%s.dmg' % version) if pa: @@ -577,7 +592,7 @@ def main(): else: print('Not signed') else: - # buid deb package + # build deb package system2( 'mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb') system2('dpkg-deb -R rustdesk.deb tmpdeb') diff --git a/build.rs b/build.rs index 054eb3700..ad745667d 100644 --- a/build.rs +++ b/build.rs @@ -76,52 +76,12 @@ fn install_oboe() { //cc::Build::new().file("oboe.cc").include(include).compile("oboe_wrapper"); } -#[cfg(feature = "flutter")] -fn gen_flutter_rust_bridge() { - if !std::env::var("RUN_FFIGEN").is_ok() { - return; - } - use lib_flutter_rust_bridge_codegen::{ - config_parse, frb_codegen, get_symbols_if_no_duplicates, RawOpts, - }; - let llvm_path = match std::env::var("LLVM_HOME") { - Ok(path) => Some(vec![path]), - Err(_) => None, - }; - // Tell Cargo that if the given file changes, to rerun this build script. - println!("cargo:rerun-if-changed=src/flutter_ffi.rs"); - // Options for frb_codegen - let raw_opts = RawOpts { - // Path of input Rust code - rust_input: vec!["src/flutter_ffi.rs".to_string()], - // Path of output generated Dart code - dart_output: vec!["flutter/lib/generated_bridge.dart".to_string()], - // Path of output generated C header - c_output: Some(vec!["flutter/macos/Runner/bridge_generated.h".to_string()]), - /// Path to the installed LLVM - llvm_path, - // for other options use defaults - ..Default::default() - }; - // get opts from raw opts - let configs = config_parse(raw_opts); - // generation of rust api for ffi - let all_symbols = get_symbols_if_no_duplicates(&configs).unwrap(); - for config in configs.iter() { - frb_codegen(config, &all_symbols).unwrap(); - } -} - fn main() { hbb_common::gen_version(); install_oboe(); // there is problem with cfg(target_os) in build.rs, so use our workaround // let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); // if target_os == "android" || target_os == "ios" { - #[cfg(feature = "flutter")] - gen_flutter_rust_bridge(); - // return; - // } #[cfg(all(windows, feature = "inline"))] build_manifest(); #[cfg(windows)] diff --git a/flutter/android/app/build.gradle b/flutter/android/app/build.gradle index 9973c8f62..9e32e163e 100644 --- a/flutter/android/app/build.gradle +++ b/flutter/android/app/build.gradle @@ -104,7 +104,7 @@ flutter { dependencies { implementation "androidx.media:media:1.6.0" - implementation 'com.github.getActivity:XXPermissions:16.2' + implementation 'com.github.getActivity:XXPermissions:18.5' implementation("org.jetbrains.kotlin:kotlin-stdlib") { version { strictly("$kotlin_version") } } } diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml index b3c655917..1b99801bf 100644 --- a/flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter/android/app/src/main/AndroidManifest.xml @@ -61,6 +61,14 @@ + + + + + + + + - \ No newline at end of file + diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index 8dccca448..c6a77f36b 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.10' repositories { google() jcenter() diff --git a/flutter/ios/Podfile.lock b/flutter/ios/Podfile.lock index 1ad5f6360..1336513c0 100644 --- a/flutter/ios/Podfile.lock +++ b/flutter/ios/Podfile.lock @@ -55,7 +55,7 @@ PODS: - SDWebImage (5.15.5): - SDWebImage/Core (= 5.15.5) - SDWebImage/Core (5.15.5) - - sqflite (0.0.2): + - sqflite (0.0.3): - Flutter - FMDB (>= 2.7.5) - SwiftyGif (5.4.4) @@ -65,7 +65,8 @@ PODS: - Flutter - video_player_avfoundation (0.0.1): - Flutter - - wakelock (0.0.1): + - FlutterMacOS + - wakelock_plus (0.0.1): - Flutter DEPENDENCIES: @@ -80,8 +81,8 @@ DEPENDENCIES: - sqflite (from `.symlinks/plugins/sqflite/ios`) - uni_links (from `.symlinks/plugins/uni_links/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`) - - wakelock (from `.symlinks/plugins/wakelock/ios`) + - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) + - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) SPEC REPOS: trunk: @@ -116,30 +117,30 @@ EXTERNAL SOURCES: url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" video_player_avfoundation: - :path: ".symlinks/plugins/video_player_avfoundation/ios" - wakelock: - :path: ".symlinks/plugins/wakelock/ios" + :path: ".symlinks/plugins/video_player_avfoundation/darwin" + wakelock_plus: + :path: ".symlinks/plugins/wakelock_plus/ios" SPEC CHECKSUMS: - device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed + device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 file_picker: ce3938a0df3cc1ef404671531facef740d03f920 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb + image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5 MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb - package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe - sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 + sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f uni_links: d97da20c7701486ba192624d99bffaaffcfc298a - url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2 - video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff - wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f + url_launcher_ios: 68d46cc9766d0c41dbdc884310529557e3cd7a86 + video_player_avfoundation: 8563f13d8fc8b2c29dc2d09e60b660e4e8128837 + wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 PODFILE CHECKSUM: 2aff76ba0ac13439479560d1d03e9b4479f5c9e1 diff --git a/flutter/ios/Runner.xcodeproj/project.pbxproj b/flutter/ios/Runner.xcodeproj/project.pbxproj index 0813abb11..4004fb493 100644 --- a/flutter/ios/Runner.xcodeproj/project.pbxproj +++ b/flutter/ios/Runner.xcodeproj/project.pbxproj @@ -159,7 +159,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -434,7 +434,7 @@ "-framework", "\"video_player_avfoundation\"", "-framework", - "\"wakelock\"", + "\"wakelock_plus\"", ); PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -632,7 +632,7 @@ "-framework", "\"video_player_avfoundation\"", "-framework", - "\"wakelock\"", + "\"wakelock_plus\"", ); PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -722,7 +722,7 @@ "-framework", "\"video_player_avfoundation\"", "-framework", - "\"wakelock\"", + "\"wakelock_plus\"", ); PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a33..a6b826db2 100644 --- a/flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/flutter/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLIconFile + + CFBundleURLName + com.carriez.rustdesk + CFBundleURLSchemes + + rustdesk + + + LSRequiresIPhoneOS UIApplicationSupportsIndirectInputEvents diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 9c8404c56..4d23614ca 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1955,6 +1955,7 @@ bool handleUriLink({List? cmdArgs, Uri? uri, String? uriString}) { List? urlLinkToCmdArgs(Uri uri) { String? command; String? id; + final options = ["connect", "play", "file-transfer", "port-forward", "rdp"]; if (uri.authority.isEmpty && uri.path.split('').every((char) => char == '/')) { return []; @@ -1962,18 +1963,33 @@ List? urlLinkToCmdArgs(Uri uri) { // For compatibility command = '--connect'; id = uri.path.substring("/new/".length); - } else if (['connect', "play", 'file-transfer', 'port-forward', 'rdp'] - .contains(uri.authority)) { + } else if (options.contains(uri.authority)) { + final optionIndex = options.indexOf(uri.authority); command = '--${uri.authority}'; if (uri.path.length > 1) { id = uri.path.substring(1); } + if (isMobile && id != null) { + if (optionIndex == 0 || optionIndex == 1) { + connect(Get.context!, id); + } else if (optionIndex == 2) { + connect(Get.context!, id, isFileTransfer: true); + } + return null; + } } else if (uri.authority.length > 2 && uri.path.length <= 1) { // rustdesk:// command = '--connect'; id = uri.authority; } + if (isMobile){ + if (id != null){ + connect(Get.context!, id); + return null; + } + } + List args = List.empty(growable: true); if (command != null && id != null) { args.add(command); diff --git a/flutter/lib/common/widgets/dialog.dart b/flutter/lib/common/widgets/dialog.dart index d1e14d2fb..2d2fbe63d 100644 --- a/flutter/lib/common/widgets/dialog.dart +++ b/flutter/lib/common/widgets/dialog.dart @@ -972,7 +972,7 @@ void showRestartRemoteDevice(PeerInfo pi, String id, SessionID sessionId, title: Row(children: [ Icon(Icons.warning_rounded, color: Colors.redAccent, size: 28), Flexible( - child: Text(translate("Restart Remote Device")) + child: Text(translate("Restart remote device")) .paddingOnly(left: 10)), ]), content: Text( @@ -1248,25 +1248,41 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async { double fpsInitValue = 30; bool qualitySet = false; bool fpsSet = false; + + bool? direct; + try { + direct = + ConnectionTypeState.find(id).direct.value == ConnectionType.strDirect; + } catch (_) {} + bool hideFps = (await bind.mainIsUsingPublicServer() && direct != true) || + versionCmp(ffi.ffiModel.pi.version, '1.2.0') < 0; + bool hideMoreQuality = + (await bind.mainIsUsingPublicServer() && direct != true) || + versionCmp(ffi.ffiModel.pi.version, '1.2.2') < 0; + setCustomValues({double? quality, double? fps}) async { if (quality != null) { qualitySet = true; await bind.sessionSetCustomImageQuality( sessionId: sessionId, value: quality.toInt()); + print("quality:$quality"); } if (fps != null) { fpsSet = true; await bind.sessionSetCustomFps(sessionId: sessionId, fps: fps.toInt()); + print("fps:$fps"); } if (!qualitySet) { qualitySet = true; await bind.sessionSetCustomImageQuality( sessionId: sessionId, value: qualityInitValue.toInt()); + print("qualityInitValue:$qualityInitValue"); } - if (!fpsSet) { + if (!hideFps && !fpsSet) { fpsSet = true; await bind.sessionSetCustomFps( sessionId: sessionId, fps: fpsInitValue.toInt()); + print("fpsInitValue:$fpsInitValue"); } } @@ -1279,7 +1295,9 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async { final quality = await bind.sessionGetCustomImageQuality(sessionId: sessionId); qualityInitValue = quality != null && quality.isNotEmpty ? quality[0].toDouble() : 50.0; - if (qualityInitValue < 10 || qualityInitValue > 2000) { + if ((hideMoreQuality && qualityInitValue > 100) || + qualityInitValue < 10 || + qualityInitValue > 2000) { qualityInitValue = 50; } // fps @@ -1289,20 +1307,14 @@ customImageQualityDialog(SessionID sessionId, String id, FFI ffi) async { if (fpsInitValue < 5 || fpsInitValue > 120) { fpsInitValue = 30; } - bool? direct; - try { - direct = - ConnectionTypeState.find(id).direct.value == ConnectionType.strDirect; - } catch (_) {} - bool notShowFps = (await bind.mainIsUsingPublicServer() && direct != true) || - versionCmp(ffi.ffiModel.pi.version, '1.2.0') < 0; final content = customImageQualityWidget( initQuality: qualityInitValue, initFps: fpsInitValue, setQuality: (v) => setCustomValues(quality: v), setFps: (v) => setCustomValues(fps: v), - showFps: !notShowFps); + showFps: !hideFps, + showMoreQuality: !hideMoreQuality); msgBoxCommon(ffi.dialogManager, 'Custom Image Quality', content, [btnClose]); } diff --git a/flutter/lib/common/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart index b19034c68..cfe2e1927 100644 --- a/flutter/lib/common/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -27,45 +27,44 @@ class DraggableChatWindow extends StatelessWidget { @override Widget build(BuildContext context) { return isIOS - ? IOSDraggable ( - position: position, - chatModel: chatModel, - width: width, - height: height, - builder: (context) { - return Column( - children: [ - _buildMobileAppBar(context), - Expanded( - child: ChatPage(chatModel: chatModel), - ), - ], - ); - }, - ) - : Draggable( - checkKeyboard: true, - position: position, - width: width, - height: height, - chatModel: chatModel, - builder: (context, onPanUpdate) { - final child = - Scaffold( - resizeToAvoidBottomInset: false, - appBar: CustomAppBar( - onPanUpdate: onPanUpdate, - appBar: isDesktop - ? _buildDesktopAppBar(context) - : _buildMobileAppBar(context), + ? IOSDraggable( + position: position, + chatModel: chatModel, + width: width, + height: height, + builder: (context) { + return Column( + children: [ + _buildMobileAppBar(context), + Expanded( + child: ChatPage(chatModel: chatModel), ), - body: ChatPage(chatModel: chatModel), - ); - return Container( - decoration: - BoxDecoration(border: Border.all(color: MyTheme.border)), - child: child); - }); + ], + ); + }, + ) + : Draggable( + checkKeyboard: true, + position: position, + width: width, + height: height, + chatModel: chatModel, + builder: (context, onPanUpdate) { + final child = Scaffold( + resizeToAvoidBottomInset: false, + appBar: CustomAppBar( + onPanUpdate: onPanUpdate, + appBar: isDesktop + ? _buildDesktopAppBar(context) + : _buildMobileAppBar(context), + ), + body: ChatPage(chatModel: chatModel), + ); + return Container( + decoration: + BoxDecoration(border: Border.all(color: MyTheme.border)), + child: child); + }); } Widget _buildMobileAppBar(BuildContext context) { @@ -354,14 +353,14 @@ class _DraggableState extends State { } class IOSDraggable extends StatefulWidget { - const IOSDraggable({ - Key? key, - this.position = Offset.zero, - this.chatModel, - required this.width, - required this.height, - required this.builder}) - : super(key: key); + const IOSDraggable( + {Key? key, + this.position = Offset.zero, + this.chatModel, + required this.width, + required this.height, + required this.builder}) + : super(key: key); final Offset position; final ChatModel? chatModel; @@ -423,7 +422,7 @@ class _IOSDraggableState extends State { _lastBottomHeight = bottomHeight; } -@override + @override Widget build(BuildContext context) { checkKeyboard(); return Stack( @@ -439,12 +438,12 @@ class _IOSDraggableState extends State { _chatModel?.setChatWindowPosition(_position); }, child: Material( - child: - Container( - width: _width, - height: _height, - decoration: BoxDecoration(border: Border.all(color: MyTheme.border)), - child: widget.builder(context), + child: Container( + width: _width, + height: _height, + decoration: + BoxDecoration(border: Border.all(color: MyTheme.border)), + child: widget.builder(context), ), ), ), @@ -499,6 +498,7 @@ class QualityMonitor extends StatelessWidget { "${qualityMonitorModel.data.targetBitrate ?? '-'}kb"), _row( "Codec", qualityMonitorModel.data.codecFormat ?? '-'), + _row("Chroma", qualityMonitorModel.data.chroma ?? '-'), ], ), ) diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index a071d8f40..406bab66c 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -495,7 +495,7 @@ abstract class BasePeerCard extends StatelessWidget { return _connectCommonAction( context, id, - translate('Transfer File'), + translate('Transfer file'), isFileTransfer: true, ); } @@ -505,7 +505,7 @@ abstract class BasePeerCard extends StatelessWidget { return _connectCommonAction( context, id, - translate('TCP Tunneling'), + translate('TCP tunneling'), isTcpTunneling: true, ); } @@ -568,7 +568,7 @@ abstract class BasePeerCard extends StatelessWidget { MenuEntryBase _createShortCutAction(String id) { return MenuEntryButton( childBuilder: (TextStyle? style) => Text( - translate('Create Desktop Shortcut'), + translate('Create desktop shortcut'), style: style, ), proc: () { @@ -818,7 +818,7 @@ abstract class BasePeerCard extends StatelessWidget { MenuEntryBase _addToAb(Peer peer) { return MenuEntryButton( childBuilder: (TextStyle? style) => Text( - translate('Add to Address Book'), + translate('Add to address book'), style: style, ), proc: () { diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index d472d086f..f6844e837 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -75,9 +75,11 @@ class _PeerTabPageState extends State void initState() { final uiType = bind.getLocalFlutterOption(k: 'peer-card-ui-type'); if (uiType != '') { - peerCardUiType.value = int.parse(uiType) == PeerUiType.list.index - ? PeerUiType.list - : PeerUiType.grid; + peerCardUiType.value = int.parse(uiType) == 0 + ? PeerUiType.grid + : int.parse(uiType) == 1 + ? PeerUiType.tile + : PeerUiType.list; } hideAbTagsPanel.value = bind.mainGetLocalOption(key: "hideAbTagsPanel").isNotEmpty; @@ -454,7 +456,7 @@ class _PeerTabPageState extends State }); }, child: Tooltip( - message: translate('Add to Address Book'), + message: translate('Add to address book'), child: Icon(model.icons[PeerTabIndex.ab.index])), ).marginOnly(left: isMobile ? 11 : 6), ); @@ -763,8 +765,6 @@ class PeerViewDropdown extends StatefulWidget { } class _PeerViewDropdownState extends State { - RelativeRect menuPos = RelativeRect.fromLTRB(0, 0, 0, 0); - @override Widget build(BuildContext context) { final List types = [PeerUiType.grid, PeerUiType.tile, PeerUiType.list]; @@ -804,6 +804,7 @@ class _PeerViewDropdownState extends State { )))); } + var menuPos = RelativeRect.fromLTRB(0, 0, 0, 0); return _hoverAction( context: context, child: Tooltip( @@ -819,16 +820,14 @@ class _PeerViewDropdownState extends State { onTapDown: (details) { final x = details.globalPosition.dx; final y = details.globalPosition.dy; - setState(() { - menuPos = RelativeRect.fromLTRB(x, y, x, y); - }); + menuPos = RelativeRect.fromLTRB(x, y, x, y); }, onTap: () => showMenu( context: context, position: menuPos, items: items, elevation: 8, - ), + ) ); } } diff --git a/flutter/lib/common/widgets/setting_widgets.dart b/flutter/lib/common/widgets/setting_widgets.dart index 771b65ab5..26fd219ed 100644 --- a/flutter/lib/common/widgets/setting_widgets.dart +++ b/flutter/lib/common/widgets/setting_widgets.dart @@ -10,7 +10,11 @@ customImageQualityWidget( required double initFps, required Function(double) setQuality, required Function(double) setFps, - required bool showFps}) { + required bool showFps, + required bool showMoreQuality}) { + if (!showMoreQuality && initQuality > 100) { + initQuality = 50; + } final qualityValue = initQuality.obs; final fpsValue = initFps.obs; @@ -69,7 +73,7 @@ customImageQualityWidget( style: const TextStyle(fontSize: 15), )), // mobile doesn't have enough space - if (!isMobile) + if (showMoreQuality && !isMobile) Expanded( flex: 1, child: Row( @@ -85,7 +89,7 @@ customImageQualityWidget( )) ], )), - if (isMobile) + if (showMoreQuality && isMobile) Obx(() => Row( children: [ Expanded( @@ -160,7 +164,8 @@ customImageQualitySetting() { setFps: (v) { bind.mainSetUserDefaultOption(key: fpsKey, value: v.toString()); }, - showFps: true); + showFps: true, + showMoreQuality: true); } Future setServerConfig( @@ -265,7 +270,7 @@ List ServerConfigImportExportWidgets( return [ Tooltip( - message: translate('Import Server Config'), + message: translate('Import server config'), child: IconButton( icon: Icon(Icons.paste, color: Colors.grey), onPressed: import), ), diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index 28b10785b..f2f9d551c 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -133,7 +133,7 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { if (isDesktop) { v.add( TTextMenu( - child: Text(translate('Transfer File')), + child: Text(translate('Transfer file')), onPressed: () => connect(context, id, isFileTransfer: true)), ); } @@ -141,7 +141,7 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { if (isDesktop) { v.add( TTextMenu( - child: Text(translate('TCP Tunneling')), + child: Text(translate('TCP tunneling')), onPressed: () => connect(context, id, isTcpTunneling: true)), ); } @@ -176,7 +176,7 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { pi.platform == kPeerPlatformMacOS)) { v.add( TTextMenu( - child: Text(translate('Restart Remote Device')), + child: Text(translate('Restart remote device')), onPressed: () => showRestartRemoteDevice(pi, id, sessionId, ffi.dialogManager)), ); @@ -191,6 +191,7 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { } // blockUserInput if (ffi.ffiModel.keyboard && + ffi.ffiModel.permissions['block_input'] != false && pi.platform == kPeerPlatformWindows) // privacy-mode != true ?? { v.add(TTextMenu( @@ -436,9 +437,9 @@ Future> toolbarDisplayToggle( child: Text(translate('Mute')))); } // file copy and paste - if (Platform.isWindows && - pi.platform == kPeerPlatformWindows && - perms['file'] != false) { + if (perms['file'] != false && + bind.mainHasFileClipboard() && + pi.platformAdditions.containsKey(kPlatformAdditionsHasFileClipboard)) { final option = 'enable-file-transfer'; final value = bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); @@ -547,5 +548,22 @@ Future> toolbarDisplayToggle( child: Text(translate('Use all my displays for the remote session')))); } + // 444 + final codec_format = ffi.qualityMonitorModel.data.codecFormat; + if (versionCmp(pi.version, "1.2.4") >= 0 && + (codec_format == "AV1" || codec_format == "VP9")) { + final option = 'i444'; + final value = + bind.sessionGetToggleOptionSync(sessionId: sessionId, arg: option); + v.add(TToggleMenu( + value: value, + onChanged: (value) async { + if (value == null) return; + await bind.sessionToggleOption(sessionId: sessionId, value: option); + bind.sessionChangePreferCodec(sessionId: sessionId); + }, + child: Text(translate('True color (4:4:4)')))); + } + return v; } diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 073edbfec..460894c31 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -22,6 +22,7 @@ const String kPlatformAdditionsIsWayland = "is_wayland"; const String kPlatformAdditionsHeadless = "headless"; const String kPlatformAdditionsIsInstalled = "is_installed"; const String kPlatformAdditionsVirtualDisplays = "virtual_displays"; +const String kPlatformAdditionsHasFileClipboard = "has_file_clipboard"; const String kPeerPlatformWindows = "Windows"; const String kPeerPlatformLinux = "Linux"; diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index bf923d388..6ad252384 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -50,6 +50,7 @@ class _ConnectionPageState extends State return list.sublist(0, n); } } + bool isPeersLoading = false; bool isPeersLoaded = false; @@ -81,7 +82,7 @@ class _ConnectionPageState extends State if (Get.isRegistered()) { Get.delete(); } - if (Get.isRegistered()){ + if (Get.isRegistered()) { Get.delete(); } super.dispose(); @@ -157,9 +158,9 @@ class _ConnectionPageState extends State await Future.delayed(Duration(milliseconds: 100)); peers = await getAllPeers(); setState(() { - isPeersLoading = false; - isPeersLoaded = true; - }); + isPeersLoading = false; + isPeersLoaded = true; + }); } /// UI for the remote ID TextField. @@ -177,148 +178,173 @@ class _ConnectionPageState extends State Row( children: [ Expanded( - child: AutoSizeText( - translate('Control Remote Desktop'), - maxLines: 1, - style: Theme.of(context) - .textTheme - .titleLarge - ?.merge(TextStyle(height: 1)), - ), - ), + child: Row( + children: [ + AutoSizeText( + translate('Control Remote Desktop'), + maxLines: 1, + style: Theme.of(context) + .textTheme + .titleLarge + ?.merge(TextStyle(height: 1)), + ).marginOnly(right: 4), + Tooltip( + waitDuration: Duration(milliseconds: 0), + message: translate("id_input_tip"), + child: Icon( + Icons.help_outline_outlined, + size: 16, + color: Theme.of(context) + .textTheme + .titleLarge + ?.color + ?.withOpacity(0.5), + ), + ), + ], + )), ], ).marginOnly(bottom: 15), Row( children: [ Expanded( - child: - Autocomplete( - optionsBuilder: (TextEditingValue textEditingValue) { - if (textEditingValue.text == '') { - return const Iterable.empty(); - } - else if (peers.isEmpty && !isPeersLoaded) { - Peer emptyPeer = Peer( - id: '', - username: '', - hostname: '', - alias: '', - platform: '', - tags: [], - hash: '', - forceAlwaysRelay: false, - rdpPort: '', - rdpUsername: '', - loginName: '', + child: Autocomplete( + optionsBuilder: (TextEditingValue textEditingValue) { + if (textEditingValue.text == '') { + return const Iterable.empty(); + } else if (peers.isEmpty && !isPeersLoaded) { + Peer emptyPeer = Peer( + id: '', + username: '', + hostname: '', + alias: '', + platform: '', + tags: [], + hash: '', + forceAlwaysRelay: false, + rdpPort: '', + rdpUsername: '', + loginName: '', + ); + return [emptyPeer]; + } else { + String textWithoutSpaces = + textEditingValue.text.replaceAll(" ", ""); + if (int.tryParse(textWithoutSpaces) != null) { + textEditingValue = TextEditingValue( + text: textWithoutSpaces, + selection: textEditingValue.selection, ); - return [emptyPeer]; } - else { - String textWithoutSpaces = textEditingValue.text.replaceAll(" ", ""); - if (int.tryParse(textWithoutSpaces) != null) { - textEditingValue = TextEditingValue( - text: textWithoutSpaces, - selection: textEditingValue.selection, - ); - } - String textToFind = textEditingValue.text.toLowerCase(); + String textToFind = textEditingValue.text.toLowerCase(); - return peers.where((peer) => - peer.id.toLowerCase().contains(textToFind) || - peer.username.toLowerCase().contains(textToFind) || - peer.hostname.toLowerCase().contains(textToFind) || - peer.alias.toLowerCase().contains(textToFind)) - .toList(); + return peers + .where((peer) => + peer.id.toLowerCase().contains(textToFind) || + peer.username + .toLowerCase() + .contains(textToFind) || + peer.hostname + .toLowerCase() + .contains(textToFind) || + peer.alias.toLowerCase().contains(textToFind)) + .toList(); + } + }, + fieldViewBuilder: ( + BuildContext context, + TextEditingController fieldTextEditingController, + FocusNode fieldFocusNode, + VoidCallback onFieldSubmitted, + ) { + fieldTextEditingController.text = _idController.text; + Get.put(fieldTextEditingController); + fieldFocusNode.addListener(() async { + _idInputFocused.value = fieldFocusNode.hasFocus; + if (fieldFocusNode.hasFocus && !isPeersLoading) { + _fetchPeers(); } - }, + }); + final textLength = + fieldTextEditingController.value.text.length; + // select all to facilitate removing text, just following the behavior of address input of chrome + fieldTextEditingController.selection = + TextSelection(baseOffset: 0, extentOffset: textLength); + return Obx(() => TextField( + autocorrect: false, + enableSuggestions: false, + keyboardType: TextInputType.visiblePassword, + focusNode: fieldFocusNode, + style: const TextStyle( + fontFamily: 'WorkSans', + fontSize: 22, + height: 1.4, + ), + maxLines: 1, + cursorColor: + Theme.of(context).textTheme.titleLarge?.color, + decoration: InputDecoration( + filled: false, + counterText: '', + hintText: _idInputFocused.value + ? null + : translate('Enter Remote ID'), + contentPadding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 13)), + controller: fieldTextEditingController, + inputFormatters: [IDTextInputFormatter()], + onChanged: (v) { + _idController.id = v; + }, + )); + }, + onSelected: (option) { + setState(() { + _idController.id = option.id; + FocusScope.of(context).unfocus(); + }); + }, + optionsViewBuilder: (BuildContext context, + AutocompleteOnSelected onSelected, + Iterable options) { + double maxHeight = options.length * 50; + maxHeight = maxHeight > 200 ? 200 : maxHeight; - fieldViewBuilder: (BuildContext context, - TextEditingController fieldTextEditingController, - FocusNode fieldFocusNode , - VoidCallback onFieldSubmitted, - ) { - fieldTextEditingController.text = _idController.text; - Get.put(fieldTextEditingController); - fieldFocusNode.addListener(() async { - _idInputFocused.value = fieldFocusNode.hasFocus; - if (fieldFocusNode.hasFocus && !isPeersLoading){ - _fetchPeers(); - } - }); - final textLength = fieldTextEditingController.value.text.length; - // select all to facilitate removing text, just following the behavior of address input of chrome - fieldTextEditingController.selection = TextSelection(baseOffset: 0, extentOffset: textLength); - return Obx(() => - TextField( - maxLength: 90, - autocorrect: false, - enableSuggestions: false, - keyboardType: TextInputType.visiblePassword, - focusNode: fieldFocusNode, - style: const TextStyle( - fontFamily: 'WorkSans', - fontSize: 22, - height: 1.4, - ), - maxLines: 1, - cursorColor: Theme.of(context).textTheme.titleLarge?.color, - decoration: InputDecoration( - filled: false, - counterText: '', - hintText: _idInputFocused.value - ? null - : translate('Enter Remote ID'), - contentPadding: const EdgeInsets.symmetric( - horizontal: 15, vertical: 13)), - controller: fieldTextEditingController, - inputFormatters: [IDTextInputFormatter()], - onChanged: (v) { - _idController.id = v; - }, - )); - }, - onSelected: (option) { - setState(() { - _idController.id = option.id; - FocusScope.of(context).unfocus(); - }); - }, - optionsViewBuilder: (BuildContext context, AutocompleteOnSelected onSelected, Iterable options) { - double maxHeight = options.length * 50; - maxHeight = maxHeight > 200 ? 200 : maxHeight; - - return Align( - alignment: Alignment.topLeft, - child: ClipRRect( + return Align( + alignment: Alignment.topLeft, + child: ClipRRect( borderRadius: BorderRadius.circular(5), child: Material( - elevation: 4, - child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: maxHeight, - maxWidth: 319, - ), - child: peers.isEmpty && isPeersLoading - ? Container( - height: 80, - child: Center( - child: CircularProgressIndicator( - strokeWidth: 2, - ), - ) - ) - : Padding( - padding: const EdgeInsets.only(top: 5), - child: ListView( - children: options.map((peer) => AutocompletePeerTile(onSelect: () => onSelected(peer), peer: peer)).toList(), + elevation: 4, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: maxHeight, + maxWidth: 319, ), + child: peers.isEmpty && isPeersLoading + ? Container( + height: 80, + child: Center( + child: CircularProgressIndicator( + strokeWidth: 2, + ), + )) + : Padding( + padding: const EdgeInsets.only(top: 5), + child: ListView( + children: options + .map((peer) => AutocompletePeerTile( + onSelect: () => + onSelected(peer), + peer: peer)) + .toList(), + ), + ), ), - ), - )), - ); - }, - ) - ), + )), + ); + }, + )), ], ), Padding( @@ -329,7 +355,7 @@ class _ConnectionPageState extends State Button( isOutline: true, onTap: () => onConnect(isFileTransfer: true), - text: "Transfer File", + text: "Transfer file", ), const SizedBox( width: 17, @@ -382,7 +408,7 @@ class _ConnectionPageState extends State onTap: () async { await start_service(true); }, - child: Text(translate("Start Service"), + child: Text(translate("Start service"), style: TextStyle( decoration: TextDecoration.underline, fontSize: em))) diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index 59118f7cd..25670e628 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -336,11 +336,17 @@ class _DesktopHomePageState extends State } if (Platform.isWindows) { if (!bind.mainIsInstalled()) { - return buildInstallCard( - "", "install_tip", "Install", bind.mainGotoInstall); + return buildInstallCard("", "install_tip", "Install", () async { + await rustDeskWinManager.closeAllSubWindows(); + bind.mainGotoInstall(); + }); } else if (bind.mainIsInstalledLowerVersion()) { - return buildInstallCard("Status", "Your installation is lower version.", - "Click to upgrade", bind.mainUpdateMe); + return buildInstallCard( + "Status", "Your installation is lower version.", "Click to upgrade", + () async { + await rustDeskWinManager.closeAllSubWindows(); + bind.mainUpdateMe(); + }); } } else if (Platform.isMacOS) { if (!bind.mainIsCanScreenRecording(prompt: false)) { @@ -384,13 +390,16 @@ class _DesktopHomePageState extends State final keyShowSelinuxHelpTip = "show-selinux-help-tip"; if (bind.mainGetLocalOption(key: keyShowSelinuxHelpTip) != 'N') { LinuxCards.add(buildInstallCard( - "Warning", "selinux_tip", "", () async {}, - marginTop: LinuxCards.isEmpty ? 20.0 : 5.0, - help: 'Help', - link: - 'https://rustdesk.com/docs/en/client/linux/#permissions-issue', - closeButton: true, - closeOption: keyShowSelinuxHelpTip, + "Warning", + "selinux_tip", + "", + () async {}, + marginTop: LinuxCards.isEmpty ? 20.0 : 5.0, + help: 'Help', + link: + 'https://rustdesk.com/docs/en/client/linux/#permissions-issue', + closeButton: true, + closeOption: keyShowSelinuxHelpTip, )); } } @@ -418,7 +427,11 @@ class _DesktopHomePageState extends State Widget buildInstallCard(String title, String content, String btnText, GestureTapCallback onPressed, - {double marginTop = 20.0, String? help, String? link, bool? closeButton, String? closeOption}) { + {double marginTop = 20.0, + String? help, + String? link, + bool? closeButton, + String? closeOption}) { void closeCard() async { if (closeOption != null) { await bind.mainSetLocalOption(key: closeOption, value: 'N'); @@ -439,89 +452,90 @@ class _DesktopHomePageState extends State Container( margin: EdgeInsets.only(top: marginTop), child: Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - colors: [ - Color.fromARGB(255, 226, 66, 188), - Color.fromARGB(255, 244, 114, 124), - ], - )), - padding: EdgeInsets.all(20), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: (title.isNotEmpty - ? [ - Center( - child: Text( - translate(title), - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 15), - ).marginOnly(bottom: 6)), - ] - : []) + - [ - Text( - translate(content), - style: TextStyle( - height: 1.5, - color: Colors.white, - fontWeight: FontWeight.normal, - fontSize: 13), - ).marginOnly(bottom: 20) - ] + - (btnText.isNotEmpty - ? [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FixedWidthButton( - width: 150, - padding: 8, - isOutline: true, - text: translate(btnText), - textColor: Colors.white, - borderColor: Colors.white, - textSize: 20, - radius: 10, - onTap: onPressed, - ) - ]) - ] - : []) + - (help != null - ? [ - Center( - child: InkWell( - onTap: () async => - await launchUrl(Uri.parse(link!)), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + colors: [ + Color.fromARGB(255, 226, 66, 188), + Color.fromARGB(255, 244, 114, 124), + ], + )), + padding: EdgeInsets.all(20), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: (title.isNotEmpty + ? [ + Center( child: Text( - translate(help), - style: TextStyle( - decoration: TextDecoration.underline, - color: Colors.white, - fontSize: 12), - )).marginOnly(top: 6)), - ] - : []))), + translate(title), + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 15), + ).marginOnly(bottom: 6)), + ] + : []) + + [ + Text( + translate(content), + style: TextStyle( + height: 1.5, + color: Colors.white, + fontWeight: FontWeight.normal, + fontSize: 13), + ).marginOnly(bottom: 20) + ] + + (btnText.isNotEmpty + ? [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FixedWidthButton( + width: 150, + padding: 8, + isOutline: true, + text: translate(btnText), + textColor: Colors.white, + borderColor: Colors.white, + textSize: 20, + radius: 10, + onTap: onPressed, + ) + ]) + ] + : []) + + (help != null + ? [ + Center( + child: InkWell( + onTap: () async => + await launchUrl(Uri.parse(link!)), + child: Text( + translate(help), + style: TextStyle( + decoration: + TextDecoration.underline, + color: Colors.white, + fontSize: 12), + )).marginOnly(top: 6)), + ] + : []))), ), if (closeButton != null && closeButton == true) - Positioned( - top: 18, - right: 0, - child: IconButton( - icon: Icon( - Icons.close, - color: Colors.white, - size: 20, + Positioned( + top: 18, + right: 0, + child: IconButton( + icon: Icon( + Icons.close, + color: Colors.white, + size: 20, + ), + onPressed: closeCard, ), - onPressed: closeCard, ), - ), ], ); } diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index 416cbaa5e..3c8b5878d 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -632,23 +632,27 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { }).marginOnly(left: _kContentHMargin), Column( children: [ - _OptionCheckBox(context, 'Enable Keyboard/Mouse', 'enable-keyboard', + _OptionCheckBox(context, 'Enable keyboard/mouse', 'enable-keyboard', enabled: enabled, fakeValue: fakeValue), - _OptionCheckBox(context, 'Enable Clipboard', 'enable-clipboard', + _OptionCheckBox(context, 'Enable clipboard', 'enable-clipboard', enabled: enabled, fakeValue: fakeValue), _OptionCheckBox( - context, 'Enable File Transfer', 'enable-file-transfer', + context, 'Enable file transfer', 'enable-file-transfer', enabled: enabled, fakeValue: fakeValue), - _OptionCheckBox(context, 'Enable Audio', 'enable-audio', + _OptionCheckBox(context, 'Enable audio', 'enable-audio', enabled: enabled, fakeValue: fakeValue), - _OptionCheckBox(context, 'Enable TCP Tunneling', 'enable-tunnel', + _OptionCheckBox(context, 'Enable TCP tunneling', 'enable-tunnel', enabled: enabled, fakeValue: fakeValue), _OptionCheckBox( - context, 'Enable Remote Restart', 'enable-remote-restart', + context, 'Enable remote restart', 'enable-remote-restart', enabled: enabled, fakeValue: fakeValue), _OptionCheckBox( - context, 'Enable Recording Session', 'enable-record-session', + context, 'Enable recording session', 'enable-record-session', enabled: enabled, fakeValue: fakeValue), + if (Platform.isWindows) + _OptionCheckBox( + context, 'Enable blocking user input', 'enable-block-input', + enabled: enabled, fakeValue: fakeValue), _OptionCheckBox(context, 'Enable remote configuration modification', 'allow-remote-config-modification', enabled: enabled, fakeValue: fakeValue), @@ -769,7 +773,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { bool enabled = !locked; return _Card(title: 'Security', children: [ shareRdp(context, enabled), - _OptionCheckBox(context, 'Deny LAN Discovery', 'enable-lan-discovery', + _OptionCheckBox(context, 'Deny LAN discovery', 'enable-lan-discovery', reverse: true, enabled: enabled), ...directIp(context), whitelist(), @@ -809,7 +813,7 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin { update() => setState(() {}); RxBool applyEnabled = false.obs; return [ - _OptionCheckBox(context, 'Enable Direct IP Access', 'direct-server', + _OptionCheckBox(context, 'Enable direct IP access', 'direct-server', update: update, enabled: !locked), () { // Simple temp wrapper for PR check @@ -1320,6 +1324,7 @@ class _DisplayState extends State<_Display> { otherRow('Lock after session end', 'lock_after_session_end'), otherRow('Privacy mode', 'privacy_mode'), otherRow('Reverse mouse wheel', 'reverse_mouse_wheel'), + otherRow('True color (4:4:4)', 'i444'), ]; if (useTextureRender) { children.add(otherRow('Show displays as individual windows', diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 1ac7987d7..b8a897a65 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -15,7 +15,7 @@ import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/models/file_model.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import '../../consts.dart'; import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; @@ -91,7 +91,7 @@ class _FileManagerPageState extends State }); Get.put(_ffi, tag: 'ft_${widget.id}'); if (!Platform.isLinux) { - Wakelock.enable(); + WakelockPlus.enable(); } debugPrint("File manager page init success with id ${widget.id}"); _ffi.dialogManager.setOverlayState(_overlayKeyState); @@ -104,7 +104,7 @@ class _FileManagerPageState extends State _ffi.close(); _ffi.dialogManager.dismissAll(); if (!Platform.isLinux) { - Wakelock.disable(); + WakelockPlus.disable(); } Get.delete(tag: 'ft_${widget.id}'); }); @@ -1126,10 +1126,11 @@ class _FileManagerViewState extends State { void _onSelectedChanged(SelectedItems selectedItems, List entries, Entry entry, bool isLocal) { - final isCtrlDown = RawKeyboard.instance.keysPressed - .contains(LogicalKeyboardKey.controlLeft); + 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.shiftLeft) || + RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftRight); if (isCtrlDown) { if (selectedItems.items.contains(entry)) { selectedItems.remove(entry); diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index ffc29b02f..5b67ed77b 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -8,7 +8,7 @@ import 'package:flutter_custom_cursor/cursor_manager.dart' as custom_cursor_manager; import 'package:get/get.dart'; import 'package:provider/provider.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:flutter_custom_cursor/flutter_custom_cursor.dart'; import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart'; @@ -123,7 +123,7 @@ class _RemotePageState extends State .showLoading(translate('Connecting...'), onCancel: closeConnection); }); if (!Platform.isLinux) { - Wakelock.enable(); + WakelockPlus.enable(); } _ffi.ffiModel.updateEventListener(sessionId, widget.id); @@ -183,7 +183,7 @@ class _RemotePageState extends State _isWindowBlur = false; } if (!Platform.isLinux) { - Wakelock.enable(); + WakelockPlus.enable(); } } @@ -192,7 +192,7 @@ class _RemotePageState extends State void onWindowMaximize() { super.onWindowMaximize(); if (!Platform.isLinux) { - Wakelock.enable(); + WakelockPlus.enable(); } } @@ -200,7 +200,7 @@ class _RemotePageState extends State void onWindowMinimize() { super.onWindowMinimize(); if (!Platform.isLinux) { - Wakelock.disable(); + WakelockPlus.disable(); } } @@ -228,7 +228,7 @@ class _RemotePageState extends State overlays: SystemUiOverlay.values); } if (!Platform.isLinux) { - await Wakelock.disable(); + await WakelockPlus.disable(); } await Get.delete(tag: widget.id); removeSharedStates(widget.id); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 3b56ef4cc..c97a0ef3f 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -386,7 +386,7 @@ class _ConnectionTabPageState extends State { pi.platform == kPeerPlatformMacOS)) { menu.add(MenuEntryButton( childBuilder: (TextStyle? style) => Text( - translate('Restart Remote Device'), + translate('Restart remote device'), style: style, ), proc: () => showRestartRemoteDevice( diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index 89a43adf6..1fbd52c45 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/models/chat_model.dart'; +import 'package:flutter_hbb/models/cm_file_model.dart'; import 'package:flutter_hbb/utils/platform_channel.dart'; import 'package:get/get.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; @@ -482,8 +483,8 @@ class _CmHeaderState extends State<_CmHeader> client.type_() != ClientType.file), child: IconButton( onPressed: () => checkClickTime(client.id, () { - if (client.type_() != ClientType.file) { - gFFI.chatModel.toggleCMSidePage(); + if (client.type_() == ClientType.file) { + gFFI.chatModel.toggleCMFilePage(); } else { gFFI.chatModel .toggleCMChatPage(MessageKey(client.peerId, client.id)); @@ -519,6 +520,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { Function(bool)? onTap, String tooltipText) { return Tooltip( message: "$tooltipText: ${enabled ? "ON" : "OFF"}", + waitDuration: Duration.zero, child: Container( decoration: BoxDecoration( color: enabled ? MyTheme.accent : Colors.grey[700], @@ -535,7 +537,6 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { child: Icon( iconData, color: Colors.white, - size: 32, ), ), ], @@ -547,9 +548,11 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { @override Widget build(BuildContext context) { + final crossAxisCount = 4; + final spacing = 10.0; return Container( width: double.infinity, - height: 200.0, + height: 160.0, margin: EdgeInsets.all(5.0), padding: EdgeInsets.all(5.0), decoration: BoxDecoration( @@ -574,10 +577,10 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { ).marginOnly(left: 4.0, bottom: 8.0), Expanded( child: GridView.count( - crossAxisCount: 3, - padding: EdgeInsets.symmetric(horizontal: 20.0), - mainAxisSpacing: 20.0, - crossAxisSpacing: 20.0, + crossAxisCount: crossAxisCount, + padding: EdgeInsets.symmetric(horizontal: spacing), + mainAxisSpacing: spacing, + crossAxisSpacing: spacing, children: [ buildPermissionIcon( client.keyboard, @@ -589,7 +592,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { client.keyboard = enabled; }); }, - translate('Allow using keyboard and mouse'), + translate('Enable keyboard/mouse'), ), buildPermissionIcon( client.clipboard, @@ -601,7 +604,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { client.clipboard = enabled; }); }, - translate('Allow using clipboard'), + translate('Enable clipboard'), ), buildPermissionIcon( client.audio, @@ -613,7 +616,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { client.audio = enabled; }); }, - translate('Allow hearing sound'), + translate('Enable audio'), ), buildPermissionIcon( client.file, @@ -625,7 +628,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { client.file = enabled; }); }, - translate('Allow file copy and paste'), + translate('Enable file copy and paste'), ), buildPermissionIcon( client.restart, @@ -637,7 +640,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { client.restart = enabled; }); }, - translate('Allow remote restart'), + translate('Enable remote restart'), ), buildPermissionIcon( client.recording, @@ -649,8 +652,24 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { client.recording = enabled; }); }, - translate('Allow recording session'), - ) + translate('Enable recording session'), + ), + // only windows support block input + if (Platform.isWindows) + buildPermissionIcon( + client.blockInput, + Icons.block, + (enabled) { + bind.cmSwitchPermission( + connId: client.id, + name: "block_input", + enabled: enabled); + setState(() { + client.blockInput = enabled; + }); + }, + translate('Enable blocking user input'), + ) ], ), ), @@ -975,6 +994,49 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> { ); } + iconLabel(CmFileLog item) { + switch (item.action) { + case CmFileAction.none: + return Container(); + case CmFileAction.localToRemote: + case CmFileAction.remoteToLocal: + return Column( + children: [ + Transform.rotate( + angle: item.action == CmFileAction.remoteToLocal ? 0 : pi, + child: SvgPicture.asset( + "assets/arrow.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + ), + Text(item.action == CmFileAction.remoteToLocal + ? translate('Send') + : translate('Receive')) + ], + ); + case CmFileAction.remove: + return Column( + children: [ + Icon( + Icons.delete, + color: Theme.of(context).tabBarTheme.labelColor, + ), + Text(translate('Delete')) + ], + ); + case CmFileAction.createDir: + return Column( + children: [ + Icon( + Icons.create_new_folder, + color: Theme.of(context).tabBarTheme.labelColor, + ), + Text(translate('Create Folder')) + ], + ); + } + } + Widget statusList() { return PreferredSize( preferredSize: const Size(200, double.infinity), @@ -983,7 +1045,7 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> { child: Obx( () { final jobTable = gFFI.cmFileModel.currentJobTable; - statusListView(List jobs) => ListView.builder( + statusListView(List jobs) => ListView.builder( controller: ScrollController(), itemBuilder: (BuildContext context, int index) { final item = jobs[index]; @@ -998,22 +1060,7 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> { children: [ SizedBox( width: 50, - child: Column( - children: [ - Transform.rotate( - angle: item.isRemoteToLocal ? 0 : pi, - child: SvgPicture.asset( - "assets/arrow.svg", - color: Theme.of(context) - .tabBarTheme - .labelColor, - ), - ), - Text(item.isRemoteToLocal - ? translate('Send') - : translate('Receive')) - ], - ), + child: iconLabel(item), ).paddingOnly(left: 15), const SizedBox( width: 16.0, @@ -1048,8 +1095,9 @@ class __FileTransferLogPageState extends State<_FileTransferLogPage> { ), ), Offstage( - offstage: - item.state == JobState.inProgress, + offstage: !(item.isTransfer() && + item.state != + JobState.inProgress), child: Text( translate( item.display(), diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index d8b330407..dac8ae528 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -107,7 +107,7 @@ class _ToolbarTheme { static const double dividerHeight = 12.0; static const double buttonSize = 32; - static const double buttonHMargin = 3; + static const double buttonHMargin = 2; static const double buttonVMargin = 6; static const double iconRadius = 8; static const double elevation = 3; @@ -125,12 +125,13 @@ class _ToolbarTheme { : EdgeInsets.fromLTRB(6, 14, 6, 14); static const double menuButtonBorderRadius = 3.0; + static get borderColor => + MyTheme.currentThemeMode() == ThemeMode.light ? bordLight : bordDark; + static final defaultMenuStyle = MenuStyle( side: MaterialStateProperty.all(BorderSide( width: 1, - color: MyTheme.currentThemeMode() == ThemeMode.light - ? _ToolbarTheme.bordLight - : _ToolbarTheme.bordDark, + color: borderColor, )), shape: MaterialStatePropertyAll(RoundedRectangleBorder( borderRadius: BorderRadius.circular(_ToolbarTheme.menuBorderRadius))), @@ -141,6 +142,19 @@ class _ToolbarTheme { padding: MaterialStatePropertyAll(EdgeInsets.zero), overlayColor: MaterialStatePropertyAll(Colors.transparent), ); + + static Widget borderWrapper(Widget child, BorderRadius borderRadius) { + return Container( + decoration: BoxDecoration( + border: Border.all( + color: borderColor, + width: 1, + ), + borderRadius: borderRadius, + ), + child: child, + ); + } } typedef DismissFunc = void Function(); @@ -420,6 +434,9 @@ class _RemoteToolbarState extends State { if (show.isTrue && _dragging.isFalse) { triggerAutoHide(); } + final borderRadius = BorderRadius.vertical( + bottom: Radius.circular(5), + ); return Align( alignment: FractionalOffset(_fractionX.value, 0), child: Offstage( @@ -427,6 +444,7 @@ class _RemoteToolbarState extends State { child: Material( elevation: _ToolbarTheme.elevation, shadowColor: MyTheme.color(context).shadow, + borderRadius: borderRadius, child: _DraggableShowHide( sessionId: widget.ffi.sessionId, dragging: _dragging, @@ -434,6 +452,7 @@ class _RemoteToolbarState extends State { show: show, setFullscreen: _setFullscreen, setMinimize: _minimize, + borderRadius: borderRadius, ), ), ), @@ -475,13 +494,14 @@ class _RemoteToolbarState extends State { } toolbarItems.add(_RecordMenu()); toolbarItems.add(_CloseMenu(id: widget.id, ffi: widget.ffi)); + final toolbarBorderRadius = BorderRadius.all(Radius.circular(4.0)); return Column( mainAxisSize: MainAxisSize.min, children: [ Material( elevation: _ToolbarTheme.elevation, shadowColor: MyTheme.color(context).shadow, - borderRadius: BorderRadius.all(Radius.circular(4.0)), + borderRadius: toolbarBorderRadius, color: Theme.of(context) .menuBarTheme .style @@ -491,13 +511,15 @@ class _RemoteToolbarState extends State { scrollDirection: Axis.horizontal, child: Theme( data: themeData(), - child: Row( - children: [ - SizedBox(width: _ToolbarTheme.buttonHMargin * 2), - ...toolbarItems, - SizedBox(width: _ToolbarTheme.buttonHMargin * 2) - ], - ), + child: _ToolbarTheme.borderWrapper( + Row( + children: [ + SizedBox(width: _ToolbarTheme.buttonHMargin * 2), + ...toolbarItems, + SizedBox(width: _ToolbarTheme.buttonHMargin * 2) + ], + ), + toolbarBorderRadius), ), ), ), @@ -598,14 +620,19 @@ class _MonitorMenu extends StatelessWidget { useTextureRender && ffi.ffiModel.pi.isSupportMultiDisplay; @override - Widget build(BuildContext context) => - showMonitorsToolbar ? buildMultiMonitorMenu() : buildMonitorMenu(); + Widget build(BuildContext context) => showMonitorsToolbar + ? buildMultiMonitorMenu() + : Obx(() => buildMonitorMenu()); Widget buildMonitorMenu() { + final width = SimpleWrapper(0); + final monitorsIcon = + globalMonitorsWidget(width, Colors.white, Colors.black38); return _IconSubmenuButton( tooltip: 'Select Monitor', - icon: icon(), + icon: monitorsIcon, ffi: ffi, + width: width.value, color: _ToolbarTheme.blueColor, hoverColor: _ToolbarTheme.hoverBlueColor, menuStyle: MenuStyle( @@ -644,26 +671,37 @@ class _MonitorMenu extends StatelessWidget { child: Text(translate('Show displays as individual windows'))); } + buildOneMonitorButton(i, curDisplay) => Text( + '${i + 1}', + style: TextStyle( + color: i == curDisplay + ? _ToolbarTheme.blueColor + : _ToolbarTheme.inactiveColor, + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ); + List buildMonitorList(bool isMulti) { final List monitorList = []; final pi = ffi.ffiModel.pi; - getMonitorText(int i) { - if (i == kAllDisplayValue) { - if (pi.displays.length == 2) { - return '1|2'; - } else { - return 'ALL'; - } - } else { - return (i + 1).toString(); - } - } - buildMonitorButton(int i) => Obx(() { RxInt display = CurrentDisplayState.find(id); + + final isAllMonitors = i == kAllDisplayValue; + final width = SimpleWrapper(0); + Widget? monitorsIcon; + if (isAllMonitors) { + monitorsIcon = globalMonitorsWidget( + width, Colors.white, _ToolbarTheme.blueColor); + } return _IconMenuButton( - tooltip: isMulti ? '' : '#${i + 1} monitor', + tooltip: isMulti + ? '' + : isAllMonitors + ? 'all monitors' + : '#${i + 1} monitor', hMargin: isMulti ? null : 6, vMargin: isMulti ? null : 12, topLevel: false, @@ -673,33 +711,25 @@ class _MonitorMenu extends StatelessWidget { hoverColor: i == display.value ? _ToolbarTheme.hoverBlueColor : _ToolbarTheme.hoverInactiveColor, - icon: Container( - alignment: AlignmentDirectional.center, - constraints: - const BoxConstraints(minHeight: _ToolbarTheme.height), - child: Stack( - alignment: Alignment.center, - children: [ - SvgPicture.asset( - "assets/screen.svg", - colorFilter: - ColorFilter.mode(Colors.white, BlendMode.srcIn), - ), - Obx( - () => Text( - getMonitorText(i), - style: TextStyle( - color: i == display.value - ? _ToolbarTheme.blueColor - : _ToolbarTheme.inactiveColor, - fontSize: 12, - fontWeight: FontWeight.bold, - ), + width: isAllMonitors ? width.value : null, + icon: isAllMonitors + ? monitorsIcon + : Container( + alignment: AlignmentDirectional.center, + constraints: + const BoxConstraints(minHeight: _ToolbarTheme.height), + child: Stack( + alignment: Alignment.center, + children: [ + SvgPicture.asset( + "assets/screen.svg", + colorFilter: + ColorFilter.mode(Colors.white, BlendMode.srcIn), + ), + Obx(() => buildOneMonitorButton(i, display.value)), + ], ), ), - ], - ), - ), onPressed: () => onPressed(i, pi), ); }); @@ -713,26 +743,69 @@ class _MonitorMenu extends StatelessWidget { return monitorList; } - icon() { - final pi = ffi.ffiModel.pi; + globalMonitorsWidget( + SimpleWrapper width, Color activeTextColor, Color activeBgColor) { + getMonitors() { + final pi = ffi.ffiModel.pi; + RxInt display = CurrentDisplayState.find(id); + final rect = ffi.ffiModel.globalDisplaysRect(); + if (rect == null) { + return Offstage(); + } + + final scale = _ToolbarTheme.buttonSize / rect.height * 0.75; + final startY = (_ToolbarTheme.buttonSize - rect.height * scale) * 0.5; + final startX = startY; + + final children = []; + for (var i = 0; i < pi.displays.length; i++) { + final d = pi.displays[i]; + final fontSize = (d.width * scale < d.height * scale + ? d.width * scale + : d.height * scale) * + 0.65; + children.add(Positioned( + left: (d.x - rect.left) * scale + startX, + top: (d.y - rect.top) * scale + startY, + width: d.width * scale, + height: d.height * scale, + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey, + width: 1.0, + ), + color: display.value == i ? activeBgColor : Colors.white, + ), + child: Center( + child: Text( + '${i + 1}', + style: TextStyle( + color: display.value == i + ? activeTextColor + : _ToolbarTheme.inactiveColor, + fontSize: fontSize, + fontWeight: FontWeight.bold, + ), + )), + ), + )); + } + width.value = rect.width * scale + startX * 2; + return SizedBox( + width: width.value, + height: rect.height * scale + startY * 2, + child: Stack( + children: children, + ), + ); + } + return Stack( alignment: Alignment.center, children: [ - SvgPicture.asset( - "assets/screen.svg", - colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn), - ), - Obx(() { - RxInt display = CurrentDisplayState.find(id); - return Text( - '${display.value == kAllDisplayValue ? 'A' : '${display.value + 1}'}/${pi.displays.length}', - style: const TextStyle( - color: _ToolbarTheme.blueColor, - fontSize: 8, - fontWeight: FontWeight.bold, - ), - ); - }), + SizedBox(height: _ToolbarTheme.buttonSize), + getMonitors(), ], ); } @@ -1150,8 +1223,9 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { @override Widget build(BuildContext context) { final isVirtualDisplay = ffiModel.isVirtualDisplayResolution; - final visible = - ffiModel.keyboard && (isVirtualDisplay || resolutions.length > 1); + final visible = ffiModel.keyboard && + (isVirtualDisplay || resolutions.length > 1) && + pi.currentDisplay != kAllDisplayValue; if (!visible) return Offstage(); final showOriginalBtn = ffiModel.isOriginalResolutionSet && !ffiModel.isOriginalResolution; @@ -1761,6 +1835,7 @@ class _IconMenuButton extends StatefulWidget { final double? hMargin; final double? vMargin; final bool topLevel; + final double? width; const _IconMenuButton({ Key? key, this.assetName, @@ -1772,6 +1847,7 @@ class _IconMenuButton extends StatefulWidget { this.hMargin, this.vMargin, this.topLevel = true, + this.width, }) : super(key: key); @override @@ -1792,7 +1868,7 @@ class _IconMenuButtonState extends State<_IconMenuButton> { height: _ToolbarTheme.buttonSize, ); var button = SizedBox( - width: _ToolbarTheme.buttonSize, + width: widget.width ?? _ToolbarTheme.buttonSize, height: _ToolbarTheme.buttonSize, child: MenuItemButton( style: ButtonStyle( @@ -1839,18 +1915,20 @@ class _IconSubmenuButton extends StatefulWidget { final List menuChildren; final MenuStyle? menuStyle; final FFI ffi; + final double? width; - _IconSubmenuButton( - {Key? key, - this.svg, - this.icon, - required this.tooltip, - required this.color, - required this.hoverColor, - required this.menuChildren, - required this.ffi, - this.menuStyle}) - : super(key: key); + _IconSubmenuButton({ + Key? key, + this.svg, + this.icon, + required this.tooltip, + required this.color, + required this.hoverColor, + required this.menuChildren, + required this.ffi, + this.menuStyle, + this.width, + }) : super(key: key); @override State<_IconSubmenuButton> createState() => _IconSubmenuButtonState(); @@ -1870,7 +1948,7 @@ class _IconSubmenuButtonState extends State<_IconSubmenuButton> { height: _ToolbarTheme.buttonSize, ); final button = SizedBox( - width: _ToolbarTheme.buttonSize, + width: widget.width ?? _ToolbarTheme.buttonSize, height: _ToolbarTheme.buttonSize, child: SubmenuButton( menuStyle: widget.menuStyle ?? _ToolbarTheme.defaultMenuStyle, @@ -2016,6 +2094,7 @@ class _DraggableShowHide extends StatefulWidget { final RxDouble fractionX; final RxBool dragging; final RxBool show; + final BorderRadius borderRadius; final Function(bool) setFullscreen; final Function() setMinimize; @@ -2028,6 +2107,7 @@ class _DraggableShowHide extends StatefulWidget { required this.show, required this.setFullscreen, required this.setMinimize, + required this.borderRadius, }) : super(key: key); @override @@ -2164,9 +2244,11 @@ class _DraggableShowHideState extends State<_DraggableShowHide> { .style ?.backgroundColor ?.resolve(MaterialState.values.toSet()), - borderRadius: BorderRadius.vertical( - bottom: Radius.circular(5), + border: Border.all( + color: _ToolbarTheme.borderColor, + width: 1, ), + borderRadius: widget.borderRadius, ), child: SizedBox( height: 20, diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 7f1449ca4..67931d353 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -583,32 +583,19 @@ class WindowActionPanelState extends State void onWindowClose() async { mainWindowClose() async => await windowManager.hide(); notMainWindowClose(WindowController controller) async { - if (widget.tabController.length == 0) { - debugPrint("close emtpy multiwindow, hide"); - await controller.hide(); - await rustDeskWinManager - .call(WindowType.Main, kWindowEventHide, {"id": kWindowId!}); - } else { + if (widget.tabController.length != 0) { debugPrint("close not emtpy multiwindow from taskbar"); if (Platform.isWindows) { await controller.show(); await controller.focus(); final res = await widget.onClose?.call() ?? true; - if (res) { - Future.delayed(Duration.zero, () async { - // onWindowClose will be called again to hide - await WindowController.fromWindowId(kWindowId!).close(); - }); - } - } else { - // ubuntu22.04 windowOnTop not work from taskbar - widget.tabController.clear(); - Future.delayed(Duration.zero, () async { - // onWindowClose will be called again to hide - await WindowController.fromWindowId(kWindowId!).close(); - }); + if (!res) return; } + widget.tabController.clear(); } + await controller.hide(); + await rustDeskWinManager + .call(WindowType.Main, kWindowEventHide, {"id": kWindowId!}); } macOSWindowClose( diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index d7dd5acee..b8a7a8e45 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -156,6 +156,7 @@ void runMobileApp() async { await Future.wait([gFFI.abModel.loadCache(), gFFI.groupModel.loadCache()]); gFFI.userModel.refreshCurrentUser(); runApp(App()); + await initUniLinks(); } void runMultiWindow( diff --git a/flutter/lib/mobile/pages/connection_page.dart b/flutter/lib/mobile/pages/connection_page.dart index a4e3c7f4d..824ceafec 100644 --- a/flutter/lib/mobile/pages/connection_page.dart +++ b/flutter/lib/mobile/pages/connection_page.dart @@ -54,10 +54,12 @@ class _ConnectionPageState extends State { } bool isPeersLoading = false; bool isPeersLoaded = false; + StreamSubscription? _uniLinksSubscription; @override void initState() { super.initState(); + _uniLinksSubscription = listenUniLinks(); if (_idController.text.isEmpty) { () async { final lastRemoteId = await bind.mainGetLastRemoteId(); @@ -312,6 +314,7 @@ class _ConnectionPageState extends State { @override void dispose() { + _uniLinksSubscription?.cancel(); _idController.dispose(); if (Get.isRegistered()) { Get.delete(); diff --git a/flutter/lib/mobile/pages/file_manager_page.dart b/flutter/lib/mobile/pages/file_manager_page.dart index 3fceba240..5344c5da3 100644 --- a/flutter/lib/mobile/pages/file_manager_page.dart +++ b/flutter/lib/mobile/pages/file_manager_page.dart @@ -5,7 +5,7 @@ import 'package:flutter_breadcrumb/flutter_breadcrumb.dart'; import 'package:flutter_hbb/models/file_model.dart'; import 'package:get/get.dart'; import 'package:toggle_switch/toggle_switch.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import '../../common.dart'; import '../../common/widgets/dialog.dart'; @@ -73,7 +73,7 @@ class _FileManagerPageState extends State { .showLoading(translate('Connecting...'), onCancel: closeConnection); }); gFFI.ffiModel.updateEventListener(gFFI.sessionId, widget.id); - Wakelock.enable(); + WakelockPlus.enable(); } @override @@ -81,7 +81,7 @@ class _FileManagerPageState extends State { model.close().whenComplete(() { gFFI.close(); gFFI.dialogManager.dismissAll(); - Wakelock.disable(); + WakelockPlus.disable(); }); super.dispose(); } diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 99d91c4c4..6afb58eac 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -11,7 +11,7 @@ import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import '../../common.dart'; import '../../common/widgets/overlay.dart'; @@ -60,7 +60,7 @@ class _RemotePageState extends State { gFFI.dialogManager .showLoading(translate('Connecting...'), onCancel: closeConnection); }); - Wakelock.enable(); + WakelockPlus.enable(); _physicalFocusNode.requestFocus(); gFFI.ffiModel.updateEventListener(sessionId, widget.id); gFFI.inputModel.listenToMouse(true); @@ -88,7 +88,7 @@ class _RemotePageState extends State { gFFI.dialogManager.dismissAll(); await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: SystemUiOverlay.values); - await Wakelock.disable(); + await WakelockPlus.disable(); await keyboardSubscription.cancel(); removeSharedStates(widget.id); } diff --git a/flutter/lib/mobile/pages/server_page.dart b/flutter/lib/mobile/pages/server_page.dart index 5dc6f3526..b7a48e5bc 100644 --- a/flutter/lib/mobile/pages/server_page.dart +++ b/flutter/lib/mobile/pages/server_page.dart @@ -217,7 +217,7 @@ class ServiceNotRunningNotification extends StatelessWidget { serverModel.toggleService(); } }, - label: Text(translate("Start Service"))) + label: Text(translate("Start service"))) ], )); } @@ -561,7 +561,7 @@ class _PermissionCheckerState extends State { serverModel.toggleService), PermissionRow(translate("Input Control"), serverModel.inputOk, serverModel.toggleInput), - PermissionRow(translate("Transfer File"), serverModel.fileOk, + PermissionRow(translate("Transfer file"), serverModel.fileOk, serverModel.toggleFile), hasAudioPermission ? PermissionRow(translate("Audio Capture"), serverModel.audioOk, diff --git a/flutter/lib/mobile/pages/settings_page.dart b/flutter/lib/mobile/pages/settings_page.dart index 87d2eb8a1..f1f9bb39f 100644 --- a/flutter/lib/mobile/pages/settings_page.dart +++ b/flutter/lib/mobile/pages/settings_page.dart @@ -221,7 +221,7 @@ class _SettingsState extends State with WidgetsBindingObserver { final List enhancementsTiles = []; final List shareScreenTiles = [ SettingsTile.switchTile( - title: Text(translate('Deny LAN Discovery')), + title: Text(translate('Deny LAN discovery')), initialValue: _denyLANDiscovery, onToggle: (v) async { await bind.mainSetOption( @@ -270,7 +270,7 @@ class _SettingsState extends State with WidgetsBindingObserver { }, ), SettingsTile.switchTile( - title: Text(translate('Enable Recording Session')), + title: Text(translate('Enable recording session')), initialValue: _enableRecordSession, onToggle: (v) async { await bind.mainSetOption( @@ -407,7 +407,7 @@ class _SettingsState extends State with WidgetsBindingObserver { enhancementsTiles.add(SettingsTile.switchTile( initialValue: _enableStartOnBoot, title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("${translate('Start on Boot')} (beta)"), + Text("${translate('Start on boot')} (beta)"), Text( '* ${translate('Start the screen sharing service on boot, requires special permissions')}', style: Theme.of(context).textTheme.bodySmall), @@ -797,6 +797,7 @@ class __DisplayPageState extends State<_DisplayPage> { otherRow('Lock after session end', 'lock_after_session_end'), otherRow('Privacy mode', 'privacy_mode'), otherRow('Touch mode', 'touch-mode'), + otherRow('True color (4:4:4)', 'i444'), ], ), ]), diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index 93cdbbed5..5f5a1d707 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -285,6 +285,10 @@ class ChatModel with ChangeNotifier { await toggleCMSidePage(); } + toggleCMFilePage() async { + await toggleCMSidePage(); + } + var _togglingCMSidePage = false; // protect order for await toggleCMSidePage() async { if (_togglingCMSidePage) return false; @@ -296,6 +300,13 @@ class ChatModel with ChangeNotifier { await windowManager.setSizeAlignment( kConnectionManagerWindowSizeClosedChat, Alignment.topRight); } else { + final currentSelectedTab = + gFFI.serverModel.tabController.state.value.selectedTabInfo; + final client = parent.target?.serverModel.clients.firstWhereOrNull( + (client) => client.id.toString() == currentSelectedTab.key); + if (client != null) { + client.unreadChatMessageCount.value = 0; + } requestChatInputFocus(); await windowManager.show(); await windowManager.setSizeAlignment( diff --git a/flutter/lib/models/cm_file_model.dart b/flutter/lib/models/cm_file_model.dart index d372db6c3..ce9b711a2 100644 --- a/flutter/lib/models/cm_file_model.dart +++ b/flutter/lib/models/cm_file_model.dart @@ -10,8 +10,8 @@ import 'file_model.dart'; class CmFileModel { final WeakReference parent; - final currentJobTable = RxList(); - final _jobTables = HashMap>.fromEntries([]); + final currentJobTable = RxList(); + final _jobTables = HashMap>.fromEntries([]); Stopwatch stopwatch = Stopwatch(); int _lastElapsed = 0; @@ -19,14 +19,24 @@ class CmFileModel { void updateCurrentClientId(int id) { if (_jobTables[id] == null) { - _jobTables[id] = RxList(); + _jobTables[id] = RxList(); } Future.delayed(Duration.zero, () { currentJobTable.value = _jobTables[id]!; }); } - onFileTransferLog(dynamic log) { + onFileTransferLog(Map evt) { + if (evt['transfer'] != null) { + _onFileTransfer(evt['transfer']); + } else if (evt['remove'] != null) { + _onFileRemove(evt['remove']); + } else if (evt['create_dir'] != null) { + _onDirCreate(evt['create_dir']); + } + } + + _onFileTransfer(dynamic log) { try { dynamic d = jsonDecode(log); if (!stopwatch.isRunning) stopwatch.start(); @@ -56,9 +66,9 @@ class CmFileModel { debugPrint("jobTable should not be null"); return; } - JobProgress? job = jobTable.firstWhereOrNull((e) => e.id == data.id); + CmFileLog? job = jobTable.firstWhereOrNull((e) => e.id == data.id); if (job == null) { - job = JobProgress(); + job = CmFileLog(); jobTable.add(job); final currentSelectedTab = gFFI.serverModel.tabController.state.value.selectedTabInfo; @@ -68,14 +78,14 @@ class CmFileModel { } } job.id = data.id; - job.isRemoteToLocal = data.isRemote; + job.action = + data.isRemote ? CmFileAction.remoteToLocal : CmFileAction.localToRemote; job.fileName = data.path; job.totalSize = data.totalSize; job.finishedSize = data.finishedSize; if (job.finishedSize > data.totalSize) { job.finishedSize = data.totalSize; } - job.isRemoteToLocal = data.isRemote; if (job.finishedSize > 0) { if (job.finishedSize < job.totalSize) { @@ -99,6 +109,119 @@ class CmFileModel { } jobTable.refresh(); } + + _onFileRemove(dynamic log) { + try { + dynamic d = jsonDecode(log); + FileActionLog data = FileActionLog.fromJson(d); + Client? client = + gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId); + var jobTable = _jobTables[data.connId]; + if (jobTable == null) { + debugPrint("jobTable should not be null"); + return; + } + int removeUnreadCount = 0; + if (data.dir) { + bool isChild(String parent, String child) { + if (child.startsWith(parent) && child.length > parent.length) { + final suffix = child.substring(parent.length); + return suffix.startsWith('/') || suffix.startsWith('\\'); + } + return false; + } + + removeUnreadCount = jobTable + .where((e) => + e.action == CmFileAction.remove && + isChild(data.path, e.fileName)) + .length; + jobTable.removeWhere((e) => + e.action == CmFileAction.remove && isChild(data.path, e.fileName)); + } + jobTable.add(CmFileLog() + ..id = data.id + ..fileName = data.path + ..action = CmFileAction.remove + ..state = JobState.done); + final currentSelectedTab = + gFFI.serverModel.tabController.state.value.selectedTabInfo; + if (!(gFFI.chatModel.isShowCMSidePage && + currentSelectedTab.key == data.connId.toString())) { + // Wrong number if unreadCount changes during deletion, which rarely happens + RxInt? rx = client?.unreadChatMessageCount; + if (rx != null) { + if (rx.value >= removeUnreadCount) { + rx.value -= removeUnreadCount; + } + rx.value += 1; + } + } + jobTable.refresh(); + } catch (e) { + debugPrint('$e'); + } + } + + _onDirCreate(dynamic log) { + try { + dynamic d = jsonDecode(log); + FileActionLog data = FileActionLog.fromJson(d); + Client? client = + gFFI.serverModel.clients.firstWhereOrNull((e) => e.id == data.connId); + var jobTable = _jobTables[data.connId]; + if (jobTable == null) { + debugPrint("jobTable should not be null"); + return; + } + jobTable.add(CmFileLog() + ..id = data.id + ..fileName = data.path + ..action = CmFileAction.createDir + ..state = JobState.done); + final currentSelectedTab = + gFFI.serverModel.tabController.state.value.selectedTabInfo; + if (!(gFFI.chatModel.isShowCMSidePage && + currentSelectedTab.key == data.connId.toString())) { + client?.unreadChatMessageCount.value += 1; + } + jobTable.refresh(); + } catch (e) { + debugPrint('$e'); + } + } +} + +enum CmFileAction { + none, + remoteToLocal, + localToRemote, + remove, + createDir, +} + +class CmFileLog { + JobState state = JobState.none; + var id = 0; + var speed = 0.0; + var finishedSize = 0; + var totalSize = 0; + CmFileAction action = CmFileAction.none; + var fileName = ""; + var err = ""; + int lastTransferredSize = 0; + + String display() { + if (state == JobState.done && err == "skipped") { + return translate("Skipped"); + } + return state.display(); + } + + bool isTransfer() { + return action == CmFileAction.remoteToLocal || + action == CmFileAction.localToRemote; + } } class TransferJobSerdeData { @@ -140,3 +263,25 @@ class TransferJobSerdeData { error: d['error'] ?? '', ); } + +class FileActionLog { + int id = 0; + int connId = 0; + String path = ''; + bool dir = false; + + FileActionLog({ + required this.connId, + required this.id, + required this.path, + required this.dir, + }); + + FileActionLog.fromJson(dynamic d) + : this( + connId: d['connId'] ?? 0, + id: d['id'] ?? 0, + path: d['path'] ?? '', + dir: d['dir'] ?? false, + ); +} diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index ae1d9b701..5adae878c 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -1006,7 +1006,7 @@ extension JobStateDisplay on JobState { case JobState.none: return translate("Waiting"); case JobState.inProgress: - return translate("Transfer File"); + return translate("Transfer file"); case JobState.done: return translate("Finished"); case JobState.error: diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 0aad54b37..12f2d5e4c 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -138,8 +138,9 @@ class FfiModel with ChangeNotifier { sessionId = parent.target!.sessionId; } - Rect? displaysRect() { - final displays = _pi.getCurDisplays(); + Rect? globalDisplaysRect() => _getDisplaysRect(_pi.displays); + Rect? displaysRect() => _getDisplaysRect(_pi.getCurDisplays()); + Rect? _getDisplaysRect(List displays) { if (displays.isEmpty) { return null; } @@ -352,7 +353,7 @@ class FfiModel with ChangeNotifier { } } else if (name == "cm_file_transfer_log") { if (isDesktop) { - gFFI.cmFileModel.onFileTransferLog(evt['log']); + gFFI.cmFileModel.onFileTransferLog(evt); } } else { debugPrint('Unknown event name: $name'); @@ -430,15 +431,19 @@ class FfiModel with ChangeNotifier { handleSwitchDisplay( Map evt, SessionID sessionId, String peerId) { - final curDisplay = int.parse(evt['display']); + final display = int.parse(evt['display']); if (_pi.currentDisplay != kAllDisplayValue) { if (bind.peerGetDefaultSessionsCount(id: peerId) > 1) { - if (curDisplay != _pi.currentDisplay) { + if (display != _pi.currentDisplay) { return; } } - _pi.currentDisplay = curDisplay; + if (!_pi.isSupportMultiUiSession) { + _pi.currentDisplay = display; + } + // If `isSupportMultiUiSession` is true, the switch display message should not be used to update current display. + // It is only used to update the display info. } var newDisplay = Display(); @@ -451,16 +456,24 @@ class FfiModel with ChangeNotifier { int.tryParse(evt['original_width']) ?? kInvalidResolutionValue; newDisplay.originalHeight = int.tryParse(evt['original_height']) ?? kInvalidResolutionValue; - _pi.displays[curDisplay] = newDisplay; + _pi.displays[display] = newDisplay; - updateCurDisplay(sessionId); - try { - CurrentDisplayState.find(peerId).value = curDisplay; - } catch (e) { - // + if (!_pi.isSupportMultiUiSession || _pi.currentDisplay == display) { + updateCurDisplay(sessionId); } + + if (!_pi.isSupportMultiUiSession) { + try { + CurrentDisplayState.find(peerId).value = display; + } catch (e) { + // + } + } + parent.target?.recordingModel.onSwitchDisplay(); - handleResolutions(peerId, evt['resolutions']); + if (!_pi.isSupportMultiUiSession || _pi.currentDisplay == display) { + handleResolutions(peerId, evt['resolutions']); + } notifyListeners(); } @@ -627,7 +640,9 @@ class FfiModel with ChangeNotifier { /// Handle the peer info event based on [evt]. handlePeerInfo(Map evt, String peerId, bool isCache) async { - cachedPeerData.peerInfo = evt; + // Map clone is required here, otherwise "evt" may be changed by other threads through the reference. + // Because this function is asynchronous, there's an "await" in this function. + cachedPeerData.peerInfo = {...evt}; // recent peer updated by handle_peer_info(ui_session_interface.rs) --> handle_peer_info(client.rs) --> save_config(client.rs) bind.mainLoadRecentPeers(); @@ -666,11 +681,12 @@ class FfiModel with ChangeNotifier { if (connType == ConnType.fileTransfer) { parent.target?.fileModel.onReady(); } else if (connType == ConnType.defaultConn) { - _pi.displays = []; + List newDisplays = []; List displays = json.decode(evt['displays']); for (int i = 0; i < displays.length; ++i) { - _pi.displays.add(evtToDisplay(displays[i])); + newDisplays.add(evtToDisplay(displays[i])); } + _pi.displays.value = newDisplays; _pi.displaysCount.value = _pi.displays.length; if (_pi.currentDisplay < _pi.displays.length) { // now replaced to _updateCurDisplay @@ -860,7 +876,7 @@ class FfiModel with ChangeNotifier { for (int i = 0; i < displays.length; ++i) { newDisplays.add(evtToDisplay(displays[i])); } - _pi.displays = newDisplays; + _pi.displays.value = newDisplays; _pi.displaysCount.value = _pi.displays.length; if (_pi.currentDisplay == kAllDisplayValue) { @@ -908,11 +924,11 @@ class FfiModel with ChangeNotifier { _pi.platformAdditions.remove(kPlatformAdditionsVirtualDisplays); } else { try { - final updateJson = json.decode(updateData); + final updateJson = json.decode(updateData) as Map; for (final key in updateJson.keys) { _pi.platformAdditions[key] = updateJson[key]; } - if (!updateJson.contains(kPlatformAdditionsVirtualDisplays)) { + if (!updateJson.containsKey(kPlatformAdditionsVirtualDisplays)) { _pi.platformAdditions.remove(kPlatformAdditionsVirtualDisplays); } } catch (e) { @@ -1845,6 +1861,7 @@ class QualityMonitorData { String? delay; String? targetBitrate; String? codecFormat; + String? chroma; } class QualityMonitorModel with ChangeNotifier { @@ -1898,6 +1915,9 @@ class QualityMonitorModel with ChangeNotifier { if ((evt['codec_format'] as String).isNotEmpty) { _data.codecFormat = evt['codec_format']; } + if ((evt['chroma'] as String).isNotEmpty) { + _data.chroma = evt['chroma']; + } notifyListeners(); } catch (e) { // @@ -2321,7 +2341,7 @@ class PeerInfo with ChangeNotifier { bool isSupportMultiUiSession = false; int currentDisplay = 0; int primaryDisplay = kInvalidDisplayIndex; - List displays = []; + RxList displays = [].obs; Features features = Features(); List resolutions = []; Map platformAdditions = {}; diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index cdf7f54b2..ef74a17a2 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -15,7 +15,7 @@ import 'package:path_provider/path_provider.dart'; import '../common.dart'; import '../generated_bridge.dart'; -class RgbaFrame extends Struct { +final class RgbaFrame extends Struct { @Uint32() external int len; external Pointer data; diff --git a/flutter/lib/models/peer_tab_model.dart b/flutter/lib/models/peer_tab_model.dart index e4971d9ac..09d308c18 100644 --- a/flutter/lib/models/peer_tab_model.dart +++ b/flutter/lib/models/peer_tab_model.dart @@ -22,10 +22,10 @@ class PeerTabModel with ChangeNotifier { int get currentTab => _currentTab; int _currentTab = 0; // index in tabNames List tabNames = [ - 'Recent Sessions', + 'Recent sessions', 'Favorites', 'Discovered', - 'Address Book', + 'Address book', 'Group', ]; final List icons = [ diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index 9fb91f463..1422596c2 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -8,7 +8,7 @@ import 'package:flutter_hbb/main.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:get/get.dart'; -import 'package:wakelock/wakelock.dart'; +import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:window_manager/window_manager.dart'; import '../common.dart'; @@ -380,7 +380,7 @@ class ServerModel with ChangeNotifier { await bind.mainStartService(); updateClientState(); if (Platform.isAndroid) { - Wakelock.enable(); + WakelockPlus.enable(); } } @@ -393,7 +393,7 @@ class ServerModel with ChangeNotifier { notifyListeners(); if (!Platform.isLinux) { // current linux is not supported - Wakelock.disable(); + WakelockPlus.disable(); } } @@ -690,6 +690,7 @@ class Client { bool file = false; bool restart = false; bool recording = false; + bool blockInput = false; bool disconnected = false; bool fromSwitch = false; bool inVoiceCall = false; @@ -713,6 +714,7 @@ class Client { file = json['file']; restart = json['restart']; recording = json['recording']; + blockInput = json['block_input']; disconnected = json['disconnected']; fromSwitch = json['from_switch']; inVoiceCall = json['in_voice_call']; @@ -733,6 +735,7 @@ class Client { data['file'] = file; data['restart'] = restart; data['recording'] = recording; + data['block_input'] = blockInput; data['disconnected'] = disconnected; data['from_switch'] = fromSwitch; return data; diff --git a/flutter/macos/Podfile b/flutter/macos/Podfile index 22d9caad2..049abe295 100644 --- a/flutter/macos/Podfile +++ b/flutter/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.12' +platform :osx, '10.14' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/flutter/macos/Podfile.lock b/flutter/macos/Podfile.lock index 3f6ebb9b6..40105d0fe 100644 --- a/flutter/macos/Podfile.lock +++ b/flutter/macos/Podfile.lock @@ -3,7 +3,9 @@ PODS: - FlutterMacOS - desktop_multi_window (0.0.1): - FlutterMacOS - - device_info_plus_macos (0.0.1): + - device_info_plus (0.0.1): + - FlutterMacOS + - file_selector_macos (0.0.1): - FlutterMacOS - flutter_custom_cursor (0.0.1): - FlutterMacOS @@ -27,7 +29,10 @@ PODS: - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS - - wakelock_macos (0.0.1): + - video_player_avfoundation (0.0.1): + - Flutter + - FlutterMacOS + - wakelock_plus (0.0.1): - FlutterMacOS - window_manager (0.2.0): - FlutterMacOS @@ -37,7 +42,8 @@ PODS: DEPENDENCIES: - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) - desktop_multi_window (from `Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos`) - - device_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos`) + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) + - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) - flutter_custom_cursor (from `Flutter/ephemeral/.symlinks/plugins/flutter_custom_cursor/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) @@ -47,7 +53,8 @@ DEPENDENCIES: - texture_rgba_renderer (from `Flutter/ephemeral/.symlinks/plugins/texture_rgba_renderer/macos`) - uni_links_desktop (from `Flutter/ephemeral/.symlinks/plugins/uni_links_desktop/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) + - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) + - wakelock_plus (from `Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos`) - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) - window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`) @@ -60,8 +67,10 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos desktop_multi_window: :path: Flutter/ephemeral/.symlinks/plugins/desktop_multi_window/macos - device_info_plus_macos: - :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus_macos/macos + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos + file_selector_macos: + :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos flutter_custom_cursor: :path: Flutter/ephemeral/.symlinks/plugins/flutter_custom_cursor/macos FlutterMacOS: @@ -80,8 +89,10 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/uni_links_desktop/macos url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - wakelock_macos: - :path: Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos + video_player_avfoundation: + :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin + wakelock_plus: + :path: Flutter/ephemeral/.symlinks/plugins/wakelock_plus/macos window_manager: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos window_size: @@ -90,21 +101,23 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 desktop_multi_window: 566489c048b501134f9d7fb6a2354c60a9126486 - device_info_plus_macos: 1ad388a1ef433505c4038e7dd9605aadd1e2e9c7 + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f + file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 flutter_custom_cursor: 629957115075c672287bd0fa979d863ccf6024f7 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852 + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea texture_rgba_renderer: cbed959a3c127122194a364e14b8577bd62dc8f2 uni_links_desktop: 45900fb319df48fcdea2df0756e9c2626696b026 - url_launcher_macos: c04e4fa86382d4f94f6b38f14625708be3ae52e2 - wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 + url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 + video_player_avfoundation: 8563f13d8fc8b2c29dc2d09e60b660e4e8128837 + wakelock_plus: 4783562c9a43d209c458cb9b30692134af456269 window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 window_size: 339dafa0b27a95a62a843042038fa6c3c48de195 -PODFILE CHECKSUM: c7161fcf45d4fd9025dc0f48a76d6e64e52f8176 +PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 COCOAPODS: 1.12.1 diff --git a/flutter/macos/Runner.xcodeproj/project.pbxproj b/flutter/macos/Runner.xcodeproj/project.pbxproj index c73e666c7..8aac49dd1 100644 --- a/flutter/macos/Runner.xcodeproj/project.pbxproj +++ b/flutter/macos/Runner.xcodeproj/project.pbxproj @@ -210,7 +210,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 9c428a004..12c2b8aa8 100644 --- a/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ =3.0.0 <4.0.0" - flutter: ">=3.10.0" + dart: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0" diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 1ae62f341..d0498eb68 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.2.4+39 environment: - sdk: ">=2.17.0" + sdk: '^3.1.0' dependencies: flutter: @@ -27,31 +27,29 @@ dependencies: flutter_localizations: sdk: flutter - ffi: ^2.0.1 - path_provider: ^2.0.12 - external_path: ^1.0.1 - provider: ^6.0.3 + ffi: ^2.1.0 + path_provider: ^2.1.1 + external_path: ^1.0.3 + provider: ^6.0.5 tuple: ^2.0.0 - wakelock: ^0.6.2 - # Keep this version for the compatibility of some old systems like win7. - device_info_plus: ^4.1.2 + wakelock_plus: ^1.1.3 #firebase_analytics: ^9.1.5 - package_info_plus: ^3.1.2 - url_launcher: ^6.0.9 + package_info_plus: ^4.2.0 + url_launcher: ^6.2.1 toggle_switch: ^2.1.0 - dash_chat_2: + dash_chat_2: git: url: https://github.com/rustdesk-org/Dash-Chat-2 - draggable_float_widget: ^0.0.2 + draggable_float_widget: ^0.1.0 settings_ui: ^2.0.2 flutter_breadcrumb: ^1.0.1 - http: ^0.13.4 + http: ^1.1.0 qr_code_scanner: ^1.0.0 zxing2: ^0.2.0 image_picker: ^0.8.5 image: ^4.0.17 back_button_interceptor: ^6.0.1 - flutter_rust_bridge: "<1.76.0" + flutter_rust_bridge: "1.80.1" window_manager: git: url: https://github.com/rustdesk-org/window_manager @@ -68,10 +66,7 @@ dependencies: get: ^4.6.5 visibility_detector: ^0.4.0+2 contextmenu: ^3.0.0 - desktop_drop: - git: - url: https://github.com/rustdesk-org/flutter-plugins - path: ./packages/desktop_drop + desktop_drop: ^0.4.4 scroll_pos: ^0.4.0 debounce_throttle: ^2.0.0 file_picker: ^5.1.0 @@ -84,7 +79,7 @@ dependencies: git: url: https://github.com/rustdesk-org/flutter_improved_scrolling uni_links: ^0.5.1 - uni_links_desktop: + uni_links_desktop: git: url: https://github.com/rustdesk-org/uni_links_desktop path: ^1.8.1 @@ -105,6 +100,7 @@ dependencies: url: https://github.com/21pages/dynamic_layouts.git ref: 24cb88413fa5181d949ddacbb30a65d5c459e7d9 pull_down_button: ^0.9.3 + device_info_plus: ^9.1.0 dev_dependencies: icons_launcher: ^2.0.4 @@ -113,7 +109,7 @@ dev_dependencies: build_runner: ^2.4.6 freezed: ^2.4.2 flutter_lints: ^2.0.2 - ffigen: ^7.2.4 + ffigen: ^8.0.2 # rerun: flutter pub run flutter_launcher_icons flutter_icons: diff --git a/flutter/run.sh b/flutter/run.sh index 1ce44a99e..cc6436b38 100755 --- a/flutter/run.sh +++ b/flutter/run.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -cargo install flutter_rust_bridge_codegen --version 1.75.3 --features uuid +cargo install flutter_rust_bridge_codegen --version 1.80.1 --features uuid flutter pub get ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ../src/flutter_ffi.rs --dart-output ./lib/generated_bridge.dart --c-output ./macos/Runner/bridge_generated.h # call `flutter clean` if cargo build fails diff --git a/libs/clipboard/Cargo.toml b/libs/clipboard/Cargo.toml index bc20ecfc5..c3673a9bd 100644 --- a/libs/clipboard/Cargo.toml +++ b/libs/clipboard/Cargo.toml @@ -2,16 +2,48 @@ name = "clipboard" version = "0.1.0" edition = "2021" -build= "build.rs" +build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies] cc = "1.0" +[features] +default = [] +unix-file-copy-paste = [ +"dep:x11rb", +"dep:x11-clipboard", +"dep:rand", +"dep:fuser", +"dep:libc", +"dep:dashmap", +"dep:percent-encoding", +"dep:utf16string", +"dep:once_cell", +"dep:cacao" +] + [dependencies] thiserror = "1.0" lazy_static = "1.4" serde = "1.0" serde_derive = "1.0" hbb_common = { path = "../hbb_common" } +parking_lot = {version = "0.12"} + +[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] +rand = {version = "0.8", optional = true} +fuser = {version = "0.13", optional = true} +libc = {version = "0.2", optional = true} +dashmap = {version ="5.5", optional = true} +utf16string = {version = "0.2", optional = true} +once_cell = {version = "1.18", optional = true} + +[target.'cfg(target_os = "linux")'.dependencies] +percent-encoding = {version ="2.3", optional = true} +x11-clipboard = {git="https://github.com/clslaid/x11-clipboard", branch = "feat/store-batch", optional = true} +x11rb = {version = "0.12", features = ["all-extensions"], optional = true} + +[target.'cfg(target_os = "macos")'.dependencies] +cacao = {git="https://github.com/clslaid/cacao", branch = "feat/set-file-urls", optional = true} diff --git a/libs/clipboard/README.md b/libs/clipboard/README.md index ea2b85785..6333a0644 100644 --- a/libs/clipboard/README.md +++ b/libs/clipboard/README.md @@ -3,8 +3,74 @@ Copy files and text through network. Main lowlevel logic from [FreeRDP](https://github.com/FreeRDP/FreeRDP). +To enjoy file copy and paste feature on Linux/OSX, +please build with `unix-file-copy-paste` feature. + TODO: Move this lib to a separate project. +## How it works + +Terminalogies: + +- cliprdr: this module +- local: the endpoint which initiates a file copy events +- remote: the endpoint which paste the file copied from `local` + +The main algorithm of copying and pasting files is from +[Remote Desktop Protocol: Clipboard Virtual Channel Extension](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-RDPECLIP/%5bMS-RDPECLIP%5d.pdf), +and could be concluded as: + +0. local and remote notify each other that it's ready. +1. local subscribes/listening to the system's clipboard for file copy +2. local once got file copy event, notice the remote +3. remote confirms receive and try pulls the file list +4. local updates its file-list, the remote flushes pulled file list to the clipboard +5. remote OS or desktop manager initiates a paste, making other programs reading + clipboard files. Convert those reading requests to RPCs + + - on Windows, all file reading will go through the stream file API + - on Linux/OSX, FUSE is used for converting reading requests to RPCs + - in case of local clipboard been transferred back + and leading to a dead loop, + all file copy event pointing at the FUSE directory will be ignored + +6. finishing pasting all files one by one. + +In a perspective of network data transferring: + +```mermaid +sequenceDiagram + participant l as Local + participant r as Remote + note over l, r: Initialize + l ->> r: Monitor Ready + r ->> l: Monitor Ready + loop Get clipboard update + l ->> r: Format List (I got update) + r ->> l: Format List Response (notified) + r ->> l: Format Data Request (requests file list) + activate l + note left of l: Retrive file list from system clipboard + l ->> r: Format Data Response (containing file list) + deactivate l + note over r: Update system clipboard with received file list + end + loop Some application requests copied files + note right of r: application reads file from x to x+y + note over r: the file is the a-th file on list + r ->> l: File Contents Request (read file a offset x size y) + activate l + note left of l: Find a-th file on list, read from x to x+y + l ->> r: File Contents Response (contents of file a offset x size y) + deactivate l + end +``` + +Note: In actual implementation, both sides could play send clipboard update +and request file contents. +There is no such limitation that only local can update clipboard +and copy files to remote. + ## impl ### windows @@ -14,3 +80,82 @@ TODO: Move this lib to a separate project. ![A1->B1](./docs/assets/win_A_B.png) ![B1->A1](./docs/assets/win_B_A.png) + +The protocol was originally designed as an extension of the Windows RDP, +so the specific message packages fits windows well. + +When starting cliprdr, a thread is spawn to create a invisible window +and to subscribe to OLE clipboard events. +The window's callback (see `cliprdr_proc` in `src/windows/wf_cliprdr.c`) was +set to handle a variaty of events. + +Detailed implementation is shown in pictures above. + +### Linux/OSX + +The Cliprdr Server implementation has mainly 3 parts: + +- Clipboard Client +- Local File list +- FUSE server + +#### Clipboard Client + +The clipboard client has a thread polling for file urls on clipboard. + +If the client found any updates of file urls, +after filtering out those pointing to our FUSE directory or duplicated, +send format list directly to remote. + +The cliprdr server also uses clipboard client for setting clipboard, +or retrive paths from system. + +#### Local File List + +The local file list is a temperary list of file metadata. +When receiving file contents PDU from peer, the server picks +out the file requested and open it for reading if necessary. + +Also when receiving Format Data Request PDU from remote asking for file list, +the local file list should be rebuilt from file list retrieved from Clipboard Client. + +Some caching and preloading could done on it since applications are likely to read +on the list sequentially. + +#### FUSE server + +The FUSE server could convert POSIX file reading request to File Contents +Request/Response RPCs. + +When received file list from remote, +the FUSE server will figure out the file system tree and rearrange its content. + +#### Groceries + +- The protocol was originally implemented for windows, + so paths in PDU will all be converted to DOS formats in UTF-16 LE encoding, + and datetimes will be converted to LDAP timestamp instead of + unix timestamp + + ```text + UNIX + /usr/bin/rustdesk + -> + DOS + \usr\bin\rustdesk + ``` + +- To better fit for preserving permissions on unix-like platforms, + a reserved area of FileDescriptor PDU + +- you may notice + the mountpoint is still occupied after the application quits. + That's because the FUSE server was not mounted with `AUTO_UNMOUNT`. + - It's hard to implement gressful shutdown for a multi-processed program + - `AUTO_UNMOUNT` was not enabled by default and requires enable + `user_allow_other` in configure. Letting users edit such global + configuration to use this feature might not be a good idea. + - use [`umount()`](https://man7.org/linux/man-pages/man2/umount.2.html) + syscall to unmount will also require that option. + - we currently directly call [`umount`](https://man7.org/linux/man-pages/man8/umount.8.html) + program to unmount dangling FUSE server. It worked perfectly for now. diff --git a/libs/clipboard/build.rs b/libs/clipboard/build.rs index 7eb52c75b..3902eaa40 100644 --- a/libs/clipboard/build.rs +++ b/libs/clipboard/build.rs @@ -1,43 +1,35 @@ -use cc; - +#[cfg(target_os = "windows")] fn build_c_impl() { let mut build = cc::Build::new(); - #[cfg(target_os = "windows")] build.file("src/windows/wf_cliprdr.c"); - #[cfg(target_os = "linux")] - build.file("src/X11/xf_cliprdr.c"); - #[cfg(target_os = "macos")] - build.file("src/OSX/Clipboard.m"); - build.flag_if_supported("-Wno-c++0x-extensions"); - build.flag_if_supported("-Wno-return-type-c-linkage"); - build.flag_if_supported("-Wno-invalid-offsetof"); - build.flag_if_supported("-Wno-unused-parameter"); + { + build.flag_if_supported("-Wno-c++0x-extensions"); + build.flag_if_supported("-Wno-return-type-c-linkage"); + build.flag_if_supported("-Wno-invalid-offsetof"); + build.flag_if_supported("-Wno-unused-parameter"); - if build.get_compiler().is_like_msvc() { - build.define("WIN32", ""); - // build.define("_AMD64_", ""); - build.flag("-Z7"); - build.flag("-GR-"); - // build.flag("-std:c++11"); - } else { - build.flag("-fPIC"); - // build.flag("-std=c++11"); - // build.flag("-include"); - // build.flag(&confdefs_path.to_string_lossy()); + if build.get_compiler().is_like_msvc() { + build.define("WIN32", ""); + // build.define("_AMD64_", ""); + build.flag("-Z7"); + build.flag("-GR-"); + // build.flag("-std:c++11"); + } else { + build.flag("-fPIC"); + // build.flag("-std=c++11"); + // build.flag("-include"); + // build.flag(&confdefs_path.to_string_lossy()); + } + + build.compile("mycliprdr"); } - build.compile("mycliprdr"); - - #[cfg(target_os = "windows")] println!("cargo:rerun-if-changed=src/windows/wf_cliprdr.c"); - #[cfg(target_os = "linux")] - println!("cargo:rerun-if-changed=src/X11/xf_cliprdr.c"); - #[cfg(target_os = "macos")] - println!("cargo:rerun-if-changed=src/OSX/Clipboard.m"); } fn main() { + #[cfg(target_os = "windows")] build_c_impl(); } diff --git a/libs/clipboard/src/OSX/Clipboard.m b/libs/clipboard/src/OSX/Clipboard.m deleted file mode 100644 index 658579ce0..000000000 --- a/libs/clipboard/src/OSX/Clipboard.m +++ /dev/null @@ -1,11 +0,0 @@ -#include "../cliprdr.h" - -void mac_cliprdr_init(CliprdrClientContext *cliprdr) -{ - (void)cliprdr; -} - -void mac_cliprdr_uninit(CliprdrClientContext *cliprdr) -{ - (void)cliprdr; -} diff --git a/libs/clipboard/src/X11/xf_cliprdr.c b/libs/clipboard/src/X11/xf_cliprdr.c deleted file mode 100644 index 55d34a088..000000000 --- a/libs/clipboard/src/X11/xf_cliprdr.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "../cliprdr.h" - -void xf_cliprdr_init(CliprdrClientContext* cliprdr) -{ - (void)cliprdr; -} - -void xf_cliprdr_uninit( CliprdrClientContext* cliprdr) -{ - (void)cliprdr; -} diff --git a/libs/clipboard/src/cliprdr.rs b/libs/clipboard/src/cliprdr.rs deleted file mode 100644 index c787d8ef6..000000000 --- a/libs/clipboard/src/cliprdr.rs +++ /dev/null @@ -1,573 +0,0 @@ -#![allow(dead_code)] -#![allow(non_camel_case_types)] -#![allow(unused_variables)] -#![allow(non_snake_case)] -#![allow(deref_nullptr)] - -use std::{boxed::Box, result::Result}; -use thiserror::Error; - -pub type size_t = ::std::os::raw::c_ulonglong; -pub type __vcrt_bool = bool; -pub type wchar_t = ::std::os::raw::c_ushort; - -pub type POINTER_64_INT = ::std::os::raw::c_ulonglong; -pub type INT8 = ::std::os::raw::c_schar; -pub type PINT8 = *mut ::std::os::raw::c_schar; -pub type INT16 = ::std::os::raw::c_short; -pub type PINT16 = *mut ::std::os::raw::c_short; -pub type INT32 = ::std::os::raw::c_int; -pub type PINT32 = *mut ::std::os::raw::c_int; -pub type INT64 = ::std::os::raw::c_longlong; -pub type PINT64 = *mut ::std::os::raw::c_longlong; -pub type UINT8 = ::std::os::raw::c_uchar; -pub type PUINT8 = *mut ::std::os::raw::c_uchar; -pub type UINT16 = ::std::os::raw::c_ushort; -pub type PUINT16 = *mut ::std::os::raw::c_ushort; -pub type UINT32 = ::std::os::raw::c_uint; -pub type PUINT32 = *mut ::std::os::raw::c_uint; -pub type UINT64 = ::std::os::raw::c_ulonglong; -pub type PUINT64 = *mut ::std::os::raw::c_ulonglong; -pub type LONG32 = ::std::os::raw::c_int; -pub type PLONG32 = *mut ::std::os::raw::c_int; -pub type ULONG32 = ::std::os::raw::c_uint; -pub type PULONG32 = *mut ::std::os::raw::c_uint; -pub type DWORD32 = ::std::os::raw::c_uint; -pub type PDWORD32 = *mut ::std::os::raw::c_uint; -pub type INT_PTR = ::std::os::raw::c_longlong; -pub type PINT_PTR = *mut ::std::os::raw::c_longlong; -pub type UINT_PTR = ::std::os::raw::c_ulonglong; -pub type PUINT_PTR = *mut ::std::os::raw::c_ulonglong; -pub type LONG_PTR = ::std::os::raw::c_longlong; -pub type PLONG_PTR = *mut ::std::os::raw::c_longlong; -pub type ULONG_PTR = ::std::os::raw::c_ulonglong; -pub type PULONG_PTR = *mut ::std::os::raw::c_ulonglong; -pub type SHANDLE_PTR = ::std::os::raw::c_longlong; -pub type HANDLE_PTR = ::std::os::raw::c_ulonglong; -pub type UHALF_PTR = ::std::os::raw::c_uint; -pub type PUHALF_PTR = *mut ::std::os::raw::c_uint; -pub type HALF_PTR = ::std::os::raw::c_int; -pub type PHALF_PTR = *mut ::std::os::raw::c_int; -pub type SIZE_T = ULONG_PTR; -pub type PSIZE_T = *mut ULONG_PTR; -pub type SSIZE_T = LONG_PTR; -pub type PSSIZE_T = *mut LONG_PTR; -pub type DWORD_PTR = ULONG_PTR; -pub type PDWORD_PTR = *mut ULONG_PTR; -pub type LONG64 = ::std::os::raw::c_longlong; -pub type PLONG64 = *mut ::std::os::raw::c_longlong; -pub type ULONG64 = ::std::os::raw::c_ulonglong; -pub type PULONG64 = *mut ::std::os::raw::c_ulonglong; -pub type DWORD64 = ::std::os::raw::c_ulonglong; -pub type PDWORD64 = *mut ::std::os::raw::c_ulonglong; -pub type KAFFINITY = ULONG_PTR; -pub type PKAFFINITY = *mut KAFFINITY; -pub type PVOID = *mut ::std::os::raw::c_void; -pub type CHAR = ::std::os::raw::c_char; -pub type SHORT = ::std::os::raw::c_short; -pub type LONG = ::std::os::raw::c_long; -pub type WCHAR = wchar_t; -pub type PWCHAR = *mut WCHAR; -pub type LPWCH = *mut WCHAR; -pub type PWCH = *mut WCHAR; -pub type LPCWCH = *const WCHAR; -pub type PCWCH = *const WCHAR; -pub type NWPSTR = *mut WCHAR; -pub type LPWSTR = *mut WCHAR; -pub type PWSTR = *mut WCHAR; -pub type PZPWSTR = *mut PWSTR; -pub type PCZPWSTR = *const PWSTR; -pub type LPUWSTR = *mut WCHAR; -pub type PUWSTR = *mut WCHAR; -pub type LPCWSTR = *const WCHAR; -pub type PCWSTR = *const WCHAR; -pub type PZPCWSTR = *mut PCWSTR; -pub type PCZPCWSTR = *const PCWSTR; -pub type LPCUWSTR = *const WCHAR; -pub type PCUWSTR = *const WCHAR; -pub type PZZWSTR = *mut WCHAR; -pub type PCZZWSTR = *const WCHAR; -pub type PUZZWSTR = *mut WCHAR; -pub type PCUZZWSTR = *const WCHAR; -pub type PNZWCH = *mut WCHAR; -pub type PCNZWCH = *const WCHAR; -pub type PUNZWCH = *mut WCHAR; -pub type PCUNZWCH = *const WCHAR; -pub type PCHAR = *mut CHAR; -pub type LPCH = *mut CHAR; -pub type PCH = *mut CHAR; -pub type LPCCH = *const CHAR; -pub type PCCH = *const CHAR; -pub type NPSTR = *mut CHAR; -pub type LPSTR = *mut CHAR; -pub type PSTR = *mut CHAR; -pub type PZPSTR = *mut PSTR; -pub type PCZPSTR = *const PSTR; -pub type LPCSTR = *const CHAR; -pub type PCSTR = *const CHAR; -pub type PZPCSTR = *mut PCSTR; -pub type PCZPCSTR = *const PCSTR; -pub type PZZSTR = *mut CHAR; -pub type PCZZSTR = *const CHAR; -pub type PNZCH = *mut CHAR; -pub type PCNZCH = *const CHAR; -pub type TCHAR = ::std::os::raw::c_char; -pub type PTCHAR = *mut ::std::os::raw::c_char; -pub type TBYTE = ::std::os::raw::c_uchar; -pub type PTBYTE = *mut ::std::os::raw::c_uchar; -pub type LPTCH = LPCH; -pub type PTCH = LPCH; -pub type LPCTCH = LPCCH; -pub type PCTCH = LPCCH; -pub type PTSTR = LPSTR; -pub type LPTSTR = LPSTR; -pub type PUTSTR = LPSTR; -pub type LPUTSTR = LPSTR; -pub type PCTSTR = LPCSTR; -pub type LPCTSTR = LPCSTR; -pub type PCUTSTR = LPCSTR; -pub type LPCUTSTR = LPCSTR; -pub type PZZTSTR = PZZSTR; -pub type PUZZTSTR = PZZSTR; -pub type PCZZTSTR = PCZZSTR; -pub type PCUZZTSTR = PCZZSTR; -pub type PZPTSTR = PZPSTR; -pub type PNZTCH = PNZCH; -pub type PUNZTCH = PNZCH; -pub type PCNZTCH = PCNZCH; -pub type PCUNZTCH = PCNZCH; -pub type PSHORT = *mut SHORT; -pub type PLONG = *mut LONG; -pub type ULONG = ::std::os::raw::c_ulong; -pub type PULONG = *mut ULONG; -pub type USHORT = ::std::os::raw::c_ushort; -pub type PUSHORT = *mut USHORT; -pub type UCHAR = ::std::os::raw::c_uchar; -pub type PUCHAR = *mut UCHAR; -pub type PSZ = *mut ::std::os::raw::c_char; -pub type DWORD = ::std::os::raw::c_ulong; -pub type BOOL = ::std::os::raw::c_int; -pub type BYTE = ::std::os::raw::c_uchar; -pub type WORD = ::std::os::raw::c_ushort; -pub type FLOAT = f32; -pub type PFLOAT = *mut FLOAT; -pub type PBOOL = *mut BOOL; -pub type LPBOOL = *mut BOOL; -pub type PBYTE = *mut BYTE; -pub type LPBYTE = *mut BYTE; -pub type PINT = *mut ::std::os::raw::c_int; -pub type LPINT = *mut ::std::os::raw::c_int; -pub type PWORD = *mut WORD; -pub type LPWORD = *mut WORD; -pub type LPLONG = *mut ::std::os::raw::c_long; -pub type PDWORD = *mut DWORD; -pub type LPDWORD = *mut DWORD; -pub type LPVOID = *mut ::std::os::raw::c_void; -pub type LPCVOID = *const ::std::os::raw::c_void; -pub type INT = ::std::os::raw::c_int; -pub type UINT = ::std::os::raw::c_uint; -pub type PUINT = *mut ::std::os::raw::c_uint; -pub type va_list = *mut ::std::os::raw::c_char; - -pub const TRUE: ::std::os::raw::c_int = 1; -pub const FALSE: ::std::os::raw::c_int = 0; - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_HEADER { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, -} -pub type CLIPRDR_HEADER = _CLIPRDR_HEADER; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_CAPABILITY_SET { - pub capabilitySetType: UINT16, - pub capabilitySetLength: UINT16, -} -pub type CLIPRDR_CAPABILITY_SET = _CLIPRDR_CAPABILITY_SET; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_GENERAL_CAPABILITY_SET { - pub capabilitySetType: UINT16, - pub capabilitySetLength: UINT16, - pub version: UINT32, - pub generalFlags: UINT32, -} -pub type CLIPRDR_GENERAL_CAPABILITY_SET = _CLIPRDR_GENERAL_CAPABILITY_SET; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_CAPABILITIES { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub cCapabilitiesSets: UINT32, - pub capabilitySets: *mut CLIPRDR_CAPABILITY_SET, -} -pub type CLIPRDR_CAPABILITIES = _CLIPRDR_CAPABILITIES; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_MONITOR_READY { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, -} -pub type CLIPRDR_MONITOR_READY = _CLIPRDR_MONITOR_READY; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_TEMP_DIRECTORY { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub szTempDir: [::std::os::raw::c_char; 520usize], -} -pub type CLIPRDR_TEMP_DIRECTORY = _CLIPRDR_TEMP_DIRECTORY; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_FORMAT { - pub formatId: UINT32, - pub formatName: *mut ::std::os::raw::c_char, -} -pub type CLIPRDR_FORMAT = _CLIPRDR_FORMAT; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_FORMAT_LIST { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub numFormats: UINT32, - pub formats: *mut CLIPRDR_FORMAT, -} -pub type CLIPRDR_FORMAT_LIST = _CLIPRDR_FORMAT_LIST; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_FORMAT_LIST_RESPONSE { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, -} -pub type CLIPRDR_FORMAT_LIST_RESPONSE = _CLIPRDR_FORMAT_LIST_RESPONSE; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_LOCK_CLIPBOARD_DATA { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub clipDataId: UINT32, -} -pub type CLIPRDR_LOCK_CLIPBOARD_DATA = _CLIPRDR_LOCK_CLIPBOARD_DATA; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_UNLOCK_CLIPBOARD_DATA { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub clipDataId: UINT32, -} -pub type CLIPRDR_UNLOCK_CLIPBOARD_DATA = _CLIPRDR_UNLOCK_CLIPBOARD_DATA; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_FORMAT_DATA_REQUEST { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub requestedFormatId: UINT32, -} -pub type CLIPRDR_FORMAT_DATA_REQUEST = _CLIPRDR_FORMAT_DATA_REQUEST; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_FORMAT_DATA_RESPONSE { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub requestedFormatData: *const BYTE, -} -pub type CLIPRDR_FORMAT_DATA_RESPONSE = _CLIPRDR_FORMAT_DATA_RESPONSE; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_FILE_CONTENTS_REQUEST { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub streamId: UINT32, - pub listIndex: UINT32, - pub dwFlags: UINT32, - pub nPositionLow: UINT32, - pub nPositionHigh: UINT32, - pub cbRequested: UINT32, - pub haveClipDataId: BOOL, - pub clipDataId: UINT32, -} -pub type CLIPRDR_FILE_CONTENTS_REQUEST = _CLIPRDR_FILE_CONTENTS_REQUEST; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _CLIPRDR_FILE_CONTENTS_RESPONSE { - pub connID: UINT32, - pub msgType: UINT16, - pub msgFlags: UINT16, - pub dataLen: UINT32, - pub streamId: UINT32, - pub cbRequested: UINT32, - pub requestedData: *const BYTE, -} -pub type CLIPRDR_FILE_CONTENTS_RESPONSE = _CLIPRDR_FILE_CONTENTS_RESPONSE; -pub type CliprdrClientContext = _cliprdr_client_context; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct _NOTIFICATION_MESSAGE { - pub r#type: UINT32, // 0 - info, 1 - warning, 2 - error - pub msg: *const BYTE, - pub details: *const BYTE, -} -pub type NOTIFICATION_MESSAGE = _NOTIFICATION_MESSAGE; -pub type pcCliprdrServerCapabilities = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - capabilities: *const CLIPRDR_CAPABILITIES, - ) -> UINT, ->; -pub type pcCliprdrClientCapabilities = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - capabilities: *const CLIPRDR_CAPABILITIES, - ) -> UINT, ->; -pub type pcCliprdrMonitorReady = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - monitorReady: *const CLIPRDR_MONITOR_READY, - ) -> UINT, ->; -pub type pcCliprdrTempDirectory = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - tempDirectory: *const CLIPRDR_TEMP_DIRECTORY, - ) -> UINT, ->; -pub type pcNotifyClipboardMsg = - ::std::option::Option UINT>; -pub type pcCliprdrClientFormatList = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - formatList: *const CLIPRDR_FORMAT_LIST, - ) -> UINT, ->; -pub type pcCliprdrServerFormatList = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - formatList: *const CLIPRDR_FORMAT_LIST, - ) -> UINT, ->; -pub type pcCliprdrClientFormatListResponse = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - formatListResponse: *const CLIPRDR_FORMAT_LIST_RESPONSE, - ) -> UINT, ->; -pub type pcCliprdrServerFormatListResponse = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - formatListResponse: *const CLIPRDR_FORMAT_LIST_RESPONSE, - ) -> UINT, ->; -pub type pcCliprdrClientLockClipboardData = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - lockClipboardData: *const CLIPRDR_LOCK_CLIPBOARD_DATA, - ) -> UINT, ->; -pub type pcCliprdrServerLockClipboardData = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - lockClipboardData: *const CLIPRDR_LOCK_CLIPBOARD_DATA, - ) -> UINT, ->; -pub type pcCliprdrClientUnlockClipboardData = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - unlockClipboardData: *const CLIPRDR_UNLOCK_CLIPBOARD_DATA, - ) -> UINT, ->; -pub type pcCliprdrServerUnlockClipboardData = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - unlockClipboardData: *const CLIPRDR_UNLOCK_CLIPBOARD_DATA, - ) -> UINT, ->; -pub type pcCliprdrClientFormatDataRequest = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - formatDataRequest: *const CLIPRDR_FORMAT_DATA_REQUEST, - ) -> UINT, ->; -pub type pcCliprdrServerFormatDataRequest = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - formatDataRequest: *const CLIPRDR_FORMAT_DATA_REQUEST, - ) -> UINT, ->; -pub type pcCliprdrClientFormatDataResponse = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - formatDataResponse: *const CLIPRDR_FORMAT_DATA_RESPONSE, - ) -> UINT, ->; -pub type pcCliprdrServerFormatDataResponse = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - formatDataResponse: *const CLIPRDR_FORMAT_DATA_RESPONSE, - ) -> UINT, ->; -pub type pcCliprdrClientFileContentsRequest = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - fileContentsRequest: *const CLIPRDR_FILE_CONTENTS_REQUEST, - ) -> UINT, ->; -pub type pcCliprdrServerFileContentsRequest = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - fileContentsRequest: *const CLIPRDR_FILE_CONTENTS_REQUEST, - ) -> UINT, ->; -pub type pcCliprdrClientFileContentsResponse = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - fileContentsResponse: *const CLIPRDR_FILE_CONTENTS_RESPONSE, - ) -> UINT, ->; -pub type pcCliprdrServerFileContentsResponse = ::std::option::Option< - unsafe extern "C" fn( - context: *mut CliprdrClientContext, - fileContentsResponse: *const CLIPRDR_FILE_CONTENTS_RESPONSE, - ) -> UINT, ->; - -// TODO: hide more members of clipboard context -#[repr(C)] -#[derive(Debug, Clone)] -pub struct _cliprdr_client_context { - pub Custom: *mut ::std::os::raw::c_void, - pub EnableFiles: BOOL, - pub EnableOthers: BOOL, - pub IsStopped: BOOL, - pub ResponseWaitTimeoutSecs: UINT32, - pub ServerCapabilities: pcCliprdrServerCapabilities, - pub ClientCapabilities: pcCliprdrClientCapabilities, - pub MonitorReady: pcCliprdrMonitorReady, - pub TempDirectory: pcCliprdrTempDirectory, - pub NotifyClipboardMsg: pcNotifyClipboardMsg, - pub ClientFormatList: pcCliprdrClientFormatList, - pub ServerFormatList: pcCliprdrServerFormatList, - pub ClientFormatListResponse: pcCliprdrClientFormatListResponse, - pub ServerFormatListResponse: pcCliprdrServerFormatListResponse, - pub ClientLockClipboardData: pcCliprdrClientLockClipboardData, - pub ServerLockClipboardData: pcCliprdrServerLockClipboardData, - pub ClientUnlockClipboardData: pcCliprdrClientUnlockClipboardData, - pub ServerUnlockClipboardData: pcCliprdrServerUnlockClipboardData, - pub ClientFormatDataRequest: pcCliprdrClientFormatDataRequest, - pub ServerFormatDataRequest: pcCliprdrServerFormatDataRequest, - pub ClientFormatDataResponse: pcCliprdrClientFormatDataResponse, - pub ServerFormatDataResponse: pcCliprdrServerFormatDataResponse, - pub ClientFileContentsRequest: pcCliprdrClientFileContentsRequest, - pub ServerFileContentsRequest: pcCliprdrServerFileContentsRequest, - pub ClientFileContentsResponse: pcCliprdrClientFileContentsResponse, - pub ServerFileContentsResponse: pcCliprdrServerFileContentsResponse, - pub LastRequestedFormatId: UINT32, -} - -// #[link(name = "user32")] -// #[link(name = "ole32")] -extern "C" { - pub(crate) fn init_cliprdr(context: *mut CliprdrClientContext) -> BOOL; - pub(crate) fn uninit_cliprdr(context: *mut CliprdrClientContext) -> BOOL; - pub(crate) fn empty_cliprdr(context: *mut CliprdrClientContext, connID: UINT32) -> BOOL; -} - -#[derive(Error, Debug)] -pub enum CliprdrError { - #[error("invalid cliprdr name")] - CliprdrName, - #[error("failed to init cliprdr")] - CliprdrInit, - #[error("unknown cliprdr error")] - Unknown, -} - -impl CliprdrClientContext { - pub fn create( - enable_files: bool, - enable_others: bool, - response_wait_timeout_secs: u32, - notify_callback: pcNotifyClipboardMsg, - client_format_list: pcCliprdrClientFormatList, - client_format_list_response: pcCliprdrClientFormatListResponse, - client_format_data_request: pcCliprdrClientFormatDataRequest, - client_format_data_response: pcCliprdrClientFormatDataResponse, - client_file_contents_request: pcCliprdrClientFileContentsRequest, - client_file_contents_response: pcCliprdrClientFileContentsResponse, - ) -> Result, CliprdrError> { - let context = CliprdrClientContext { - Custom: 0 as *mut _, - EnableFiles: if enable_files { TRUE } else { FALSE }, - EnableOthers: if enable_others { TRUE } else { FALSE }, - IsStopped: FALSE, - ResponseWaitTimeoutSecs: response_wait_timeout_secs, - ServerCapabilities: None, - ClientCapabilities: None, - MonitorReady: None, - TempDirectory: None, - NotifyClipboardMsg: notify_callback, - ClientFormatList: client_format_list, - ServerFormatList: None, - ClientFormatListResponse: client_format_list_response, - ServerFormatListResponse: None, - ClientLockClipboardData: None, - ServerLockClipboardData: None, - ClientUnlockClipboardData: None, - ServerUnlockClipboardData: None, - ClientFormatDataRequest: client_format_data_request, - ServerFormatDataRequest: None, - ClientFormatDataResponse: client_format_data_response, - ServerFormatDataResponse: None, - ClientFileContentsRequest: client_file_contents_request, - ServerFileContentsRequest: None, - ClientFileContentsResponse: client_file_contents_response, - ServerFileContentsResponse: None, - LastRequestedFormatId: 0, - }; - let mut context = Box::new(context); - unsafe { - if FALSE == init_cliprdr(&mut (*context)) { - println!("Failed to init cliprdr"); - Err(CliprdrError::CliprdrInit) - } else { - Ok(context) - } - } - } -} - -impl Drop for CliprdrClientContext { - fn drop(&mut self) { - unsafe { - if FALSE == uninit_cliprdr(&mut *self) { - println!("Failed to uninit cliprdr"); - } else { - println!("Succeeded to uninit cliprdr"); - } - } - } -} diff --git a/libs/clipboard/src/context_send.rs b/libs/clipboard/src/context_send.rs index a2053d852..f3606509f 100644 --- a/libs/clipboard/src/context_send.rs +++ b/libs/clipboard/src/context_send.rs @@ -1,68 +1,72 @@ -use crate::cliprdr::*; -use hbb_common::log; +use hbb_common::{log, ResultType}; use std::sync::Mutex; +use crate::CliprdrServiceContext; + const CLIPBOARD_RESPONSE_WAIT_TIMEOUT_SECS: u32 = 30; lazy_static::lazy_static! { - static ref CONTEXT_SEND: ContextSend = ContextSend{addr: Mutex::new(0)}; + static ref CONTEXT_SEND: ContextSend = ContextSend{addr: Mutex::new(None)}; } pub struct ContextSend { - addr: Mutex, + addr: Mutex>>, } impl ContextSend { #[inline] pub fn is_enabled() -> bool { - *CONTEXT_SEND.addr.lock().unwrap() != 0 + CONTEXT_SEND.addr.lock().unwrap().is_some() } pub fn set_is_stopped() { - let _res = Self::proc(|c| { - c.IsStopped = TRUE; - 0 - }); + let _res = Self::proc(|c| c.set_is_stopped().map_err(|e| e.into())); } pub fn enable(enabled: bool) { let mut lock = CONTEXT_SEND.addr.lock().unwrap(); if enabled { - if *lock == 0 { - match crate::create_cliprdr_context( - true, - false, - CLIPBOARD_RESPONSE_WAIT_TIMEOUT_SECS, - ) { - Ok(context) => { - log::info!("clipboard context for file transfer created."); - *lock = Box::into_raw(context) as _; - } - Err(err) => { - log::error!( - "Create clipboard context for file transfer: {}", - err.to_string() - ); - } + if lock.is_some() { + return; + } + match crate::create_cliprdr_context(true, false, CLIPBOARD_RESPONSE_WAIT_TIMEOUT_SECS) { + Ok(context) => { + log::info!("clipboard context for file transfer created."); + *lock = Some(context) + } + Err(err) => { + log::error!( + "create clipboard context for file transfer: {}", + err.to_string() + ); } } - } else { - if *lock != 0 { - unsafe { - let _ = Box::from_raw(*lock as *mut CliprdrClientContext); - } - log::info!("clipboard context for file transfer destroyed."); - *lock = 0; - } + } else if let Some(_clp) = lock.take() { + *lock = None; + log::info!("clipboard context for file transfer destroyed."); } } - pub fn proc u32>(f: F) -> u32 { - let lock = CONTEXT_SEND.addr.lock().unwrap(); - if *lock != 0 { - unsafe { f(&mut *(*lock as *mut CliprdrClientContext)) } - } else { - 0 + /// make sure the clipboard context is enabled. + pub fn make_sure_enabled() -> ResultType<()> { + let mut lock = CONTEXT_SEND.addr.lock().unwrap(); + if lock.is_some() { + return Ok(()); + } + + let ctx = crate::create_cliprdr_context(true, false, CLIPBOARD_RESPONSE_WAIT_TIMEOUT_SECS)?; + *lock = Some(ctx); + log::info!("clipboard context for file transfer recreated."); + Ok(()) + } + + pub fn proc) -> ResultType<()>>( + f: F, + ) -> ResultType<()> { + let mut lock = CONTEXT_SEND.addr.lock().unwrap(); + match lock.as_mut() { + Some(context) => f(context), + None => Ok(()), } } } diff --git a/libs/clipboard/src/lib.rs b/libs/clipboard/src/lib.rs index 7cba12d1a..1a9a04757 100644 --- a/libs/clipboard/src/lib.rs +++ b/libs/clipboard/src/lib.rs @@ -1,6 +1,13 @@ -use cliprdr::*; +#[allow(dead_code)] +use std::{ + path::PathBuf, + sync::{Arc, Mutex, RwLock}, +}; + +#[cfg(any(target_os = "windows", feature = "unix-file-copy-paste",))] +use hbb_common::{allow_err, log}; use hbb_common::{ - allow_err, lazy_static, log, + lazy_static, tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, Mutex as TokioMutex, @@ -8,19 +15,59 @@ use hbb_common::{ ResultType, }; use serde_derive::{Deserialize, Serialize}; -use std::{ - boxed::Box, - ffi::{CStr, CString}, - sync::{Arc, Mutex, RwLock}, -}; +use thiserror::Error; -pub mod cliprdr; pub mod context_send; +pub mod platform; pub use context_send::*; +#[cfg(target_os = "windows")] const ERR_CODE_SERVER_FUNCTION_NONE: u32 = 0x00000001; +#[cfg(target_os = "windows")] const ERR_CODE_INVALID_PARAMETER: u32 = 0x00000002; +pub(crate) use platform::create_cliprdr_context; + +/// Ability to handle Clipboard File from remote rustdesk client +/// +/// # Note +/// There actually should be 2 parts to implement a useable clipboard file service, +/// but this only contains the RPC server part. +/// The local listener and transport part is too platform specific to wrap up in typeclasses. +pub trait CliprdrServiceContext: Send + Sync { + /// set to be stopped + fn set_is_stopped(&mut self) -> Result<(), CliprdrError>; + /// clear the content on clipboard + fn empty_clipboard(&mut self, conn_id: i32) -> Result; + + /// run as a server for clipboard RPC + fn server_clip_file(&mut self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError>; +} + +#[derive(Error, Debug)] +pub enum CliprdrError { + #[error("invalid cliprdr name")] + CliprdrName, + #[error("failed to init cliprdr")] + CliprdrInit, + #[error("cliprdr out of memory")] + CliprdrOutOfMemory, + #[error("cliprdr internal error")] + ClipboardInternalError, + #[error("cliprdr occupied")] + ClipboardOccupied, + #[error("conversion failure")] + ConversionFailure, + #[error("failure to read clipboard")] + OpenClipboard, + #[error("failure to read file metadata or content")] + FileError { path: PathBuf, err: std::io::Error }, + #[error("invalid request")] + InvalidRequest { description: String }, + #[error("unknown cliprdr error")] + Unknown(u32), +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "t", content = "c")] pub enum ClipboardFile { @@ -63,6 +110,7 @@ pub enum ClipboardFile { struct MsgChannel { peer_id: String, conn_id: i32, + #[allow(dead_code)] sender: UnboundedSender, receiver: Arc>>, } @@ -74,19 +122,19 @@ lazy_static::lazy_static! { impl ClipboardFile { pub fn is_stopping_allowed(&self) -> bool { - match self { + matches!( + self, ClipboardFile::MonitorReady - | ClipboardFile::FormatList { .. } - | ClipboardFile::FormatDataRequest { .. } => true, - _ => false, - } + | ClipboardFile::FormatList { .. } + | ClipboardFile::FormatDataRequest { .. } + ) } pub fn is_stopping_allowed_from_peer(&self) -> bool { - match self { - ClipboardFile::MonitorReady | ClipboardFile::FormatList { .. } => true, - _ => false, - } + matches!( + self, + ClipboardFile::MonitorReady | ClipboardFile::FormatList { .. } + ) } } @@ -148,8 +196,21 @@ pub fn get_rx_cliprdr_server(conn_id: i32) -> Arc bool { - unsafe { TRUE == cliprdr::empty_cliprdr(context, conn_id as u32) } -} - -pub fn server_clip_file( - context: &mut CliprdrClientContext, - conn_id: i32, - msg: ClipboardFile, -) -> u32 { - let mut ret = 0; - match msg { - ClipboardFile::NotifyCallback { .. } => { - // unreachable - } - ClipboardFile::MonitorReady => { - log::debug!("server_monitor_ready called"); - ret = server_monitor_ready(context, conn_id); - log::debug!( - "server_monitor_ready called, conn_id {}, return {}", - conn_id, - ret - ); - } - ClipboardFile::FormatList { format_list } => { - log::debug!( - "server_format_list called, conn_id {}, format_list: {:?}", - conn_id, - &format_list - ); - ret = server_format_list(context, conn_id, format_list); - log::debug!( - "server_format_list called, conn_id {}, return {}", - conn_id, - ret - ); - } - ClipboardFile::FormatListResponse { msg_flags } => { - log::debug!("server_format_list_response called"); - ret = server_format_list_response(context, conn_id, msg_flags); - log::debug!( - "server_format_list_response called, conn_id {}, msg_flags {}, return {}", - conn_id, - msg_flags, - ret - ); - } - ClipboardFile::FormatDataRequest { - requested_format_id, - } => { - log::debug!("server_format_data_request called"); - ret = server_format_data_request(context, conn_id, requested_format_id); - log::debug!( - "server_format_data_request called, conn_id {}, requested_format_id {}, return {}", - conn_id, - requested_format_id, - ret - ); - } - ClipboardFile::FormatDataResponse { - msg_flags, - format_data, - } => { - log::debug!("server_format_data_response called"); - ret = server_format_data_response(context, conn_id, msg_flags, format_data); - log::debug!( - "server_format_data_response called, conn_id {}, msg_flags: {}, return {}", - conn_id, - msg_flags, - ret - ); - } - ClipboardFile::FileContentsRequest { - stream_id, - list_index, - dw_flags, - n_position_low, - n_position_high, - cb_requested, - have_clip_data_id, - clip_data_id, - } => { - log::debug!("server_file_contents_request called"); - ret = server_file_contents_request( - context, - conn_id, - stream_id, - list_index, - dw_flags, - n_position_low, - n_position_high, - cb_requested, - have_clip_data_id, - clip_data_id, - ); - log::debug!("server_file_contents_request called, conn_id {}, stream_id: {}, list_index {}, dw_flags {}, n_position_low {}, n_position_high {}, cb_requested {}, have_clip_data_id {}, clip_data_id {}, return {}", conn_id, - stream_id, - list_index, - dw_flags, - n_position_low, - n_position_high, - cb_requested, - have_clip_data_id, - clip_data_id, - ret - ); - } - ClipboardFile::FileContentsResponse { - msg_flags, - stream_id, - requested_data, - } => { - log::debug!("server_file_contents_response called"); - ret = server_file_contents_response( - context, - conn_id, - msg_flags, - stream_id, - requested_data, - ); - log::debug!("server_file_contents_response called, conn_id {}, msg_flags {}, stream_id {}, return {}", - conn_id, - msg_flags, - stream_id, - ret - ); - } - } - ret -} - -pub fn server_monitor_ready(context: &mut CliprdrClientContext, conn_id: i32) -> u32 { - unsafe { - let monitor_ready = CLIPRDR_MONITOR_READY { - connID: conn_id as UINT32, - msgType: 0 as UINT16, - msgFlags: 0 as UINT16, - dataLen: 0 as UINT32, - }; - if let Some(f) = context.MonitorReady { - let ret = f(context, &monitor_ready); - ret as u32 - } else { - ERR_CODE_SERVER_FUNCTION_NONE - } - } -} - -pub fn server_format_list( - context: &mut CliprdrClientContext, - conn_id: i32, - format_list: Vec<(i32, String)>, -) -> u32 { - unsafe { - let num_formats = format_list.len() as UINT32; - let mut formats = format_list - .into_iter() - .map(|format| { - if format.1.is_empty() { - CLIPRDR_FORMAT { - formatId: format.0 as UINT32, - formatName: 0 as *mut _, - } - } else { - let n = match CString::new(format.1) { - Ok(n) => n, - Err(_) => CString::new("").unwrap(), - }; - CLIPRDR_FORMAT { - formatId: format.0 as UINT32, - formatName: n.into_raw(), - } - } - }) - .collect::>(); - - let format_list = CLIPRDR_FORMAT_LIST { - connID: conn_id as UINT32, - msgType: 0 as UINT16, - msgFlags: 0 as UINT16, - dataLen: 0 as UINT32, - numFormats: num_formats, - formats: formats.as_mut_ptr(), - }; - - let ret = if let Some(f) = context.ServerFormatList { - f(context, &format_list) - } else { - ERR_CODE_SERVER_FUNCTION_NONE - }; - - for f in formats { - if !f.formatName.is_null() { - // retake pointer to free memory - let _ = CString::from_raw(f.formatName); - } - } - - ret as u32 - } -} - -pub fn server_format_list_response( - context: &mut CliprdrClientContext, - conn_id: i32, - msg_flags: i32, -) -> u32 { - unsafe { - let format_list_response = CLIPRDR_FORMAT_LIST_RESPONSE { - connID: conn_id as UINT32, - msgType: 0 as UINT16, - msgFlags: msg_flags as UINT16, - dataLen: 0 as UINT32, - }; - - if let Some(f) = context.ServerFormatListResponse { - f(context, &format_list_response) - } else { - ERR_CODE_SERVER_FUNCTION_NONE - } - } -} - -pub fn server_format_data_request( - context: &mut CliprdrClientContext, - conn_id: i32, - requested_format_id: i32, -) -> u32 { - unsafe { - let format_data_request = CLIPRDR_FORMAT_DATA_REQUEST { - connID: conn_id as UINT32, - msgType: 0 as UINT16, - msgFlags: 0 as UINT16, - dataLen: 0 as UINT32, - requestedFormatId: requested_format_id as UINT32, - }; - if let Some(f) = context.ServerFormatDataRequest { - f(context, &format_data_request) - } else { - ERR_CODE_SERVER_FUNCTION_NONE - } - } -} - -pub fn server_format_data_response( - context: &mut CliprdrClientContext, - conn_id: i32, - msg_flags: i32, - mut format_data: Vec, -) -> u32 { - unsafe { - let format_data_response = CLIPRDR_FORMAT_DATA_RESPONSE { - connID: conn_id as UINT32, - msgType: 0 as UINT16, - msgFlags: msg_flags as UINT16, - dataLen: format_data.len() as UINT32, - requestedFormatData: format_data.as_mut_ptr(), - }; - if let Some(f) = context.ServerFormatDataResponse { - f(context, &format_data_response) - } else { - ERR_CODE_SERVER_FUNCTION_NONE - } - } -} - -pub fn server_file_contents_request( - context: &mut CliprdrClientContext, - conn_id: i32, - stream_id: i32, - list_index: i32, - dw_flags: i32, - n_position_low: i32, - n_position_high: i32, - cb_requested: i32, - have_clip_data_id: bool, - clip_data_id: i32, -) -> u32 { - unsafe { - let file_contents_request = CLIPRDR_FILE_CONTENTS_REQUEST { - connID: conn_id as UINT32, - msgType: 0 as UINT16, - msgFlags: 0 as UINT16, - dataLen: 0 as UINT32, - streamId: stream_id as UINT32, - listIndex: list_index as UINT32, - dwFlags: dw_flags as UINT32, - nPositionLow: n_position_low as UINT32, - nPositionHigh: n_position_high as UINT32, - cbRequested: cb_requested as UINT32, - haveClipDataId: if have_clip_data_id { TRUE } else { FALSE }, - clipDataId: clip_data_id as UINT32, - }; - if let Some(f) = context.ServerFileContentsRequest { - f(context, &file_contents_request) - } else { - ERR_CODE_SERVER_FUNCTION_NONE - } - } -} - -pub fn server_file_contents_response( - context: &mut CliprdrClientContext, - conn_id: i32, - msg_flags: i32, - stream_id: i32, - mut requested_data: Vec, -) -> u32 { - unsafe { - let file_contents_response = CLIPRDR_FILE_CONTENTS_RESPONSE { - connID: conn_id as UINT32, - msgType: 0 as UINT16, - msgFlags: msg_flags as UINT16, - dataLen: 4 + requested_data.len() as UINT32, - streamId: stream_id as UINT32, - cbRequested: requested_data.len() as UINT32, - requestedData: requested_data.as_mut_ptr(), - }; - if let Some(f) = context.ServerFileContentsResponse { - f(context, &file_contents_response) - } else { - ERR_CODE_SERVER_FUNCTION_NONE - } - } -} - -pub fn create_cliprdr_context( - enable_files: bool, - enable_others: bool, - response_wait_timeout_secs: u32, -) -> ResultType> { - Ok(CliprdrClientContext::create( - enable_files, - enable_others, - response_wait_timeout_secs, - Some(notify_callback), - Some(client_format_list), - Some(client_format_list_response), - Some(client_format_data_request), - Some(client_format_data_response), - Some(client_file_contents_request), - Some(client_file_contents_response), - )?) -} - -extern "C" fn notify_callback(conn_id: UINT32, msg: *const NOTIFICATION_MESSAGE) -> UINT { - log::debug!("notify_callback called"); - let data = unsafe { - let msg = &*msg; - let details = if msg.details.is_null() { - Ok("") - } else { - CStr::from_ptr(msg.details as _).to_str() - }; - match (CStr::from_ptr(msg.msg as _).to_str(), details) { - (Ok(m), Ok(d)) => { - let msgtype = format!( - "custom-{}-nocancel-nook-hasclose", - if msg.r#type == 0 { - "info" - } else if msg.r#type == 1 { - "warn" - } else { - "error" - } - ); - let title = "Clipboard"; - let text = if d.is_empty() { - m.to_string() - } else { - format!("{} {}", m, d) - }; - ClipboardFile::NotifyCallback { - r#type: msgtype, - title: title.to_string(), - text, - } - } - _ => { - log::error!("notify_callback: failed to convert msg"); - return ERR_CODE_INVALID_PARAMETER; - } - } - }; +#[cfg(feature = "unix-file-copy-paste")] +#[inline] +fn send_data_to_all(data: ClipboardFile) { // no need to handle result here - send_data(conn_id as _, data); - - 0 -} - -extern "C" fn client_format_list( - _context: *mut CliprdrClientContext, - clip_format_list: *const CLIPRDR_FORMAT_LIST, -) -> UINT { - let conn_id; - let mut format_list: Vec<(i32, String)> = Vec::new(); - unsafe { - let mut i = 0u32; - while i < (*clip_format_list).numFormats { - let format_data = &(*(*clip_format_list).formats.offset(i as isize)); - if format_data.formatName.is_null() { - format_list.push((format_data.formatId as i32, "".to_owned())); - } else { - let format_name = CStr::from_ptr(format_data.formatName).to_str(); - let format_name = match format_name { - Ok(n) => n.to_owned(), - Err(_) => { - log::warn!("failed to get format name"); - "".to_owned() - } - }; - format_list.push((format_data.formatId as i32, format_name)); - } - // log::debug!("format list item {}: format id: {}, format name: {}", i, format_data.formatId, &format_name); - i += 1; - } - conn_id = (*clip_format_list).connID as i32; + for msg_channel in VEC_MSG_CHANNEL.read().unwrap().iter() { + allow_err!(msg_channel.sender.send(data.clone())); } - log::debug!( - "client_format_list called, client id: {}, format_list: {:?}", - conn_id, - &format_list - ); - let data = ClipboardFile::FormatList { format_list }; - // no need to handle result here - if conn_id == 0 { - // msg_channel is used for debug, VEC_MSG_CHANNEL cannot be inspected by the debugger. - let msg_channel = VEC_MSG_CHANNEL.read().unwrap(); - msg_channel - .iter() - .for_each(|msg_channel| allow_err!(msg_channel.sender.send(data.clone()))); - } else { - send_data(conn_id, data); - } - - 0 -} - -extern "C" fn client_format_list_response( - _context: *mut CliprdrClientContext, - format_list_response: *const CLIPRDR_FORMAT_LIST_RESPONSE, -) -> UINT { - let conn_id; - let msg_flags; - unsafe { - conn_id = (*format_list_response).connID as i32; - msg_flags = (*format_list_response).msgFlags as i32; - } - log::debug!( - "client_format_list_response called, client id: {}, msg_flags: {}", - conn_id, - msg_flags - ); - let data = ClipboardFile::FormatListResponse { msg_flags }; - send_data(conn_id, data); - - 0 -} - -extern "C" fn client_format_data_request( - _context: *mut CliprdrClientContext, - format_data_request: *const CLIPRDR_FORMAT_DATA_REQUEST, -) -> UINT { - let conn_id; - let requested_format_id; - unsafe { - conn_id = (*format_data_request).connID as i32; - requested_format_id = (*format_data_request).requestedFormatId as i32; - } - let data = ClipboardFile::FormatDataRequest { - requested_format_id, - }; - log::debug!( - "client_format_data_request called, conn_id: {}, requested_format_id: {}", - conn_id, - requested_format_id - ); - // no need to handle result here - send_data(conn_id, data); - - 0 -} - -extern "C" fn client_format_data_response( - _context: *mut CliprdrClientContext, - format_data_response: *const CLIPRDR_FORMAT_DATA_RESPONSE, -) -> UINT { - let conn_id; - let msg_flags; - let format_data; - unsafe { - conn_id = (*format_data_response).connID as i32; - msg_flags = (*format_data_response).msgFlags as i32; - if (*format_data_response).requestedFormatData.is_null() { - format_data = Vec::new(); - } else { - format_data = std::slice::from_raw_parts( - (*format_data_response).requestedFormatData, - (*format_data_response).dataLen as usize, - ) - .to_vec(); - } - } - log::debug!( - "client_format_data_response called, client id: {}, msg_flags: {}", - conn_id, - msg_flags - ); - let data = ClipboardFile::FormatDataResponse { - msg_flags, - format_data, - }; - send_data(conn_id, data); - - 0 -} - -extern "C" fn client_file_contents_request( - _context: *mut CliprdrClientContext, - file_contents_request: *const CLIPRDR_FILE_CONTENTS_REQUEST, -) -> UINT { - // TODO: support huge file? - // if (!cliprdr->hasHugeFileSupport) - // { - // if (((UINT64)fileContentsRequest->cbRequested + fileContentsRequest->nPositionLow) > - // UINT32_MAX) - // return ERROR_INVALID_PARAMETER; - // if (fileContentsRequest->nPositionHigh != 0) - // return ERROR_INVALID_PARAMETER; - // } - - let conn_id; - let stream_id; - let list_index; - let dw_flags; - let n_position_low; - let n_position_high; - let cb_requested; - let have_clip_data_id; - let clip_data_id; - unsafe { - conn_id = (*file_contents_request).connID as i32; - stream_id = (*file_contents_request).streamId as i32; - list_index = (*file_contents_request).listIndex as i32; - dw_flags = (*file_contents_request).dwFlags as i32; - n_position_low = (*file_contents_request).nPositionLow as i32; - n_position_high = (*file_contents_request).nPositionHigh as i32; - cb_requested = (*file_contents_request).cbRequested as i32; - have_clip_data_id = (*file_contents_request).haveClipDataId == TRUE; - clip_data_id = (*file_contents_request).clipDataId as i32; - } - let data = ClipboardFile::FileContentsRequest { - stream_id, - list_index, - dw_flags, - n_position_low, - n_position_high, - cb_requested, - have_clip_data_id, - clip_data_id, - }; - log::debug!("client_file_contents_request called, data: {:?}", &data); - send_data(conn_id, data); - - 0 -} - -extern "C" fn client_file_contents_response( - _context: *mut CliprdrClientContext, - file_contents_response: *const CLIPRDR_FILE_CONTENTS_RESPONSE, -) -> UINT { - let conn_id; - let msg_flags; - let stream_id; - let requested_data; - unsafe { - conn_id = (*file_contents_response).connID as i32; - msg_flags = (*file_contents_response).msgFlags as i32; - stream_id = (*file_contents_response).streamId as i32; - if (*file_contents_response).requestedData.is_null() { - requested_data = Vec::new(); - } else { - requested_data = std::slice::from_raw_parts( - (*file_contents_response).requestedData, - (*file_contents_response).cbRequested as usize, - ) - .to_vec(); - } - } - let data = ClipboardFile::FileContentsResponse { - msg_flags, - stream_id, - requested_data, - }; - log::debug!( - "client_file_contents_response called, conn_id: {}, msg_flags: {}, stream_id: {}", - conn_id, - msg_flags, - stream_id - ); - send_data(conn_id, data); - - 0 } #[cfg(test)] diff --git a/libs/clipboard/src/platform/fuse.rs b/libs/clipboard/src/platform/fuse.rs new file mode 100644 index 000000000..950dff958 --- /dev/null +++ b/libs/clipboard/src/platform/fuse.rs @@ -0,0 +1,1182 @@ +//! fuse server implement +//! we use fuse to provide file readers, warping data transfer to file interfaces +//! +//! # Name encoding +//! +//! There are different collection of characters forbidden in file names on different platforms: +//! - windows: `\/:*?"<>|` +//! - macos: `:/` +//! - linux: `/` +//! +//! what makes it troublesome is windows also used '\' as path separator. +//! +//! For now, we transfer all file names with windows separators, UTF-16 encoded. +//! *Need a way to transfer file names with '\' safely*. +//! Maybe we can use URL encoded file names and '/' seperators as a new standard, while keep the support to old schemes. +//! +//! # Note +//! - all files on FS should be read only, and mark the owner to be the current user +//! - any write operations, hard links, and symbolic links on the FS should be denied + +use std::{ + collections::{BTreeMap, HashMap}, + ffi::OsString, + path::{Path, PathBuf}, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + mpsc::{Receiver, Sender}, + Arc, + }, + time::{Duration, SystemTime}, +}; + +use fuser::{ReplyDirectory, FUSE_ROOT_ID}; +use hbb_common::{ + bytes::{Buf, Bytes}, + log, +}; +use parking_lot::{Condvar, Mutex}; +use utf16string::WStr; + +use crate::{send_data, ClipboardFile, CliprdrError}; + +use super::LDAP_EPOCH_DELTA; + +/// fuse server ready retry max times +const READ_RETRY: i32 = 3; + +/// block size for fuse, align to our asynchronic request size over FileContentsRequest. +pub const BLOCK_SIZE: u32 = 4 * 1024 * 1024; + +/// read only permission +const PERM_READ: u16 = 0o444; +/// read and write permission +const PERM_RW: u16 = 0o644; +/// only self can read and readonly +const PERM_SELF_RO: u16 = 0o400; +/// rwx +const PERM_RWX: u16 = 0o755; +/// max length of file name +const MAX_NAME_LEN: usize = 255; + +/// fuse client +/// this is a proxy to the fuse server +#[derive(Debug)] +pub struct FuseClient { + server: Arc>, +} + +impl fuser::Filesystem for FuseClient { + fn init( + &mut self, + req: &fuser::Request<'_>, + config: &mut fuser::KernelConfig, + ) -> Result<(), libc::c_int> { + let mut server = self.server.lock(); + server.init(req, config) + } + + fn lookup( + &mut self, + req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + reply: fuser::ReplyEntry, + ) { + let mut server = self.server.lock(); + server.lookup(req, parent, name, reply) + } + + fn opendir(&mut self, req: &fuser::Request<'_>, ino: u64, flags: i32, reply: fuser::ReplyOpen) { + let mut server = self.server.lock(); + server.opendir(req, ino, flags, reply) + } + + fn readdir( + &mut self, + req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + reply: fuser::ReplyDirectory, + ) { + let mut server = self.server.lock(); + server.readdir(req, ino, fh, offset, reply) + } + + fn releasedir( + &mut self, + req: &fuser::Request<'_>, + ino: u64, + fh: u64, + _flags: i32, + reply: fuser::ReplyEmpty, + ) { + let mut server = self.server.lock(); + server.releasedir(req, ino, fh, _flags, reply) + } + + fn open(&mut self, req: &fuser::Request<'_>, ino: u64, flags: i32, reply: fuser::ReplyOpen) { + let mut server = self.server.lock(); + server.open(req, ino, flags, reply) + } + + fn read( + &mut self, + req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + size: u32, + flags: i32, + lock_owner: Option, + reply: fuser::ReplyData, + ) { + let mut server = self.server.lock(); + server.read(req, ino, fh, offset, size, flags, lock_owner, reply) + } + + fn release( + &mut self, + req: &fuser::Request<'_>, + ino: u64, + fh: u64, + _flags: i32, + _lock_owner: Option, + _flush: bool, + reply: fuser::ReplyEmpty, + ) { + let mut server = self.server.lock(); + server.release(req, ino, fh, _flags, _lock_owner, _flush, reply) + } + + fn getattr(&mut self, req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyAttr) { + let mut server = self.server.lock(); + server.getattr(req, ino, reply) + } + + fn statfs(&mut self, req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyStatfs) { + let mut server = self.server.lock(); + server.statfs(req, ino, reply) + } +} + +/// fuse server +/// provides a read-only file system +#[derive(Debug)] +pub(crate) struct FuseServer { + generation: AtomicU64, + files: Vec, + // file handle counter + file_handle_counter: AtomicU64, + // timeout + timeout: Duration, + // file read reply channel + rx: Receiver, +} + +impl FuseServer { + /// create a new fuse server + pub fn new(timeout: Duration) -> (Self, Sender) { + let (tx, rx) = std::sync::mpsc::channel(); + ( + Self { + generation: AtomicU64::new(0), + files: Vec::new(), + file_handle_counter: AtomicU64::new(0), + timeout, + rx, + }, + tx, + ) + } + + pub fn client(server: Arc>) -> FuseClient { + FuseClient { server } + } +} + +impl FuseServer { + pub fn load_file_list(&mut self, files: Vec) -> Result<(), CliprdrError> { + let tree = FuseNode::build_tree(files)?; + self.files = tree; + self.generation.fetch_add(1, Ordering::Relaxed); + Ok(()) + } +} + +impl fuser::Filesystem for FuseServer { + fn init( + &mut self, + _req: &fuser::Request<'_>, + _config: &mut fuser::KernelConfig, + ) -> Result<(), libc::c_int> { + if self.files.is_empty() { + // create a root file + let root = FuseNode::new_root(); + self.files.push(root); + } + Ok(()) + } + + fn lookup( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + reply: fuser::ReplyEntry, + ) { + if name.len() > MAX_NAME_LEN { + log::debug!("fuse: name too long"); + reply.error(libc::ENAMETOOLONG); + return; + } + + let entries = &self.files; + + let generation = self.generation.load(Ordering::Relaxed); + + let parent_entry = match entries.get(parent as usize - 1) { + Some(f) => f, + None => { + log::error!("fuse: parent not found"); + reply.error(libc::ENOENT); + return; + } + }; + + if parent_entry.attributes.kind != FileType::Directory { + log::error!("fuse: parent is not a directory"); + + reply.error(libc::ENOTDIR); + return; + } + + let children_inodes = &parent_entry.children; + + for inode in children_inodes.iter().copied() { + let child = &entries[inode as usize - 1]; + let entry_name = OsString::from(&child.name); + + if &entry_name.as_os_str() == &name { + let ttl = std::time::Duration::new(0, 0); + reply.entry(&ttl, &(&child.attributes).into(), generation); + log::debug!("fuse: found child"); + return; + } + } + // error + reply.error(libc::ENOENT); + log::debug!("fuse: child not found"); + } + + fn opendir( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + _flags: i32, + reply: fuser::ReplyOpen, + ) { + let files = &self.files; + let Some(entry) = files.get(ino as usize - 1) else { + reply.error(libc::ENOENT); + log::error!("fuse: opendir: entry not found"); + return; + }; + if entry.attributes.kind != FileType::Directory { + reply.error(libc::ENOTDIR); + log::error!("fuse: opendir: entry is not a directory"); + return; + } + // in gc, deny open + if entry.marked() { + log::error!("fuse: opendir: entry is in gc"); + reply.error(libc::EBUSY); + return; + } + + let fh = self.alloc_fd(); + entry.add_handler(fh); + reply.opened(fh, 0); + } + + fn readdir( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + mut reply: ReplyDirectory, + ) { + let files = &self.files; + let Some(entry) = files.get(ino as usize - 1) else { + reply.error(libc::ENOENT); + log::error!("fuse: readdir: entry not found"); + return; + }; + if !entry.have_handler(fh) { + reply.error(libc::EBADF); + log::error!("fuse: readdir: entry has no such handler"); + return; + } + if entry.attributes.kind != FileType::Directory { + reply.error(libc::ENOTDIR); + log::error!("fuse: readdir: entry is not a directory"); + return; + } + + let offset = offset as usize; + let mut entries = Vec::new(); + + let self_entry = (ino, FileType::Directory, OsString::from(".")); + entries.push(self_entry); + + if let Some(parent_inode) = entry.parent { + entries.push((parent_inode, FileType::Directory, OsString::from(".."))); + } + + for inode in entry.children.iter().copied() { + let child = &files[inode as usize - 1]; + let kind = child.attributes.kind; + let name = OsString::from(&child.name); + let child_entry = (inode, kind, name.to_owned()); + entries.push(child_entry); + } + + for (i, entry) in entries.into_iter().enumerate().skip(offset) { + if reply.add(entry.0, i as i64 + 1, entry.1.into(), entry.2) { + break; + } + } + + reply.ok(); + } + + fn releasedir( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + _flags: i32, + reply: fuser::ReplyEmpty, + ) { + let files = &self.files; + let Some(entry) = files.get(ino as usize - 1) else { + reply.error(libc::ENOENT); + log::error!("fuse: releasedir: entry not found"); + return; + }; + if entry.attributes.kind != FileType::Directory { + reply.error(libc::ENOTDIR); + log::error!("fuse: releasedir: entry is not a directory"); + return; + } + if !entry.have_handler(fh) { + reply.error(libc::EBADF); + log::error!("fuse: releasedir: entry has no such handler"); + return; + } + + let _ = entry.unregister_handler(fh); + reply.ok(); + } + + fn open(&mut self, _req: &fuser::Request<'_>, ino: u64, _flags: i32, reply: fuser::ReplyOpen) { + let files = &self.files; + let Some(entry) = files.get(ino as usize - 1) else { + reply.error(libc::ENOENT); + log::error!("fuse: open: entry not found"); + return; + }; + + // todo: support link file + if entry.attributes.kind != FileType::File { + reply.error(libc::ENFILE); + log::error!("fuse: open: entry is not a file"); + return; + } + + // check gc + if entry.marked() { + reply.error(libc::EBUSY); + log::error!("fuse: open: entry is in gc"); + return; + } + + let fh = self.alloc_fd(); + entry.add_handler(fh); + reply.opened(fh, 0); + } + + // todo: implement retry + fn read( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + size: u32, + _flags: i32, + _lock_owner: Option, + reply: fuser::ReplyData, + ) { + let files = &self.files; + let Some(entry) = files.get(ino as usize - 1) else { + reply.error(libc::ENOENT); + log::error!("fuse: read: entry not found"); + return; + }; + if !entry.have_handler(fh) { + reply.error(libc::EBADF); + log::error!("fuse: read: entry has no such handler"); + return; + } + if entry.attributes.kind != FileType::File { + reply.error(libc::ENFILE); + log::error!("fuse: read: entry is not a file"); + return; + } + + if entry.marked() { + reply.error(libc::EBUSY); + log::error!("fuse: read: entry is in gc"); + return; + } + + let bytes = match self.read_node(entry, offset, size) { + Ok(b) => b, + Err(e) => { + log::error!("failed to read entry: {:?}", e); + reply.error(libc::EIO); + return; + } + }; + + reply.data(bytes.as_slice()); + } + + fn release( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + _flags: i32, + _lock_owner: Option, + _flush: bool, + reply: fuser::ReplyEmpty, + ) { + let files = &self.files; + let Some(entry) = files.get(ino as usize - 1) else { + reply.error(libc::ENOENT); + log::error!("fuse: release: entry not found"); + return; + }; + + if entry.unregister_handler(fh).is_err() { + reply.error(libc::EBADF); + log::error!("fuse: release: entry has no such handler"); + return; + } + reply.ok(); + } + + fn getattr(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyAttr) { + let files = &self.files; + let Some(entry) = files.get(ino as usize - 1) else { + reply.error(libc::ENOENT); + log::error!("fuse: getattr: entry not found"); + return; + }; + + let attr = (&entry.attributes).into(); + reply.attr(&std::time::Duration::default(), &attr) + } + + fn statfs(&mut self, _req: &fuser::Request<'_>, _ino: u64, reply: fuser::ReplyStatfs) { + let mut blocks = 0; + for file in self.files.iter() { + blocks += file.attributes.size / (BLOCK_SIZE as u64) + + (file.attributes.size % (BLOCK_SIZE as u64) != 0) as u64; + } + reply.statfs(blocks, 0, 0, 0, 0, BLOCK_SIZE, 512, BLOCK_SIZE) + } +} + +impl FuseServer { + // get files and directory path right in root of FUSE fs + pub fn list_root(&self) -> Vec { + let files = &self.files; + let children = &files[0].children; + let mut paths = Vec::with_capacity(children.len()); + for inode in children.iter().copied() { + let idx = inode as usize - 1; + paths.push(PathBuf::from(&files[idx].name)); + } + paths + } + + /// allocate a new file descriptor + fn alloc_fd(&self) -> u64 { + self.file_handle_counter.fetch_add(1, Ordering::Relaxed) + } + + fn read_node( + &self, + node: &FuseNode, + offset: i64, + size: u32, + ) -> Result, std::io::Error> { + // todo: async and concurrent read, generate stream_id per request + log::debug!( + "reading {:?} offset {} size {} on stream: {}", + node.name, + offset, + size, + node.stream_id + ); + + let cb_requested = unsafe { + // convert `size` from u32 to i32 + // yet with same bit representation + std::mem::transmute::(size) + }; + + let (n_position_high, n_position_low) = + ((offset >> 32) as i32, (offset & (u32::MAX as i64)) as i32); + let request = ClipboardFile::FileContentsRequest { + stream_id: node.stream_id, + list_index: node.index as i32, + dw_flags: 2, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id: false, + clip_data_id: 0, + }; + + send_data(node.conn_id, request.clone()); + + log::debug!( + "waiting for read reply for {:?} on stream: {}", + node.name, + node.stream_id + ); + + let mut retry_times = 0; + + loop { + let reply = self.rx.recv_timeout(self.timeout).map_err(|e| { + log::error!("failed to receive file list from channel: {:?}", e); + std::io::Error::new(std::io::ErrorKind::TimedOut, e) + })?; + + match reply { + ClipboardFile::FileContentsResponse { + msg_flags, + stream_id, + requested_data, + } => { + if stream_id != node.stream_id { + log::debug!("stream id mismatch, ignore"); + continue; + } + + if msg_flags & 1 == 0 { + retry_times += 1; + if retry_times > READ_RETRY { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "failure request", + )); + } + + send_data(node.conn_id, request.clone()); + continue; + } + return Ok(requested_data); + } + _ => { + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "invalid reply", + )) + } + } + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FileDescription { + pub conn_id: i32, + pub name: PathBuf, + pub kind: FileType, + pub atime: SystemTime, + pub last_modified: SystemTime, + pub last_metadata_changed: SystemTime, + pub creation_time: SystemTime, + + pub size: u64, + + pub perm: u16, +} + +impl FileDescription { + fn parse_file_descriptor( + bytes: &mut Bytes, + conn_id: i32, + ) -> Result { + let flags = bytes.get_u32_le(); + // skip reserved 32 bytes + bytes.advance(32); + let attributes = bytes.get_u32_le(); + + // in original specification, this is 16 bytes reserved + // we use the last 4 bytes to store the file mode + // skip reserved 12 bytes + bytes.advance(12); + let perm = bytes.get_u32_le() as u16; + + // last write time from 1601-01-01 00:00:00, in 100ns + let last_write_time = bytes.get_u64_le(); + // file size + let file_size_high = bytes.get_u32_le(); + let file_size_low = bytes.get_u32_le(); + // utf16 file name, double \0 terminated, in 520 bytes block + // read with another pointer, and advance the main pointer + let block = bytes.clone(); + bytes.advance(520); + + let block = &block[..520]; + let wstr = WStr::from_utf16le(block).map_err(|e| { + log::error!("cannot convert file descriptor path: {:?}", e); + CliprdrError::ConversionFailure + })?; + + let from_unix = flags & 0x08 != 0; + + let valid_attributes = flags & 0x04 != 0; + if !valid_attributes { + return Err(CliprdrError::InvalidRequest { + description: "file description must have valid attributes".to_string(), + }); + } + + // todo: check normal, hidden, system, readonly, archive... + let directory = attributes & 0x10 != 0; + let normal = attributes == 0x80; + let hidden = attributes & 0x02 != 0; + let readonly = attributes & 0x01 != 0; + + let perm = if from_unix { + // as is + perm + // cannot set as is... + } else if normal { + PERM_RWX + } else if readonly { + PERM_READ + } else if hidden { + PERM_SELF_RO + } else if directory { + PERM_RWX + } else { + PERM_RW + }; + + let kind = if directory { + FileType::Directory + } else { + FileType::File + }; + + let valid_size = flags & 0x40 != 0; + let size = if valid_size { + ((file_size_high as u64) << 32) + file_size_low as u64 + } else { + 0 + }; + + let valid_write_time = flags & 0x20 != 0; + let last_modified = if valid_write_time && last_write_time >= LDAP_EPOCH_DELTA { + let last_write_time = (last_write_time - LDAP_EPOCH_DELTA) * 100; + let last_write_time = Duration::from_nanos(last_write_time); + SystemTime::UNIX_EPOCH + last_write_time + } else { + SystemTime::UNIX_EPOCH + }; + + let name = wstr.to_utf8().replace('\\', "/"); + let name = PathBuf::from(name.trim_end_matches('\0')); + + let desc = FileDescription { + conn_id, + name, + kind, + atime: last_modified, + last_modified, + last_metadata_changed: last_modified, + + creation_time: last_modified, + size, + perm, + }; + + Ok(desc) + } + + /// parse file descriptions from a format data response PDU + /// which containing a CSPTR_FILEDESCRIPTORW indicated format data + pub fn parse_file_descriptors( + file_descriptor_pdu: Vec, + conn_id: i32, + ) -> Result, CliprdrError> { + let mut data = Bytes::from(file_descriptor_pdu); + if data.remaining() < 4 { + return Err(CliprdrError::InvalidRequest { + description: "file descriptor request with infficient length".to_string(), + }); + } + + let count = data.get_u32_le() as usize; + if data.remaining() == 0 && count == 0 { + return Ok(Vec::new()); + } + + if data.remaining() != 592 * count { + return Err(CliprdrError::InvalidRequest { + description: "file descriptor request with invalid length".to_string(), + }); + } + + let mut files = Vec::with_capacity(count); + for _ in 0..count { + let desc = Self::parse_file_descriptor(&mut data, conn_id)?; + files.push(desc); + } + + Ok(files) + } +} + +/// a node in the FUSE file tree +#[derive(Debug)] +struct FuseNode { + /// connection id + pub conn_id: i32, + + // todo: use stream_id to identify a FileContents request-reply + // instead of a whole file + /// stream id + pub stream_id: i32, + + /// file index in peer's file list + /// NOTE: + /// it is NOT the same as inode, this is the index in the file list + pub index: usize, + + /// parent inode + pub parent: Option, + + /// file name + pub name: String, + /// file attributes + pub attributes: InodeAttributes, + /// children inodes + pub children: Vec, + + /// marked gc + pub file_handlers: FileHandles, +} + +impl FuseNode { + pub fn from_description(inode: Inode, desc: FileDescription) -> Self { + Self { + conn_id: desc.conn_id, + stream_id: rand::random(), + index: inode as usize - 2, + name: desc.name.to_str().unwrap().to_owned(), + parent: None, + attributes: InodeAttributes::from_description(inode, desc), + children: Vec::new(), + file_handlers: FileHandles::new(), + } + } + + pub fn new_root() -> Self { + Self { + conn_id: 0, + stream_id: rand::random(), + index: 0, + name: String::from("/"), + parent: None, + attributes: InodeAttributes::new_root(), + children: Vec::new(), + file_handlers: FileHandles::new(), + } + } + + #[allow(unused)] + pub fn is_file(&self) -> bool { + self.attributes.kind == FileType::File + } + + pub fn marked(&self) -> bool { + self.file_handlers.marked() + } + + pub fn add_handler(&self, fh: u64) { + self.file_handlers.add_handler(fh) + } + + pub fn unregister_handler(&self, fh: u64) -> Result<(), std::io::Error> { + self.file_handlers.unregister(fh) + } + + pub fn have_handler(&self, fh: u64) -> bool { + self.file_handlers.have_handler(fh) + } + + /// add a child inode + fn add_child(&mut self, inode: Inode) { + self.children.push(inode); + } + + /// calculate the file tree from a pre-ordered file list + /// ## implement detail: + /// - a new root entry will be prepended to the list + /// - all file names will be trimed to the last component + pub fn build_tree(files: Vec) -> Result, CliprdrError> { + // capacity set to file count + 1 (root) + let mut tree_list = Vec::with_capacity(files.len() + 1); + let root = Self::new_root(); + tree_list.push(root); + + // build the tree first + // root map, name -> inode + let mut sub_root_map = HashMap::new(); + sub_root_map.insert(Path::new("/").to_path_buf(), FUSE_ROOT_ID); + sub_root_map.insert(Path::new("").to_path_buf(), FUSE_ROOT_ID); + + for file in files.into_iter() { + let name = file.name.clone(); + + let inode = tree_list.len() as u64 + FUSE_ROOT_ID; + let parent_inode = match name.parent() { + Some(parent) => sub_root_map.get(parent).copied().unwrap_or(FUSE_ROOT_ID), + None => { + // parent should be root + FUSE_ROOT_ID + } + }; + tree_list[parent_inode as usize - 1].add_child(inode); + + let base_name = name.file_name().ok_or_else(|| { + let err = std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!("invalid file name {}", file.name.display()), + ); + CliprdrError::FileError { + path: file.name.clone(), + err, + } + })?; + + let mut desc = file.clone(); + + if desc.kind == FileType::Directory { + sub_root_map.insert(desc.name.clone(), inode); + } + + desc.name = Path::new(base_name).to_path_buf(); + + let mut fuse_node = FuseNode::from_description(inode, desc); + fuse_node.parent = Some(parent_inode); + tree_list.push(fuse_node); + } + Ok(tree_list) + } +} + +pub type Inode = u64; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FileType { + File, + Directory, + // todo: support symlink + Symlink, +} + +impl From for fuser::FileType { + fn from(value: FileType) -> Self { + match value { + FileType::File => Self::RegularFile, + FileType::Directory => Self::Directory, + FileType::Symlink => Self::Symlink, + } + } +} + +#[derive(Debug, Clone)] +pub struct InodeAttributes { + inode: Inode, + size: u64, + // file reference meta + // should be the only mutable field in this struct + last_accessed: std::time::SystemTime, + last_modified: std::time::SystemTime, + last_metadata_changed: std::time::SystemTime, + creation_time: std::time::SystemTime, + kind: FileType, + perm: u16, + + // not implemented + _xattrs: BTreeMap, Vec>, +} + +impl InodeAttributes { + pub fn new(inode: u64, size: u64, perm: u16, kind: FileType) -> Self { + Self { + inode, + size, + last_accessed: std::time::SystemTime::now(), + last_modified: std::time::SystemTime::now(), + last_metadata_changed: std::time::SystemTime::now(), + creation_time: std::time::SystemTime::now(), + kind, + perm, + _xattrs: BTreeMap::new(), + } + } + + pub fn from_description(inode: u64, desc: FileDescription) -> Self { + Self { + inode, + size: desc.size, + last_modified: desc.last_modified, + last_metadata_changed: desc.last_metadata_changed, + creation_time: desc.creation_time, + last_accessed: SystemTime::now(), + kind: desc.kind, + perm: desc.perm, + + _xattrs: BTreeMap::new(), + } + } + + pub fn new_root() -> Self { + Self::new(FUSE_ROOT_ID, 0, PERM_RWX, FileType::Directory) + } + + pub fn access(&mut self) { + self.last_accessed = std::time::SystemTime::now(); + } +} + +impl From<&InodeAttributes> for fuser::FileAttr { + fn from(value: &InodeAttributes) -> Self { + let blocks = if value.size % BLOCK_SIZE as u64 == 0 { + value.size / BLOCK_SIZE as u64 + } else { + value.size / BLOCK_SIZE as u64 + 1 + }; + Self { + ino: value.inode, + size: value.size, + blocks, + atime: value.last_accessed, + mtime: value.last_modified, + ctime: value.last_metadata_changed, + crtime: value.creation_time, + kind: value.kind.into(), + + // read only + perm: value.perm, + + nlink: 1, + // set to current user + uid: unsafe { libc::getuid() }, + // set to current user + gid: unsafe { libc::getgid() }, + rdev: 0, + blksize: BLOCK_SIZE, + // todo: support macos flags + flags: 0, + } + } +} + +#[derive(Debug)] +struct FileHandles { + waiter: Condvar, + handlers: Mutex>, + gc: AtomicBool, +} + +impl FileHandles { + pub fn new() -> Self { + Self { + waiter: Condvar::new(), + // the vector in handlers is sorted, from small to big + // prove: + // - later allocated handler will be bigger than previous ones + // - new handlers will append to the end of the vector + // - dropping old handlers won't affect the ordering + handlers: Mutex::new(Vec::new()), + gc: AtomicBool::new(false), + } + } + + pub fn add_handler(&self, fh: u64) { + if self.marked() { + panic!("adding new handler to a marked ref counter"); + } + self.handlers.lock().push(fh); + } + + pub fn marked(&self) -> bool { + self.gc.load(Ordering::Relaxed) + } + + pub fn have_handler(&self, handler: u64) -> bool { + let handlers = self.handlers.lock(); + handlers.binary_search(&handler).is_ok() + } + + pub fn unregister(&self, handler: u64) -> Result<(), std::io::Error> { + let mut handlers = self.handlers.lock(); + + let Ok(idx) = handlers.binary_search(&handler) else { + let e = std::io::Error::new(std::io::ErrorKind::InvalidInput, "invalid handler"); + return Err(e); + }; + + handlers.remove(idx); + self.waiter.notify_all(); + Ok(()) + } +} + +#[cfg(test)] +mod fuse_test { + use std::str::FromStr; + + use super::*; + + // todo: more tests needed! + fn desc_gen(name: &str, kind: FileType) -> FileDescription { + FileDescription { + conn_id: 0, + name: PathBuf::from(name), + kind, + atime: SystemTime::UNIX_EPOCH, + last_modified: SystemTime::UNIX_EPOCH, + last_metadata_changed: SystemTime::UNIX_EPOCH, + creation_time: SystemTime::UNIX_EPOCH, + + size: 0, + perm: 0, + } + } + fn generate_descriptions(prefix: &str) -> Vec { + let (d0_path, f0_path, f1_path, d1_path, f2_path, f3_path) = if prefix.is_empty() { + ( + "folder0".to_string(), + "folder0/file0".to_string(), + "folder0/file1".to_string(), + "folder1".to_string(), + "folder1/file2".to_string(), + "folder1/📄3".to_string(), + ) + } else { + ( + format!("{}/folder0", prefix), + format!("{}/folder0/file0", prefix), + format!("{}/folder0/file1", prefix), + format!("{}/folder1", prefix), + format!("{}/folder1/file2", prefix), + format!("{}/folder1/📄3", prefix), + ) + }; + let folder0 = desc_gen(&d0_path, FileType::Directory); + let file0 = desc_gen(&f0_path, FileType::File); + let file1 = desc_gen(&f1_path, FileType::File); + let folder1 = desc_gen(&d1_path, FileType::Directory); + let file2 = desc_gen(&f2_path, FileType::File); + let file3 = desc_gen(&f3_path, FileType::File); + + vec![folder0, file0, file1, folder1, file2, file3] + } + + fn build_tree(prefix: &str) { + let source_list = generate_descriptions(prefix); + + let build_res = FuseNode::build_tree(source_list); + assert!(build_res.is_ok()); + let tree_list = build_res.unwrap(); + + assert_eq!(tree_list.len(), 7); + + assert_eq!(tree_list[0].name, "/"); + assert_eq!(tree_list[1].name, "folder0"); + assert_eq!(tree_list[2].name, "file0"); + assert_eq!(tree_list[3].name, "file1"); + assert_eq!(tree_list[4].name, "folder1"); + assert_eq!(tree_list[5].name, "file2"); + assert_eq!(tree_list[6].name, "📄3"); + + assert_eq!(tree_list[0].children, vec![2, 5]); + assert_eq!(tree_list[1].children, vec![3, 4]); + assert!(tree_list[2].children.is_empty()); + assert!(tree_list[3].children.is_empty()); + assert_eq!(tree_list[4].children, vec![6, 7]); + assert!(tree_list[5].children.is_empty()); + assert!(tree_list[6].children.is_empty()); + + for (idx, node) in tree_list.iter().skip(1).enumerate() { + assert_eq!(idx, node.index) + } + } + + fn build_single_file(prefix: &str) { + let raw_name = "衬衫的价格为 9 镑 15 便士.txt"; + let f_name = if prefix == "" { + raw_name.to_string() + } else { + prefix.to_string() + "/" + raw_name + }; + let desc = desc_gen(&f_name, FileType::File); + let tree = FuseNode::build_tree(vec![desc]).unwrap(); + + assert_eq!(tree.len(), 2); + assert_eq!(tree[0].name, "/"); + assert_eq!(tree[0].children, vec![2]); + + assert_eq!(tree[1].name, raw_name); + assert_eq!(tree[1].index, 0); + assert_eq!(tree[1].attributes.kind, FileType::File); + } + + #[test] + fn test_parse_single() { + build_single_file(""); + build_single_file("/"); + build_single_file("test"); + build_single_file("/test"); + build_single_file("🗂"); + build_single_file("/🗂"); + } + + #[test] + fn test_parse_tree() { + build_tree(""); + build_tree("/"); + build_tree("test"); + build_tree("/test"); + build_tree("/test/test"); + build_tree("🗂"); + build_tree("/🗂"); + build_tree("🗂/test"); + } +} diff --git a/libs/clipboard/src/platform/mod.rs b/libs/clipboard/src/platform/mod.rs new file mode 100644 index 000000000..0a2988d2c --- /dev/null +++ b/libs/clipboard/src/platform/mod.rs @@ -0,0 +1,88 @@ +use crate::{CliprdrError, CliprdrServiceContext}; + +#[cfg(target_os = "windows")] +pub mod windows; +#[cfg(target_os = "windows")] +pub fn create_cliprdr_context( + enable_files: bool, + enable_others: bool, + response_wait_timeout_secs: u32, +) -> crate::ResultType> { + let boxed = + windows::create_cliprdr_context(enable_files, enable_others, response_wait_timeout_secs)? + as Box<_>; + Ok(boxed) +} + +#[cfg(feature = "unix-file-copy-paste")] +#[cfg(any(target_os = "linux", target_os = "macos"))] +/// use FUSE for file pasting on these platforms +pub mod fuse; +#[cfg(feature = "unix-file-copy-paste")] +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub mod unix; +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub fn create_cliprdr_context( + _enable_files: bool, + _enable_others: bool, + _response_wait_timeout_secs: u32, +) -> crate::ResultType> { + #[cfg(feature = "unix-file-copy-paste")] + { + use std::{fs::Permissions, os::unix::prelude::PermissionsExt}; + + use hbb_common::{config::APP_NAME, log}; + + if !_enable_files { + return Ok(Box::new(DummyCliprdrContext {}) as Box<_>); + } + + let timeout = std::time::Duration::from_secs(_response_wait_timeout_secs as u64); + + let app_name = APP_NAME.read().unwrap().clone(); + + let mnt_path = format!("/tmp/{}/{}", app_name, "cliprdr"); + + // this function must be called after the main IPC is up + std::fs::create_dir(&mnt_path).ok(); + std::fs::set_permissions(&mnt_path, Permissions::from_mode(0o777)).ok(); + + log::info!("clear previously mounted cliprdr FUSE"); + if let Err(e) = std::process::Command::new("umount").arg(&mnt_path).status() { + log::warn!("umount {:?} may fail: {:?}", mnt_path, e); + } + + let unix_ctx = unix::ClipboardContext::new(timeout, mnt_path.parse().unwrap())?; + log::debug!("start cliprdr FUSE"); + unix_ctx.run().expect("failed to start cliprdr FUSE"); + + Ok(Box::new(unix_ctx) as Box<_>) + } + + #[cfg(not(feature = "unix-file-copy-paste"))] + return Ok(Box::new(DummyCliprdrContext {}) as Box<_>); +} + +struct DummyCliprdrContext {} + +impl CliprdrServiceContext for DummyCliprdrContext { + fn set_is_stopped(&mut self) -> Result<(), CliprdrError> { + Ok(()) + } + fn empty_clipboard(&mut self, _conn_id: i32) -> Result { + Ok(true) + } + fn server_clip_file( + &mut self, + _conn_id: i32, + _msg: crate::ClipboardFile, + ) -> Result<(), crate::CliprdrError> { + Ok(()) + } +} + +#[cfg(feature = "unix-file-copy-paste")] +#[cfg(any(target_os = "linux", target_os = "macos"))] +// begin of epoch used by microsoft +// 1601-01-01 00:00:00 + LDAP_EPOCH_DELTA*(100 ns) = 1970-01-01 00:00:00 +const LDAP_EPOCH_DELTA: u64 = 116444772610000000; diff --git a/libs/clipboard/src/platform/unix/local_file.rs b/libs/clipboard/src/platform/unix/local_file.rs new file mode 100644 index 000000000..c9186c4fa --- /dev/null +++ b/libs/clipboard/src/platform/unix/local_file.rs @@ -0,0 +1,367 @@ +use std::{ + collections::HashSet, + fs::File, + io::{BufRead, BufReader, Read, Seek}, + os::unix::prelude::PermissionsExt, + path::PathBuf, + sync::atomic::{AtomicU64, Ordering}, + time::SystemTime, +}; + +use hbb_common::{ + bytes::{BufMut, BytesMut}, + log, +}; +use utf16string::WString; + +use crate::{ + platform::{fuse::BLOCK_SIZE, LDAP_EPOCH_DELTA}, + CliprdrError, +}; + +/// has valid file attributes +const FLAGS_FD_ATTRIBUTES: u32 = 0x04; +/// has valid file size +const FLAGS_FD_SIZE: u32 = 0x40; +/// has valid last write time +const FLAGS_FD_LAST_WRITE: u32 = 0x20; +/// show progress +const FLAGS_FD_PROGRESSUI: u32 = 0x4000; +/// transferred from unix, contains file mode +/// P.S. this flag is not used in windows +const FLAGS_FD_UNIX_MODE: u32 = 0x08; + +#[derive(Debug)] +pub(super) struct LocalFile { + pub path: PathBuf, + + pub handle: Option>, + pub offset: AtomicU64, + + pub name: String, + pub size: u64, + pub last_write_time: SystemTime, + pub is_dir: bool, + pub perm: u32, + pub read_only: bool, + pub hidden: bool, + pub system: bool, + pub archive: bool, + pub normal: bool, +} + +impl LocalFile { + pub fn try_open(path: &PathBuf) -> Result { + let mt = std::fs::metadata(path).map_err(|e| CliprdrError::FileError { + path: path.clone(), + err: e, + })?; + let size = mt.len() as u64; + let is_dir = mt.is_dir(); + let read_only = mt.permissions().readonly(); + let system = false; + let hidden = path.to_string_lossy().starts_with('.'); + let archive = false; + let normal = !(is_dir || read_only || system || hidden || archive); + let last_write_time = mt.modified().unwrap_or(SystemTime::UNIX_EPOCH); + + let perm = mt.permissions().mode(); + + let name = path + .display() + .to_string() + .trim_start_matches('/') + .replace('/', "\\"); + + // NOTE: open files lazily + let handle = None; + let offset = AtomicU64::new(0); + + Ok(Self { + name, + path: path.clone(), + handle, + offset, + size, + last_write_time, + is_dir, + read_only, + system, + hidden, + perm, + archive, + normal, + }) + } + pub fn as_bin(&self) -> Vec { + let mut buf = BytesMut::with_capacity(592); + + let read_only_flag = if self.read_only { 0x1 } else { 0 }; + let hidden_flag = if self.hidden { 0x2 } else { 0 }; + let system_flag = if self.system { 0x4 } else { 0 }; + let directory_flag = if self.is_dir { 0x10 } else { 0 }; + let archive_flag = if self.archive { 0x20 } else { 0 }; + let normal_flag = if self.normal { 0x80 } else { 0 }; + + let file_attributes: u32 = read_only_flag + | hidden_flag + | system_flag + | directory_flag + | archive_flag + | normal_flag; + + let win32_time = self + .last_write_time + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() as u64 + / 100 + + LDAP_EPOCH_DELTA; + + let size_high = (self.size >> 32) as u32; + let size_low = (self.size & (u32::MAX as u64)) as u32; + + let path = self.path.to_string_lossy().to_string(); + + let wstr: WString = WString::from(&path); + let name = wstr.as_bytes(); + + log::trace!( + "put file to list: name_len {}, name {}", + name.len(), + &self.name + ); + + let flags = FLAGS_FD_SIZE + | FLAGS_FD_LAST_WRITE + | FLAGS_FD_ATTRIBUTES + | FLAGS_FD_PROGRESSUI + | FLAGS_FD_UNIX_MODE; + + // flags, 4 bytes + buf.put_u32_le(flags); + // 32 bytes reserved + buf.put(&[0u8; 32][..]); + // file attributes, 4 bytes + buf.put_u32_le(file_attributes); + + // NOTE: this is not used in windows + // in the specification, this is 16 bytes reserved + // lets use the last 4 bytes to store the file mode + // + // 12 bytes reserved + buf.put(&[0u8; 12][..]); + // file permissions, 4 bytes + buf.put_u32_le(self.perm); + + // last write time, 8 bytes + buf.put_u64_le(win32_time); + // file size (high) + buf.put_u32_le(size_high); + // file size (low) + buf.put_u32_le(size_low); + // put name and padding to 520 bytes + let name_len = name.len(); + buf.put(name); + buf.put(&vec![0u8; 520 - name_len][..]); + + buf.to_vec() + } + + #[inline] + pub fn load_handle(&mut self) -> Result<(), CliprdrError> { + if !self.is_dir && self.handle.is_none() { + let handle = std::fs::File::open(&self.path).map_err(|e| CliprdrError::FileError { + path: self.path.clone(), + err: e, + })?; + let mut reader = BufReader::with_capacity(BLOCK_SIZE as usize * 2, handle); + reader.fill_buf().map_err(|e| CliprdrError::FileError { + path: self.path.clone(), + err: e, + })?; + self.handle = Some(reader); + }; + Ok(()) + } + + pub fn read_exact_at(&mut self, buf: &mut [u8], offset: u64) -> Result<(), CliprdrError> { + self.load_handle()?; + + let handle = self.handle.as_mut().unwrap(); + + if offset != self.offset.load(Ordering::Relaxed) { + handle + .seek(std::io::SeekFrom::Start(offset)) + .map_err(|e| CliprdrError::FileError { + path: self.path.clone(), + err: e, + })?; + } + handle + .read_exact(buf) + .map_err(|e| CliprdrError::FileError { + path: self.path.clone(), + err: e, + })?; + let new_offset = offset + (buf.len() as u64); + self.offset.store(new_offset, Ordering::Relaxed); + + // gc file handle + if new_offset >= self.size { + self.offset.store(0, Ordering::Relaxed); + self.handle = None; + } + + Ok(()) + } +} + +pub(super) fn construct_file_list(paths: &[PathBuf]) -> Result, CliprdrError> { + fn constr_file_lst( + path: &PathBuf, + file_list: &mut Vec, + visited: &mut HashSet, + ) -> Result<(), CliprdrError> { + // prevent fs loop + if visited.contains(path) { + return Ok(()); + } + visited.insert(path.clone()); + + let local_file = LocalFile::try_open(path)?; + file_list.push(local_file); + + let mt = std::fs::metadata(path).map_err(|e| CliprdrError::FileError { + path: path.clone(), + err: e, + })?; + + if mt.is_dir() { + let dir = std::fs::read_dir(path).unwrap(); + for entry in dir { + let entry = entry.unwrap(); + let path = entry.path(); + constr_file_lst(&path, file_list, visited)?; + } + } + Ok(()) + } + + let mut file_list = Vec::new(); + let mut visited = HashSet::new(); + + for path in paths { + constr_file_lst(path, &mut file_list, &mut visited)?; + } + Ok(file_list) +} + +#[cfg(test)] +mod file_list_test { + use std::{path::PathBuf, sync::atomic::AtomicU64}; + + use hbb_common::bytes::{BufMut, BytesMut}; + + use crate::{platform::fuse::FileDescription, CliprdrError}; + + use super::LocalFile; + + #[inline] + fn generate_tree(prefix: &str) -> Vec { + // generate a tree of local files, no handles + // - / + // |- a.txt + // |- b + // |- c.txt + #[inline] + fn generate_file(path: &str, name: &str, is_dir: bool) -> LocalFile { + LocalFile { + path: PathBuf::from(path), + handle: None, + name: name.to_string(), + size: 0, + offset: AtomicU64::new(0), + last_write_time: std::time::SystemTime::UNIX_EPOCH, + read_only: false, + is_dir, + perm: 0o754, + hidden: false, + system: false, + archive: false, + normal: false, + } + } + + let p = prefix; + + let (r_path, a_path, b_path, c_path) = if !prefix.is_empty() { + ( + p.to_string(), + format!("{}/a.txt", p), + format!("{}/b", p), + format!("{}/b/c.txt", p), + ) + } else { + ( + ".".to_string(), + "a.txt".to_string(), + "b".to_string(), + "b/c.txt".to_string(), + ) + }; + + let root = generate_file(&r_path, ".", true); + let a = generate_file(&a_path, "a.txt", false); + let b = generate_file(&b_path, "b", true); + let c = generate_file(&c_path, "c.txt", false); + + vec![root, a, b, c] + } + + fn as_bin_parse_test(prefix: &str) -> Result<(), CliprdrError> { + let tree = generate_tree(prefix); + let mut pdu = BytesMut::with_capacity(4 + 592 * tree.len()); + pdu.put_u32_le(tree.len() as u32); + for file in tree { + pdu.put(file.as_bin().as_slice()); + } + + let parsed = FileDescription::parse_file_descriptors(pdu.to_vec(), 0)?; + assert_eq!(parsed.len(), 4); + + if !prefix.is_empty() { + assert_eq!(parsed[0].name.to_str().unwrap(), format!("{}", prefix)); + assert_eq!( + parsed[1].name.to_str().unwrap(), + format!("{}/a.txt", prefix) + ); + assert_eq!(parsed[2].name.to_str().unwrap(), format!("{}/b", prefix)); + assert_eq!( + parsed[3].name.to_str().unwrap(), + format!("{}/b/c.txt", prefix) + ); + } else { + assert_eq!(parsed[0].name.to_str().unwrap(), "."); + assert_eq!(parsed[1].name.to_str().unwrap(), "a.txt"); + assert_eq!(parsed[2].name.to_str().unwrap(), "b"); + assert_eq!(parsed[3].name.to_str().unwrap(), "b/c.txt"); + } + + assert!(parsed[0].perm & 0o777 == 0o754); + assert!(parsed[1].perm & 0o777 == 0o754); + assert!(parsed[2].perm & 0o777 == 0o754); + assert!(parsed[3].perm & 0o777 == 0o754); + + Ok(()) + } + + #[test] + fn test_parse_file_descriptors() -> Result<(), CliprdrError> { + as_bin_parse_test("")?; + as_bin_parse_test("/")?; + as_bin_parse_test("test")?; + as_bin_parse_test("/test")?; + Ok(()) + } +} diff --git a/libs/clipboard/src/platform/unix/mod.rs b/libs/clipboard/src/platform/unix/mod.rs new file mode 100644 index 000000000..a58361f82 --- /dev/null +++ b/libs/clipboard/src/platform/unix/mod.rs @@ -0,0 +1,600 @@ +use std::{ + path::PathBuf, + sync::{mpsc::Sender, Arc}, + time::Duration, +}; + +use dashmap::DashMap; +use fuser::MountOption; +use hbb_common::{ + bytes::{BufMut, BytesMut}, + log, +}; +use lazy_static::lazy_static; +use parking_lot::Mutex; + +use crate::{ + platform::{fuse::FileDescription, unix::local_file::construct_file_list}, + send_data, ClipboardFile, CliprdrError, CliprdrServiceContext, +}; + +use self::local_file::LocalFile; +#[cfg(target_os = "linux")] +use self::url::{encode_path_to_uri, parse_plain_uri_list}; + +use super::fuse::FuseServer; + +#[cfg(target_os = "linux")] +/// clipboard implementation of x11 +pub mod x11; + +#[cfg(target_os = "macos")] +/// clipboard implementation of macos +pub mod ns_clipboard; + +pub mod local_file; + +#[cfg(target_os = "linux")] +pub mod url; + +// not actual format id, just a placeholder +const FILEDESCRIPTOR_FORMAT_ID: i32 = 49334; +const FILEDESCRIPTORW_FORMAT_NAME: &str = "FileGroupDescriptorW"; +// not actual format id, just a placeholder +const FILECONTENTS_FORMAT_ID: i32 = 49267; +const FILECONTENTS_FORMAT_NAME: &str = "FileContents"; + +lazy_static! { + static ref REMOTE_FORMAT_MAP: DashMap = DashMap::from_iter( + [ + ( + FILEDESCRIPTOR_FORMAT_ID, + FILEDESCRIPTORW_FORMAT_NAME.to_string() + ), + (FILECONTENTS_FORMAT_ID, FILECONTENTS_FORMAT_NAME.to_string()) + ] + .iter() + .cloned() + ); +} + +fn get_local_format(remote_id: i32) -> Option { + REMOTE_FORMAT_MAP.get(&remote_id).map(|s| s.clone()) +} + +fn add_remote_format(local_name: &str, remote_id: i32) { + REMOTE_FORMAT_MAP.insert(remote_id, local_name.to_string()); +} + +trait SysClipboard: Send + Sync { + fn start(&self); + + fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError>; + fn get_file_list(&self) -> Vec; +} + +#[cfg(target_os = "linux")] +fn get_sys_clipboard(ignore_path: &PathBuf) -> Result, CliprdrError> { + #[cfg(feature = "wayland")] + { + unimplemented!() + } + #[cfg(not(feature = "wayland"))] + { + use x11::*; + let x11_clip = X11Clipboard::new(ignore_path)?; + Ok(Box::new(x11_clip) as Box<_>) + } +} + +#[cfg(target_os = "macos")] +fn get_sys_clipboard(ignore_path: &PathBuf) -> Result, CliprdrError> { + use ns_clipboard::*; + let ns_pb = NsPasteboard::new(ignore_path)?; + Ok(Box::new(ns_pb) as Box<_>) +} + +#[derive(Debug)] +enum FileContentsRequest { + Size { + stream_id: i32, + file_idx: usize, + }, + + Range { + stream_id: i32, + file_idx: usize, + offset: u64, + length: u64, + }, +} + +pub struct ClipboardContext { + pub fuse_mount_point: PathBuf, + /// stores fuse background session handle + fuse_handle: Mutex>, + + /// a sender of clipboard file contents pdu to fuse server + fuse_tx: Sender, + fuse_server: Arc>, + + clipboard: Arc, + local_files: Mutex>, +} + +impl ClipboardContext { + pub fn new(timeout: Duration, mount_path: PathBuf) -> Result { + // assert mount path exists + let fuse_mount_point = mount_path.canonicalize().map_err(|e| { + log::error!("failed to canonicalize mount path: {:?}", e); + CliprdrError::CliprdrInit + })?; + + let (fuse_server, fuse_tx) = FuseServer::new(timeout); + + let fuse_server = Arc::new(Mutex::new(fuse_server)); + + let clipboard = get_sys_clipboard(&fuse_mount_point)?; + let clipboard = Arc::from(clipboard) as Arc<_>; + let local_files = Mutex::new(vec![]); + + Ok(Self { + fuse_mount_point, + fuse_server, + fuse_tx, + fuse_handle: Mutex::new(None), + clipboard, + local_files, + }) + } + + pub fn run(&self) -> Result<(), CliprdrError> { + if !self.is_stopped() { + return Ok(()); + } + + let mut fuse_handle = self.fuse_handle.lock(); + + let mount_path = &self.fuse_mount_point; + + let mnt_opts = [ + MountOption::FSName("rustdesk-cliprdr-fs".to_string()), + MountOption::NoAtime, + MountOption::RO, + ]; + log::info!( + "mounting clipboard FUSE to {}", + self.fuse_mount_point.display() + ); + + let new_handle = fuser::spawn_mount2( + FuseServer::client(self.fuse_server.clone()), + mount_path, + &mnt_opts, + ) + .map_err(|e| { + log::error!("failed to mount cliprdr fuse: {:?}", e); + CliprdrError::CliprdrInit + })?; + *fuse_handle = Some(new_handle); + + let clipboard = self.clipboard.clone(); + + std::thread::spawn(move || { + log::debug!("start listening clipboard"); + clipboard.start(); + }); + + Ok(()) + } + + /// set clipboard data from file list + pub fn set_clipboard(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> { + let prefix = self.fuse_mount_point.clone(); + let paths: Vec = paths.iter().cloned().map(|p| prefix.join(p)).collect(); + log::debug!("setting clipboard with paths: {:?}", paths); + self.clipboard.set_file_list(&paths)?; + log::debug!("clipboard set, paths: {:?}", paths); + Ok(()) + } + + fn serve_file_contents( + &self, + conn_id: i32, + request: FileContentsRequest, + ) -> Result<(), CliprdrError> { + let mut file_list = self.local_files.lock(); + + let (file_idx, file_contents_resp) = match request { + FileContentsRequest::Size { + stream_id, + file_idx, + } => { + log::debug!("file contents (size) requested from conn: {}", conn_id); + let Some(file) = file_list.get(file_idx) else { + log::error!( + "invalid file index {} requested from conn: {}", + file_idx, + conn_id + ); + resp_file_contents_fail(conn_id, stream_id); + + return Err(CliprdrError::InvalidRequest { + description: format!( + "invalid file index {} requested from conn: {}", + file_idx, conn_id + ), + }); + }; + + log::debug!( + "conn {} requested file-{}: {}", + conn_id, + file_idx, + file.name + ); + + let size = file.size; + ( + file_idx, + ClipboardFile::FileContentsResponse { + msg_flags: 0x1, + stream_id, + requested_data: size.to_le_bytes().to_vec(), + }, + ) + } + FileContentsRequest::Range { + stream_id, + file_idx, + offset, + length, + } => { + log::debug!( + "file contents (range from {} length {}) request from conn: {}", + offset, + length, + conn_id + ); + let Some(file) = file_list.get_mut(file_idx) else { + log::error!( + "invalid file index {} requested from conn: {}", + file_idx, + conn_id + ); + resp_file_contents_fail(conn_id, stream_id); + return Err(CliprdrError::InvalidRequest { + description: format!( + "invalid file index {} requested from conn: {}", + file_idx, conn_id + ), + }); + }; + log::debug!( + "conn {} requested file-{}: {}", + conn_id, + file_idx, + file.name + ); + + if offset > file.size { + log::error!("invalid reading offset requested from conn: {}", conn_id); + resp_file_contents_fail(conn_id, stream_id); + + return Err(CliprdrError::InvalidRequest { + description: format!( + "invalid reading offset requested from conn: {}", + conn_id + ), + }); + } + let read_size = if offset + length > file.size { + file.size - offset + } else { + length + }; + + let mut buf = vec![0u8; read_size as usize]; + + file.read_exact_at(&mut buf, offset)?; + + ( + file_idx, + ClipboardFile::FileContentsResponse { + msg_flags: 0x1, + stream_id, + requested_data: buf, + }, + ) + } + }; + + send_data(conn_id, file_contents_resp); + log::debug!("file contents sent to conn: {}", conn_id); + // hot reload next file + for next_file in file_list.iter_mut().skip(file_idx + 1) { + if !next_file.is_dir { + next_file.load_handle()?; + break; + } + } + Ok(()) + } +} + +fn resp_file_contents_fail(conn_id: i32, stream_id: i32) { + let resp = ClipboardFile::FileContentsResponse { + msg_flags: 0x2, + stream_id, + requested_data: vec![], + }; + send_data(conn_id, resp) +} + +impl ClipboardContext { + pub fn is_stopped(&self) -> bool { + self.fuse_handle.lock().is_none() + } + + pub fn sync_local_files(&self) -> Result<(), CliprdrError> { + let mut local_files = self.local_files.lock(); + let clipboard_files = self.clipboard.get_file_list(); + let local_file_list: Vec = local_files.iter().map(|f| f.path.clone()).collect(); + if local_file_list == clipboard_files { + return Ok(()); + } + let new_files = construct_file_list(&clipboard_files)?; + *local_files = new_files; + Ok(()) + } + + pub fn serve(&self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError> { + log::debug!("serve clipboard file from conn: {}", conn_id); + if self.is_stopped() { + log::debug!("cliprdr stopped, restart it"); + self.run()?; + } + match msg { + ClipboardFile::NotifyCallback { .. } => { + unreachable!() + } + ClipboardFile::MonitorReady => { + log::debug!("server_monitor_ready called"); + + self.send_file_list(conn_id)?; + + Ok(()) + } + + ClipboardFile::FormatList { format_list } => { + log::debug!("server_format_list called"); + // filter out "FileGroupDescriptorW" and "FileContents" + let fmt_lst: Vec<(i32, String)> = format_list + .into_iter() + .filter(|(_, name)| { + name == FILEDESCRIPTORW_FORMAT_NAME || name == FILECONTENTS_FORMAT_NAME + }) + .collect(); + if fmt_lst.len() != 2 { + log::debug!("no supported formats"); + return Ok(()); + } + log::debug!("supported formats: {:?}", fmt_lst); + let file_contents_id = fmt_lst + .iter() + .find(|(_, name)| name == FILECONTENTS_FORMAT_NAME) + .map(|(id, _)| *id) + .unwrap(); + let file_descriptor_id = fmt_lst + .iter() + .find(|(_, name)| name == FILEDESCRIPTORW_FORMAT_NAME) + .map(|(id, _)| *id) + .unwrap(); + + add_remote_format(FILECONTENTS_FORMAT_NAME, file_contents_id); + add_remote_format(FILEDESCRIPTORW_FORMAT_NAME, file_descriptor_id); + + // sync file system from peer + let data = ClipboardFile::FormatDataRequest { + requested_format_id: file_descriptor_id, + }; + send_data(conn_id, data); + + Ok(()) + } + ClipboardFile::FormatListResponse { msg_flags } => { + log::debug!("server_format_list_response called"); + if msg_flags != 0x1 { + send_format_list(conn_id) + } else { + Ok(()) + } + } + ClipboardFile::FormatDataRequest { + requested_format_id, + } => { + log::debug!("server_format_data_request called"); + let Some(format) = get_local_format(requested_format_id) else { + log::error!( + "got unsupported format data request: id={} from conn={}", + requested_format_id, + conn_id + ); + resp_format_data_failure(conn_id); + return Ok(()); + }; + + if format == FILEDESCRIPTORW_FORMAT_NAME { + self.send_file_list(conn_id)?; + } else if format == FILECONTENTS_FORMAT_NAME { + log::error!( + "try to read file contents with FormatDataRequest from conn={}", + conn_id + ); + resp_format_data_failure(conn_id); + } else { + log::error!( + "got unsupported format data request: id={} from conn={}", + requested_format_id, + conn_id + ); + resp_format_data_failure(conn_id); + } + Ok(()) + } + ClipboardFile::FormatDataResponse { + msg_flags, + format_data, + } => { + log::debug!( + "server_format_data_response called, msg_flags={}", + msg_flags + ); + + if msg_flags != 0x1 { + resp_format_data_failure(conn_id); + return Ok(()); + } + + log::debug!("parsing file descriptors"); + // this must be a file descriptor format data + let files = FileDescription::parse_file_descriptors(format_data, conn_id)?; + + let paths = { + let mut fuse_guard = self.fuse_server.lock(); + fuse_guard.load_file_list(files)?; + + fuse_guard.list_root() + }; + + log::debug!("load file list: {:?}", paths); + self.set_clipboard(&paths)?; + Ok(()) + } + ClipboardFile::FileContentsResponse { .. } => { + log::debug!("server_file_contents_response called"); + // we don't know its corresponding request, no resend can be performed + self.fuse_tx.send(msg).map_err(|e| { + log::error!("failed to send file contents response to fuse: {:?}", e); + CliprdrError::ClipboardInternalError + })?; + Ok(()) + } + ClipboardFile::FileContentsRequest { + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + .. + } => { + log::debug!("server_file_contents_request called"); + let fcr = if dw_flags == 0x1 { + FileContentsRequest::Size { + stream_id, + file_idx: list_index as usize, + } + } else if dw_flags == 0x2 { + let offset = (n_position_high as u64) << 32 | n_position_low as u64; + let length = cb_requested as u64; + + FileContentsRequest::Range { + stream_id, + file_idx: list_index as usize, + offset, + length, + } + } else { + log::error!("got invalid FileContentsRequest from conn={}", conn_id); + resp_file_contents_fail(conn_id, stream_id); + return Ok(()); + }; + + self.serve_file_contents(conn_id, fcr) + } + } + } + + fn send_file_list(&self, conn_id: i32) -> Result<(), CliprdrError> { + self.sync_local_files()?; + + let file_list = self.local_files.lock(); + send_file_list(&*file_list, conn_id) + } +} + +impl CliprdrServiceContext for ClipboardContext { + fn set_is_stopped(&mut self) -> Result<(), CliprdrError> { + // unmount the fuse + if let Some(fuse_handle) = self.fuse_handle.lock().take() { + fuse_handle.join(); + } + // we don't stop the clipboard, keep listening in case of restart + Ok(()) + } + + fn empty_clipboard(&mut self, _conn_id: i32) -> Result { + self.clipboard.set_file_list(&[])?; + Ok(true) + } + + fn server_clip_file(&mut self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError> { + self.serve(conn_id, msg) + } +} + +fn resp_format_data_failure(conn_id: i32) { + let data = ClipboardFile::FormatDataResponse { + msg_flags: 0x2, + format_data: vec![], + }; + send_data(conn_id, data) +} + +fn send_format_list(conn_id: i32) -> Result<(), CliprdrError> { + log::debug!("send format list to remote, conn={}", conn_id); + let fd_format_name = get_local_format(FILEDESCRIPTOR_FORMAT_ID) + .unwrap_or(FILEDESCRIPTORW_FORMAT_NAME.to_string()); + let fc_format_name = + get_local_format(FILECONTENTS_FORMAT_ID).unwrap_or(FILECONTENTS_FORMAT_NAME.to_string()); + let format_list = ClipboardFile::FormatList { + format_list: vec![ + (FILEDESCRIPTOR_FORMAT_ID, fd_format_name), + (FILECONTENTS_FORMAT_ID, fc_format_name), + ], + }; + + send_data(conn_id, format_list); + log::debug!("format list to remote dispatched, conn={}", conn_id); + Ok(()) +} + +fn build_file_list_pdu(files: &[LocalFile]) -> Vec { + let mut data = BytesMut::with_capacity(4 + 592 * files.len()); + data.put_u32_le(files.len() as u32); + for file in files.iter() { + data.put(file.as_bin().as_slice()); + } + + data.to_vec() +} + +fn send_file_list(files: &[LocalFile], conn_id: i32) -> Result<(), CliprdrError> { + log::debug!( + "send file list to remote, conn={}, list={:?}", + conn_id, + files.iter().map(|f| f.path.display()).collect::>() + ); + + let format_data = build_file_list_pdu(files); + + send_data( + conn_id, + ClipboardFile::FormatDataResponse { + msg_flags: 1, + format_data, + }, + ); + Ok(()) +} diff --git a/libs/clipboard/src/platform/unix/ns_clipboard.rs b/libs/clipboard/src/platform/unix/ns_clipboard.rs new file mode 100644 index 000000000..32c60a464 --- /dev/null +++ b/libs/clipboard/src/platform/unix/ns_clipboard.rs @@ -0,0 +1,97 @@ +use std::{collections::BTreeSet, path::PathBuf}; + +use cacao::pasteboard::{Pasteboard, PasteboardName}; +use hbb_common::log; +use parking_lot::Mutex; + +use crate::{platform::unix::send_format_list, CliprdrError}; + +use super::SysClipboard; + +#[inline] +fn wait_file_list() -> Option> { + let pb = Pasteboard::named(PasteboardName::General); + pb.get_file_urls() + .ok() + .map(|v| v.into_iter().map(|nsurl| nsurl.pathbuf()).collect()) +} + +#[inline] +fn set_file_list(file_list: &[PathBuf]) -> Result<(), CliprdrError> { + let pb = Pasteboard::named(PasteboardName::General); + pb.set_files(file_list.to_vec()) + .map_err(|_| CliprdrError::ClipboardInternalError) +} + +pub struct NsPasteboard { + ignore_path: PathBuf, + + former_file_list: Mutex>, +} + +impl NsPasteboard { + pub fn new(ignore_path: &PathBuf) -> Result { + Ok(Self { + ignore_path: ignore_path.to_owned(), + former_file_list: Mutex::new(vec![]), + }) + } +} + +impl SysClipboard for NsPasteboard { + fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> { + *self.former_file_list.lock() = paths.to_vec(); + set_file_list(paths) + } + + fn start(&self) { + { + *self.former_file_list.lock() = vec![]; + } + + loop { + let file_list = match wait_file_list() { + Some(v) => v, + None => { + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } + }; + + let filtered = file_list + .into_iter() + .filter(|pb| !pb.starts_with(&self.ignore_path)) + .collect::>(); + + if filtered.is_empty() { + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } + + { + let mut former = self.former_file_list.lock(); + + let filtered_st: BTreeSet<_> = filtered.iter().collect(); + let former_st = former.iter().collect::>(); + if filtered_st == former_st { + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } + + *former = filtered; + } + + if let Err(e) = send_format_list(0) { + log::warn!("failed to send format list: {}", e); + break; + } + + std::thread::sleep(std::time::Duration::from_millis(100)); + } + log::debug!("stop listening file related atoms on clipboard"); + } + + fn get_file_list(&self) -> Vec { + self.former_file_list.lock().clone() + } +} diff --git a/libs/clipboard/src/platform/unix/url.rs b/libs/clipboard/src/platform/unix/url.rs new file mode 100644 index 000000000..ce97810c4 --- /dev/null +++ b/libs/clipboard/src/platform/unix/url.rs @@ -0,0 +1,75 @@ +use std::path::{Path, PathBuf}; + +use crate::CliprdrError; + +// on x11, path will be encode as +// "/home/rustdesk/pictures/🖼️.png" -> "file:///home/rustdesk/pictures/%F0%9F%96%BC%EF%B8%8F.png" +// url encode and decode is needed +const ENCODE_SET: percent_encoding::AsciiSet = percent_encoding::CONTROLS.add(b' ').remove(b'/'); + +pub(super) fn encode_path_to_uri(path: &PathBuf) -> String { + let encoded = percent_encoding::percent_encode(path.to_str().unwrap().as_bytes(), &ENCODE_SET) + .to_string(); + format!("file://{}", encoded) +} + +pub(super) fn parse_uri_to_path(encoded_uri: &str) -> Result { + let encoded_path = encoded_uri.trim_start_matches("file://"); + let path_str = percent_encoding::percent_decode_str(encoded_path) + .decode_utf8() + .map_err(|_| CliprdrError::ConversionFailure)?; + let path_str = path_str.to_string(); + + Ok(Path::new(&path_str).to_path_buf()) +} + +// helper parse function +// convert 'text/uri-list' data to a list of valid Paths +// # Note +// - none utf8 data will lead to error +pub(super) fn parse_plain_uri_list(v: Vec) -> Result, CliprdrError> { + let text = String::from_utf8(v).map_err(|_| CliprdrError::ConversionFailure)?; + parse_uri_list(&text) +} + +// helper parse function +// convert 'text/uri-list' data to a list of valid Paths +// # Note +// - none utf8 data will lead to error +pub(super) fn parse_uri_list(text: &str) -> Result, CliprdrError> { + let mut list = Vec::new(); + + for line in text.lines() { + if !line.starts_with("file://") { + continue; + } + let decoded = parse_uri_to_path(line)?; + list.push(decoded) + } + Ok(list) +} + +#[cfg(test)] +mod uri_test { + #[test] + fn test_conversion() { + let path = std::path::PathBuf::from("/home/rustdesk/pictures/🖼️.png"); + let uri = super::encode_path_to_uri(&path); + assert_eq!( + uri, + "file:///home/rustdesk/pictures/%F0%9F%96%BC%EF%B8%8F.png" + ); + let convert_back = super::parse_uri_to_path(&uri).unwrap(); + assert_eq!(path, convert_back); + } + + #[test] + fn parse_list() { + let uri_list = r#"file:///home/rustdesk/pictures/%F0%9F%96%BC%EF%B8%8F.png +file:///home/rustdesk/pictures/%F0%9F%96%BC%EF%B8%8F.png +"#; + let list = super::parse_uri_list(uri_list.into()).unwrap(); + assert!(list.len() == 2); + assert_eq!(list[0], list[1]); + } +} diff --git a/libs/clipboard/src/platform/unix/x11.rs b/libs/clipboard/src/platform/unix/x11.rs new file mode 100644 index 000000000..4ca3a2c0b --- /dev/null +++ b/libs/clipboard/src/platform/unix/x11.rs @@ -0,0 +1,162 @@ +use std::{collections::BTreeSet, path::PathBuf}; + +use hbb_common::log; +use once_cell::sync::OnceCell; +use parking_lot::Mutex; +use x11_clipboard::Clipboard; +use x11rb::protocol::xproto::Atom; + +use crate::{platform::unix::send_format_list, CliprdrError}; + +use super::{encode_path_to_uri, parse_plain_uri_list, SysClipboard}; + +static X11_CLIPBOARD: OnceCell = OnceCell::new(); + +fn get_clip() -> Result<&'static Clipboard, CliprdrError> { + X11_CLIPBOARD.get_or_try_init(|| Clipboard::new().map_err(|_| CliprdrError::CliprdrInit)) +} + +pub struct X11Clipboard { + ignore_path: PathBuf, + text_uri_list: Atom, + gnome_copied_files: Atom, + nautilus_clipboard: Atom, + + former_file_list: Mutex>, +} + +impl X11Clipboard { + pub fn new(ignore_path: &PathBuf) -> Result { + let clipboard = get_clip()?; + let text_uri_list = clipboard + .setter + .get_atom("text/uri-list") + .map_err(|_| CliprdrError::CliprdrInit)?; + let gnome_copied_files = clipboard + .setter + .get_atom("x-special/gnome-copied-files") + .map_err(|_| CliprdrError::CliprdrInit)?; + let nautilus_clipboard = clipboard + .setter + .get_atom("x-special/nautilus-clipboard") + .map_err(|_| CliprdrError::CliprdrInit)?; + Ok(Self { + ignore_path: ignore_path.to_owned(), + text_uri_list, + gnome_copied_files, + nautilus_clipboard, + former_file_list: Mutex::new(vec![]), + }) + } + + fn load(&self, target: Atom) -> Result, CliprdrError> { + let clip = get_clip()?.setter.atoms.clipboard; + let prop = get_clip()?.setter.atoms.property; + // NOTE: + // # why not use `load_wait` + // load_wait is likely to wait forever, which is not what we want + let res = get_clip()?.load_wait(clip, target, prop); + match res { + Ok(res) => Ok(res), + Err(x11_clipboard::error::Error::UnexpectedType(_)) => Ok(vec![]), + Err(x11_clipboard::error::Error::Timeout) => { + log::debug!("x11 clipboard get content timeout."); + Err(CliprdrError::ClipboardInternalError) + } + Err(e) => { + log::debug!("x11 clipboard get content fail: {:?}", e); + Err(CliprdrError::ClipboardInternalError) + } + } + } + + fn store_batch(&self, batch: Vec<(Atom, Vec)>) -> Result<(), CliprdrError> { + let clip = get_clip()?.setter.atoms.clipboard; + log::debug!("try to store clipboard content"); + get_clip()? + .store_batch(clip, batch) + .map_err(|_| CliprdrError::ClipboardInternalError) + } + + fn wait_file_list(&self) -> Result>, CliprdrError> { + let v = self.load(self.text_uri_list)?; + let p = parse_plain_uri_list(v)?; + Ok(Some(p)) + } +} + +impl SysClipboard for X11Clipboard { + fn set_file_list(&self, paths: &[PathBuf]) -> Result<(), CliprdrError> { + *self.former_file_list.lock() = paths.to_vec(); + + let uri_list: Vec = paths.iter().map(encode_path_to_uri).collect(); + let uri_list = uri_list.join("\n"); + let text_uri_list_data = uri_list.as_bytes().to_vec(); + let gnome_copied_files_data = ["copy\n".as_bytes(), uri_list.as_bytes()].concat(); + let batch = vec![ + (self.text_uri_list, text_uri_list_data), + (self.gnome_copied_files, gnome_copied_files_data.clone()), + (self.nautilus_clipboard, gnome_copied_files_data), + ]; + self.store_batch(batch) + .map_err(|_| CliprdrError::ClipboardInternalError) + } + + fn start(&self) { + { + // clear cached file list + *self.former_file_list.lock() = vec![]; + } + loop { + let sth = match self.wait_file_list() { + Ok(sth) => sth, + Err(e) => { + log::warn!("failed to get file list from clipboard: {}", e); + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } + }; + + let Some(paths) = sth else { + // just sleep + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + }; + + let filtered = paths + .into_iter() + .filter(|pb| !pb.starts_with(&self.ignore_path)) + .collect::>(); + + if filtered.is_empty() { + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } + + { + let mut former = self.former_file_list.lock(); + + let filtered_st: BTreeSet<_> = filtered.iter().collect(); + let former_st = former.iter().collect::>(); + if filtered_st == former_st { + std::thread::sleep(std::time::Duration::from_millis(100)); + continue; + } + + *former = filtered; + } + + if let Err(e) = send_format_list(0) { + log::warn!("failed to send format list: {}", e); + break; + } + + std::thread::sleep(std::time::Duration::from_millis(100)); + } + log::debug!("stop listening file related atoms on clipboard"); + } + + fn get_file_list(&self) -> Vec { + self.former_file_list.lock().clone() + } +} diff --git a/libs/clipboard/src/platform/windows.rs b/libs/clipboard/src/platform/windows.rs new file mode 100644 index 000000000..1148112b5 --- /dev/null +++ b/libs/clipboard/src/platform/windows.rs @@ -0,0 +1,1219 @@ +//! windows implementation +#![allow(dead_code)] +#![allow(non_camel_case_types)] +#![allow(unused_variables)] +#![allow(non_snake_case)] +#![allow(deref_nullptr)] + +use std::{ + boxed::Box, + ffi::{CStr, CString}, + result::Result, +}; +use crate::{ + allow_err, send_data, ClipboardFile, CliprdrError, CliprdrServiceContext, ResultType, + ERR_CODE_INVALID_PARAMETER, ERR_CODE_SERVER_FUNCTION_NONE, VEC_MSG_CHANNEL, +}; +use hbb_common::log; + +// only used error code will be recorded here +/// success +const CHANNEL_RC_OK: u32 = 0; +/// error code from WinError.h +/// success +const ERROR_SUCCESS: u32 = 0; +/// allocation failure +const CHANNEL_RC_NO_MEMORY: u32 = 12; +/// error code from WinError.h +/// used by FreeRDP to represent errors. +const ERROR_INTERNAL_ERROR: u32 = 0x54F; + +pub type size_t = ::std::os::raw::c_ulonglong; +pub type __vcrt_bool = bool; +pub type wchar_t = ::std::os::raw::c_ushort; + +pub type POINTER_64_INT = ::std::os::raw::c_ulonglong; +pub type INT8 = ::std::os::raw::c_schar; +pub type PINT8 = *mut ::std::os::raw::c_schar; +pub type INT16 = ::std::os::raw::c_short; +pub type PINT16 = *mut ::std::os::raw::c_short; +pub type INT32 = ::std::os::raw::c_int; +pub type PINT32 = *mut ::std::os::raw::c_int; +pub type INT64 = ::std::os::raw::c_longlong; +pub type PINT64 = *mut ::std::os::raw::c_longlong; +pub type UINT8 = ::std::os::raw::c_uchar; +pub type PUINT8 = *mut ::std::os::raw::c_uchar; +pub type UINT16 = ::std::os::raw::c_ushort; +pub type PUINT16 = *mut ::std::os::raw::c_ushort; +pub type UINT32 = ::std::os::raw::c_uint; +pub type PUINT32 = *mut ::std::os::raw::c_uint; +pub type UINT64 = ::std::os::raw::c_ulonglong; +pub type PUINT64 = *mut ::std::os::raw::c_ulonglong; +pub type LONG32 = ::std::os::raw::c_int; +pub type PLONG32 = *mut ::std::os::raw::c_int; +pub type ULONG32 = ::std::os::raw::c_uint; +pub type PULONG32 = *mut ::std::os::raw::c_uint; +pub type DWORD32 = ::std::os::raw::c_uint; +pub type PDWORD32 = *mut ::std::os::raw::c_uint; +pub type INT_PTR = ::std::os::raw::c_longlong; +pub type PINT_PTR = *mut ::std::os::raw::c_longlong; +pub type UINT_PTR = ::std::os::raw::c_ulonglong; +pub type PUINT_PTR = *mut ::std::os::raw::c_ulonglong; +pub type LONG_PTR = ::std::os::raw::c_longlong; +pub type PLONG_PTR = *mut ::std::os::raw::c_longlong; +pub type ULONG_PTR = ::std::os::raw::c_ulonglong; +pub type PULONG_PTR = *mut ::std::os::raw::c_ulonglong; +pub type SHANDLE_PTR = ::std::os::raw::c_longlong; +pub type HANDLE_PTR = ::std::os::raw::c_ulonglong; +pub type UHALF_PTR = ::std::os::raw::c_uint; +pub type PUHALF_PTR = *mut ::std::os::raw::c_uint; +pub type HALF_PTR = ::std::os::raw::c_int; +pub type PHALF_PTR = *mut ::std::os::raw::c_int; +pub type SIZE_T = ULONG_PTR; +pub type PSIZE_T = *mut ULONG_PTR; +pub type SSIZE_T = LONG_PTR; +pub type PSSIZE_T = *mut LONG_PTR; +pub type DWORD_PTR = ULONG_PTR; +pub type PDWORD_PTR = *mut ULONG_PTR; +pub type LONG64 = ::std::os::raw::c_longlong; +pub type PLONG64 = *mut ::std::os::raw::c_longlong; +pub type ULONG64 = ::std::os::raw::c_ulonglong; +pub type PULONG64 = *mut ::std::os::raw::c_ulonglong; +pub type DWORD64 = ::std::os::raw::c_ulonglong; +pub type PDWORD64 = *mut ::std::os::raw::c_ulonglong; +pub type KAFFINITY = ULONG_PTR; +pub type PKAFFINITY = *mut KAFFINITY; +pub type PVOID = *mut ::std::os::raw::c_void; +pub type CHAR = ::std::os::raw::c_char; +pub type SHORT = ::std::os::raw::c_short; +pub type LONG = ::std::os::raw::c_long; +pub type WCHAR = wchar_t; +pub type PWCHAR = *mut WCHAR; +pub type LPWCH = *mut WCHAR; +pub type PWCH = *mut WCHAR; +pub type LPCWCH = *const WCHAR; +pub type PCWCH = *const WCHAR; +pub type NWPSTR = *mut WCHAR; +pub type LPWSTR = *mut WCHAR; +pub type PWSTR = *mut WCHAR; +pub type PZPWSTR = *mut PWSTR; +pub type PCZPWSTR = *const PWSTR; +pub type LPUWSTR = *mut WCHAR; +pub type PUWSTR = *mut WCHAR; +pub type LPCWSTR = *const WCHAR; +pub type PCWSTR = *const WCHAR; +pub type PZPCWSTR = *mut PCWSTR; +pub type PCZPCWSTR = *const PCWSTR; +pub type LPCUWSTR = *const WCHAR; +pub type PCUWSTR = *const WCHAR; +pub type PZZWSTR = *mut WCHAR; +pub type PCZZWSTR = *const WCHAR; +pub type PUZZWSTR = *mut WCHAR; +pub type PCUZZWSTR = *const WCHAR; +pub type PNZWCH = *mut WCHAR; +pub type PCNZWCH = *const WCHAR; +pub type PUNZWCH = *mut WCHAR; +pub type PCUNZWCH = *const WCHAR; +pub type PCHAR = *mut CHAR; +pub type LPCH = *mut CHAR; +pub type PCH = *mut CHAR; +pub type LPCCH = *const CHAR; +pub type PCCH = *const CHAR; +pub type NPSTR = *mut CHAR; +pub type LPSTR = *mut CHAR; +pub type PSTR = *mut CHAR; +pub type PZPSTR = *mut PSTR; +pub type PCZPSTR = *const PSTR; +pub type LPCSTR = *const CHAR; +pub type PCSTR = *const CHAR; +pub type PZPCSTR = *mut PCSTR; +pub type PCZPCSTR = *const PCSTR; +pub type PZZSTR = *mut CHAR; +pub type PCZZSTR = *const CHAR; +pub type PNZCH = *mut CHAR; +pub type PCNZCH = *const CHAR; +pub type TCHAR = ::std::os::raw::c_char; +pub type PTCHAR = *mut ::std::os::raw::c_char; +pub type TBYTE = ::std::os::raw::c_uchar; +pub type PTBYTE = *mut ::std::os::raw::c_uchar; +pub type LPTCH = LPCH; +pub type PTCH = LPCH; +pub type LPCTCH = LPCCH; +pub type PCTCH = LPCCH; +pub type PTSTR = LPSTR; +pub type LPTSTR = LPSTR; +pub type PUTSTR = LPSTR; +pub type LPUTSTR = LPSTR; +pub type PCTSTR = LPCSTR; +pub type LPCTSTR = LPCSTR; +pub type PCUTSTR = LPCSTR; +pub type LPCUTSTR = LPCSTR; +pub type PZZTSTR = PZZSTR; +pub type PUZZTSTR = PZZSTR; +pub type PCZZTSTR = PCZZSTR; +pub type PCUZZTSTR = PCZZSTR; +pub type PZPTSTR = PZPSTR; +pub type PNZTCH = PNZCH; +pub type PUNZTCH = PNZCH; +pub type PCNZTCH = PCNZCH; +pub type PCUNZTCH = PCNZCH; +pub type PSHORT = *mut SHORT; +pub type PLONG = *mut LONG; +pub type ULONG = ::std::os::raw::c_ulong; +pub type PULONG = *mut ULONG; +pub type USHORT = ::std::os::raw::c_ushort; +pub type PUSHORT = *mut USHORT; +pub type UCHAR = ::std::os::raw::c_uchar; +pub type PUCHAR = *mut UCHAR; +pub type PSZ = *mut ::std::os::raw::c_char; +pub type DWORD = ::std::os::raw::c_ulong; +pub type BOOL = ::std::os::raw::c_int; +pub type BYTE = ::std::os::raw::c_uchar; +pub type WORD = ::std::os::raw::c_ushort; +pub type FLOAT = f32; +pub type PFLOAT = *mut FLOAT; +pub type PBOOL = *mut BOOL; +pub type LPBOOL = *mut BOOL; +pub type PBYTE = *mut BYTE; +pub type LPBYTE = *mut BYTE; +pub type PINT = *mut ::std::os::raw::c_int; +pub type LPINT = *mut ::std::os::raw::c_int; +pub type PWORD = *mut WORD; +pub type LPWORD = *mut WORD; +pub type LPLONG = *mut ::std::os::raw::c_long; +pub type PDWORD = *mut DWORD; +pub type LPDWORD = *mut DWORD; +pub type LPVOID = *mut ::std::os::raw::c_void; +pub type LPCVOID = *const ::std::os::raw::c_void; +pub type INT = ::std::os::raw::c_int; +pub type UINT = ::std::os::raw::c_uint; +pub type PUINT = *mut ::std::os::raw::c_uint; +pub type va_list = *mut ::std::os::raw::c_char; + +pub const TRUE: ::std::os::raw::c_int = 1; +pub const FALSE: ::std::os::raw::c_int = 0; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_HEADER { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, +} +pub type CLIPRDR_HEADER = _CLIPRDR_HEADER; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_CAPABILITY_SET { + pub capabilitySetType: UINT16, + pub capabilitySetLength: UINT16, +} +pub type CLIPRDR_CAPABILITY_SET = _CLIPRDR_CAPABILITY_SET; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_GENERAL_CAPABILITY_SET { + pub capabilitySetType: UINT16, + pub capabilitySetLength: UINT16, + pub version: UINT32, + pub generalFlags: UINT32, +} +pub type CLIPRDR_GENERAL_CAPABILITY_SET = _CLIPRDR_GENERAL_CAPABILITY_SET; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_CAPABILITIES { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub cCapabilitiesSets: UINT32, + pub capabilitySets: *mut CLIPRDR_CAPABILITY_SET, +} +pub type CLIPRDR_CAPABILITIES = _CLIPRDR_CAPABILITIES; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_MONITOR_READY { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, +} +pub type CLIPRDR_MONITOR_READY = _CLIPRDR_MONITOR_READY; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_TEMP_DIRECTORY { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub szTempDir: [::std::os::raw::c_char; 520usize], +} +pub type CLIPRDR_TEMP_DIRECTORY = _CLIPRDR_TEMP_DIRECTORY; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_FORMAT { + pub formatId: UINT32, + pub formatName: *mut ::std::os::raw::c_char, +} +pub type CLIPRDR_FORMAT = _CLIPRDR_FORMAT; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_FORMAT_LIST { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub numFormats: UINT32, + pub formats: *mut CLIPRDR_FORMAT, +} +pub type CLIPRDR_FORMAT_LIST = _CLIPRDR_FORMAT_LIST; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_FORMAT_LIST_RESPONSE { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, +} +pub type CLIPRDR_FORMAT_LIST_RESPONSE = _CLIPRDR_FORMAT_LIST_RESPONSE; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_LOCK_CLIPBOARD_DATA { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub clipDataId: UINT32, +} +pub type CLIPRDR_LOCK_CLIPBOARD_DATA = _CLIPRDR_LOCK_CLIPBOARD_DATA; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_UNLOCK_CLIPBOARD_DATA { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub clipDataId: UINT32, +} +pub type CLIPRDR_UNLOCK_CLIPBOARD_DATA = _CLIPRDR_UNLOCK_CLIPBOARD_DATA; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_FORMAT_DATA_REQUEST { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub requestedFormatId: UINT32, +} +pub type CLIPRDR_FORMAT_DATA_REQUEST = _CLIPRDR_FORMAT_DATA_REQUEST; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_FORMAT_DATA_RESPONSE { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub requestedFormatData: *const BYTE, +} +pub type CLIPRDR_FORMAT_DATA_RESPONSE = _CLIPRDR_FORMAT_DATA_RESPONSE; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_FILE_CONTENTS_REQUEST { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub streamId: UINT32, + pub listIndex: UINT32, + pub dwFlags: UINT32, + pub nPositionLow: UINT32, + pub nPositionHigh: UINT32, + pub cbRequested: UINT32, + pub haveClipDataId: BOOL, + pub clipDataId: UINT32, +} +pub type CLIPRDR_FILE_CONTENTS_REQUEST = _CLIPRDR_FILE_CONTENTS_REQUEST; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _CLIPRDR_FILE_CONTENTS_RESPONSE { + pub connID: UINT32, + pub msgType: UINT16, + pub msgFlags: UINT16, + pub dataLen: UINT32, + pub streamId: UINT32, + pub cbRequested: UINT32, + pub requestedData: *const BYTE, +} +pub type CLIPRDR_FILE_CONTENTS_RESPONSE = _CLIPRDR_FILE_CONTENTS_RESPONSE; +pub type CliprdrClientContext = _cliprdr_client_context; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _NOTIFICATION_MESSAGE { + pub r#type: UINT32, // 0 - info, 1 - warning, 2 - error + pub msg: *const BYTE, + pub details: *const BYTE, +} +pub type NOTIFICATION_MESSAGE = _NOTIFICATION_MESSAGE; +pub type pcCliprdrServerCapabilities = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + capabilities: *const CLIPRDR_CAPABILITIES, + ) -> UINT, +>; +pub type pcCliprdrClientCapabilities = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + capabilities: *const CLIPRDR_CAPABILITIES, + ) -> UINT, +>; +pub type pcCliprdrMonitorReady = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + monitorReady: *const CLIPRDR_MONITOR_READY, + ) -> UINT, +>; +pub type pcCliprdrTempDirectory = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + tempDirectory: *const CLIPRDR_TEMP_DIRECTORY, + ) -> UINT, +>; +pub type pcNotifyClipboardMsg = ::std::option::Option< + unsafe extern "C" fn(connID: UINT32, msg: *const NOTIFICATION_MESSAGE) -> UINT, +>; +pub type pcCliprdrClientFormatList = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + formatList: *const CLIPRDR_FORMAT_LIST, + ) -> UINT, +>; +pub type pcCliprdrServerFormatList = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + formatList: *const CLIPRDR_FORMAT_LIST, + ) -> UINT, +>; +pub type pcCliprdrClientFormatListResponse = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + formatListResponse: *const CLIPRDR_FORMAT_LIST_RESPONSE, + ) -> UINT, +>; +pub type pcCliprdrServerFormatListResponse = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + formatListResponse: *const CLIPRDR_FORMAT_LIST_RESPONSE, + ) -> UINT, +>; +pub type pcCliprdrClientLockClipboardData = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + lockClipboardData: *const CLIPRDR_LOCK_CLIPBOARD_DATA, + ) -> UINT, +>; +pub type pcCliprdrServerLockClipboardData = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + lockClipboardData: *const CLIPRDR_LOCK_CLIPBOARD_DATA, + ) -> UINT, +>; +pub type pcCliprdrClientUnlockClipboardData = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + unlockClipboardData: *const CLIPRDR_UNLOCK_CLIPBOARD_DATA, + ) -> UINT, +>; +pub type pcCliprdrServerUnlockClipboardData = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + unlockClipboardData: *const CLIPRDR_UNLOCK_CLIPBOARD_DATA, + ) -> UINT, +>; +pub type pcCliprdrClientFormatDataRequest = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + formatDataRequest: *const CLIPRDR_FORMAT_DATA_REQUEST, + ) -> UINT, +>; +pub type pcCliprdrServerFormatDataRequest = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + formatDataRequest: *const CLIPRDR_FORMAT_DATA_REQUEST, + ) -> UINT, +>; +pub type pcCliprdrClientFormatDataResponse = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + formatDataResponse: *const CLIPRDR_FORMAT_DATA_RESPONSE, + ) -> UINT, +>; +pub type pcCliprdrServerFormatDataResponse = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + formatDataResponse: *const CLIPRDR_FORMAT_DATA_RESPONSE, + ) -> UINT, +>; +pub type pcCliprdrClientFileContentsRequest = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + fileContentsRequest: *const CLIPRDR_FILE_CONTENTS_REQUEST, + ) -> UINT, +>; +pub type pcCliprdrServerFileContentsRequest = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + fileContentsRequest: *const CLIPRDR_FILE_CONTENTS_REQUEST, + ) -> UINT, +>; +pub type pcCliprdrClientFileContentsResponse = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + fileContentsResponse: *const CLIPRDR_FILE_CONTENTS_RESPONSE, + ) -> UINT, +>; +pub type pcCliprdrServerFileContentsResponse = ::std::option::Option< + unsafe extern "C" fn( + context: *mut CliprdrClientContext, + fileContentsResponse: *const CLIPRDR_FILE_CONTENTS_RESPONSE, + ) -> UINT, +>; + +// TODO: hide more members of clipboard context +#[repr(C)] +#[derive(Debug, Clone)] +pub struct _cliprdr_client_context { + pub Custom: *mut ::std::os::raw::c_void, + pub EnableFiles: BOOL, + pub EnableOthers: BOOL, + pub IsStopped: BOOL, + pub ResponseWaitTimeoutSecs: UINT32, + pub ServerCapabilities: pcCliprdrServerCapabilities, + pub ClientCapabilities: pcCliprdrClientCapabilities, + pub MonitorReady: pcCliprdrMonitorReady, + pub TempDirectory: pcCliprdrTempDirectory, + pub NotifyClipboardMsg: pcNotifyClipboardMsg, + pub ClientFormatList: pcCliprdrClientFormatList, + pub ServerFormatList: pcCliprdrServerFormatList, + pub ClientFormatListResponse: pcCliprdrClientFormatListResponse, + pub ServerFormatListResponse: pcCliprdrServerFormatListResponse, + pub ClientLockClipboardData: pcCliprdrClientLockClipboardData, + pub ServerLockClipboardData: pcCliprdrServerLockClipboardData, + pub ClientUnlockClipboardData: pcCliprdrClientUnlockClipboardData, + pub ServerUnlockClipboardData: pcCliprdrServerUnlockClipboardData, + pub ClientFormatDataRequest: pcCliprdrClientFormatDataRequest, + pub ServerFormatDataRequest: pcCliprdrServerFormatDataRequest, + pub ClientFormatDataResponse: pcCliprdrClientFormatDataResponse, + pub ServerFormatDataResponse: pcCliprdrServerFormatDataResponse, + pub ClientFileContentsRequest: pcCliprdrClientFileContentsRequest, + pub ServerFileContentsRequest: pcCliprdrServerFileContentsRequest, + pub ClientFileContentsResponse: pcCliprdrClientFileContentsResponse, + pub ServerFileContentsResponse: pcCliprdrServerFileContentsResponse, + pub LastRequestedFormatId: UINT32, +} + +// #[link(name = "user32")] +// #[link(name = "ole32")] +extern "C" { + pub(crate) fn init_cliprdr(context: *mut CliprdrClientContext) -> BOOL; + pub(crate) fn uninit_cliprdr(context: *mut CliprdrClientContext) -> BOOL; + pub(crate) fn empty_cliprdr(context: *mut CliprdrClientContext, connID: UINT32) -> BOOL; +} + +unsafe impl Send for CliprdrClientContext {} + +unsafe impl Sync for CliprdrClientContext {} + +impl CliprdrClientContext { + pub fn create( + enable_files: bool, + enable_others: bool, + response_wait_timeout_secs: u32, + notify_callback: pcNotifyClipboardMsg, + client_format_list: pcCliprdrClientFormatList, + client_format_list_response: pcCliprdrClientFormatListResponse, + client_format_data_request: pcCliprdrClientFormatDataRequest, + client_format_data_response: pcCliprdrClientFormatDataResponse, + client_file_contents_request: pcCliprdrClientFileContentsRequest, + client_file_contents_response: pcCliprdrClientFileContentsResponse, + ) -> Result, CliprdrError> { + let context = CliprdrClientContext { + Custom: 0 as *mut _, + EnableFiles: if enable_files { TRUE } else { FALSE }, + EnableOthers: if enable_others { TRUE } else { FALSE }, + IsStopped: FALSE, + ResponseWaitTimeoutSecs: response_wait_timeout_secs, + ServerCapabilities: None, + ClientCapabilities: None, + MonitorReady: None, + TempDirectory: None, + NotifyClipboardMsg: notify_callback, + ClientFormatList: client_format_list, + ServerFormatList: None, + ClientFormatListResponse: client_format_list_response, + ServerFormatListResponse: None, + ClientLockClipboardData: None, + ServerLockClipboardData: None, + ClientUnlockClipboardData: None, + ServerUnlockClipboardData: None, + ClientFormatDataRequest: client_format_data_request, + ServerFormatDataRequest: None, + ClientFormatDataResponse: client_format_data_response, + ServerFormatDataResponse: None, + ClientFileContentsRequest: client_file_contents_request, + ServerFileContentsRequest: None, + ClientFileContentsResponse: client_file_contents_response, + ServerFileContentsResponse: None, + LastRequestedFormatId: 0, + }; + let mut context = Box::new(context); + unsafe { + if FALSE == init_cliprdr(&mut (*context)) { + println!("Failed to init cliprdr"); + Err(CliprdrError::CliprdrInit) + } else { + Ok(context) + } + } + } +} + +impl Drop for CliprdrClientContext { + fn drop(&mut self) { + unsafe { + if FALSE == uninit_cliprdr(&mut *self) { + println!("Failed to uninit cliprdr"); + } else { + println!("Succeeded to uninit cliprdr"); + } + } + } +} + +impl CliprdrServiceContext for CliprdrClientContext { + fn set_is_stopped(&mut self) -> Result<(), CliprdrError> { + self.IsStopped = TRUE; + Ok(()) + } + + fn empty_clipboard(&mut self, conn_id: i32) -> Result { + Ok(empty_clipboard(self, conn_id)) + } + + fn server_clip_file(&mut self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError> { + let ret = server_clip_file(self, conn_id, msg); + ret_to_result(ret) + } +} + +fn ret_to_result(ret: u32) -> Result<(), CliprdrError> { + match ret { + #[allow(unreachable_patterns)] + // CHANNEL_RC_OK is unreachable, but ignore it + ERROR_SUCCESS | CHANNEL_RC_OK => Ok(()), + CHANNEL_RC_NO_MEMORY => Err(CliprdrError::CliprdrOutOfMemory), + ERROR_INTERNAL_ERROR => Err(CliprdrError::ClipboardInternalError), + e => Err(CliprdrError::Unknown(e)), + } +} +pub fn empty_clipboard(context: &mut CliprdrClientContext, conn_id: i32) -> bool { + unsafe { TRUE == empty_cliprdr(context, conn_id as u32) } +} + +pub fn server_clip_file( + context: &mut CliprdrClientContext, + conn_id: i32, + msg: ClipboardFile, +) -> u32 { + let mut ret = 0; + match msg { + ClipboardFile::NotifyCallback { .. } => { + // unreachable + } + ClipboardFile::MonitorReady => { + log::debug!("server_monitor_ready called"); + ret = server_monitor_ready(context, conn_id); + log::debug!( + "server_monitor_ready called, conn_id {}, return {}", + conn_id, + ret + ); + } + ClipboardFile::FormatList { format_list } => { + log::debug!( + "server_format_list called, conn_id {}, format_list: {:?}", + conn_id, + &format_list + ); + ret = server_format_list(context, conn_id, format_list); + log::debug!( + "server_format_list called, conn_id {}, return {}", + conn_id, + ret + ); + } + ClipboardFile::FormatListResponse { msg_flags } => { + log::debug!("server_format_list_response called"); + ret = server_format_list_response(context, conn_id, msg_flags); + log::debug!( + "server_format_list_response called, conn_id {}, msg_flags {}, return {}", + conn_id, + msg_flags, + ret + ); + } + ClipboardFile::FormatDataRequest { + requested_format_id, + } => { + log::debug!("server_format_data_request called"); + ret = server_format_data_request(context, conn_id, requested_format_id); + log::debug!( + "server_format_data_request called, conn_id {}, requested_format_id {}, return {}", + conn_id, + requested_format_id, + ret + ); + } + ClipboardFile::FormatDataResponse { + msg_flags, + format_data, + } => { + log::debug!("server_format_data_response called"); + ret = server_format_data_response(context, conn_id, msg_flags, format_data); + log::debug!( + "server_format_data_response called, conn_id {}, msg_flags: {}, return {}", + conn_id, + msg_flags, + ret + ); + } + ClipboardFile::FileContentsRequest { + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + } => { + log::debug!("server_file_contents_request called"); + ret = server_file_contents_request( + context, + conn_id, + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + ); + log::debug!("server_file_contents_request called, conn_id {}, stream_id: {}, list_index {}, dw_flags {}, n_position_low {}, n_position_high {}, cb_requested {}, have_clip_data_id {}, clip_data_id {}, return {}", conn_id, + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + ret + ); + } + ClipboardFile::FileContentsResponse { + msg_flags, + stream_id, + requested_data, + } => { + log::debug!("server_file_contents_response called"); + ret = server_file_contents_response( + context, + conn_id, + msg_flags, + stream_id, + requested_data, + ); + log::debug!("server_file_contents_response called, conn_id {}, msg_flags {}, stream_id {}, return {}", + conn_id, + msg_flags, + stream_id, + ret + ); + } + } + ret +} + +pub fn server_monitor_ready(context: &mut CliprdrClientContext, conn_id: i32) -> u32 { + unsafe { + let monitor_ready = CLIPRDR_MONITOR_READY { + connID: conn_id as UINT32, + msgType: 0 as UINT16, + msgFlags: 0 as UINT16, + dataLen: 0 as UINT32, + }; + if let Some(f) = context.MonitorReady { + let ret = f(context, &monitor_ready); + ret as u32 + } else { + ERR_CODE_SERVER_FUNCTION_NONE + } + } +} + +pub fn server_format_list( + context: &mut CliprdrClientContext, + conn_id: i32, + format_list: Vec<(i32, String)>, +) -> u32 { + unsafe { + let num_formats = format_list.len() as UINT32; + let mut formats = format_list + .into_iter() + .map(|format| { + if format.1.is_empty() { + CLIPRDR_FORMAT { + formatId: format.0 as UINT32, + formatName: 0 as *mut _, + } + } else { + let n = match CString::new(format.1) { + Ok(n) => n, + Err(_) => CString::new("").unwrap(), + }; + CLIPRDR_FORMAT { + formatId: format.0 as UINT32, + formatName: n.into_raw(), + } + } + }) + .collect::>(); + + let format_list = CLIPRDR_FORMAT_LIST { + connID: conn_id as UINT32, + msgType: 0 as UINT16, + msgFlags: 0 as UINT16, + dataLen: 0 as UINT32, + numFormats: num_formats, + formats: formats.as_mut_ptr(), + }; + + let ret = if let Some(f) = context.ServerFormatList { + f(context, &format_list) + } else { + ERR_CODE_SERVER_FUNCTION_NONE + }; + + for f in formats { + if !f.formatName.is_null() { + // retake pointer to free memory + let _ = CString::from_raw(f.formatName); + } + } + + ret as u32 + } +} + +pub fn server_format_list_response( + context: &mut CliprdrClientContext, + conn_id: i32, + msg_flags: i32, +) -> u32 { + unsafe { + let format_list_response = CLIPRDR_FORMAT_LIST_RESPONSE { + connID: conn_id as UINT32, + msgType: 0 as UINT16, + msgFlags: msg_flags as UINT16, + dataLen: 0 as UINT32, + }; + + if let Some(f) = context.ServerFormatListResponse { + f(context, &format_list_response) + } else { + ERR_CODE_SERVER_FUNCTION_NONE + } + } +} + +pub fn server_format_data_request( + context: &mut CliprdrClientContext, + conn_id: i32, + requested_format_id: i32, +) -> u32 { + unsafe { + let format_data_request = CLIPRDR_FORMAT_DATA_REQUEST { + connID: conn_id as UINT32, + msgType: 0 as UINT16, + msgFlags: 0 as UINT16, + dataLen: 0 as UINT32, + requestedFormatId: requested_format_id as UINT32, + }; + if let Some(f) = context.ServerFormatDataRequest { + f(context, &format_data_request) + } else { + ERR_CODE_SERVER_FUNCTION_NONE + } + } +} + +pub fn server_format_data_response( + context: &mut CliprdrClientContext, + conn_id: i32, + msg_flags: i32, + mut format_data: Vec, +) -> u32 { + unsafe { + let format_data_response = CLIPRDR_FORMAT_DATA_RESPONSE { + connID: conn_id as UINT32, + msgType: 0 as UINT16, + msgFlags: msg_flags as UINT16, + dataLen: format_data.len() as UINT32, + requestedFormatData: format_data.as_mut_ptr(), + }; + if let Some(f) = context.ServerFormatDataResponse { + f(context, &format_data_response) + } else { + ERR_CODE_SERVER_FUNCTION_NONE + } + } +} + +pub fn server_file_contents_request( + context: &mut CliprdrClientContext, + conn_id: i32, + stream_id: i32, + list_index: i32, + dw_flags: i32, + n_position_low: i32, + n_position_high: i32, + cb_requested: i32, + have_clip_data_id: bool, + clip_data_id: i32, +) -> u32 { + unsafe { + let file_contents_request = CLIPRDR_FILE_CONTENTS_REQUEST { + connID: conn_id as UINT32, + msgType: 0 as UINT16, + msgFlags: 0 as UINT16, + dataLen: 0 as UINT32, + streamId: stream_id as UINT32, + listIndex: list_index as UINT32, + dwFlags: dw_flags as UINT32, + nPositionLow: n_position_low as UINT32, + nPositionHigh: n_position_high as UINT32, + cbRequested: cb_requested as UINT32, + haveClipDataId: if have_clip_data_id { TRUE } else { FALSE }, + clipDataId: clip_data_id as UINT32, + }; + if let Some(f) = context.ServerFileContentsRequest { + f(context, &file_contents_request) + } else { + ERR_CODE_SERVER_FUNCTION_NONE + } + } +} + +pub fn server_file_contents_response( + context: &mut CliprdrClientContext, + conn_id: i32, + msg_flags: i32, + stream_id: i32, + mut requested_data: Vec, +) -> u32 { + unsafe { + let file_contents_response = CLIPRDR_FILE_CONTENTS_RESPONSE { + connID: conn_id as UINT32, + msgType: 0 as UINT16, + msgFlags: msg_flags as UINT16, + dataLen: 4 + requested_data.len() as UINT32, + streamId: stream_id as UINT32, + cbRequested: requested_data.len() as UINT32, + requestedData: requested_data.as_mut_ptr(), + }; + if let Some(f) = context.ServerFileContentsResponse { + f(context, &file_contents_response) + } else { + ERR_CODE_SERVER_FUNCTION_NONE + } + } +} + +pub fn create_cliprdr_context( + enable_files: bool, + enable_others: bool, + response_wait_timeout_secs: u32, +) -> ResultType> { + Ok(CliprdrClientContext::create( + enable_files, + enable_others, + response_wait_timeout_secs, + Some(notify_callback), + Some(client_format_list), + Some(client_format_list_response), + Some(client_format_data_request), + Some(client_format_data_response), + Some(client_file_contents_request), + Some(client_file_contents_response), + )?) +} + +extern "C" fn notify_callback(conn_id: UINT32, msg: *const NOTIFICATION_MESSAGE) -> UINT { + log::debug!("notify_callback called"); + let data = unsafe { + let msg = &*msg; + let details = if msg.details.is_null() { + Ok("") + } else { + CStr::from_ptr(msg.details as _).to_str() + }; + match (CStr::from_ptr(msg.msg as _).to_str(), details) { + (Ok(m), Ok(d)) => { + let msgtype = format!( + "custom-{}-nocancel-nook-hasclose", + if msg.r#type == 0 { + "info" + } else if msg.r#type == 1 { + "warn" + } else { + "error" + } + ); + let title = "Clipboard"; + let text = if d.is_empty() { + m.to_string() + } else { + format!("{} {}", m, d) + }; + ClipboardFile::NotifyCallback { + r#type: msgtype, + title: title.to_string(), + text, + } + } + _ => { + log::error!("notify_callback: failed to convert msg"); + return ERR_CODE_INVALID_PARAMETER; + } + } + }; + // no need to handle result here + send_data(conn_id as _, data); + + 0 +} + +extern "C" fn client_format_list( + _context: *mut CliprdrClientContext, + clip_format_list: *const CLIPRDR_FORMAT_LIST, +) -> UINT { + let conn_id; + let mut format_list: Vec<(i32, String)> = Vec::new(); + unsafe { + let mut i = 0u32; + while i < (*clip_format_list).numFormats { + let format_data = &(*(*clip_format_list).formats.offset(i as isize)); + if format_data.formatName.is_null() { + format_list.push((format_data.formatId as i32, "".to_owned())); + } else { + let format_name = CStr::from_ptr(format_data.formatName).to_str(); + let format_name = match format_name { + Ok(n) => n.to_owned(), + Err(_) => { + log::warn!("failed to get format name"); + "".to_owned() + } + }; + format_list.push((format_data.formatId as i32, format_name)); + } + // log::debug!("format list item {}: format id: {}, format name: {}", i, format_data.formatId, &format_name); + i += 1; + } + conn_id = (*clip_format_list).connID as i32; + } + log::debug!( + "client_format_list called, client id: {}, format_list: {:?}", + conn_id, + &format_list + ); + let data = ClipboardFile::FormatList { format_list }; + // no need to handle result here + if conn_id == 0 { + // msg_channel is used for debug, VEC_MSG_CHANNEL cannot be inspected by the debugger. + let msg_channel = VEC_MSG_CHANNEL.read().unwrap(); + msg_channel + .iter() + .for_each(|msg_channel| allow_err!(msg_channel.sender.send(data.clone()))); + } else { + send_data(conn_id, data); + } + + 0 +} + +extern "C" fn client_format_list_response( + _context: *mut CliprdrClientContext, + format_list_response: *const CLIPRDR_FORMAT_LIST_RESPONSE, +) -> UINT { + let conn_id; + let msg_flags; + unsafe { + conn_id = (*format_list_response).connID as i32; + msg_flags = (*format_list_response).msgFlags as i32; + } + log::debug!( + "client_format_list_response called, client id: {}, msg_flags: {}", + conn_id, + msg_flags + ); + let data = ClipboardFile::FormatListResponse { msg_flags }; + send_data(conn_id, data); + + 0 +} + +extern "C" fn client_format_data_request( + _context: *mut CliprdrClientContext, + format_data_request: *const CLIPRDR_FORMAT_DATA_REQUEST, +) -> UINT { + let conn_id; + let requested_format_id; + unsafe { + conn_id = (*format_data_request).connID as i32; + requested_format_id = (*format_data_request).requestedFormatId as i32; + } + let data = ClipboardFile::FormatDataRequest { + requested_format_id, + }; + log::debug!( + "client_format_data_request called, conn_id: {}, requested_format_id: {}", + conn_id, + requested_format_id + ); + // no need to handle result here + send_data(conn_id, data); + + 0 +} + +extern "C" fn client_format_data_response( + _context: *mut CliprdrClientContext, + format_data_response: *const CLIPRDR_FORMAT_DATA_RESPONSE, +) -> UINT { + let conn_id; + let msg_flags; + let format_data; + unsafe { + conn_id = (*format_data_response).connID as i32; + msg_flags = (*format_data_response).msgFlags as i32; + if (*format_data_response).requestedFormatData.is_null() { + format_data = Vec::new(); + } else { + format_data = std::slice::from_raw_parts( + (*format_data_response).requestedFormatData, + (*format_data_response).dataLen as usize, + ) + .to_vec(); + } + } + log::debug!( + "client_format_data_response called, client id: {}, msg_flags: {}", + conn_id, + msg_flags + ); + let data = ClipboardFile::FormatDataResponse { + msg_flags, + format_data, + }; + send_data(conn_id, data); + + 0 +} + +extern "C" fn client_file_contents_request( + _context: *mut CliprdrClientContext, + file_contents_request: *const CLIPRDR_FILE_CONTENTS_REQUEST, +) -> UINT { + // TODO: support huge file? + // if (!cliprdr->hasHugeFileSupport) + // { + // if (((UINT64)fileContentsRequest->cbRequested + fileContentsRequest->nPositionLow) > + // UINT32_MAX) + // return ERROR_INVALID_PARAMETER; + // if (fileContentsRequest->nPositionHigh != 0) + // return ERROR_INVALID_PARAMETER; + // } + + let conn_id; + let stream_id; + let list_index; + let dw_flags; + let n_position_low; + let n_position_high; + let cb_requested; + let have_clip_data_id; + let clip_data_id; + unsafe { + conn_id = (*file_contents_request).connID as i32; + stream_id = (*file_contents_request).streamId as i32; + list_index = (*file_contents_request).listIndex as i32; + dw_flags = (*file_contents_request).dwFlags as i32; + n_position_low = (*file_contents_request).nPositionLow as i32; + n_position_high = (*file_contents_request).nPositionHigh as i32; + cb_requested = (*file_contents_request).cbRequested as i32; + have_clip_data_id = (*file_contents_request).haveClipDataId == TRUE; + clip_data_id = (*file_contents_request).clipDataId as i32; + } + let data = ClipboardFile::FileContentsRequest { + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + }; + log::debug!("client_file_contents_request called, data: {:?}", &data); + send_data(conn_id, data); + + 0 +} + +extern "C" fn client_file_contents_response( + _context: *mut CliprdrClientContext, + file_contents_response: *const CLIPRDR_FILE_CONTENTS_RESPONSE, +) -> UINT { + let conn_id; + let msg_flags; + let stream_id; + let requested_data; + unsafe { + conn_id = (*file_contents_response).connID as i32; + msg_flags = (*file_contents_response).msgFlags as i32; + stream_id = (*file_contents_response).streamId as i32; + if (*file_contents_response).requestedData.is_null() { + requested_data = Vec::new(); + } else { + requested_data = std::slice::from_raw_parts( + (*file_contents_response).requestedData, + (*file_contents_response).cbRequested as usize, + ) + .to_vec(); + } + } + let data = ClipboardFile::FileContentsResponse { + msg_flags, + stream_id, + requested_data, + }; + log::debug!( + "client_file_contents_response called, conn_id: {}, msg_flags: {}, stream_id: {}", + conn_id, + msg_flags, + stream_id + ); + send_data(conn_id, data); + + 0 +} diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index 5c93ccd04..eb5dde257 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" [dependencies] flexi_logger = { version = "0.25", features = ["async"] } protobuf = { version = "3.2", features = ["with-bytes"] } -tokio = { version = "1.28", features = ["full"] } +tokio = { version = "=1.28.1", features = ["full"] } tokio-util = { version = "0.7", features = ["full"] } futures = "0.3" bytes = { version = "1.4", features = ["serde"] } diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 454bc0d3d..5fddf2cd4 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -17,6 +17,11 @@ message YUV { int32 stride = 2; } +enum Chroma { + I420 = 0; + I444 = 1; +} + message VideoFrame { oneof union { EncodedVideoFrames vp9s = 6; @@ -83,11 +88,20 @@ message Features { bool privacy_mode = 1; } +message CodecAbility { + bool vp8 = 1; + bool vp9 = 2; + bool av1 = 3; + bool h264 = 4; + bool h265 = 5; +} + message SupportedEncoding { bool h264 = 1; bool h265 = 2; bool vp8 = 3; bool av1 = 4; + CodecAbility i444 = 5; } message PeerInfo { @@ -512,6 +526,7 @@ message PermissionInfo { File = 4; Restart = 5; Recording = 6; + BlockInput = 7; } Permission permission = 1; @@ -541,6 +556,8 @@ message SupportedDecoding { PreferCodec prefer = 4; int32 ability_vp8 = 5; int32 ability_av1 = 6; + CodecAbility i444 = 7; + Chroma prefer_chroma = 8; } message OptionMessage { diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 6a154156b..b094e230c 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -91,10 +91,11 @@ const CHARS: &[char] = &[ ]; pub const RENDEZVOUS_SERVERS: &[&str] = &["rs-ny.rustdesk.com"]; +pub const PUBLIC_RS_PUB_KEY: &str = "OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw="; pub const RS_PUB_KEY: &str = match option_env!("RS_PUB_KEY") { Some(key) if !key.is_empty() => key, - _ => "OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=", + _ => PUBLIC_RS_PUB_KEY, }; pub const RENDEZVOUS_PORT: i32 = 21116; @@ -1229,6 +1230,10 @@ impl PeerConfig { if !mp.contains_key(key) { mp.insert(key.to_owned(), UserDefaultConfig::read().get(key)); } + key = "i444"; + if !mp.contains_key(key) { + mp.insert(key.to_owned(), UserDefaultConfig::read().get(key)); + } } } diff --git a/libs/portable/generate.py b/libs/portable/generate.py index 640f2ae6a..61d8c78f7 100644 --- a/libs/portable/generate.py +++ b/libs/portable/generate.py @@ -1,4 +1,3 @@ -from ast import parse import os import optparse from hashlib import md5 @@ -47,7 +46,7 @@ def write_metadata(md5_table: dict, output_folder: str, exe: str): f.write((len(path)).to_bytes(length=length_count, byteorder='big')) f.write(path) # data length & compressed data - f.write((data_length).to_bytes( + f.write(data_length.to_bytes( length=length_count, byteorder='big')) f.write(compressed_data) # md5 code @@ -65,6 +64,8 @@ def build_portable(output_folder: str): # Linux: python3 generate.py -f ../rustdesk-portable-packer/test -o . -e ./test/main.py # Windows: python3 .\generate.py -f ..\rustdesk\flutter\build\windows\runner\Debug\ -o . -e ..\rustdesk\flutter\build\windows\runner\Debug\rustdesk.exe + + if __name__ == '__main__': parser = optparse.OptionParser() parser.add_option("-f", "--folder", dest="folder", diff --git a/libs/scrap/build.rs b/libs/scrap/build.rs index 709e64aa9..7d162adfa 100644 --- a/libs/scrap/build.rs +++ b/libs/scrap/build.rs @@ -197,6 +197,7 @@ fn main() { find_package("libyuv"); gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*"); gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*"); + gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*"); // there is problem with cfg(target_os) in build.rs, so use our workaround let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); diff --git a/libs/scrap/examples/benchmark.rs b/libs/scrap/examples/benchmark.rs index 83fba99d7..fadf4a052 100644 --- a/libs/scrap/examples/benchmark.rs +++ b/libs/scrap/examples/benchmark.rs @@ -1,13 +1,20 @@ use docopt::Docopt; -use hbb_common::env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; +use hbb_common::{ + env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}, + log, +}; use scrap::{ aom::{AomDecoder, AomEncoder, AomEncoderConfig}, codec::{EncoderApi, EncoderCfg, Quality as Q}, - Capturer, Display, TraitCapturer, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, + convert_to_yuv, Capturer, Display, TraitCapturer, VpxDecoder, VpxDecoderConfig, VpxEncoder, + VpxEncoderConfig, VpxVideoCodecId::{self, *}, STRIDE_ALIGN, }; -use std::{io::Write, time::Instant}; +use std::{ + io::Write, + time::{Duration, Instant}, +}; // cargo run --package scrap --example benchmark --release --features hwcodec @@ -15,7 +22,7 @@ const USAGE: &'static str = " Codec benchmark. Usage: - benchmark [--count=COUNT] [--quality=QUALITY] [--hw-pixfmt=PIXFMT] + benchmark [--count=COUNT] [--quality=QUALITY] [--i444] benchmark (-h | --help) Options: @@ -23,24 +30,17 @@ Options: --count=COUNT Capture frame count [default: 100]. --quality=QUALITY Video quality [default: Balanced]. Valid values: Best, Balanced, Low. - --hw-pixfmt=PIXFMT Hardware codec pixfmt. [default: i420] - Valid values: i420, nv12. + --i444 I444. "; -#[derive(Debug, serde::Deserialize)] +#[derive(Debug, serde::Deserialize, Clone, Copy)] struct Args { flag_count: usize, flag_quality: Quality, - flag_hw_pixfmt: Pixfmt, + flag_i444: bool, } -#[derive(Debug, serde::Deserialize)] -enum Pixfmt { - I420, - NV12, -} - -#[derive(Debug, serde::Deserialize)] +#[derive(Debug, serde::Deserialize, Clone, Copy)] enum Quality { Best, Balanced, @@ -54,31 +54,6 @@ fn main() { .unwrap_or_else(|e| e.exit()); let quality = args.flag_quality; let yuv_count = args.flag_count; - let (yuvs, width, height) = capture_yuv(yuv_count); - println!( - "benchmark {}x{} quality:{:?}k hw_pixfmt:{:?}", - width, height, quality, args.flag_hw_pixfmt - ); - let quality = match quality { - Quality::Best => Q::Best, - Quality::Balanced => Q::Balanced, - Quality::Low => Q::Low, - }; - [VP8, VP9].map(|c| test_vpx(c, &yuvs, width, height, quality, yuv_count)); - test_av1(&yuvs, width, height, quality, yuv_count); - #[cfg(feature = "hwcodec")] - { - use hwcodec::AVPixelFormat; - let hw_pixfmt = match args.flag_hw_pixfmt { - Pixfmt::I420 => AVPixelFormat::AV_PIX_FMT_YUV420P, - Pixfmt::NV12 => AVPixelFormat::AV_PIX_FMT_NV12, - }; - let yuvs = hw::vpx_yuv_to_hw_yuv(yuvs, width, height, hw_pixfmt); - hw::test(&yuvs, width, height, quality, yuv_count, hw_pixfmt); - } -} - -fn capture_yuv(yuv_count: usize) -> (Vec>, usize, usize) { let mut index = 0; let mut displays = Display::all().unwrap(); for i in 0..displays.len() { @@ -88,28 +63,45 @@ fn capture_yuv(yuv_count: usize) -> (Vec>, usize, usize) { } } let d = displays.remove(index); - let mut c = Capturer::new(d, true).unwrap(); - let mut v = vec![]; - loop { - if let Ok(frame) = c.frame(std::time::Duration::from_millis(30)) { - v.push(frame.0.to_vec()); - print!("\rcapture {}/{}", v.len(), yuv_count); - std::io::stdout().flush().ok(); - if v.len() == yuv_count { - println!(); - return (v, c.width(), c.height()); - } - } + let mut c = Capturer::new(d).unwrap(); + let width = c.width(); + let height = c.height(); + + println!( + "benchmark {}x{} quality:{:?}, i444:{:?}", + width, height, quality, args.flag_i444 + ); + let quality = match quality { + Quality::Best => Q::Best, + Quality::Balanced => Q::Balanced, + Quality::Low => Q::Low, + }; + [VP8, VP9].map(|codec| { + test_vpx( + &mut c, + codec, + width, + height, + quality, + yuv_count, + if codec == VP8 { false } else { args.flag_i444 }, + ) + }); + test_av1(&mut c, width, height, quality, yuv_count, args.flag_i444); + #[cfg(feature = "hwcodec")] + { + hw::test(&mut c, width, height, quality, yuv_count); } } fn test_vpx( + c: &mut Capturer, codec_id: VpxVideoCodecId, - yuvs: &Vec>, width: usize, height: usize, quality: Q, yuv_count: usize, + i444: bool, ) { let config = EncoderCfg::VPX(VpxEncoderConfig { width: width as _, @@ -118,28 +110,53 @@ fn test_vpx( codec: codec_id, keyframe_interval: None, }); - let mut encoder = VpxEncoder::new(config).unwrap(); + let mut encoder = VpxEncoder::new(config, i444).unwrap(); let mut vpxs = vec![]; let start = Instant::now(); let mut size = 0; - for yuv in yuvs { - for ref frame in encoder - .encode(start.elapsed().as_millis() as _, yuv, STRIDE_ALIGN) - .unwrap() - { - size += frame.data.len(); - vpxs.push(frame.data.to_vec()); + let mut yuv = Vec::new(); + let mut mid_data = Vec::new(); + let mut counter = 0; + let mut time_sum = Duration::ZERO; + loop { + match c.frame(std::time::Duration::from_millis(30)) { + Ok(frame) => { + let tmp_timer = Instant::now(); + convert_to_yuv(&frame, encoder.yuvfmt(), &mut yuv, &mut mid_data); + for ref frame in encoder + .encode(start.elapsed().as_millis() as _, &yuv, STRIDE_ALIGN) + .unwrap() + { + size += frame.data.len(); + vpxs.push(frame.data.to_vec()); + counter += 1; + print!("\r{codec_id:?} {}/{}", counter, yuv_count); + std::io::stdout().flush().ok(); + } + for ref frame in encoder.flush().unwrap() { + size += frame.data.len(); + vpxs.push(frame.data.to_vec()); + counter += 1; + print!("\r{codec_id:?} {}/{}", counter, yuv_count); + std::io::stdout().flush().ok(); + } + time_sum += tmp_timer.elapsed(); + } + Err(e) => { + log::error!("{e:?}"); + } } - for ref frame in encoder.flush().unwrap() { - size += frame.data.len(); - vpxs.push(frame.data.to_vec()); + if counter >= yuv_count { + println!(); + break; } } + assert_eq!(vpxs.len(), yuv_count); println!( "{:?} encode: {:?}, {} byte", codec_id, - start.elapsed() / yuv_count as _, + time_sum / yuv_count as _, size / yuv_count ); @@ -156,30 +173,58 @@ fn test_vpx( ); } -fn test_av1(yuvs: &Vec>, width: usize, height: usize, quality: Q, yuv_count: usize) { +fn test_av1( + c: &mut Capturer, + width: usize, + height: usize, + quality: Q, + yuv_count: usize, + i444: bool, +) { let config = EncoderCfg::AOM(AomEncoderConfig { width: width as _, height: height as _, quality, keyframe_interval: None, }); - let mut encoder = AomEncoder::new(config).unwrap(); + let mut encoder = AomEncoder::new(config, i444).unwrap(); let start = Instant::now(); let mut size = 0; - let mut av1s = vec![]; - for yuv in yuvs { - for ref frame in encoder - .encode(start.elapsed().as_millis() as _, yuv, STRIDE_ALIGN) - .unwrap() - { - size += frame.data.len(); - av1s.push(frame.data.to_vec()); + let mut av1s: Vec> = vec![]; + let mut yuv = Vec::new(); + let mut mid_data = Vec::new(); + let mut counter = 0; + let mut time_sum = Duration::ZERO; + loop { + match c.frame(std::time::Duration::from_millis(30)) { + Ok(frame) => { + let tmp_timer = Instant::now(); + convert_to_yuv(&frame, encoder.yuvfmt(), &mut yuv, &mut mid_data); + for ref frame in encoder + .encode(start.elapsed().as_millis() as _, &yuv, STRIDE_ALIGN) + .unwrap() + { + size += frame.data.len(); + av1s.push(frame.data.to_vec()); + counter += 1; + print!("\rAV1 {}/{}", counter, yuv_count); + std::io::stdout().flush().ok(); + } + time_sum += tmp_timer.elapsed(); + } + Err(e) => { + log::error!("{e:?}"); + } + } + if counter >= yuv_count { + println!(); + break; } } assert_eq!(av1s.len(), yuv_count); println!( "AV1 encode: {:?}, {} byte", - start.elapsed() / yuv_count as _, + time_sum / yuv_count as _, size / yuv_count ); let mut decoder = AomDecoder::new().unwrap(); @@ -193,165 +238,101 @@ fn test_av1(yuvs: &Vec>, width: usize, height: usize, quality: Q, yuv_co #[cfg(feature = "hwcodec")] mod hw { - use super::*; - use hwcodec::{ - decode::{DecodeContext, Decoder}, - encode::{EncodeContext, Encoder}, - ffmpeg::{ffmpeg_linesize_offset_length, CodecInfo, CodecInfos}, - AVPixelFormat, - Quality::*, - RateControl::*, - }; + use hwcodec::ffmpeg::CodecInfo; use scrap::{ - codec::codec_thread_num, - convert::{ - hw::{hw_bgra_to_i420, hw_bgra_to_nv12}, - i420_to_bgra, - }, - HW_STRIDE_ALIGN, + codec::HwEncoderConfig, + hwcodec::{HwDecoder, HwEncoder}, }; - pub fn test( - yuvs: &Vec>, + use super::*; + + pub fn test(c: &mut Capturer, width: usize, height: usize, quality: Q, yuv_count: usize) { + let best = HwEncoder::best(); + let mut h264s = Vec::new(); + let mut h265s = Vec::new(); + if let Some(info) = best.h264 { + test_encoder(width, height, quality, info, c, yuv_count, &mut h264s); + } + if let Some(info) = best.h265 { + test_encoder(width, height, quality, info, c, yuv_count, &mut h265s); + } + let best = HwDecoder::best(); + if let Some(info) = best.h264 { + test_decoder(info, &h264s); + } + if let Some(info) = best.h265 { + test_decoder(info, &h265s); + } + } + + fn test_encoder( width: usize, height: usize, quality: Q, + info: CodecInfo, + c: &mut Capturer, yuv_count: usize, - pixfmt: AVPixelFormat, + h26xs: &mut Vec>, ) { - let bitrate = scrap::hwcodec::HwEncoder::convert_quality(quality); - let ctx = EncodeContext { - name: String::from(""), - width: width as _, - height: height as _, - pixfmt, - align: 0, - bitrate: bitrate as i32 * 1000, - timebase: [1, 30], - gop: 60, - quality: Quality_Default, - rc: RC_DEFAULT, - thread_count: codec_thread_num() as _, - }; - - let encoders = Encoder::available_encoders(ctx.clone()); - println!("hw encoders: {}", encoders.len()); - let best = CodecInfo::score(encoders.clone()); - for info in encoders { - test_encoder(info.clone(), ctx.clone(), yuvs, is_best(&best, &info)); - } - - let (h264s, h265s) = prepare_h26x(best, ctx.clone(), yuvs); - assert!(h264s.is_empty() || h264s.len() == yuv_count); - assert!(h265s.is_empty() || h265s.len() == yuv_count); - let decoders = Decoder::available_decoders(); - println!("hw decoders: {}", decoders.len()); - let best = CodecInfo::score(decoders.clone()); - for info in decoders { - let h26xs = if info.name.contains("h264") { - &h264s - } else { - &h265s - }; - if h26xs.len() == yuvs.len() { - test_decoder(info.clone(), h26xs, is_best(&best, &info)); - } - } - } - - fn test_encoder(info: CodecInfo, ctx: EncodeContext, yuvs: &Vec>, best: bool) { - let mut ctx = ctx; - ctx.name = info.name; - let mut encoder = Encoder::new(ctx.clone()).unwrap(); - let start = Instant::now(); + let mut encoder = HwEncoder::new( + EncoderCfg::HW(HwEncoderConfig { + name: info.name.clone(), + width, + height, + quality, + keyframe_interval: None, + }), + false, + ) + .unwrap(); let mut size = 0; - for yuv in yuvs { - let frames = encoder.encode(yuv).unwrap(); - for frame in frames { - size += frame.data.len(); + + let mut yuv = Vec::new(); + let mut mid_data = Vec::new(); + let mut counter = 0; + let mut time_sum = Duration::ZERO; + loop { + match c.frame(std::time::Duration::from_millis(30)) { + Ok(frame) => { + let tmp_timer = Instant::now(); + convert_to_yuv(&frame, encoder.yuvfmt(), &mut yuv, &mut mid_data); + for ref frame in encoder.encode(&yuv).unwrap() { + size += frame.data.len(); + + h26xs.push(frame.data.to_vec()); + counter += 1; + print!("\r{:?} {}/{}", info.name, counter, yuv_count); + std::io::stdout().flush().ok(); + } + time_sum += tmp_timer.elapsed(); + } + Err(e) => { + log::error!("{e:?}"); + } + } + if counter >= yuv_count { + println!(); + break; } } println!( - "{}{}: {:?}, {} byte", - if best { "*" } else { "" }, - ctx.name, - start.elapsed() / yuvs.len() as _, - size / yuvs.len(), + "{}: {:?}, {} byte", + info.name, + time_sum / yuv_count as u32, + size / yuv_count, ); } - fn test_decoder(info: CodecInfo, h26xs: &Vec>, best: bool) { - let ctx = DecodeContext { - name: info.name, - device_type: info.hwdevice, - thread_count: codec_thread_num() as _, - }; - - let mut decoder = Decoder::new(ctx.clone()).unwrap(); + fn test_decoder(info: CodecInfo, h26xs: &Vec>) { + let mut decoder = HwDecoder::new(info.clone()).unwrap(); let start = Instant::now(); let mut cnt = 0; for h26x in h26xs { let _ = decoder.decode(h26x).unwrap(); cnt += 1; } - let device = format!("{:?}", ctx.device_type).to_lowercase(); + let device = format!("{:?}", info.hwdevice).to_lowercase(); let device = device.split("_").last().unwrap(); - println!( - "{}{} {}: {:?}", - if best { "*" } else { "" }, - ctx.name, - device, - start.elapsed() / cnt - ); - } - - fn prepare_h26x( - best: CodecInfos, - ctx: EncodeContext, - yuvs: &Vec>, - ) -> (Vec>, Vec>) { - let f = |info: Option| { - let mut h26xs = vec![]; - if let Some(info) = info { - let mut ctx = ctx.clone(); - ctx.name = info.name; - let mut encoder = Encoder::new(ctx).unwrap(); - for yuv in yuvs { - let h26x = encoder.encode(yuv).unwrap(); - for frame in h26x { - h26xs.push(frame.data.to_vec()); - } - } - } - h26xs - }; - (f(best.h264), f(best.h265)) - } - - fn is_best(best: &CodecInfos, info: &CodecInfo) -> bool { - Some(info.clone()) == best.h264 || Some(info.clone()) == best.h265 - } - - pub fn vpx_yuv_to_hw_yuv( - yuvs: Vec>, - width: usize, - height: usize, - pixfmt: AVPixelFormat, - ) -> Vec> { - let yuvs = yuvs; - let mut bgra = vec![]; - let mut v = vec![]; - let (linesize, offset, length) = - ffmpeg_linesize_offset_length(pixfmt, width, height, HW_STRIDE_ALIGN).unwrap(); - for mut yuv in yuvs { - i420_to_bgra(width, height, &yuv, &mut bgra); - if pixfmt == AVPixelFormat::AV_PIX_FMT_YUV420P { - hw_bgra_to_i420(width, height, &linesize, &offset, length, &bgra, &mut yuv); - } else { - hw_bgra_to_nv12(width, height, &linesize, &offset, length, &bgra, &mut yuv); - } - v.push(yuv); - } - v + println!("{} {}: {:?}", info.name, device, start.elapsed() / cnt); } } diff --git a/libs/scrap/examples/capture_mag.rs b/libs/scrap/examples/capture_mag.rs index 281c99328..ab265b3dc 100644 --- a/libs/scrap/examples/capture_mag.rs +++ b/libs/scrap/examples/capture_mag.rs @@ -3,7 +3,7 @@ extern crate scrap; use scrap::Display; #[cfg(windows)] -use scrap::{i420_to_rgb, CapturerMag, TraitCapturer}; +use scrap::{CapturerMag, TraitCapturer}; #[cfg(windows)] use std::fs::File; @@ -24,6 +24,8 @@ fn get_display(i: usize) -> Display { fn record(i: usize) { use std::time::Duration; + use scrap::TraitFrame; + for d in Display::all().unwrap() { println!("{:?} {} {}", d.origin(), d.width(), d.height()); } @@ -32,9 +34,8 @@ fn record(i: usize) { let (w, h) = (display.width(), display.height()); { - let mut capture_mag = - CapturerMag::new(display.origin(), display.width(), display.height(), false) - .expect("Couldn't begin capture."); + let mut capture_mag = CapturerMag::new(display.origin(), display.width(), display.height()) + .expect("Couldn't begin capture."); let wnd_cls = ""; let wnd_name = "RustDeskPrivacyWindow"; if false == capture_mag.exclude(wnd_cls, wnd_name).unwrap() { @@ -43,7 +44,8 @@ fn record(i: usize) { println!("Filter window for cls {} name {}", wnd_cls, wnd_name); } - let frame = capture_mag.frame(Duration::from_millis(0)).unwrap(); + let captured_frame = capture_mag.frame(Duration::from_millis(0)).unwrap(); + let frame = captured_frame.data(); println!("Capture data len: {}, Saving...", frame.len()); let mut bitflipped = Vec::with_capacity(w * h * 4); @@ -68,9 +70,8 @@ fn record(i: usize) { } { - let mut capture_mag = - CapturerMag::new(display.origin(), display.width(), display.height(), true) - .expect("Couldn't begin capture."); + let mut capture_mag = CapturerMag::new(display.origin(), display.width(), display.height()) + .expect("Couldn't begin capture."); let wnd_cls = ""; let wnd_title = "RustDeskPrivacyWindow"; if false == capture_mag.exclude(wnd_cls, wnd_title).unwrap() { @@ -79,19 +80,28 @@ fn record(i: usize) { println!("Filter window for cls {} title {}", wnd_cls, wnd_title); } - let buffer = capture_mag.frame(Duration::from_millis(0)).unwrap(); - println!("Capture data len: {}, Saving...", buffer.len()); + let frame = capture_mag.frame(Duration::from_millis(0)).unwrap(); + println!("Capture data len: {}, Saving...", frame.data().len()); - let mut frame = Default::default(); - i420_to_rgb(w, h, &buffer, &mut frame); + let mut raw = Vec::new(); + unsafe { + scrap::ARGBToRAW( + frame.data().as_ptr(), + frame.stride()[0] as _, + (&mut raw).as_mut_ptr(), + (w * 3) as _, + w as _, + h as _, + ) + }; let mut bitflipped = Vec::with_capacity(w * h * 4); - let stride = frame.len() / h; + let stride = raw.len() / h; for y in 0..h { for x in 0..w { let i = stride * y + 3 * x; - bitflipped.extend_from_slice(&[frame[i], frame[i + 1], frame[i + 2], 255]); + bitflipped.extend_from_slice(&[raw[i], raw[i + 1], raw[i + 2], 255]); } } let name = format!("capture_mag_{}_2.png", i); diff --git a/libs/scrap/examples/ffplay.rs b/libs/scrap/examples/ffplay.rs index b8fe16e35..02b602256 100644 --- a/libs/scrap/examples/ffplay.rs +++ b/libs/scrap/examples/ffplay.rs @@ -1,5 +1,7 @@ use std::time::Duration; +use scrap::TraitFrame; + extern crate scrap; fn main() { @@ -27,16 +29,16 @@ fn main() { .spawn() .expect("This example requires ffplay."); - let mut capturer = Capturer::new(d, false).unwrap(); + let mut capturer = Capturer::new(d).unwrap(); let mut out = child.stdin.unwrap(); loop { match capturer.frame(Duration::from_millis(0)) { Ok(frame) => { // Write the frame, removing end-of-row padding. - let stride = frame.len() / h; + let stride = frame.stride()[0]; let rowlen = 4 * w; - for row in frame.chunks(stride) { + for row in frame.data().chunks(stride) { let row = &row[..rowlen]; out.write_all(row).unwrap(); } diff --git a/libs/scrap/examples/record-screen.rs b/libs/scrap/examples/record-screen.rs index 6640d5698..91ea1ecee 100644 --- a/libs/scrap/examples/record-screen.rs +++ b/libs/scrap/examples/record-screen.rs @@ -17,7 +17,7 @@ use scrap::codec::{EncoderApi, EncoderCfg, Quality as Q}; use webm::mux; use webm::mux::Track; -use scrap::vpxcodec as vpx_encode; +use scrap::{convert_to_yuv, vpxcodec as vpx_encode}; use scrap::{Capturer, Display, TraitCapturer, STRIDE_ALIGN}; const USAGE: &'static str = " @@ -110,13 +110,16 @@ fn main() -> io::Result<()> { Quality::Balanced => Q::Balanced, Quality::Low => Q::Low, }; - let mut vpx = vpx_encode::VpxEncoder::new(EncoderCfg::VPX(vpx_encode::VpxEncoderConfig { - width, - height, - quality, - codec: vpx_codec, - keyframe_interval: None, - })) + let mut vpx = vpx_encode::VpxEncoder::new( + EncoderCfg::VPX(vpx_encode::VpxEncoderConfig { + width, + height, + quality, + codec: vpx_codec, + keyframe_interval: None, + }), + false, + ) .unwrap(); // Start recording. @@ -136,7 +139,9 @@ fn main() -> io::Result<()> { let spf = Duration::from_nanos(1_000_000_000 / args.flag_fps); // Capturer object is expensive, avoiding to create it frequently. - let mut c = Capturer::new(d, true).unwrap(); + let mut c = Capturer::new(d).unwrap(); + let mut yuv = Vec::new(); + let mut mid_data = Vec::new(); while !stop.load(Ordering::Acquire) { let now = Instant::now(); let time = now - start; @@ -147,8 +152,8 @@ fn main() -> io::Result<()> { if let Ok(frame) = c.frame(Duration::from_millis(0)) { let ms = time.as_secs() * 1000 + time.subsec_millis() as u64; - - for frame in vpx.encode(ms as i64, &frame, STRIDE_ALIGN).unwrap() { + convert_to_yuv(&frame, vpx.yuvfmt(), &mut yuv, &mut mid_data); + for frame in vpx.encode(ms as i64, &yuv, STRIDE_ALIGN).unwrap() { vt.add_frame(frame.data, frame.pts as u64 * 1_000_000, frame.key); } } diff --git a/libs/scrap/examples/screenshot.rs b/libs/scrap/examples/screenshot.rs index 636768eb8..651f56634 100644 --- a/libs/scrap/examples/screenshot.rs +++ b/libs/scrap/examples/screenshot.rs @@ -6,7 +6,7 @@ use std::io::ErrorKind::WouldBlock; use std::thread; use std::time::Duration; -use scrap::{i420_to_rgb, Capturer, Display, TraitCapturer}; +use scrap::{Capturer, Display, TraitCapturer, TraitFrame}; fn main() { let n = Display::all().unwrap().len(); @@ -28,14 +28,14 @@ fn record(i: usize) { } let display = get_display(i); - let mut capturer = Capturer::new(display, false).expect("Couldn't begin capture."); + let mut capturer = Capturer::new(display).expect("Couldn't begin capture."); let (w, h) = (capturer.width(), capturer.height()); loop { // Wait until there's a frame. - let buffer = match capturer.frame(Duration::from_millis(0)) { - Ok(buffer) => buffer, + let frame = match capturer.frame(Duration::from_millis(0)) { + Ok(frame) => frame, Err(error) => { if error.kind() == WouldBlock { // Keep spinning. @@ -46,6 +46,7 @@ fn record(i: usize) { } } }; + let buffer = frame.data(); println!("Captured data len: {}, Saving...", buffer.len()); // Flip the BGRA image into a RGBA image. @@ -77,14 +78,14 @@ fn record(i: usize) { drop(capturer); let display = get_display(i); - let mut capturer = Capturer::new(display, true).expect("Couldn't begin capture."); + let mut capturer = Capturer::new(display).expect("Couldn't begin capture."); let (w, h) = (capturer.width(), capturer.height()); loop { // Wait until there's a frame. - let buffer = match capturer.frame(Duration::from_millis(0)) { - Ok(buffer) => buffer, + let frame = match capturer.frame(Duration::from_millis(0)) { + Ok(frame) => frame, Err(error) => { if error.kind() == WouldBlock { // Keep spinning. @@ -95,18 +96,28 @@ fn record(i: usize) { } } }; + let buffer = frame.data(); println!("Captured data len: {}, Saving...", buffer.len()); - let mut frame = Default::default(); - i420_to_rgb(w, h, &buffer, &mut frame); + let mut raw = Vec::new(); + unsafe { + scrap::ARGBToRAW( + buffer.as_ptr(), + frame.stride()[0] as _, + (&mut raw).as_mut_ptr(), + (w * 3) as _, + w as _, + h as _, + ) + }; let mut bitflipped = Vec::with_capacity(w * h * 4); - let stride = frame.len() / h; + let stride = raw.len() / h; for y in 0..h { for x in 0..w { let i = stride * y + 3 * x; - bitflipped.extend_from_slice(&[frame[i], frame[i + 1], frame[i + 2], 255]); + bitflipped.extend_from_slice(&[raw[i], raw[i + 1], raw[i + 2], 255]); } } let name = format!("screenshot{}_2.png", i); diff --git a/libs/scrap/src/bindings/yuv_ffi.h b/libs/scrap/src/bindings/yuv_ffi.h new file mode 100644 index 000000000..1ea24ef0b --- /dev/null +++ b/libs/scrap/src/bindings/yuv_ffi.h @@ -0,0 +1,6 @@ +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index 5daa872c6..538b52b69 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -1,5 +1,5 @@ use crate::android::ffi::*; -use crate::rgba_to_i420; +use crate::Pixfmt; use lazy_static::lazy_static; use serde_json::Value; use std::collections::HashMap; @@ -12,15 +12,15 @@ lazy_static! { pub struct Capturer { display: Display, - bgra: Vec, + rgba: Vec, saved_raw_data: Vec, // for faster compare and copy } impl Capturer { - pub fn new(display: Display, _yuv: bool) -> io::Result { + pub fn new(display: Display) -> io::Result { Ok(Capturer { display, - bgra: Vec::new(), + rgba: Vec::new(), saved_raw_data: Vec::new(), }) } @@ -35,22 +35,62 @@ impl Capturer { } impl crate::TraitCapturer for Capturer { - fn set_use_yuv(&mut self, _use_yuv: bool) {} - fn frame<'a>(&'a mut self, _timeout: Duration) -> io::Result> { if let Some(buf) = get_video_raw() { crate::would_block_if_equal(&mut self.saved_raw_data, buf)?; - rgba_to_i420(self.width(), self.height(), buf, &mut self.bgra); - Ok(Frame::RAW(&self.bgra)) + // Is it safe to directly return buf without copy? + self.rgba.resize(buf.len(), 0); + unsafe { + std::ptr::copy_nonoverlapping(buf.as_ptr(), self.rgba.as_mut_ptr(), buf.len()) + }; + Ok(Frame::new(&self.rgba, self.width(), self.height())) } else { return Err(io::ErrorKind::WouldBlock.into()); } } } -pub enum Frame<'a> { - RAW(&'a [u8]), - Empty, +pub struct Frame<'a> { + data: &'a [u8], + width: usize, + height: usize, + stride: Vec, +} + +impl<'a> Frame<'a> { + pub fn new(data: &'a [u8], width: usize, height: usize) -> Self { + let stride0 = data.len() / height; + let mut stride = Vec::new(); + stride.push(stride0); + Frame { + data, + width, + height, + stride, + } + } +} + +impl<'a> crate::TraitFrame for Frame<'a> { + fn data(&self) -> &[u8] { + self.data + } + + fn width(&self) -> usize { + self.width + } + + fn height(&self) -> usize { + self.height + } + + fn stride(&self) -> Vec { + self.stride.clone() + } + + fn pixfmt(&self) -> Pixfmt { + Pixfmt::RGBA + } } pub struct Display { diff --git a/libs/scrap/src/common/aom.rs b/libs/scrap/src/common/aom.rs index 445b22517..975a82d64 100644 --- a/libs/scrap/src/common/aom.rs +++ b/libs/scrap/src/common/aom.rs @@ -9,11 +9,12 @@ include!(concat!(env!("OUT_DIR"), "/aom_ffi.rs")); use crate::codec::{base_bitrate, codec_thread_num, Quality}; use crate::{codec::EncoderApi, EncodeFrame, STRIDE_ALIGN}; use crate::{common::GoogleImage, generate_call_macro, generate_call_ptr_macro, Error, Result}; +use crate::{EncodeYuvFormat, Pixfmt}; use hbb_common::{ anyhow::{anyhow, Context}, bytes::Bytes, log, - message_proto::{EncodedVideoFrame, EncodedVideoFrames, VideoFrame}, + message_proto::{Chroma, EncodedVideoFrame, EncodedVideoFrames, VideoFrame}, ResultType, }; use std::{ptr, slice}; @@ -52,6 +53,8 @@ pub struct AomEncoder { ctx: aom_codec_ctx_t, width: usize, height: usize, + i444: bool, + yuvfmt: EncodeYuvFormat, } // https://webrtc.googlesource.com/src/+/refs/heads/main/modules/video_coding/codecs/av1/libaom_av1_encoder.cc @@ -95,6 +98,7 @@ mod webrtc { pub fn enc_cfg( i: *const aom_codec_iface, cfg: AomEncoderConfig, + i444: bool, ) -> ResultType { let mut c = unsafe { std::mem::MaybeUninit::zeroed().assume_init() }; call_aom!(aom_codec_enc_config_default(i, &mut c, kUsageProfile)); @@ -139,6 +143,9 @@ mod webrtc { c.g_pass = aom_enc_pass::AOM_RC_ONE_PASS; // One-pass rate control c.g_lag_in_frames = kLagInFrames; // No look ahead when lag equals 0. + // https://aomedia.googlesource.com/aom/+/refs/tags/v3.6.0/av1/common/enums.h#82 + c.g_profile = if i444 { 1 } else { 0 }; + Ok(c) } @@ -210,14 +217,14 @@ mod webrtc { } impl EncoderApi for AomEncoder { - fn new(cfg: crate::codec::EncoderCfg) -> ResultType + fn new(cfg: crate::codec::EncoderCfg, i444: bool) -> ResultType where Self: Sized, { match cfg { crate::codec::EncoderCfg::AOM(config) => { let i = call_aom_ptr!(aom_codec_av1_cx()); - let c = webrtc::enc_cfg(i, config)?; + let c = webrtc::enc_cfg(i, config, i444)?; let mut ctx = Default::default(); // Flag options: AOM_CODEC_USE_PSNR and AOM_CODEC_USE_HIGHBITDEPTH @@ -234,6 +241,8 @@ impl EncoderApi for AomEncoder { ctx, width: config.width as _, height: config.height as _, + i444, + yuvfmt: Self::get_yuvfmt(config.width, config.height, i444), }) } _ => Err(anyhow!("encoder type mismatch")), @@ -255,8 +264,8 @@ impl EncoderApi for AomEncoder { } } - fn use_yuv(&self) -> bool { - true + fn yuvfmt(&self) -> crate::EncodeYuvFormat { + self.yuvfmt.clone() } fn set_quality(&mut self, quality: Quality) -> ResultType<()> { @@ -282,14 +291,20 @@ impl EncoderApi for AomEncoder { impl AomEncoder { pub fn encode(&mut self, pts: i64, data: &[u8], stride_align: usize) -> Result { - if 2 * data.len() < 3 * self.width * self.height { + let bpp = if self.i444 { 24 } else { 12 }; + if data.len() < self.width * self.height * bpp / 8 { return Err(Error::FailedCall("len not enough".to_string())); } + let fmt = if self.i444 { + aom_img_fmt::AOM_IMG_FMT_I444 + } else { + aom_img_fmt::AOM_IMG_FMT_I420 + }; let mut image = Default::default(); call_aom_ptr!(aom_img_wrap( &mut image, - aom_img_fmt::AOM_IMG_FMT_I420, + fmt, self.width as _, self.height as _, stride_align as _, @@ -359,6 +374,34 @@ impl AomEncoder { (q_min, q_max) } + + fn get_yuvfmt(width: u32, height: u32, i444: bool) -> EncodeYuvFormat { + let mut img = Default::default(); + let fmt = if i444 { + aom_img_fmt::AOM_IMG_FMT_I444 + } else { + aom_img_fmt::AOM_IMG_FMT_I420 + }; + unsafe { + aom_img_wrap( + &mut img, + fmt, + width as _, + height as _, + crate::STRIDE_ALIGN as _, + 0x1 as _, + ); + } + let pixfmt = if i444 { Pixfmt::I444 } else { Pixfmt::I420 }; + EncodeYuvFormat { + pixfmt, + w: img.w as _, + h: img.h as _, + stride: img.stride.map(|s| s as usize).to_vec(), + u: img.planes[1] as usize - img.planes[0] as usize, + v: img.planes[2] as usize - img.planes[0] as usize, + } + } } impl Drop for AomEncoder { @@ -524,6 +567,13 @@ impl GoogleImage for Image { fn planes(&self) -> Vec<*mut u8> { self.inner().planes.iter().map(|p| *p as *mut u8).collect() } + + fn chroma(&self) -> Chroma { + match self.inner().fmt { + aom_img_fmt::AOM_IMG_FMT_I444 => Chroma::I444, + _ => Chroma::I420, + } + } } impl Drop for Image { diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 0a3eca248..c2149288e 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -14,7 +14,7 @@ use crate::{ aom::{self, AomDecoder, AomEncoder, AomEncoderConfig}, common::GoogleImage, vpxcodec::{self, VpxDecoder, VpxDecoderConfig, VpxEncoder, VpxEncoderConfig, VpxVideoCodecId}, - CodecName, ImageRgb, + CodecName, EncodeYuvFormat, ImageRgb, }; use hbb_common::{ @@ -23,7 +23,7 @@ use hbb_common::{ config::PeerConfig, log, message_proto::{ - supported_decoding::PreferCodec, video_frame, EncodedVideoFrames, + supported_decoding::PreferCodec, video_frame, Chroma, CodecAbility, EncodedVideoFrames, SupportedDecoding, SupportedEncoding, VideoFrame, }, sysinfo::{System, SystemExt}, @@ -56,13 +56,13 @@ pub enum EncoderCfg { } pub trait EncoderApi { - fn new(cfg: EncoderCfg) -> ResultType + fn new(cfg: EncoderCfg, i444: bool) -> ResultType where Self: Sized; fn encode_to_message(&mut self, frame: &[u8], ms: i64) -> ResultType; - fn use_yuv(&self) -> bool; + fn yuvfmt(&self) -> EncodeYuvFormat; fn set_quality(&mut self, quality: Quality) -> ResultType<()>; @@ -107,18 +107,18 @@ pub enum EncodingUpdate { } impl Encoder { - pub fn new(config: EncoderCfg) -> ResultType { - log::info!("new encoder:{:?}", config); + pub fn new(config: EncoderCfg, i444: bool) -> ResultType { + log::info!("new encoder:{config:?}, i444:{i444}"); match config { EncoderCfg::VPX(_) => Ok(Encoder { - codec: Box::new(VpxEncoder::new(config)?), + codec: Box::new(VpxEncoder::new(config, i444)?), }), EncoderCfg::AOM(_) => Ok(Encoder { - codec: Box::new(AomEncoder::new(config)?), + codec: Box::new(AomEncoder::new(config, i444)?), }), #[cfg(feature = "hwcodec")] - EncoderCfg::HW(_) => match HwEncoder::new(config) { + EncoderCfg::HW(_) => match HwEncoder::new(config, i444) { Ok(hw) => Ok(Encoder { codec: Box::new(hw), }), @@ -230,6 +230,12 @@ impl Encoder { let mut encoding = SupportedEncoding { vp8: true, av1: true, + i444: Some(CodecAbility { + vp9: true, + av1: true, + ..Default::default() + }) + .into(), ..Default::default() }; #[cfg(feature = "hwcodec")] @@ -240,18 +246,41 @@ impl Encoder { } encoding } + + pub fn use_i444(config: &EncoderCfg) -> bool { + let decodings = PEER_DECODINGS.lock().unwrap().clone(); + let prefer_i444 = decodings + .iter() + .all(|d| d.1.prefer_chroma == Chroma::I444.into()); + let i444_useable = match config { + EncoderCfg::VPX(vpx) => match vpx.codec { + VpxVideoCodecId::VP8 => false, + VpxVideoCodecId::VP9 => decodings.iter().all(|d| d.1.i444.vp9), + }, + EncoderCfg::AOM(_) => decodings.iter().all(|d| d.1.i444.av1), + EncoderCfg::HW(_) => false, + }; + prefer_i444 && i444_useable && !decodings.is_empty() + } } impl Decoder { pub fn supported_decodings(id_for_perfer: Option<&str>) -> SupportedDecoding { + let (prefer, prefer_chroma) = Self::preference(id_for_perfer); + #[allow(unused_mut)] let mut decoding = SupportedDecoding { ability_vp8: 1, ability_vp9: 1, ability_av1: 1, - prefer: id_for_perfer - .map_or(PreferCodec::Auto, |id| Self::codec_preference(id)) - .into(), + i444: Some(CodecAbility { + vp9: true, + av1: true, + ..Default::default() + }) + .into(), + prefer: prefer.into(), + prefer_chroma: prefer_chroma.into(), ..Default::default() }; #[cfg(feature = "hwcodec")] @@ -314,31 +343,33 @@ impl Decoder { &mut self, frame: &video_frame::Union, rgb: &mut ImageRgb, + chroma: &mut Option, ) -> ResultType { match frame { video_frame::Union::Vp8s(vp8s) => { if let Some(vp8) = &mut self.vp8 { - Decoder::handle_vpxs_video_frame(vp8, vp8s, rgb) + Decoder::handle_vpxs_video_frame(vp8, vp8s, rgb, chroma) } else { bail!("vp8 decoder not available"); } } video_frame::Union::Vp9s(vp9s) => { if let Some(vp9) = &mut self.vp9 { - Decoder::handle_vpxs_video_frame(vp9, vp9s, rgb) + Decoder::handle_vpxs_video_frame(vp9, vp9s, rgb, chroma) } else { bail!("vp9 decoder not available"); } } video_frame::Union::Av1s(av1s) => { if let Some(av1) = &mut self.av1 { - Decoder::handle_av1s_video_frame(av1, av1s, rgb) + Decoder::handle_av1s_video_frame(av1, av1s, rgb, chroma) } else { bail!("av1 decoder not available"); } } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { + *chroma = Some(Chroma::I420); if let Some(decoder) = &mut self.hw.h264 { Decoder::handle_hw_video_frame(decoder, h264s, rgb, &mut self.i420) } else { @@ -347,6 +378,7 @@ impl Decoder { } #[cfg(feature = "hwcodec")] video_frame::Union::H265s(h265s) => { + *chroma = Some(Chroma::I420); if let Some(decoder) = &mut self.hw.h265 { Decoder::handle_hw_video_frame(decoder, h265s, rgb, &mut self.i420) } else { @@ -355,6 +387,7 @@ impl Decoder { } #[cfg(feature = "mediacodec")] video_frame::Union::H264s(h264s) => { + *chroma = Some(Chroma::I420); if let Some(decoder) = &mut self.media_codec.h264 { Decoder::handle_mediacodec_video_frame(decoder, h264s, rgb) } else { @@ -363,6 +396,7 @@ impl Decoder { } #[cfg(feature = "mediacodec")] video_frame::Union::H265s(h265s) => { + *chroma = Some(Chroma::I420); if let Some(decoder) = &mut self.media_codec.h265 { Decoder::handle_mediacodec_video_frame(decoder, h265s, rgb) } else { @@ -378,6 +412,7 @@ impl Decoder { decoder: &mut VpxDecoder, vpxs: &EncodedVideoFrames, rgb: &mut ImageRgb, + chroma: &mut Option, ) -> ResultType { let mut last_frame = vpxcodec::Image::new(); for vpx in vpxs.frames.iter() { @@ -393,6 +428,7 @@ impl Decoder { if last_frame.is_null() { Ok(false) } else { + *chroma = Some(last_frame.chroma()); last_frame.to(rgb); Ok(true) } @@ -403,6 +439,7 @@ impl Decoder { decoder: &mut AomDecoder, av1s: &EncodedVideoFrames, rgb: &mut ImageRgb, + chroma: &mut Option, ) -> ResultType { let mut last_frame = aom::Image::new(); for av1 in av1s.frames.iter() { @@ -418,6 +455,7 @@ impl Decoder { if last_frame.is_null() { Ok(false) } else { + *chroma = Some(last_frame.chroma()); last_frame.to(rgb); Ok(true) } @@ -457,12 +495,16 @@ impl Decoder { return Ok(false); } - fn codec_preference(id: &str) -> PreferCodec { - let codec = PeerConfig::load(id) - .options + fn preference(id: Option<&str>) -> (PreferCodec, Chroma) { + let id = id.unwrap_or_default(); + if id.is_empty() { + return (PreferCodec::Auto, Chroma::I420); + } + let options = PeerConfig::load(id).options; + let codec = options .get("codec-preference") .map_or("".to_owned(), |c| c.to_owned()); - if codec == "vp8" { + let codec = if codec == "vp8" { PreferCodec::VP8 } else if codec == "vp9" { PreferCodec::VP9 @@ -474,7 +516,13 @@ impl Decoder { PreferCodec::H265 } else { PreferCodec::Auto - } + }; + let chroma = if options.get("i444") == Some(&"Y".to_string()) { + Chroma::I444 + } else { + Chroma::I420 + }; + (codec, chroma) } } diff --git a/libs/scrap/src/common/convert.rs b/libs/scrap/src/common/convert.rs index 65e3a16ad..3bf0e7a03 100644 --- a/libs/scrap/src/common/convert.rs +++ b/libs/scrap/src/common/convert.rs @@ -1,367 +1,25 @@ -use super::vpx::*; -use std::os::raw::c_int; +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(improper_ctypes)] +#![allow(dead_code)] -extern "C" { - // seems libyuv uses reverse byte order compared with our view +include!(concat!(env!("OUT_DIR"), "/yuv_ffi.rs")); - pub fn ARGBRotate( - src_argb: *const u8, - src_stride_argb: c_int, - dst_argb: *mut u8, - dst_stride_argb: c_int, - src_width: c_int, - src_height: c_int, - mode: c_int, - ) -> c_int; +#[cfg(not(target_os = "ios"))] +use crate::Frame; +use crate::{generate_call_macro, EncodeYuvFormat, TraitFrame}; +use hbb_common::{bail, log, ResultType}; - pub fn ARGBMirror( - src_argb: *const u8, - src_stride_argb: c_int, - dst_argb: *mut u8, - dst_stride_argb: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - pub fn ARGBToI420( - src_bgra: *const u8, - src_stride_bgra: c_int, - dst_y: *mut u8, - dst_stride_y: c_int, - dst_u: *mut u8, - dst_stride_u: c_int, - dst_v: *mut u8, - dst_stride_v: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - pub fn ABGRToI420( - src_rgba: *const u8, - src_stride_rgba: c_int, - dst_y: *mut u8, - dst_stride_y: c_int, - dst_u: *mut u8, - dst_stride_u: c_int, - dst_v: *mut u8, - dst_stride_v: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - pub fn ARGBToNV12( - src_bgra: *const u8, - src_stride_bgra: c_int, - dst_y: *mut u8, - dst_stride_y: c_int, - dst_uv: *mut u8, - dst_stride_uv: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - pub fn NV12ToI420( - src_y: *const u8, - src_stride_y: c_int, - src_uv: *const u8, - src_stride_uv: c_int, - dst_y: *mut u8, - dst_stride_y: c_int, - dst_u: *mut u8, - dst_stride_u: c_int, - dst_v: *mut u8, - dst_stride_v: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - // I420ToRGB24: RGB little endian (bgr in memory) - // I420ToRaw: RGB big endian (rgb in memory) to RGBA. - pub fn I420ToRAW( - src_y: *const u8, - src_stride_y: c_int, - src_u: *const u8, - src_stride_u: c_int, - src_v: *const u8, - src_stride_v: c_int, - dst_rgba: *mut u8, - dst_stride_raw: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - pub fn I420ToARGB( - src_y: *const u8, - src_stride_y: c_int, - src_u: *const u8, - src_stride_u: c_int, - src_v: *const u8, - src_stride_v: c_int, - dst_rgba: *mut u8, - dst_stride_rgba: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - pub fn I420ToABGR( - src_y: *const u8, - src_stride_y: c_int, - src_u: *const u8, - src_stride_u: c_int, - src_v: *const u8, - src_stride_v: c_int, - dst_rgba: *mut u8, - dst_stride_rgba: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - pub fn NV12ToARGB( - src_y: *const u8, - src_stride_y: c_int, - src_uv: *const u8, - src_stride_uv: c_int, - dst_rgba: *mut u8, - dst_stride_rgba: c_int, - width: c_int, - height: c_int, - ) -> c_int; - - pub fn NV12ToABGR( - src_y: *const u8, - src_stride_y: c_int, - src_uv: *const u8, - src_stride_uv: c_int, - dst_rgba: *mut u8, - dst_stride_rgba: c_int, - width: c_int, - height: c_int, - ) -> c_int; -} - -// https://github.com/webmproject/libvpx/blob/master/vpx/src/vpx_image.c -#[inline] -fn get_vpx_i420_stride( - width: usize, - height: usize, - stride_align: usize, -) -> (usize, usize, usize, usize, usize, usize) { - let mut img = Default::default(); - unsafe { - vpx_img_wrap( - &mut img, - vpx_img_fmt::VPX_IMG_FMT_I420, - width as _, - height as _, - stride_align as _, - 0x1 as _, - ); - } - ( - img.w as _, - img.h as _, - img.stride[0] as _, - img.stride[1] as _, - img.planes[1] as usize - img.planes[0] as usize, - img.planes[2] as usize - img.planes[0] as usize, - ) -} - -pub fn i420_to_rgb(width: usize, height: usize, src: &[u8], dst: &mut Vec) { - let (_, _, src_stride_y, src_stride_uv, u, v) = - get_vpx_i420_stride(width, height, super::STRIDE_ALIGN); - let src_y = src.as_ptr(); - let src_u = src[u..].as_ptr(); - let src_v = src[v..].as_ptr(); - dst.resize(width * height * 3, 0); - unsafe { - super::I420ToRAW( - src_y, - src_stride_y as _, - src_u, - src_stride_uv as _, - src_v, - src_stride_uv as _, - dst.as_mut_ptr(), - (width * 3) as _, - width as _, - height as _, - ); - }; -} - -pub fn i420_to_bgra(width: usize, height: usize, src: &[u8], dst: &mut Vec) { - let (_, _, src_stride_y, src_stride_uv, u, v) = - get_vpx_i420_stride(width, height, super::STRIDE_ALIGN); - let src_y = src.as_ptr(); - let src_u = src[u..].as_ptr(); - let src_v = src[v..].as_ptr(); - dst.resize(width * height * 4, 0); - unsafe { - super::I420ToARGB( - src_y, - src_stride_y as _, - src_u, - src_stride_uv as _, - src_v, - src_stride_uv as _, - dst.as_mut_ptr(), - (width * 3) as _, - width as _, - height as _, - ); - }; -} - -pub fn bgra_to_i420(width: usize, height: usize, src: &[u8], dst: &mut Vec) { - let (_, h, dst_stride_y, dst_stride_uv, u, v) = - get_vpx_i420_stride(width, height, super::STRIDE_ALIGN); - dst.resize(h * dst_stride_y * 2, 0); // waste some memory to ensure memory safety - let dst_y = dst.as_mut_ptr(); - let dst_u = dst[u..].as_mut_ptr(); - let dst_v = dst[v..].as_mut_ptr(); - unsafe { - ARGBToI420( - src.as_ptr(), - (src.len() / height) as _, - dst_y, - dst_stride_y as _, - dst_u, - dst_stride_uv as _, - dst_v, - dst_stride_uv as _, - width as _, - height as _, - ); - } -} - -pub fn rgba_to_i420(width: usize, height: usize, src: &[u8], dst: &mut Vec) { - let (_, h, dst_stride_y, dst_stride_uv, u, v) = - get_vpx_i420_stride(width, height, super::STRIDE_ALIGN); - dst.resize(h * dst_stride_y * 2, 0); // waste some memory to ensure memory safety - let dst_y = dst.as_mut_ptr(); - let dst_u = dst[u..].as_mut_ptr(); - let dst_v = dst[v..].as_mut_ptr(); - unsafe { - ABGRToI420( - src.as_ptr(), - (src.len() / height) as _, - dst_y, - dst_stride_y as _, - dst_u, - dst_stride_uv as _, - dst_v, - dst_stride_uv as _, - width as _, - height as _, - ); - } -} - -pub unsafe fn nv12_to_i420( - src_y: *const u8, - src_stride_y: c_int, - src_uv: *const u8, - src_stride_uv: c_int, - width: usize, - height: usize, - dst: &mut Vec, -) { - let (_, h, dst_stride_y, dst_stride_uv, u, v) = - get_vpx_i420_stride(width, height, super::STRIDE_ALIGN); - dst.resize(h * dst_stride_y * 2, 0); // waste some memory to ensure memory safety - let dst_y = dst.as_mut_ptr(); - let dst_u = dst[u..].as_mut_ptr(); - let dst_v = dst[v..].as_mut_ptr(); - NV12ToI420( - src_y, - src_stride_y, - src_uv, - src_stride_uv, - dst_y, - dst_stride_y as _, - dst_u, - dst_stride_uv as _, - dst_v, - dst_stride_uv as _, - width as _, - height as _, - ); -} +generate_call_macro!(call_yuv, false); #[cfg(feature = "hwcodec")] pub mod hw { + use super::*; use crate::ImageFormat; - use hbb_common::{anyhow::anyhow, ResultType}; #[cfg(target_os = "windows")] use hwcodec::{ffmpeg::ffmpeg_linesize_offset_length, AVPixelFormat}; - pub fn hw_bgra_to_i420( - width: usize, - height: usize, - stride: &[i32], - offset: &[i32], - length: i32, - src: &[u8], - dst: &mut Vec, - ) { - let stride_y = stride[0] as usize; - let stride_u = stride[1] as usize; - let stride_v = stride[2] as usize; - let offset_u = offset[0] as usize; - let offset_v = offset[1] as usize; - - dst.resize(length as _, 0); - let dst_y = dst.as_mut_ptr(); - let dst_u = dst[offset_u..].as_mut_ptr(); - let dst_v = dst[offset_v..].as_mut_ptr(); - unsafe { - super::ARGBToI420( - src.as_ptr(), - (src.len() / height) as _, - dst_y, - stride_y as _, - dst_u, - stride_u as _, - dst_v, - stride_v as _, - width as _, - height as _, - ); - } - } - - pub fn hw_bgra_to_nv12( - width: usize, - height: usize, - stride: &[i32], - offset: &[i32], - length: i32, - src: &[u8], - dst: &mut Vec, - ) { - let stride_y = stride[0] as usize; - let stride_uv = stride[1] as usize; - let offset_uv = offset[0] as usize; - - dst.resize(length as _, 0); - let dst_y = dst.as_mut_ptr(); - let dst_uv = dst[offset_uv..].as_mut_ptr(); - unsafe { - super::ARGBToNV12( - src.as_ptr(), - (src.len() / height) as _, - dst_y, - stride_y as _, - dst_uv, - stride_uv as _, - width as _, - height as _, - ); - } - } - #[cfg(target_os = "windows")] pub fn hw_nv12_to( fmt: ImageFormat, @@ -386,61 +44,59 @@ pub mod hw { let i420_stride_v = linesize_i420[2]; i420.resize(i420_len as _, 0); - unsafe { - let i420_offset_y = i420.as_ptr().add(0) as _; - let i420_offset_u = i420.as_ptr().add(offset_i420[0] as _) as _; - let i420_offset_v = i420.as_ptr().add(offset_i420[1] as _) as _; - super::NV12ToI420( - src_y.as_ptr(), - nv12_stride_y as _, - src_uv.as_ptr(), - nv12_stride_uv as _, - i420_offset_y, - i420_stride_y, - i420_offset_u, - i420_stride_u, - i420_offset_v, - i420_stride_v, - width as _, - height as _, - ); - match fmt { - ImageFormat::ARGB => { - super::I420ToARGB( - i420_offset_y, - i420_stride_y, - i420_offset_u, - i420_stride_u, - i420_offset_v, - i420_stride_v, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ); - } - ImageFormat::ABGR => { - super::I420ToABGR( - i420_offset_y, - i420_stride_y, - i420_offset_u, - i420_stride_u, - i420_offset_v, - i420_stride_v, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ); - } - _ => { - return Err(anyhow!("unsupported image format")); - } + let i420_offset_y = unsafe { i420.as_ptr().add(0) as _ }; + let i420_offset_u = unsafe { i420.as_ptr().add(offset_i420[0] as _) as _ }; + let i420_offset_v = unsafe { i420.as_ptr().add(offset_i420[1] as _) as _ }; + call_yuv!(NV12ToI420( + src_y.as_ptr(), + nv12_stride_y as _, + src_uv.as_ptr(), + nv12_stride_uv as _, + i420_offset_y, + i420_stride_y, + i420_offset_u, + i420_stride_u, + i420_offset_v, + i420_stride_v, + width as _, + height as _, + )); + match fmt { + ImageFormat::ARGB => { + call_yuv!(I420ToARGB( + i420_offset_y, + i420_stride_y, + i420_offset_u, + i420_stride_u, + i420_offset_v, + i420_stride_v, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + )); } - return Ok(()); - }; + ImageFormat::ABGR => { + call_yuv!(I420ToABGR( + i420_offset_y, + i420_stride_y, + i420_offset_u, + i420_stride_u, + i420_offset_v, + i420_stride_v, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + )); + } + _ => { + bail!("unsupported image format"); + } + } + return Ok(()); } - return Err(anyhow!("get linesize offset failed")); + bail!("get linesize offset failed"); } #[cfg(not(target_os = "windows"))] @@ -457,41 +113,34 @@ pub mod hw { _align: usize, ) -> ResultType<()> { dst.resize(width * height * 4, 0); - unsafe { - match fmt { - ImageFormat::ARGB => { - match super::NV12ToARGB( - src_y.as_ptr(), - src_stride_y as _, - src_uv.as_ptr(), - src_stride_uv as _, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ) { - 0 => Ok(()), - _ => Err(anyhow!("NV12ToARGB failed")), - } - } - ImageFormat::ABGR => { - match super::NV12ToABGR( - src_y.as_ptr(), - src_stride_y as _, - src_uv.as_ptr(), - src_stride_uv as _, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ) { - 0 => Ok(()), - _ => Err(anyhow!("NV12ToABGR failed")), - } - } - _ => Err(anyhow!("unsupported image format")), + match fmt { + ImageFormat::ARGB => { + call_yuv!(NV12ToARGB( + src_y.as_ptr(), + src_stride_y as _, + src_uv.as_ptr(), + src_stride_uv as _, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + )); } + ImageFormat::ABGR => { + call_yuv!(NV12ToABGR( + src_y.as_ptr(), + src_stride_y as _, + src_uv.as_ptr(), + src_stride_uv as _, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + )); + } + _ => bail!("unsupported image format"), } + Ok(()) } pub fn hw_i420_to( @@ -505,43 +154,173 @@ pub mod hw { src_stride_u: usize, src_stride_v: usize, dst: &mut Vec, - ) { + ) -> ResultType<()> { let src_y = src_y.as_ptr(); let src_u = src_u.as_ptr(); let src_v = src_v.as_ptr(); dst.resize(width * height * 4, 0); - unsafe { - match fmt { - ImageFormat::ARGB => { - super::I420ToARGB( - src_y, - src_stride_y as _, - src_u, - src_stride_u as _, - src_v, - src_stride_v as _, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ); - } - ImageFormat::ABGR => { - super::I420ToABGR( - src_y, - src_stride_y as _, - src_u, - src_stride_u as _, - src_v, - src_stride_v as _, - dst.as_mut_ptr(), - (width * 4) as _, - width as _, - height as _, - ); - } - _ => {} + match fmt { + ImageFormat::ARGB => { + call_yuv!(I420ToARGB( + src_y, + src_stride_y as _, + src_u, + src_stride_u as _, + src_v, + src_stride_v as _, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + )); } + ImageFormat::ABGR => { + call_yuv!(I420ToABGR( + src_y, + src_stride_y as _, + src_u, + src_stride_u as _, + src_v, + src_stride_v as _, + dst.as_mut_ptr(), + (width * 4) as _, + width as _, + height as _, + )); + } + _ => bail!("unsupported image format"), }; + Ok(()) } } +#[cfg(not(target_os = "ios"))] +pub fn convert_to_yuv( + captured: &Frame, + dst_fmt: EncodeYuvFormat, + dst: &mut Vec, + mid_data: &mut Vec, +) -> ResultType<()> { + let src = captured.data(); + let src_stride = captured.stride(); + let src_pixfmt = captured.pixfmt(); + let src_width = captured.width(); + let src_height = captured.height(); + if src_width > dst_fmt.w || src_height > dst_fmt.h { + bail!( + "src rect > dst rect: ({src_width}, {src_height}) > ({},{})", + dst_fmt.w, + dst_fmt.h + ); + } + if src_pixfmt == crate::Pixfmt::BGRA || src_pixfmt == crate::Pixfmt::RGBA { + if src.len() < src_stride[0] * src_height { + bail!( + "wrong src len, {} < {} * {}", + src.len(), + src_stride[0], + src_height + ); + } + } + let align = |x:usize| { + (x + 63) / 64 * 64 + }; + + match (src_pixfmt, dst_fmt.pixfmt) { + (crate::Pixfmt::BGRA, crate::Pixfmt::I420) | (crate::Pixfmt::RGBA, crate::Pixfmt::I420) => { + let dst_stride_y = dst_fmt.stride[0]; + let dst_stride_uv = dst_fmt.stride[1]; + dst.resize(dst_fmt.h * dst_stride_y * 2, 0); // waste some memory to ensure memory safety + let dst_y = dst.as_mut_ptr(); + let dst_u = dst[dst_fmt.u..].as_mut_ptr(); + let dst_v = dst[dst_fmt.v..].as_mut_ptr(); + let f = if src_pixfmt == crate::Pixfmt::BGRA { + ARGBToI420 + } else { + ABGRToI420 + }; + call_yuv!(f( + src.as_ptr(), + src_stride[0] as _, + dst_y, + dst_stride_y as _, + dst_u, + dst_stride_uv as _, + dst_v, + dst_stride_uv as _, + src_width as _, + src_height as _, + )); + } + (crate::Pixfmt::BGRA, crate::Pixfmt::NV12) | (crate::Pixfmt::RGBA, crate::Pixfmt::NV12) => { + let dst_stride_y = dst_fmt.stride[0]; + let dst_stride_uv = dst_fmt.stride[1]; + dst.resize( + align(dst_fmt.h) * (align(dst_stride_y) + align(dst_stride_uv / 2)), + 0, + ); + let dst_y = dst.as_mut_ptr(); + let dst_uv = dst[dst_fmt.u..].as_mut_ptr(); + let f = if src_pixfmt == crate::Pixfmt::BGRA { + ARGBToNV12 + } else { + ABGRToNV12 + }; + call_yuv!(f( + src.as_ptr(), + src_stride[0] as _, + dst_y, + dst_stride_y as _, + dst_uv, + dst_stride_uv as _, + src_width as _, + src_height as _, + )); + } + (crate::Pixfmt::BGRA, crate::Pixfmt::I444) | (crate::Pixfmt::RGBA, crate::Pixfmt::I444) => { + let dst_stride_y = dst_fmt.stride[0]; + let dst_stride_u = dst_fmt.stride[1]; + let dst_stride_v = dst_fmt.stride[2]; + dst.resize( + align(dst_fmt.h) * (align(dst_stride_y) + align(dst_stride_u) + align(dst_stride_v)), + 0, + ); + let dst_y = dst.as_mut_ptr(); + let dst_u = dst[dst_fmt.u..].as_mut_ptr(); + let dst_v = dst[dst_fmt.v..].as_mut_ptr(); + let src = if src_pixfmt == crate::Pixfmt::BGRA { + src + } else { + mid_data.resize(src.len(), 0); + call_yuv!(ABGRToARGB( + src.as_ptr(), + src_stride[0] as _, + mid_data.as_mut_ptr(), + src_stride[0] as _, + src_width as _, + src_height as _, + )); + mid_data + }; + call_yuv!(ARGBToI444( + src.as_ptr(), + src_stride[0] as _, + dst_y, + dst_stride_y as _, + dst_u, + dst_stride_u as _, + dst_v, + dst_stride_v as _, + src_width as _, + src_height as _, + )); + } + _ => { + bail!( + "convert not support, {src_pixfmt:?} -> {:?}", + dst_fmt.pixfmt + ); + } + } + Ok(()) +} diff --git a/libs/scrap/src/common/dxgi.rs b/libs/scrap/src/common/dxgi.rs index 287d85880..3bf1783a8 100644 --- a/libs/scrap/src/common/dxgi.rs +++ b/libs/scrap/src/common/dxgi.rs @@ -1,10 +1,9 @@ -use crate::{common::TraitCapturer, dxgi}; +use crate::{common::TraitCapturer, dxgi, Pixfmt}; use std::{ io::{ self, ErrorKind::{NotFound, TimedOut, WouldBlock}, }, - ops, time::Duration, }; @@ -15,10 +14,10 @@ pub struct Capturer { } impl Capturer { - pub fn new(display: Display, yuv: bool) -> io::Result { + pub fn new(display: Display) -> io::Result { let width = display.width(); let height = display.height(); - let inner = dxgi::Capturer::new(display.0, yuv)?; + let inner = dxgi::Capturer::new(display.0)?; Ok(Capturer { inner, width, @@ -40,13 +39,9 @@ impl Capturer { } impl TraitCapturer for Capturer { - fn set_use_yuv(&mut self, use_yuv: bool) { - self.inner.set_use_yuv(use_yuv); - } - fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { match self.inner.frame(timeout.as_millis() as _) { - Ok(frame) => Ok(Frame(frame)), + Ok(frame) => Ok(Frame::new(frame, self.width, self.height)), Err(ref error) if error.kind() == TimedOut => Err(WouldBlock.into()), Err(error) => Err(error), } @@ -61,12 +56,46 @@ impl TraitCapturer for Capturer { } } -pub struct Frame<'a>(pub &'a [u8]); +pub struct Frame<'a> { + data: &'a [u8], + width: usize, + height: usize, + stride: Vec, +} -impl<'a> ops::Deref for Frame<'a> { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.0 +impl<'a> Frame<'a> { + pub fn new(data: &'a [u8], width: usize, height: usize) -> Self { + let stride0 = data.len() / height; + let mut stride = Vec::new(); + stride.push(stride0); + Frame { + data, + width, + height, + stride, + } + } +} + +impl<'a> crate::TraitFrame for Frame<'a> { + fn data(&self) -> &[u8] { + self.data + } + + fn width(&self) -> usize { + self.width + } + + fn height(&self) -> usize { + self.height + } + + fn stride(&self) -> Vec { + self.stride.clone() + } + + fn pixfmt(&self) -> Pixfmt { + Pixfmt::BGRA } } @@ -134,9 +163,9 @@ impl CapturerMag { dxgi::mag::CapturerMag::is_supported() } - pub fn new(origin: (i32, i32), width: usize, height: usize, use_yuv: bool) -> io::Result { + pub fn new(origin: (i32, i32), width: usize, height: usize) -> io::Result { Ok(CapturerMag { - inner: dxgi::mag::CapturerMag::new(origin, width, height, use_yuv)?, + inner: dxgi::mag::CapturerMag::new(origin, width, height)?, data: Vec::new(), }) } @@ -151,13 +180,13 @@ impl CapturerMag { } impl TraitCapturer for CapturerMag { - fn set_use_yuv(&mut self, use_yuv: bool) { - self.inner.set_use_yuv(use_yuv) - } - fn frame<'a>(&'a mut self, _timeout_ms: Duration) -> io::Result> { self.inner.frame(&mut self.data)?; - Ok(Frame(&self.data)) + Ok(Frame::new( + &self.data, + self.inner.get_rect().1, + self.inner.get_rect().2, + )) } fn is_gdi(&self) -> bool { diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index eaaddbfad..1b7f84fe6 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -1,6 +1,6 @@ use crate::{ codec::{base_bitrate, codec_thread_num, EncoderApi, EncoderCfg}, - hw, ImageFormat, ImageRgb, HW_STRIDE_ALIGN, + hw, ImageFormat, ImageRgb, Pixfmt, HW_STRIDE_ALIGN, }; use hbb_common::{ allow_err, @@ -31,7 +31,6 @@ const DEFAULT_RC: RateControl = RC_DEFAULT; pub struct HwEncoder { encoder: Encoder, - yuv: Vec, pub format: DataFormat, pub pixfmt: AVPixelFormat, width: u32, @@ -40,7 +39,7 @@ pub struct HwEncoder { } impl EncoderApi for HwEncoder { - fn new(cfg: EncoderCfg) -> ResultType + fn new(cfg: EncoderCfg, _i444: bool) -> ResultType where Self: Sized, { @@ -78,7 +77,6 @@ impl EncoderApi for HwEncoder { match Encoder::new(ctx.clone()) { Ok(encoder) => Ok(HwEncoder { encoder, - yuv: vec![], format, pixfmt: ctx.pixfmt, width: ctx.width as _, @@ -118,8 +116,31 @@ impl EncoderApi for HwEncoder { } } - fn use_yuv(&self) -> bool { - false + fn yuvfmt(&self) -> crate::EncodeYuvFormat { + let pixfmt = if self.pixfmt == AVPixelFormat::AV_PIX_FMT_NV12 { + Pixfmt::NV12 + } else { + Pixfmt::I420 + }; + let stride = self + .encoder + .linesize + .clone() + .drain(..) + .map(|i| i as usize) + .collect(); + crate::EncodeYuvFormat { + pixfmt, + w: self.encoder.ctx.width as _, + h: self.encoder.ctx.height as _, + stride, + u: self.encoder.offset[0] as _, + v: if pixfmt == Pixfmt::NV12 { + 0 + } else { + self.encoder.offset[1] as _ + }, + } } fn set_quality(&mut self, quality: crate::codec::Quality) -> ResultType<()> { @@ -145,29 +166,8 @@ impl HwEncoder { }) } - pub fn encode(&mut self, bgra: &[u8]) -> ResultType> { - match self.pixfmt { - AVPixelFormat::AV_PIX_FMT_YUV420P => hw::hw_bgra_to_i420( - self.encoder.ctx.width as _, - self.encoder.ctx.height as _, - &self.encoder.linesize, - &self.encoder.offset, - self.encoder.length, - bgra, - &mut self.yuv, - ), - AVPixelFormat::AV_PIX_FMT_NV12 => hw::hw_bgra_to_nv12( - self.encoder.ctx.width as _, - self.encoder.ctx.height as _, - &self.encoder.linesize, - &self.encoder.offset, - self.encoder.length, - bgra, - &mut self.yuv, - ), - } - - match self.encoder.encode(&self.yuv) { + pub fn encode(&mut self, yuv: &[u8]) -> ResultType> { + match self.encoder.encode(yuv) { Ok(v) => { let mut data = Vec::::new(); data.append(v); @@ -245,7 +245,7 @@ impl HwDecoder { pub fn decode(&mut self, data: &[u8]) -> ResultType> { match self.decoder.decode(data) { Ok(v) => Ok(v.iter().map(|f| HwDecoderImage { frame: f }).collect()), - Err(_) => Ok(vec![]), + Err(e) => Err(anyhow!(e)), } } } @@ -274,7 +274,7 @@ impl HwDecoderImage<'_> { &mut rgb.raw as _, i420, HW_STRIDE_ALIGN, - ), + )?, AVPixelFormat::AV_PIX_FMT_YUV420P => { hw::hw_i420_to( rgb.fmt(), @@ -287,10 +287,10 @@ impl HwDecoderImage<'_> { frame.linesize[1] as _, frame.linesize[2] as _, &mut rgb.raw as _, - ); - return Ok(()); + )?; } } + Ok(()) } pub fn bgra(&self, bgra: &mut Vec, i420: &mut Vec) -> ResultType<()> { diff --git a/libs/scrap/src/common/linux.rs b/libs/scrap/src/common/linux.rs index 3efcdb054..01c573d06 100644 --- a/libs/scrap/src/common/linux.rs +++ b/libs/scrap/src/common/linux.rs @@ -11,10 +11,10 @@ pub enum Capturer { } impl Capturer { - pub fn new(display: Display, yuv: bool) -> io::Result { + pub fn new(display: Display) -> io::Result { Ok(match display { - Display::X11(d) => Capturer::X11(x11::Capturer::new(d, yuv)?), - Display::WAYLAND(d) => Capturer::WAYLAND(wayland::Capturer::new(d, yuv)?), + Display::X11(d) => Capturer::X11(x11::Capturer::new(d)?), + Display::WAYLAND(d) => Capturer::WAYLAND(wayland::Capturer::new(d)?), }) } @@ -34,13 +34,6 @@ impl Capturer { } impl TraitCapturer for Capturer { - fn set_use_yuv(&mut self, use_yuv: bool) { - match self { - Capturer::X11(d) => d.set_use_yuv(use_yuv), - Capturer::WAYLAND(d) => d.set_use_yuv(use_yuv), - } - } - fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { match self { Capturer::X11(d) => d.frame(timeout), diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 75e350b49..4433e512f 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -1,5 +1,8 @@ pub use self::vpxcodec::*; -use hbb_common::message_proto::{video_frame, VideoFrame}; +use hbb_common::{ + log, + message_proto::{video_frame, Chroma, VideoFrame}, +}; use std::slice; cfg_if! { @@ -96,8 +99,6 @@ pub fn would_block_if_equal(old: &mut Vec, b: &[u8]) -> std::io::Result<()> } pub trait TraitCapturer { - fn set_use_yuv(&mut self, use_yuv: bool); - // We doesn't support #[cfg(not(any(target_os = "ios")))] fn frame<'a>(&'a mut self, timeout: std::time::Duration) -> std::io::Result>; @@ -108,6 +109,36 @@ pub trait TraitCapturer { fn set_gdi(&mut self) -> bool; } +pub trait TraitFrame { + fn data(&self) -> &[u8]; + + fn width(&self) -> usize; + + fn height(&self) -> usize; + + fn stride(&self) -> Vec; + + fn pixfmt(&self) -> Pixfmt; +} +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Pixfmt { + BGRA, + RGBA, + I420, + NV12, + I444, +} + +#[derive(Debug, Clone)] +pub struct EncodeYuvFormat { + pub pixfmt: Pixfmt, + pub w: usize, + pub h: usize, + pub stride: Vec, + pub u: usize, + pub v: usize, +} + #[cfg(x11)] #[inline] pub fn is_x11() -> bool { @@ -260,6 +291,7 @@ pub trait GoogleImage { fn height(&self) -> usize; fn stride(&self) -> Vec; fn planes(&self) -> Vec<*mut u8>; + fn chroma(&self) -> Chroma; fn get_bytes_per_row(w: usize, fmt: ImageFormat, stride: usize) -> usize { let bytes_per_pixel = match fmt { ImageFormat::Raw => 3, @@ -278,8 +310,8 @@ pub trait GoogleImage { let stride = self.stride(); let planes = self.planes(); unsafe { - match rgb.fmt() { - ImageFormat::Raw => { + match (self.chroma(), rgb.fmt()) { + (Chroma::I420, ImageFormat::Raw) => { super::I420ToRAW( planes[0], stride[0], @@ -293,7 +325,7 @@ pub trait GoogleImage { self.height() as _, ); } - ImageFormat::ARGB => { + (Chroma::I420, ImageFormat::ARGB) => { super::I420ToARGB( planes[0], stride[0], @@ -307,7 +339,7 @@ pub trait GoogleImage { self.height() as _, ); } - ImageFormat::ABGR => { + (Chroma::I420, ImageFormat::ABGR) => { super::I420ToABGR( planes[0], stride[0], @@ -321,6 +353,36 @@ pub trait GoogleImage { self.height() as _, ); } + (Chroma::I444, ImageFormat::ARGB) => { + super::I444ToARGB( + planes[0], + stride[0], + planes[1], + stride[1], + planes[2], + stride[2], + rgb.raw.as_mut_ptr(), + bytes_per_row as _, + self.width() as _, + self.height() as _, + ); + } + (Chroma::I444, ImageFormat::ABGR) => { + super::I444ToABGR( + planes[0], + stride[0], + planes[1], + stride[1], + planes[2], + stride[2], + rgb.raw.as_mut_ptr(), + bytes_per_row as _, + self.width() as _, + self.height() as _, + ); + } + // (Chroma::I444, ImageFormat::Raw), new version libyuv have I444ToRAW + _ => log::error!("unsupported pixfmt:{:?}", self.chroma()), } } } diff --git a/libs/scrap/src/common/quartz.rs b/libs/scrap/src/common/quartz.rs index dc59edac1..d84913999 100644 --- a/libs/scrap/src/common/quartz.rs +++ b/libs/scrap/src/common/quartz.rs @@ -1,18 +1,16 @@ -use crate::quartz; +use crate::{quartz, Pixfmt}; use std::marker::PhantomData; use std::sync::{Arc, Mutex, TryLockError}; -use std::{io, mem, ops}; +use std::{io, mem}; pub struct Capturer { inner: quartz::Capturer, frame: Arc>>, - use_yuv: bool, - i420: Vec, saved_raw_data: Vec, // for faster compare and copy } impl Capturer { - pub fn new(display: Display, use_yuv: bool) -> io::Result { + pub fn new(display: Display) -> io::Result { let frame = Arc::new(Mutex::new(None)); let f = frame.clone(); @@ -20,11 +18,7 @@ impl Capturer { display.0, display.width(), display.height(), - if use_yuv { - quartz::PixelFormat::YCbCr420Video - } else { - quartz::PixelFormat::Argb8888 - }, + quartz::PixelFormat::Argb8888, Default::default(), move |inner| { if let Ok(mut f) = f.lock() { @@ -37,8 +31,6 @@ impl Capturer { Ok(Capturer { inner, frame, - use_yuv, - i420: Vec::new(), saved_raw_data: Vec::new(), }) } @@ -53,10 +45,6 @@ impl Capturer { } impl crate::TraitCapturer for Capturer { - fn set_use_yuv(&mut self, use_yuv: bool) { - self.use_yuv = use_yuv; - } - fn frame<'a>(&'a mut self, _timeout_ms: std::time::Duration) -> io::Result> { match self.frame.try_lock() { Ok(mut handle) => { @@ -66,10 +54,13 @@ impl crate::TraitCapturer for Capturer { match frame { Some(mut frame) => { crate::would_block_if_equal(&mut self.saved_raw_data, frame.inner())?; - if self.use_yuv { - frame.nv12_to_i420(self.width(), self.height(), &mut self.i420); - } - Ok(Frame(frame, PhantomData)) + frame.surface_to_bgra(self.height()); + Ok(Frame { + frame, + data: PhantomData, + width: self.width(), + height: self.height(), + }) } None => Err(io::ErrorKind::WouldBlock.into()), @@ -83,12 +74,34 @@ impl crate::TraitCapturer for Capturer { } } -pub struct Frame<'a>(pub quartz::Frame, PhantomData<&'a [u8]>); +pub struct Frame<'a> { + frame: quartz::Frame, + data: PhantomData<&'a [u8]>, + width: usize, + height: usize, +} -impl<'a> ops::Deref for Frame<'a> { - type Target = [u8]; - fn deref(&self) -> &[u8] { - &*self.0 +impl<'a> crate::TraitFrame for Frame<'a> { + fn data(&self) -> &[u8] { + &*self.frame + } + + fn width(&self) -> usize { + self.width + } + + fn height(&self) -> usize { + self.height + } + + fn stride(&self) -> Vec { + let mut v = Vec::new(); + v.push(self.frame.stride()); + v + } + + fn pixfmt(&self) -> Pixfmt { + Pixfmt::BGRA } } diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index edf620465..19ede9bba 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -4,11 +4,11 @@ use hbb_common::anyhow::{anyhow, Context}; use hbb_common::log; -use hbb_common::message_proto::{EncodedVideoFrame, EncodedVideoFrames, VideoFrame}; +use hbb_common::message_proto::{Chroma, EncodedVideoFrame, EncodedVideoFrames, VideoFrame}; use hbb_common::ResultType; use crate::codec::{base_bitrate, codec_thread_num, EncoderApi, Quality}; -use crate::{GoogleImage, STRIDE_ALIGN}; +use crate::{EncodeYuvFormat, GoogleImage, Pixfmt, STRIDE_ALIGN}; use super::vpx::{vp8e_enc_control_id::*, vpx_codec_err_t::*, *}; use crate::{generate_call_macro, generate_call_ptr_macro, Error, Result}; @@ -39,6 +39,8 @@ pub struct VpxEncoder { width: usize, height: usize, id: VpxVideoCodecId, + i444: bool, + yuvfmt: EncodeYuvFormat, } pub struct VpxDecoder { @@ -46,7 +48,7 @@ pub struct VpxDecoder { } impl EncoderApi for VpxEncoder { - fn new(cfg: crate::codec::EncoderCfg) -> ResultType + fn new(cfg: crate::codec::EncoderCfg, i444: bool) -> ResultType where Self: Sized, { @@ -98,6 +100,13 @@ impl EncoderApi for VpxEncoder { } else { c.rc_target_bitrate = base_bitrate; } + // https://chromium.googlesource.com/webm/libvpx/+/refs/heads/main/vp9/common/vp9_enums.h#29 + // https://chromium.googlesource.com/webm/libvpx/+/refs/heads/main/vp8/vp8_cx_iface.c#282 + c.g_profile = if i444 && config.codec == VpxVideoCodecId::VP9 { + 1 + } else { + 0 + }; /* The VPX encoder supports two-pass encoding for rate control purposes. @@ -166,6 +175,8 @@ impl EncoderApi for VpxEncoder { width: config.width as _, height: config.height as _, id: config.codec, + i444, + yuvfmt: Self::get_yuvfmt(config.width, config.height, i444), }) } _ => Err(anyhow!("encoder type mismatch")), @@ -192,8 +203,8 @@ impl EncoderApi for VpxEncoder { } } - fn use_yuv(&self) -> bool { - true + fn yuvfmt(&self) -> crate::EncodeYuvFormat { + self.yuvfmt.clone() } fn set_quality(&mut self, quality: Quality) -> ResultType<()> { @@ -219,14 +230,20 @@ impl EncoderApi for VpxEncoder { impl VpxEncoder { pub fn encode(&mut self, pts: i64, data: &[u8], stride_align: usize) -> Result { - if 2 * data.len() < 3 * self.width * self.height { + let bpp = if self.i444 { 24 } else { 12 }; + if data.len() < self.width * self.height * bpp / 8 { return Err(Error::FailedCall("len not enough".to_string())); } + let fmt = if self.i444 { + vpx_img_fmt::VPX_IMG_FMT_I444 + } else { + vpx_img_fmt::VPX_IMG_FMT_I420 + }; let mut image = Default::default(); call_vpx_ptr!(vpx_img_wrap( &mut image, - vpx_img_fmt::VPX_IMG_FMT_I420, + fmt, self.width as _, self.height as _, stride_align as _, @@ -319,6 +336,34 @@ impl VpxEncoder { (q_min, q_max) } + + fn get_yuvfmt(width: u32, height: u32, i444: bool) -> EncodeYuvFormat { + let mut img = Default::default(); + let fmt = if i444 { + vpx_img_fmt::VPX_IMG_FMT_I444 + } else { + vpx_img_fmt::VPX_IMG_FMT_I420 + }; + unsafe { + vpx_img_wrap( + &mut img, + fmt, + width as _, + height as _, + crate::STRIDE_ALIGN as _, + 0x1 as _, + ); + } + let pixfmt = if i444 { Pixfmt::I444 } else { Pixfmt::I420 }; + EncodeYuvFormat { + pixfmt, + w: img.w as _, + h: img.h as _, + stride: img.stride.map(|s| s as usize).to_vec(), + u: img.planes[1] as usize - img.planes[0] as usize, + v: img.planes[2] as usize - img.planes[0] as usize, + } + } } impl Drop for VpxEncoder { @@ -533,6 +578,13 @@ impl GoogleImage for Image { fn planes(&self) -> Vec<*mut u8> { self.inner().planes.iter().map(|p| *p as *mut u8).collect() } + + fn chroma(&self) -> Chroma { + match self.inner().fmt { + vpx_img_fmt::VPX_IMG_FMT_I444 => Chroma::I444, + _ => Chroma::I420, + } + } } impl Drop for Image { diff --git a/libs/scrap/src/common/wayland.rs b/libs/scrap/src/common/wayland.rs index 2d3abeeaf..cf36a83a4 100644 --- a/libs/scrap/src/common/wayland.rs +++ b/libs/scrap/src/common/wayland.rs @@ -2,7 +2,7 @@ use crate::common::{x11::Frame, TraitCapturer}; use crate::wayland::{capturable::*, *}; use std::{io, sync::RwLock, time::Duration}; -pub struct Capturer(Display, Box, bool, Vec); +pub struct Capturer(Display, Box, Vec); static mut IS_CURSOR_EMBEDDED: Option = None; @@ -45,9 +45,9 @@ fn map_err(err: E) -> io::Error { } impl Capturer { - pub fn new(display: Display, yuv: bool) -> io::Result { + pub fn new(display: Display) -> io::Result { let r = display.0.recorder(false).map_err(map_err)?; - Ok(Capturer(display, r, yuv, Default::default())) + Ok(Capturer(display, r, Default::default())) } pub fn width(&self) -> usize { @@ -60,24 +60,10 @@ impl Capturer { } impl TraitCapturer for Capturer { - fn set_use_yuv(&mut self, use_yuv: bool) { - self.2 = use_yuv; - } - fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { match self.1.capture(timeout.as_millis() as _).map_err(map_err)? { - PixelProvider::BGR0(w, h, x) => Ok(Frame(if self.2 { - crate::common::bgra_to_i420(w as _, h as _, &x, &mut self.3); - &self.3[..] - } else { - x - })), - PixelProvider::RGB0(w, h, x) => Ok(Frame(if self.2 { - crate::common::rgba_to_i420(w as _, h as _, &x, &mut self.3); - &self.3[..] - } else { - x - })), + PixelProvider::BGR0(w, h, x) => Ok(Frame::new(x, crate::Pixfmt::BGRA, w, h)), + PixelProvider::RGB0(w, h, x) => Ok(Frame::new(x, crate::Pixfmt::RGBA, w,h)), PixelProvider::NONE => Err(std::io::ErrorKind::WouldBlock.into()), _ => Err(map_err("Invalid data")), } diff --git a/libs/scrap/src/common/x11.rs b/libs/scrap/src/common/x11.rs index 514608e4a..08ec5e1f1 100644 --- a/libs/scrap/src/common/x11.rs +++ b/libs/scrap/src/common/x11.rs @@ -1,13 +1,13 @@ -use crate::{common::TraitCapturer, x11}; -use std::{io, ops, time::Duration}; +use crate::{common::TraitCapturer, x11, Pixfmt, TraitFrame}; +use std::{io, time::Duration}; pub struct Capturer(x11::Capturer); pub const IS_CURSOR_EMBEDDED: bool = false; impl Capturer { - pub fn new(display: Display, yuv: bool) -> io::Result { - x11::Capturer::new(display.0, yuv).map(Capturer) + pub fn new(display: Display) -> io::Result { + x11::Capturer::new(display.0).map(Capturer) } pub fn width(&self) -> usize { @@ -20,21 +20,53 @@ impl Capturer { } impl TraitCapturer for Capturer { - fn set_use_yuv(&mut self, use_yuv: bool) { - self.0.set_use_yuv(use_yuv); - } - fn frame<'a>(&'a mut self, _timeout: Duration) -> io::Result> { - Ok(Frame(self.0.frame()?)) + Ok(self.0.frame()?) } } -pub struct Frame<'a>(pub &'a [u8]); +pub struct Frame<'a> { + pub data: &'a [u8], + pub pixfmt: Pixfmt, + pub width: usize, + pub height: usize, + pub stride: Vec, +} -impl<'a> ops::Deref for Frame<'a> { - type Target = [u8]; - fn deref(&self) -> &[u8] { - self.0 +impl<'a> Frame<'a> { + pub fn new(data: &'a [u8], pixfmt: Pixfmt, width: usize, height: usize) -> Self { + let stride0 = data.len() / height; + let mut stride = Vec::new(); + stride.push(stride0); + Self { + data, + pixfmt, + width, + height, + stride, + } + } +} + +impl<'a> TraitFrame for Frame<'a> { + fn data(&self) -> &[u8] { + self.data + } + + fn width(&self) -> usize { + self.width + } + + fn height(&self) -> usize { + self.height + } + + fn stride(&self) -> Vec { + self.stride.clone() + } + + fn pixfmt(&self) -> crate::Pixfmt { + self.pixfmt } } diff --git a/libs/scrap/src/dxgi/gdi.rs b/libs/scrap/src/dxgi/gdi.rs index 9fb0611e2..30440643b 100644 --- a/libs/scrap/src/dxgi/gdi.rs +++ b/libs/scrap/src/dxgi/gdi.rs @@ -160,7 +160,7 @@ impl CapturerGDI { stride, self.width, self.height, - 180, + crate::RotationMode::kRotate180, ); Ok(()) } diff --git a/libs/scrap/src/dxgi/mag.rs b/libs/scrap/src/dxgi/mag.rs index ac94472c0..188cf2c9c 100644 --- a/libs/scrap/src/dxgi/mag.rs +++ b/libs/scrap/src/dxgi/mag.rs @@ -245,9 +245,6 @@ pub struct CapturerMag { rect: RECT, width: usize, height: usize, - - use_yuv: bool, - data: Vec, } impl Drop for CapturerMag { @@ -262,12 +259,7 @@ impl CapturerMag { MagInterface::new().is_ok() } - pub(crate) fn new( - origin: (i32, i32), - width: usize, - height: usize, - use_yuv: bool, - ) -> Result { + pub(crate) fn new(origin: (i32, i32), width: usize, height: usize) -> Result { unsafe { let x = GetSystemMetrics(SM_XVIRTUALSCREEN); let y = GetSystemMetrics(SM_YVIRTUALSCREEN); @@ -311,8 +303,6 @@ impl CapturerMag { }, width, height, - use_yuv, - data: Vec::new(), }; unsafe { @@ -437,10 +427,6 @@ impl CapturerMag { Ok(s) } - pub(crate) fn set_use_yuv(&mut self, use_yuv: bool) { - self.use_yuv = use_yuv; - } - pub(crate) fn exclude(&mut self, cls: &str, name: &str) -> Result { let name_c = CString::new(name)?; unsafe { @@ -579,22 +565,9 @@ impl CapturerMag { )); } - if self.use_yuv { - self.data.resize(lock.1.len(), 0); - unsafe { - std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut self.data[0], self.data.len()); - } - crate::common::bgra_to_i420( - self.width as usize, - self.height as usize, - &self.data, - data, - ); - } else { - data.resize(lock.1.len(), 0); - unsafe { - std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut data[0], data.len()); - } + data.resize(lock.1.len(), 0); + unsafe { + std::ptr::copy_nonoverlapping(&mut lock.1[0], &mut data[0], data.len()); } Ok(()) @@ -651,7 +624,7 @@ mod tests { use super::*; #[test] fn test() { - let mut capture_mag = CapturerMag::new((0, 0), 1920, 1080, false).unwrap(); + let mut capture_mag = CapturerMag::new((0, 0), 1920, 1080).unwrap(); capture_mag.exclude("", "RustDeskPrivacyWindow").unwrap(); std::thread::sleep(std::time::Duration::from_millis(1000 * 10)); let mut data = Vec::new(); diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs index 4a0a53402..6acdcb9bb 100644 --- a/libs/scrap/src/dxgi/mod.rs +++ b/libs/scrap/src/dxgi/mod.rs @@ -20,6 +20,8 @@ use winapi::{ }, }; +use crate::RotationMode::*; + pub struct ComPtr(*mut T); impl ComPtr { fn is_null(&self) -> bool { @@ -45,8 +47,6 @@ pub struct Capturer { surface: ComPtr, width: usize, height: usize, - use_yuv: bool, - yuv: Vec, rotated: Vec, gdi_capturer: Option, gdi_buffer: Vec, @@ -54,7 +54,7 @@ pub struct Capturer { } impl Capturer { - pub fn new(display: Display, use_yuv: bool) -> io::Result { + pub fn new(display: Display) -> io::Result { let mut device = ptr::null_mut(); let mut context = ptr::null_mut(); let mut duplication = ptr::null_mut(); @@ -148,8 +148,6 @@ impl Capturer { width: display.width() as usize, height: display.height() as usize, display, - use_yuv, - yuv: Vec::new(), rotated: Vec::new(), gdi_capturer, gdi_buffer: Vec::new(), @@ -157,10 +155,6 @@ impl Capturer { }) } - pub fn set_use_yuv(&mut self, use_yuv: bool) { - self.use_yuv = use_yuv; - } - pub fn is_gdi(&self) -> bool { self.gdi_capturer.is_some() } @@ -259,10 +253,10 @@ impl Capturer { self.unmap(); let r = self.load_frame(timeout)?; let rotate = match self.display.rotation() { - DXGI_MODE_ROTATION_IDENTITY | DXGI_MODE_ROTATION_UNSPECIFIED => 0, - DXGI_MODE_ROTATION_ROTATE90 => 90, - DXGI_MODE_ROTATION_ROTATE180 => 180, - DXGI_MODE_ROTATION_ROTATE270 => 270, + DXGI_MODE_ROTATION_IDENTITY | DXGI_MODE_ROTATION_UNSPECIFIED => kRotate0, + DXGI_MODE_ROTATION_ROTATE90 => kRotate90, + DXGI_MODE_ROTATION_ROTATE180 => kRotate180, + DXGI_MODE_ROTATION_ROTATE270 => kRotate270, _ => { return Err(io::Error::new( io::ErrorKind::Other, @@ -270,7 +264,7 @@ impl Capturer { )); } }; - if rotate == 0 { + if rotate == kRotate0 { slice::from_raw_parts(r.0, r.1 as usize * self.height) } else { self.rotated.resize(self.width * self.height * 4, 0); @@ -279,12 +273,12 @@ impl Capturer { r.1, self.rotated.as_mut_ptr(), 4 * self.width as i32, - if rotate == 180 { + if rotate == kRotate180 { self.width } else { self.height } as _, - if rotate != 180 { + if rotate != kRotate180 { self.width } else { self.height @@ -295,19 +289,7 @@ impl Capturer { } } }; - Ok({ - if self.use_yuv { - crate::common::bgra_to_i420( - self.width as usize, - self.height as usize, - &result, - &mut self.yuv, - ); - &self.yuv[..] - } else { - result - } - }) + Ok(result) } } diff --git a/libs/scrap/src/quartz/frame.rs b/libs/scrap/src/quartz/frame.rs index 9cdb2a90a..0886e6323 100644 --- a/libs/scrap/src/quartz/frame.rs +++ b/libs/scrap/src/quartz/frame.rs @@ -5,8 +5,8 @@ use super::ffi::*; pub struct Frame { surface: IOSurfaceRef, inner: &'static [u8], - i420: *mut u8, - i420_len: usize, + bgra: Vec, + bgra_stride: usize, } impl Frame { @@ -24,8 +24,8 @@ impl Frame { Frame { surface, inner, - i420: ptr::null_mut(), - i420_len: 0, + bgra: Vec::new(), + bgra_stride: 0, } } @@ -34,23 +34,20 @@ impl Frame { self.inner } - pub fn nv12_to_i420<'a>(&'a mut self, w: usize, h: usize, i420: &'a mut Vec) { + pub fn stride(&self) -> usize { + self.bgra_stride + } + + pub fn surface_to_bgra<'a>(&'a mut self, h: usize) { unsafe { let plane0 = IOSurfaceGetBaseAddressOfPlane(self.surface, 0); - let stride0 = IOSurfaceGetBytesPerRowOfPlane(self.surface, 0); - let plane1 = IOSurfaceGetBaseAddressOfPlane(self.surface, 1); - let stride1 = IOSurfaceGetBytesPerRowOfPlane(self.surface, 1); - crate::common::nv12_to_i420( + self.bgra_stride = IOSurfaceGetBytesPerRowOfPlane(self.surface, 0); + self.bgra.resize(self.bgra_stride * h, 0); + std::ptr::copy_nonoverlapping( plane0 as _, - stride0 as _, - plane1 as _, - stride1 as _, - w, - h, - i420, + self.bgra.as_mut_ptr(), + self.bgra_stride * h, ); - self.i420 = i420.as_mut_ptr() as _; - self.i420_len = i420.len(); } } } @@ -58,14 +55,7 @@ impl Frame { impl ops::Deref for Frame { type Target = [u8]; fn deref<'a>(&'a self) -> &'a [u8] { - if self.i420.is_null() { - self.inner - } else { - unsafe { - let inner = slice::from_raw_parts(self.i420 as *const u8, self.i420_len); - inner - } - } + &self.bgra } } diff --git a/libs/scrap/src/x11/capturer.rs b/libs/scrap/src/x11/capturer.rs index 6486af55c..308df1651 100644 --- a/libs/scrap/src/x11/capturer.rs +++ b/libs/scrap/src/x11/capturer.rs @@ -2,6 +2,8 @@ use std::{io, ptr, slice}; use hbb_common::libc; +use crate::Frame; + use super::ffi::*; use super::Display; @@ -12,13 +14,11 @@ pub struct Capturer { buffer: *const u8, size: usize, - use_yuv: bool, - yuv: Vec, saved_raw_data: Vec, // for faster compare and copy } impl Capturer { - pub fn new(display: Display, use_yuv: bool) -> io::Result { + pub fn new(display: Display) -> io::Result { // Calculate dimensions. let pixel_width = 4; @@ -67,17 +67,11 @@ impl Capturer { xcbid, buffer, size, - use_yuv, - yuv: Vec::new(), saved_raw_data: Vec::new(), }; Ok(c) } - pub fn set_use_yuv(&mut self, use_yuv: bool) { - self.use_yuv = use_yuv; - } - pub fn display(&self) -> &Display { &self.display } @@ -103,16 +97,13 @@ impl Capturer { } } - pub fn frame<'b>(&'b mut self) -> std::io::Result<&'b [u8]> { + pub fn frame<'b>(&'b mut self) -> std::io::Result { self.get_image(); let result = unsafe { slice::from_raw_parts(self.buffer, self.size) }; crate::would_block_if_equal(&mut self.saved_raw_data, result)?; - Ok(if self.use_yuv { - crate::common::bgra_to_i420(self.display.w(), self.display.h(), &result, &mut self.yuv); - &self.yuv[..] - } else { - result - }) + Ok( + Frame::new(result, crate::Pixfmt::BGRA, self.display.w(), self.display.h()) + ) } } diff --git a/res/lang.py b/res/lang.py index 74492818a..4655d2cbe 100644 --- a/res/lang.py +++ b/res/lang.py @@ -5,20 +5,22 @@ import glob import sys import csv + def get_lang(lang): - out = {} - for ln in open('./src/lang/%s.rs'%lang, encoding='utf8'): - ln = ln.strip() - if ln.startswith('("'): - k, v = line_split(ln) - out[k] = v - return out + out = {} + for ln in open('./src/lang/%s.rs' % lang, encoding='utf8'): + ln = ln.strip() + if ln.startswith('("'): + k, v = line_split(ln) + out[k] = v + return out + def line_split(line): toks = line.split('", "') if len(toks) != 2: print(line) - assert(0) + assert 0 # Replace fixed position. # Because toks[1] may be v") or v"), k = toks[0][toks[0].find('"') + 1:] @@ -27,62 +29,62 @@ def line_split(line): def main(): - if len(sys.argv) == 1: - expand() - elif sys.argv[1] == '1': - to_csv() - else: - to_rs(sys.argv[1]) + if len(sys.argv) == 1: + expand() + elif sys.argv[1] == '1': + to_csv() + else: + to_rs(sys.argv[1]) def expand(): - for fn in glob.glob('./src/lang/*.rs'): - lang = os.path.basename(fn)[:-3] - if lang in ['en','template']: continue - print(lang) - dict = get_lang(lang) - fw = open("./src/lang/%s.rs"%lang, "wt", encoding='utf8') - for line in open('./src/lang/template.rs', encoding='utf8'): - line_strip = line.strip() - if line_strip.startswith('("'): - k, v = line_split(line_strip) - if k in dict: - # embraced with " to avoid empty v - line = line.replace('"%s"'%v, '"%s"'%dict[k]) - else: - line = line.replace(v, "") - fw.write(line) - else: - fw.write(line) - fw.close() + for fn in glob.glob('./src/lang/*.rs'): + lang = os.path.basename(fn)[:-3] + if lang in ['en', 'template']: continue + print(lang) + dict = get_lang(lang) + fw = open("./src/lang/%s.rs" % lang, "wt", encoding='utf8') + for line in open('./src/lang/template.rs', encoding='utf8'): + line_strip = line.strip() + if line_strip.startswith('("'): + k, v = line_split(line_strip) + if k in dict: + # embraced with " to avoid empty v + line = line.replace('"%s"' % v, '"%s"' % dict[k]) + else: + line = line.replace(v, "") + fw.write(line) + else: + fw.write(line) + fw.close() def to_csv(): - for fn in glob.glob('./src/lang/*.rs'): - lang = os.path.basename(fn)[:-3] - csvfile = open('./src/lang/%s.csv'%lang, "wt", encoding='utf8') - csvwriter = csv.writer(csvfile) - for line in open(fn, encoding='utf8'): - line_strip = line.strip() - if line_strip.startswith('("'): - k, v = line_split(line_strip) - csvwriter.writerow([k, v]) - csvfile.close() + for fn in glob.glob('./src/lang/*.rs'): + lang = os.path.basename(fn)[:-3] + csvfile = open('./src/lang/%s.csv' % lang, "wt", encoding='utf8') + csvwriter = csv.writer(csvfile) + for line in open(fn, encoding='utf8'): + line_strip = line.strip() + if line_strip.startswith('("'): + k, v = line_split(line_strip) + csvwriter.writerow([k, v]) + csvfile.close() def to_rs(lang): - csvfile = open('%s.csv'%lang, "rt", encoding='utf8') - fw = open("./src/lang/%s.rs"%lang, "wt", encoding='utf8') - fw.write('''lazy_static::lazy_static! { + csvfile = open('%s.csv' % lang, "rt", encoding='utf8') + fw = open("./src/lang/%s.rs" % lang, "wt", encoding='utf8') + fw.write('''lazy_static::lazy_static! { pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ ''') - for row in csv.reader(csvfile): - fw.write(' ("%s", "%s"),\n'%(row[0].replace('"', '\"'), row[1].replace('"', '\"'))) - fw.write(''' ].iter().cloned().collect(); + for row in csv.reader(csvfile): + fw.write(' ("%s", "%s"),\n' % (row[0].replace('"', '\"'), row[1].replace('"', '\"'))) + fw.write(''' ].iter().cloned().collect(); } ''') - fw.close() + fw.close() main() diff --git a/res/setup.nsi b/res/setup.nsi index 8e19aa7e7..a7d267559 100644 --- a/res/setup.nsi +++ b/res/setup.nsi @@ -45,7 +45,7 @@ InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}" !define MUI_LANGDLL_ALLLANGUAGES !define MUI_FINISHPAGE_SHOWREADME "" !define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED -!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Desktop Shortcut" +!define MUI_FINISHPAGE_SHOWREADME_TEXT "Create desktop shortcut" !define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateDesktopShortcut !define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_NAME}.exe" diff --git a/src/client.rs b/src/client.rs index f59ffe049..14d0e62c4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -31,8 +31,8 @@ use hbb_common::{ anyhow::{anyhow, Context}, bail, config::{ - Config, LocalConfig, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT, READ_TIMEOUT, - RELAY_PORT, + Config, LocalConfig, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT, + PUBLIC_RS_PUB_KEY, READ_TIMEOUT, RELAY_PORT, RENDEZVOUS_PORT, RENDEZVOUS_SERVERS, }, get_version_number, log, message_proto::{option_message::BoolOption, *}, @@ -55,6 +55,7 @@ use scrap::{ }; use crate::{ + check_port, common::input::{MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT, MOUSE_TYPE_DOWN, MOUSE_TYPE_UP}, is_keyboard_mode_supported, ui_session_interface::{InvokeUiSession, Session}, @@ -134,6 +135,8 @@ lazy_static::lazy_static! { static ref TEXT_CLIPBOARD_STATE: Arc> = Arc::new(Mutex::new(TextClipboardState::new())); } +const PUBLIC_SERVER: &str = "public"; + #[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub fn get_old_clipboard_text() -> &'static Arc> { @@ -219,6 +222,7 @@ impl Client { conn_type: ConnType, interface: impl Interface, ) -> ResultType<(Stream, bool, Option>)> { + debug_assert!(peer == interface.get_id()); interface.update_direct(None); interface.update_received(false); match Self::_start(peer, key, token, conn_type, interface).await { @@ -245,11 +249,8 @@ impl Client { // to-do: remember the port for each peer, so that we can retry easier if hbb_common::is_ip_str(peer) { return Ok(( - socket_client::connect_tcp( - crate::check_port(peer, RELAY_PORT + 1), - CONNECT_TIMEOUT, - ) - .await?, + socket_client::connect_tcp(check_port(peer, RELAY_PORT + 1), CONNECT_TIMEOUT) + .await?, true, None, )); @@ -262,12 +263,36 @@ impl Client { None, )); } - let (mut rendezvous_server, servers, contained) = crate::get_rendezvous_server(1_000).await; + + let other_server = interface.get_lch().read().unwrap().other_server.clone(); + let (peer, other_server, key, token) = if let Some((a, b, c)) = other_server.as_ref() { + (a.as_ref(), b.as_ref(), c.as_ref(), "") + } else { + (peer, "", key, token) + }; + let (mut rendezvous_server, servers, contained) = if other_server.is_empty() { + crate::get_rendezvous_server(1_000).await + } else { + if other_server == PUBLIC_SERVER { + ( + check_port(RENDEZVOUS_SERVERS[0], RENDEZVOUS_PORT), + RENDEZVOUS_SERVERS[1..] + .iter() + .map(|x| x.to_string()) + .collect(), + true, + ) + } else { + (check_port(other_server, RENDEZVOUS_PORT), Vec::new(), true) + } + }; + let mut socket = socket_client::connect_tcp(&*rendezvous_server, CONNECT_TIMEOUT).await; debug_assert!(!servers.contains(&rendezvous_server)); if socket.is_err() && !servers.is_empty() { log::info!("try the other servers: {:?}", servers); for server in servers { + let server = check_port(server, RENDEZVOUS_PORT); socket = socket_client::connect_tcp(&*server, CONNECT_TIMEOUT).await; if socket.is_ok() { rendezvous_server = server; @@ -423,7 +448,7 @@ impl Client { conn_type: ConnType, interface: impl Interface, ) -> ResultType<(Stream, bool, Option>)> { - let direct_failures = PeerConfig::load(peer_id).direct_failures; + let direct_failures = interface.get_lch().read().unwrap().direct_failures; let mut connect_timeout = 0; const MIN: u64 = 1000; if is_local || peer_nat_type == NatType::SYMMETRIC { @@ -484,10 +509,9 @@ impl Client { } } if !relay_server.is_empty() && (direct_failures == 0) != direct { - let mut config = PeerConfig::load(peer_id); - config.direct_failures = if direct { 0 } else { 1 }; - log::info!("direct_failures updated to {}", config.direct_failures); - config.store(peer_id); + let n = if direct { 0 } else { 1 }; + log::info!("direct_failures updated to {}", n); + interface.get_lch().write().unwrap().set_direct_failure(n); } let mut conn = conn?; log::info!("{:?} used to establish connection", start.elapsed()); @@ -648,7 +672,7 @@ impl Client { ipv4: bool, ) -> ResultType { let mut conn = socket_client::connect_tcp( - socket_client::ipv4_to_ipv6(crate::check_port(relay_server, RELAY_PORT), ipv4), + socket_client::ipv4_to_ipv6(check_port(relay_server, RELAY_PORT), ipv4), CONNECT_TIMEOUT, ) .await @@ -1017,10 +1041,16 @@ impl VideoHandler { /// Handle a new video frame. #[inline] - pub fn handle_frame(&mut self, vf: VideoFrame) -> ResultType { + pub fn handle_frame( + &mut self, + vf: VideoFrame, + chroma: &mut Option, + ) -> ResultType { match &vf.union { Some(frame) => { - let res = self.decoder.handle_video_frame(frame, &mut self.rgb); + let res = self + .decoder + .handle_video_frame(frame, &mut self.rgb, chroma); if self.record { self.recorder .lock() @@ -1082,6 +1112,7 @@ pub struct LoginConfigHandler { pub received: bool, switch_uuid: Option, pub save_ab_password_to_recent: bool, // true: connected with ab password + pub other_server: Option<(String, String, String)>, } impl Deref for LoginConfigHandler { @@ -1092,16 +1123,6 @@ impl Deref for LoginConfigHandler { } } -/// Load [`PeerConfig`] from id. -/// -/// # Arguments -/// -/// * `id` - id of peer -#[inline] -pub fn load_config(id: &str) -> PeerConfig { - PeerConfig::load(id) -} - impl LoginConfigHandler { /// Initialize the login config handler. /// @@ -1114,8 +1135,39 @@ impl LoginConfigHandler { id: String, conn_type: ConnType, switch_uuid: Option, - force_relay: bool, + mut force_relay: bool, ) { + let mut id = id; + if id.contains("@") { + let mut v = id.split("@"); + let raw_id: &str = v.next().unwrap_or_default(); + let mut server_key = v.next().unwrap_or_default().split('?'); + let server = server_key.next().unwrap_or_default(); + let args = server_key.next().unwrap_or_default(); + let key = if server == PUBLIC_SERVER { + PUBLIC_RS_PUB_KEY + } else { + let mut args_map: HashMap<&str, &str> = HashMap::new(); + for arg in args.split('&') { + if let Some(kv) = arg.find('=') { + let k = &arg[0..kv]; + let v = &arg[kv + 1..]; + args_map.insert(k, v); + } + } + let key = args_map.remove("key").unwrap_or_default(); + key + }; + + // here we can check /r@server + let real_id = crate::ui_interface::handle_relay_id(raw_id).to_string(); + if real_id != raw_id { + force_relay = true; + } + self.other_server = Some((real_id.clone(), server.to_owned(), key.to_owned())); + id = format!("{real_id}@{server}"); + } + self.id = id; self.conn_type = conn_type; let config = self.load_config(); @@ -1130,6 +1182,12 @@ impl LoginConfigHandler { self.supported_encoding = Default::default(); self.restarting_remote_device = false; self.force_relay = !self.get_option("force-always-relay").is_empty() || force_relay; + if let Some((real_id, server, key)) = &self.other_server { + let other_server_key = self.get_option("other-server-key"); + if !other_server_key.is_empty() && key.is_empty() { + self.other_server = Some((real_id.to_owned(), server.to_owned(), other_server_key)); + } + } self.direct = None; self.received = false; self.switch_uuid = switch_uuid; @@ -1149,8 +1207,9 @@ impl LoginConfigHandler { } /// Load [`PeerConfig`]. - fn load_config(&self) -> PeerConfig { - load_config(&self.id) + pub fn load_config(&self) -> PeerConfig { + debug_assert!(self.id.len() > 0); + PeerConfig::load(&self.id) } /// Save a [`PeerConfig`] into the handler. @@ -1259,6 +1318,12 @@ impl LoginConfigHandler { self.save_config(config); } + pub fn set_direct_failure(&mut self, value: i32) { + let mut config = self.load_config(); + config.direct_failures = value; + self.save_config(config); + } + /// Get a ui config of flutter for handler's [`PeerConfig`]. /// Return String if the option is found, otherwise return "". /// @@ -1411,7 +1476,7 @@ impl LoginConfigHandler { msg.image_quality = q.into(); n += 1; } else if q == "custom" { - let config = PeerConfig::load(&self.id); + let config = self.load_config(); let quality = if config.custom_image_quality.is_empty() { 50 } else { @@ -1705,6 +1770,18 @@ impl LoginConfigHandler { log::debug!("remove password of {}", self.id); } } + if let Some((_, b, c)) = self.other_server.as_ref() { + if b != PUBLIC_SERVER { + config + .options + .insert("other-server-key".to_owned(), c.clone()); + } + } + if self.force_relay { + config + .options + .insert("force-always-relay".to_owned(), "Y".to_owned()); + } #[cfg(feature = "flutter")] { // sync ab password with PeerConfig password @@ -1766,8 +1843,14 @@ impl LoginConfigHandler { let my_id = Config::get_id_or(crate::DEVICE_ID.lock().unwrap().clone()); #[cfg(not(any(target_os = "android", target_os = "ios")))] let my_id = Config::get_id(); + let (my_id, pure_id) = if let Some((id, _, _)) = self.other_server.as_ref() { + let server = Config::get_rendezvous_server(); + (format!("{my_id}@{server}"), id.clone()) + } else { + (my_id, self.id.clone()) + }; let mut lr = LoginRequest { - username: self.id.clone(), + username: pure_id, password: password.into(), my_id, my_name: crate::username(), @@ -1855,6 +1938,7 @@ pub fn start_video_audio_threads( MediaSender, Arc>>>, Arc>>, + Arc>>, ) where F: 'static + FnMut(usize, &mut scrap::ImageRgb) + Send, @@ -1866,6 +1950,9 @@ where let mut video_callback = video_callback; let fps_map = Arc::new(RwLock::new(HashMap::new())); let decode_fps_map = fps_map.clone(); + let chroma = Arc::new(RwLock::new(None)); + let chroma_cloned = chroma.clone(); + let mut last_chroma = None; std::thread::spawn(move || { #[cfg(windows)] @@ -1911,10 +1998,17 @@ where } } if let Some(handler_controller) = handler_controller_map.get_mut(display) { - match handler_controller.handler.handle_frame(vf) { + let mut tmp_chroma = None; + match handler_controller.handler.handle_frame(vf, &mut tmp_chroma) { Ok(true) => { video_callback(display, &mut handler_controller.handler.rgb); + // chroma + if tmp_chroma.is_some() && last_chroma != tmp_chroma { + last_chroma = tmp_chroma; + *chroma.write().unwrap() = tmp_chroma; + } + // fps calculation // The first frame will be very slow if handler_controller.skip_beginning < 5 { @@ -1992,6 +2086,7 @@ where audio_sender, video_queue_map_cloned, decode_fps_map, + chroma_cloned, ); } @@ -2543,40 +2638,42 @@ pub trait Interface: Send + Clone + 'static + Sized { ); async fn handle_test_delay(&self, t: TestDelay, peer: &mut Stream); - fn get_login_config_handler(&self) -> Arc>; + fn get_lch(&self) -> Arc>; + + fn get_id(&self) -> String { + self.get_lch().read().unwrap().id.clone() + } fn is_force_relay(&self) -> bool { - self.get_login_config_handler().read().unwrap().force_relay + self.get_lch().read().unwrap().force_relay } + fn swap_modifier_mouse(&self, _msg: &mut hbb_common::protos::message::MouseEvent) {} fn update_direct(&self, direct: Option) { - self.get_login_config_handler().write().unwrap().direct = direct; + self.get_lch().write().unwrap().direct = direct; } fn update_received(&self, received: bool) { - self.get_login_config_handler().write().unwrap().received = received; + self.get_lch().write().unwrap().received = received; } fn on_establish_connection_error(&self, err: String) { - log::error!("Connection closed: {}", err); let title = "Connection Error"; let text = err.to_string(); - let lc = self.get_login_config_handler(); + let lc = self.get_lch(); let direct = lc.read().unwrap().direct; let received = lc.read().unwrap().received; let relay_condition = direct == Some(true) && !received; // force relay let errno = errno::errno().0; + log::error!("Connection closed: {err}({errno})"); if relay_condition && (cfg!(windows) && (errno == 10054 || err.contains("10054")) || !cfg!(windows) && (errno == 104 || err.contains("104"))) { lc.write().unwrap().force_relay = true; - lc.write() - .unwrap() - .set_option("force-always-relay".to_owned(), "Y".to_owned()); } // relay-hint diff --git a/src/client/helper.rs b/src/client/helper.rs index 09a8d5d24..98d1239e1 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -1,9 +1,9 @@ -use std::collections::HashMap; use hbb_common::{ get_time, message_proto::{Message, VoiceCallRequest, VoiceCallResponse}, }; use scrap::CodecFormat; +use std::collections::HashMap; #[derive(Debug, Default)] pub struct QualityStatus { @@ -12,6 +12,7 @@ pub struct QualityStatus { pub delay: Option, pub target_bitrate: Option, pub codec_format: Option, + pub chroma: Option, } #[inline] diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index db7a4c5b7..d6488e401 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -7,14 +7,14 @@ use std::{ }, }; -#[cfg(windows)] -use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, ContextSend}; +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] +use clipboard::ContextSend; use crossbeam_queue::ArrayQueue; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::sleep; #[cfg(not(target_os = "ios"))] use hbb_common::tokio::sync::mpsc::error::TryRecvError; -#[cfg(windows)] +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] use hbb_common::tokio::sync::Mutex as TokioMutex; use hbb_common::{ allow_err, @@ -34,7 +34,7 @@ use hbb_common::{ sync::mpsc, time::{self, Duration, Instant, Interval}, }, - Stream, + ResultType, Stream, }; use scrap::CodecFormat; @@ -66,7 +66,7 @@ pub struct Remote { last_update_jobs_status: (Instant, HashMap), is_connected: bool, first_frame: bool, - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] client_conn_id: i32, // used for file clipboard data_count: Arc, frame_count_map: Arc>>, @@ -74,6 +74,7 @@ pub struct Remote { elevation_requested: bool, fps_control_map: HashMap, decode_fps_map: Arc>>, + chroma: Arc>>, } impl Remote { @@ -86,6 +87,7 @@ impl Remote { sender: mpsc::UnboundedSender, frame_count_map: Arc>>, decode_fps: Arc>>, + chroma: Arc>>, ) -> Self { Self { handler, @@ -101,7 +103,7 @@ impl Remote { last_update_jobs_status: (Instant::now(), Default::default()), is_connected: false, first_frame: false, - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] client_conn_id: 0, data_count: Arc::new(AtomicUsize::new(0)), frame_count_map, @@ -111,6 +113,7 @@ impl Remote { elevation_requested: false, fps_control_map: Default::default(), decode_fps_map: decode_fps, + chroma, } } @@ -124,7 +127,7 @@ impl Remote { }; match Client::start( - &self.handler.id, + &self.handler.get_id(), key, token, conn_type, @@ -146,24 +149,25 @@ impl Remote { } // just build for now - #[cfg(not(windows))] + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::(); - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] let (_tx_holder, rx) = mpsc::unbounded_channel(); - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] let mut rx_clip_client_lock = Arc::new(TokioMutex::new(rx)); - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] { let is_conn_not_default = self.handler.is_file_transfer() || self.handler.is_port_forward() || self.handler.is_rdp(); if !is_conn_not_default { + log::debug!("get cliprdr client for conn_id {}", self.client_conn_id); (self.client_conn_id, rx_clip_client_lock) = - clipboard::get_rx_cliprdr_client(&self.handler.id); + clipboard::get_rx_cliprdr_client(&self.handler.get_id()); }; } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] let mut rx_clip_client = rx_clip_client_lock.lock().await; let mut status_timer = time::interval(Duration::new(1, 0)); @@ -193,7 +197,7 @@ impl Remote { } else { if self.handler.is_restarting_remote_device() { log::info!("Restart remote device"); - self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip", ""); + self.handler.msgbox("restarting", "Restarting remote device", "remote_restarting_tip", ""); } else { log::info!("Reset by the peer"); self.handler.msgbox("error", "Connection Error", "Reset by the peer", ""); @@ -209,8 +213,8 @@ impl Remote { } } _msg = rx_clip_client.recv() => { - #[cfg(windows)] - self.handle_local_clipboard_msg(&mut peer, _msg).await; + #[cfg(any(target_os="windows", target_os="linux", target_os = "macos"))] + self.handle_local_clipboard_msg(&mut peer, _msg).await; } _ = self.timer.tick() => { if last_recv_time.elapsed() >= SEC30 { @@ -246,15 +250,23 @@ impl Remote { // Correcting the inaccuracy of status_timer (k.clone(), (*v as i32) * 1000 / elapsed as i32) }).collect::>(); + let chroma = self.chroma.read().unwrap().clone(); + let chroma = match chroma { + Some(Chroma::I444) => "4:4:4", + Some(Chroma::I420) => "4:2:0", + None => "-", + }; + let chroma = Some(chroma.to_string()); self.handler.update_quality_status(QualityStatus { speed: Some(speed), fps, + chroma, ..Default::default() }); } } } - log::debug!("Exit io_loop of id={}", self.handler.id); + log::debug!("Exit io_loop of id={}", self.handler.get_id()); // Stop client audio server. if let Some(s) = self.stop_voice_call_sender.take() { s.send(()).ok(); @@ -274,20 +286,21 @@ impl Remote { #[cfg(not(any(target_os = "android", target_os = "ios")))] if _set_disconnected_ok { - Client::try_stop_clipboard(&self.handler.id); + Client::try_stop_clipboard(&self.handler.get_id()); } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] if _set_disconnected_ok { let conn_id = self.client_conn_id; - ContextSend::proc(|context: &mut CliprdrClientContext| -> u32 { - empty_clipboard(context, conn_id); - 0 + log::debug!("try empty cliprdr for conn_id {}", conn_id); + let _ = ContextSend::proc(|context| -> ResultType<()> { + context.empty_clipboard(conn_id)?; + Ok(()) }); } } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] async fn handle_local_clipboard_msg( &self, peer: &mut crate::client::FramedStream, @@ -315,7 +328,12 @@ impl Remote { if stop { ContextSend::set_is_stopped(); } else { - allow_err!(peer.send(&crate::clipboard_file::clip_2_msg(clip)).await); + if let Err(e) = ContextSend::make_sure_enabled() { + log::error!("failed to restart clipboard context: {}", e); + }; + log::debug!("Send system clipboard message to remote"); + let msg = crate::clipboard_file::clip_2_msg(clip); + allow_err!(peer.send(&msg).await); } } }, @@ -1069,7 +1087,6 @@ impl Remote { } Some(login_response::Union::PeerInfo(pi)) => { self.handler.handle_peer_info(pi); - #[cfg(not(feature = "flutter"))] self.check_clipboard_file_context(); if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) { #[cfg(feature = "flutter")] @@ -1102,7 +1119,7 @@ impl Remote { #[cfg(not(any(target_os = "android", target_os = "ios")))] crate::plugin::handle_listen_event( crate::plugin::EVENT_ON_CONN_CLIENT.to_owned(), - self.handler.id.clone(), + self.handler.get_id(), ) } @@ -1140,7 +1157,7 @@ impl Remote { } } } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] Some(message::Union::Cliprdr(clip)) => { self.handle_cliprdr_msg(clip); } @@ -1330,6 +1347,9 @@ impl Remote { Ok(Permission::Recording) => { self.handler.set_permission("recording", p.enabled); } + Ok(Permission::BlockInput) => { + self.handler.set_permission("block_input", p.enabled); + } _ => {} } } @@ -1445,14 +1465,14 @@ impl Remote { } Some(misc::Union::SwitchBack(_)) => { #[cfg(feature = "flutter")] - self.handler.switch_back(&self.handler.id); + self.handler.switch_back(&self.handler.get_id()); } #[cfg(all(feature = "flutter", feature = "plugin_framework"))] #[cfg(not(any(target_os = "android", target_os = "ios")))] Some(misc::Union::PluginRequest(p)) => { allow_err!(crate::plugin::handle_server_event( &p.id, - &self.handler.id, + &self.handler.get_id(), &p.content )); // to-do: show message box on UI when error occurs? @@ -1700,9 +1720,14 @@ impl Remote { true } - #[cfg(not(feature = "flutter"))] fn check_clipboard_file_context(&self) { - #[cfg(windows)] + #[cfg(any( + target_os = "windows", + all( + feature = "unix-file-copy-paste", + any(target_os = "linux", target_os = "macos") + ) + ))] { let enabled = *self.handler.server_file_transfer_enabled.read().unwrap() && self.handler.lc.read().unwrap().enable_file_transfer.v; @@ -1710,8 +1735,9 @@ impl Remote { } } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] fn handle_cliprdr_msg(&self, clip: hbb_common::message_proto::Cliprdr) { + log::debug!("handling cliprdr msg from server peer"); #[cfg(feature = "flutter")] if let Some(hbb_common::message_proto::cliprdr::Union::FormatList(_)) = &clip.union { if self.client_conn_id @@ -1721,18 +1747,26 @@ impl Remote { } } - if let Some(clip) = crate::clipboard_file::msg_2_clip(clip) { - let is_stopping_allowed = clip.is_stopping_allowed_from_peer(); - let file_transfer_enabled = self.handler.lc.read().unwrap().enable_file_transfer.v; - let stop = is_stopping_allowed && !file_transfer_enabled; - log::debug!( + let Some(clip) = crate::clipboard_file::msg_2_clip(clip) else { + log::warn!("failed to decode cliprdr msg from server peer"); + return; + }; + + let is_stopping_allowed = clip.is_stopping_allowed_from_peer(); + let file_transfer_enabled = self.handler.lc.read().unwrap().enable_file_transfer.v; + let stop = is_stopping_allowed && !file_transfer_enabled; + log::debug!( "Process clipboard message from server peer, stop: {}, is_stopping_allowed: {}, file_transfer_enabled: {}", stop, is_stopping_allowed, file_transfer_enabled); - if !stop { - ContextSend::proc(|context: &mut CliprdrClientContext| -> u32 { - clipboard::server_clip_file(context, self.client_conn_id, clip) - }); - } + if !stop { + if let Err(e) = ContextSend::make_sure_enabled() { + log::error!("failed to restart clipboard context: {}", e); + }; + let _ = ContextSend::proc(|context| -> ResultType<()> { + context + .server_clip_file(self.client_conn_id, clip) + .map_err(|e| e.into()) + }); } } } diff --git a/src/common.rs b/src/common.rs index 1833d858b..0957718e4 100644 --- a/src/common.rs +++ b/src/common.rs @@ -11,9 +11,118 @@ pub enum GrabState { Exit, } -#[cfg(not(any(target_os = "android", target_os = "ios")))] +#[cfg(not(any( + target_os = "android", + target_os = "ios", + all(target_os = "linux", feature = "unix-file-copy-paste") +)))] pub use arboard::Clipboard as ClipboardContext; +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +static X11_CLIPBOARD: once_cell::sync::OnceCell = + once_cell::sync::OnceCell::new(); + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +fn get_clipboard() -> Result<&'static x11_clipboard::Clipboard, String> { + X11_CLIPBOARD + .get_or_try_init(|| x11_clipboard::Clipboard::new()) + .map_err(|e| e.to_string()) +} + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +pub struct ClipboardContext { + string_setter: x11rb::protocol::xproto::Atom, + string_getter: x11rb::protocol::xproto::Atom, + text_uri_list: x11rb::protocol::xproto::Atom, + + clip: x11rb::protocol::xproto::Atom, + prop: x11rb::protocol::xproto::Atom, +} + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +fn parse_plain_uri_list(v: Vec) -> Result { + let text = String::from_utf8(v).map_err(|_| "ConversionFailure".to_owned())?; + let mut list = String::new(); + for line in text.lines() { + if !line.starts_with("file://") { + continue; + } + let decoded = percent_encoding::percent_decode_str(line) + .decode_utf8() + .map_err(|_| "ConversionFailure".to_owned())?; + list = list + "\n" + decoded.trim_start_matches("file://"); + } + list = list.trim().to_owned(); + Ok(list) +} + +#[cfg(all(target_os = "linux", feature = "unix-file-copy-paste"))] +impl ClipboardContext { + pub fn new() -> Result { + let clipboard = get_clipboard()?; + let string_getter = clipboard + .getter + .get_atom("UTF8_STRING") + .map_err(|e| e.to_string())?; + let string_setter = clipboard + .setter + .get_atom("UTF8_STRING") + .map_err(|e| e.to_string())?; + let text_uri_list = clipboard + .getter + .get_atom("text/uri-list") + .map_err(|e| e.to_string())?; + let prop = clipboard.getter.atoms.property; + let clip = clipboard.getter.atoms.clipboard; + Ok(Self { + text_uri_list, + string_setter, + string_getter, + clip, + prop, + }) + } + + pub fn get_text(&mut self) -> Result { + let clip = self.clip; + let prop = self.prop; + + const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(120); + + let text_content = get_clipboard()? + .load(clip, self.string_getter, prop, TIMEOUT) + .map_err(|e| e.to_string())?; + + let file_urls = get_clipboard()?.load(clip, self.text_uri_list, prop, TIMEOUT); + + if file_urls.is_err() || file_urls.as_ref().unwrap().is_empty() { + log::trace!("clipboard get text, no file urls"); + return String::from_utf8(text_content).map_err(|e| e.to_string()); + } + + let file_urls = parse_plain_uri_list(file_urls.unwrap())?; + + let text_content = String::from_utf8(text_content).map_err(|e| e.to_string())?; + + if text_content.trim() == file_urls.trim() { + log::trace!("clipboard got text but polluted"); + return Err(String::from("polluted text")); + } + + Ok(text_content) + } + + pub fn set_text(&mut self, content: String) -> Result<(), String> { + let clip = self.clip; + + let value = content.clone().into_bytes(); + get_clipboard()? + .store(clip, self.string_setter, value) + .map_err(|e| e.to_string())?; + Ok(()) + } +} + #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::compress::decompress; use hbb_common::{ diff --git a/src/flutter.rs b/src/flutter.rs index 395056038..e9a0a8b08 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -471,6 +471,7 @@ impl InvokeUiSession for FlutterHandler { "codec_format", &status.codec_format.map_or(NULL, |it| it.to_string()), ), + ("chroma", &status.chroma.map_or(NULL, |it| it.to_string())), ], ); } @@ -862,7 +863,6 @@ pub fn session_add( LocalConfig::set_remote_id(&id); let session: Session = Session { - id: id.to_owned(), password, server_keyboard_enabled: Arc::new(RwLock::new(true)), server_file_transfer_enabled: Arc::new(RwLock::new(true)), @@ -1030,8 +1030,8 @@ pub mod connection_manager { self.push_event("update_voice_call_state", vec![("client", &client_json)]); } - fn file_transfer_log(&self, log: String) { - self.push_event("cm_file_transfer_log", vec![("log", &log.to_string())]); + fn file_transfer_log(&self, action: &str, log: &str) { + self.push_event("cm_file_transfer_log", vec![(action, log)]); } } @@ -1465,25 +1465,7 @@ pub mod sessions { if write_lock.is_empty() { remove_peer_key = Some(peer_key.clone()); } else { - // Set capture displays if some are not used any more. - let mut remains_displays = HashSet::new(); - for (_, h) in write_lock.iter() { - remains_displays.extend( - h.renderer - .map_display_sessions - .read() - .unwrap() - .keys() - .cloned(), - ); - } - if !remains_displays.is_empty() { - s.capture_displays( - vec![], - vec![], - remains_displays.iter().map(|d| *d as i32).collect(), - ); - } + check_remove_unused_displays(None, id, s, &write_lock); } break; } @@ -1493,12 +1475,75 @@ pub mod sessions { SESSIONS.write().unwrap().remove(&remove_peer_key?) } + #[cfg(feature = "flutter_texture_render")] + fn check_remove_unused_displays( + current: Option, + session_id: &SessionID, + session: &FlutterSession, + handlers: &HashMap, + ) { + // Set capture displays if some are not used any more. + let mut remains_displays = HashSet::new(); + if let Some(current) = current { + remains_displays.insert(current); + } + for (k, h) in handlers.iter() { + if k == session_id { + continue; + } + remains_displays.extend( + h.renderer + .map_display_sessions + .read() + .unwrap() + .keys() + .cloned(), + ); + } + if !remains_displays.is_empty() { + session.capture_displays( + vec![], + vec![], + remains_displays.iter().map(|d| *d as i32).collect(), + ); + } + } + + pub fn session_switch_display(session_id: SessionID, value: Vec) { + for s in SESSIONS.read().unwrap().values() { + let read_lock = s.ui_handler.session_handlers.read().unwrap(); + if read_lock.contains_key(&session_id) { + if value.len() == 1 { + // Switch display. + // This operation will also cause the peer to send a switch display message. + // The switch display message will contain `SupportedResolutions`, which is useful when changing resolutions. + s.switch_display(value[0]); + + // Check if other displays are needed. + #[cfg(feature = "flutter_texture_render")] + if value.len() == 1 { + check_remove_unused_displays( + Some(value[0] as _), + &session_id, + &s, + &read_lock, + ); + } + } else { + // Try capture all displays. + s.capture_displays(vec![], vec![], value); + } + break; + } + } + } + #[inline] pub fn insert_session(session_id: SessionID, conn_type: ConnType, session: FlutterSession) { SESSIONS .write() .unwrap() - .entry((session.id.clone(), conn_type)) + .entry((session.get_id(), conn_type)) .or_insert(session) .ui_handler .session_handlers diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 753848cc0..436a95587 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -430,13 +430,7 @@ pub fn session_ctrl_alt_del(session_id: SessionID) { } pub fn session_switch_display(session_id: SessionID, value: Vec) { - if let Some(session) = sessions::get_session_by_session_id(&session_id) { - if value.len() == 1 { - session.switch_display(value[0]); - } else { - session.capture_displays(vec![], vec![], value); - } - } + sessions::session_switch_display(session_id, value); } pub fn session_handle_flutter_key_event( @@ -1095,7 +1089,7 @@ pub fn main_get_user_default_option(key: String) -> SyncReturn { } pub fn main_handle_relay_id(id: String) -> String { - handle_relay_id(id) + handle_relay_id(&id).to_owned() } pub fn main_get_main_display() -> SyncReturn { @@ -1726,6 +1720,17 @@ pub fn main_use_texture_render() -> SyncReturn { } } +pub fn main_has_file_clipboard() -> SyncReturn { + let ret = cfg!(any( + target_os = "windows", + all( + feature = "unix-file-copy-paste", + any(target_os = "linux", target_os = "macos") + ) + )); + SyncReturn(ret) +} + pub fn cm_init() { #[cfg(not(any(target_os = "android", target_os = "ios")))] crate::flutter::connection_manager::cm_init(); diff --git a/src/ipc.rs b/src/ipc.rs index 2a7bf085d..aea90a498 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -177,6 +177,7 @@ pub enum Data { file_transfer_enabled: bool, restart: bool, recording: bool, + block_input: bool, from_switch: bool, }, ChatMessage { @@ -232,7 +233,7 @@ pub enum Data { Plugin(Plugin), #[cfg(windows)] SyncWinCpuUsage(Option), - FileTransferLog(String), + FileTransferLog((String, String)), #[cfg(windows)] ControlledSessionCount(usize), } diff --git a/src/lang/ar.rs b/src/lang/ar.rs index e2c4c95ea..8346144d3 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "جاهز"), ("Established", "تم الانشاء"), ("connecting_status", "جاري الاتصال بشبكة RustDesk..."), - ("Enable Service", "تفعيل الخدمة"), - ("Start Service", "بدء الخدمة"), + ("Enable service", "تفعيل الخدمة"), + ("Start service", "بدء الخدمة"), ("Service is running", "الخدمة تعمل"), ("Service is not running", "الخدمة لا تعمل"), ("not_ready_status", "غير جاهز. الرجاء التأكد من الاتصال"), ("Control Remote Desktop", "التحكم بسطح المكتب البعيد"), - ("Transfer File", "نقل ملف"), + ("Transfer file", "نقل ملف"), ("Connect", "اتصال"), - ("Recent Sessions", "الجلسات الحديثة"), - ("Address Book", "كتاب العناوين"), + ("Recent sessions", "الجلسات الحديثة"), + ("Address book", "كتاب العناوين"), ("Confirmation", "التأكيد"), - ("TCP Tunneling", "نفق TCP"), + ("TCP tunneling", "نفق TCP"), ("Remove", "ازالة"), ("Refresh random password", "تحديث كلمة مرور عشوائية"), ("Set your own password", "تعيين كلمة مرور خاصة بك"), - ("Enable Keyboard/Mouse", "تفعيل لوحة المفاتيح/الفأرة"), - ("Enable Clipboard", "تفعيل الحافظة"), - ("Enable File Transfer", "تفعيل نقل الملفات"), - ("Enable TCP Tunneling", "تفعيل نفق TCP"), + ("Enable keyboard/mouse", "تفعيل لوحة المفاتيح/الفأرة"), + ("Enable clipboard", "تفعيل الحافظة"), + ("Enable file transfer", "تفعيل نقل الملفات"), + ("Enable TCP tunneling", "تفعيل نفق TCP"), ("IP Whitelisting", "القائمة البيضاء للـ IP"), ("ID/Relay Server", "معرف خادم الوسيط"), - ("Import Server Config", "استيراد إعدادات الخادم"), + ("Import server config", "استيراد إعدادات الخادم"), ("Export Server Config", "تصدير إعدادات الخادم"), ("Import server configuration successfully", "تم استيراد إعدادات الخادم بنجاح"), ("Export server configuration successfully", "تم تصدير إعدادات الخادم بنجاح"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "قبول"), ("Dismiss", "صرف"), ("Disconnect", "قطع الاتصال"), - ("Allow using keyboard and mouse", "السماح باستخدام لوحة المفاتيح والفأرة"), - ("Allow using clipboard", "السماح باستخدام الحافظة"), - ("Allow hearing sound", "السماح بسماع الصوت"), - ("Allow file copy and paste", "السماح بالنسخ واللصق"), + ("Enable file copy and paste", "السماح بالنسخ واللصق"), ("Connected", "متصل"), ("Direct and encrypted connection", "اتصال مباشر مشفر"), ("Relayed and encrypted connection", "اتصال غير مباشر مشفر"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "جاري تسجيل الدخول..."), ("Enable RDP session sharing", "تفعيل مشاركة الجلسة باستخدام RDP"), ("Auto Login", "تسجيل دخول تلقائي"), - ("Enable Direct IP Access", "تفعيل الوصول المباشر لعنوان IP"), + ("Enable direct IP access", "تفعيل الوصول المباشر لعنوان IP"), ("Rename", "اعادة تسمية"), ("Space", "مساحة"), - ("Create Desktop Shortcut", "انشاء اختصار سطح مكتب"), + ("Create desktop shortcut", "انشاء اختصار سطح مكتب"), ("Change Path", "تغيير المسار"), ("Create Folder", "انشاء مجلد"), ("Please enter the folder name", "الرجاء ادخال اسم المجلد"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "ابق خدمة RustDesk تعمل في الخلفية"), ("Ignore Battery Optimizations", "تجاهل تحسينات البطارية"), ("android_open_battery_optimizations_tip", "اذا اردت تعطيل هذه الميزة, الرجاء الذهاب الى صفحة اعدادات تطبيق RustDesk, ابحث عن البطارية, الغ تحديد غير مقيد"), - ("Start on Boot", "البدء عند تشغيل النظام"), + ("Start on boot", "البدء عند تشغيل النظام"), ("Start the screen sharing service on boot, requires special permissions", "تشغيل خدمة مشاركة الشاشة عند بدء تشغيل النظام, يحتاج الى اذونات خاصة"), ("Connection not allowed", "الاتصال غير مسموح"), ("Legacy mode", "الوضع التقليدي"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "استخدام كلمة مرور دائمة"), ("Use both passwords", "استخدام طريقتي كلمة المرور"), ("Set permanent password", "تعيين كلمة مرور دائمة"), - ("Enable Remote Restart", "تفعيل اعداة تشغيل البعيد"), - ("Allow remote restart", "السماح باعادة تشغيل البعيد"), - ("Restart Remote Device", "اعادة تشغيل الجهاز البعيد"), + ("Enable remote restart", "تفعيل اعداة تشغيل البعيد"), + ("Restart remote device", "اعادة تشغيل الجهاز البعيد"), ("Are you sure you want to restart", "هل انت متاكد من انك تريد اعادة التشغيل؟"), - ("Restarting Remote Device", "جاري اعادة تشغيل البعيد"), + ("Restarting remote device", "جاري اعادة تشغيل البعيد"), ("remote_restarting_tip", "الجهاز البعيد يعيد التشغيل. الرجاء اغلاق هذه الرسالة واعادة الاتصال باستخدام كلمة المرور الدائمة بعد فترة بسيطة."), ("Copied", "منسوخ"), ("Exit Fullscreen", "الخروج من ملئ الشاشة"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "نفس نظام التشغيل"), ("Enable hardware codec", "تفعيل ترميز العتاد"), ("Unlock Security Settings", "فتح اعدادات الامان"), - ("Enable Audio", "تفعيل الصوت"), + ("Enable audio", "تفعيل الصوت"), ("Unlock Network Settings", "فتح اعدادات الشبكة"), ("Server", "الخادم"), ("Direct IP Access", "وصول مباشر للـ IP"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "تغيير"), ("Start session recording", "بدء تسجيل الجلسة"), ("Stop session recording", "ايقاف تسجيل الجلسة"), - ("Enable Recording Session", "تفعيل تسجيل الجلسة"), - ("Allow recording session", "السماح بتسجيل الجلسة"), - ("Enable LAN Discovery", "تفعيل اكتشاف الشبكة المحلية"), - ("Deny LAN Discovery", "رفض اكتشاف الشبكة المحلية"), + ("Enable recording session", "تفعيل تسجيل الجلسة"), + ("Enable LAN discovery", "تفعيل اكتشاف الشبكة المحلية"), + ("Deny LAN discovery", "رفض اكتشاف الشبكة المحلية"), ("Write a message", "اكتب رسالة"), ("Prompt", ""), ("Please wait for confirmation of UAC...", "الرجاء انتظار تاكيد تحكم حساب المستخدم..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "دعم Wayland لازال في المرحلة التجريبية. الرجاء استخدام X11 اذا اردت وصول كامل."), ("Right click to select tabs", "الضغط بالزر الايمن لتحديد الالسنة"), ("Skipped", "متجاوز"), - ("Add to Address Book", "اضافة الى كتاب العناوين"), + ("Add to address book", "اضافة الى كتاب العناوين"), ("Group", "مجموعة"), ("Search", "بحث"), ("Closed manually by web console", "اغلق يدويا عبر طرفية الويب"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 9f52439e4..33cf3cd92 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Llest"), ("Established", "Establert"), ("connecting_status", "Connexió a la xarxa RustDesk en progrés..."), - ("Enable Service", "Habilitar Servei"), - ("Start Service", "Iniciar Servei"), + ("Enable service", "Habilitar Servei"), + ("Start service", "Iniciar Servei"), ("Service is running", "El servei s'està executant"), ("Service is not running", "El servei no s'està executant"), ("not_ready_status", "No està llest. Comprova la teva connexió"), ("Control Remote Desktop", "Controlar escriptori remot"), - ("Transfer File", "Transferir arxiu"), + ("Transfer file", "Transferir arxiu"), ("Connect", "Connectar"), - ("Recent Sessions", "Sessions recents"), - ("Address Book", "Directori"), + ("Recent sessions", "Sessions recents"), + ("Address book", "Directori"), ("Confirmation", "Confirmació"), - ("TCP Tunneling", "Túnel TCP"), + ("TCP tunneling", "Túnel TCP"), ("Remove", "Eliminar"), ("Refresh random password", "Actualitzar contrasenya aleatòria"), ("Set your own password", "Estableix la teva pròpia contrasenya"), - ("Enable Keyboard/Mouse", "Habilitar teclat/ratolí"), - ("Enable Clipboard", "Habilitar portapapers"), - ("Enable File Transfer", "Habilitar transferència d'arxius"), - ("Enable TCP Tunneling", "Habilitar túnel TCP"), + ("Enable keyboard/mouse", "Habilitar teclat/ratolí"), + ("Enable clipboard", "Habilitar portapapers"), + ("Enable file transfer", "Habilitar transferència d'arxius"), + ("Enable TCP tunneling", "Habilitar túnel TCP"), ("IP Whitelisting", "Direccions IP admeses"), ("ID/Relay Server", "Servidor ID/Relay"), - ("Import Server Config", "Importar configuració de servidor"), + ("Import server config", "Importar configuració de servidor"), ("Export Server Config", "Exportar configuració del servidor"), ("Import server configuration successfully", "Configuració de servidor importada amb èxit"), ("Export server configuration successfully", "Configuració de servidor exportada con èxit"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Acceptar"), ("Dismiss", "Cancel·lar"), ("Disconnect", "Desconnectar"), - ("Allow using keyboard and mouse", "Permetre l'ús del teclat i ratolí"), - ("Allow using clipboard", "Permetre usar portapapers"), - ("Allow hearing sound", "Permetre escoltar so"), - ("Allow file copy and paste", "Permetre copiar i enganxar arxius"), + ("Enable file copy and paste", "Permetre copiar i enganxar arxius"), ("Connected", "Connectat"), ("Direct and encrypted connection", "Connexió directa i xifrada"), ("Relayed and encrypted connection", "connexió retransmesa i xifrada"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Iniciant sessió..."), ("Enable RDP session sharing", "Habilitar l'ús compartit de sessions RDP"), ("Auto Login", "Inici de sessió automàtic"), - ("Enable Direct IP Access", "Habilitar accés IP directe"), + ("Enable direct IP access", "Habilitar accés IP directe"), ("Rename", "Renombrar"), ("Space", "Espai"), - ("Create Desktop Shortcut", "Crear accés directe a l'escriptori"), + ("Create desktop shortcut", "Crear accés directe a l'escriptori"), ("Change Path", "Cnviar ruta"), ("Create Folder", "Crear carpeta"), ("Please enter the folder name", "Indiqui el nom de la carpeta"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Mantenir RustDesk com a servei en segon pla"), ("Ignore Battery Optimizations", "Ignorar optimizacions de la bateria"), ("android_open_battery_optimizations_tip", ""), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Connexió no disponible"), ("Legacy mode", "Mode heretat"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Utilitzar contrasenya permament"), ("Use both passwords", "Utilitzar ambdues contrasenyas"), ("Set permanent password", "Establir contrasenya permament"), - ("Enable Remote Restart", "Activar reinici remot"), - ("Allow remote restart", "Permetre reinici remot"), - ("Restart Remote Device", "Reiniciar dispositiu"), + ("Enable remote restart", "Activar reinici remot"), + ("Restart remote device", "Reiniciar dispositiu"), ("Are you sure you want to restart", "Està segur que vol reiniciar?"), - ("Restarting Remote Device", "Reiniciant dispositiu remot"), + ("Restarting remote device", "Reiniciant dispositiu remot"), ("remote_restarting_tip", "Dispositiu remot reiniciant, tanqui aquest missatge i tornis a connectar amb la contrasenya."), ("Copied", "Copiat"), ("Exit Fullscreen", "Sortir de la pantalla completa"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Tema del sistema"), ("Enable hardware codec", "Habilitar còdec per hardware"), ("Unlock Security Settings", "Desbloquejar ajustaments de seguretat"), - ("Enable Audio", "Habilitar àudio"), + ("Enable audio", "Habilitar àudio"), ("Unlock Network Settings", "Desbloquejar Ajustaments de Xarxa"), ("Server", "Servidor"), ("Direct IP Access", "Accés IP Directe"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Canviar"), ("Start session recording", "Començar gravació de sessió"), ("Stop session recording", "Aturar gravació de sessió"), - ("Enable Recording Session", "Habilitar gravació de sessió"), - ("Allow recording session", "Permetre gravació de sessió"), - ("Enable LAN Discovery", "Habilitar descobriment de LAN"), - ("Deny LAN Discovery", "Denegar descobriment de LAN"), + ("Enable recording session", "Habilitar gravació de sessió"), + ("Enable LAN discovery", "Habilitar descobriment de LAN"), + ("Deny LAN discovery", "Denegar descobriment de LAN"), ("Write a message", "Escriure un missatge"), ("Prompt", ""), ("Please wait for confirmation of UAC...", ""), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 4cf749ffa..35f6d58db 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "就绪"), ("Established", "已建立"), ("connecting_status", "正在接入 RustDesk 网络..."), - ("Enable Service", "允许服务"), - ("Start Service", "启动服务"), + ("Enable service", "允许服务"), + ("Start service", "启动服务"), ("Service is running", "服务正在运行"), ("Service is not running", "服务未运行"), ("not_ready_status", "未就绪,请检查网络连接"), ("Control Remote Desktop", "控制远程桌面"), - ("Transfer File", "传输文件"), + ("Transfer file", "传输文件"), ("Connect", "连接"), - ("Recent Sessions", "最近访问过"), - ("Address Book", "地址簿"), + ("Recent sessions", "最近访问过"), + ("Address book", "地址簿"), ("Confirmation", "确认"), - ("TCP Tunneling", "TCP 隧道"), + ("TCP tunneling", "TCP 隧道"), ("Remove", "删除"), ("Refresh random password", "刷新随机密码"), ("Set your own password", "设置密码"), - ("Enable Keyboard/Mouse", "允许控制键盘/鼠标"), - ("Enable Clipboard", "允许同步剪贴板"), - ("Enable File Transfer", "允许传输文件"), - ("Enable TCP Tunneling", "允许建立 TCP 隧道"), + ("Enable keyboard/mouse", "允许控制键盘/鼠标"), + ("Enable clipboard", "允许同步剪贴板"), + ("Enable file transfer", "允许传输文件"), + ("Enable TCP tunneling", "允许建立 TCP 隧道"), ("IP Whitelisting", "IP 白名单"), ("ID/Relay Server", "ID/中继服务器"), - ("Import Server Config", "导入服务器配置"), + ("Import server config", "导入服务器配置"), ("Export Server Config", "导出服务器配置"), ("Import server configuration successfully", "导入服务器配置信息成功"), ("Export server configuration successfully", "导出服务器配置信息成功"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "接受"), ("Dismiss", "拒绝"), ("Disconnect", "断开连接"), - ("Allow using keyboard and mouse", "允许使用键盘鼠标"), - ("Allow using clipboard", "允许使用剪贴板"), - ("Allow hearing sound", "允许听到声音"), - ("Allow file copy and paste", "允许复制粘贴文件"), + ("Enable file copy and paste", "允许复制粘贴文件"), ("Connected", "已连接"), ("Direct and encrypted connection", "加密直连"), ("Relayed and encrypted connection", "加密中继连接"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "正在登录..."), ("Enable RDP session sharing", "允许 RDP 会话共享"), ("Auto Login", "自动登录(设置断开后锁定才有效)"), - ("Enable Direct IP Access", "允许 IP 直接访问"), + ("Enable direct IP access", "允许 IP 直接访问"), ("Rename", "重命名"), ("Space", "空格"), - ("Create Desktop Shortcut", "创建桌面快捷方式"), + ("Create desktop shortcut", "创建桌面快捷方式"), ("Change Path", "更改路径"), ("Create Folder", "创建文件夹"), ("Please enter the folder name", "请输入文件夹名称"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "保持 RustDesk 后台服务"), ("Ignore Battery Optimizations", "忽略电池优化"), ("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的 RustDesk 应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"), - ("Start on Boot", "开机自启动"), + ("Start on boot", "开机自启动"), ("Start the screen sharing service on boot, requires special permissions", "开机自动启动屏幕共享服务,此功能需要一些特殊权限。"), ("Connection not allowed", "对方不允许连接"), ("Legacy mode", "传统模式"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "使用固定密码"), ("Use both passwords", "同时使用两种密码"), ("Set permanent password", "设置固定密码"), - ("Enable Remote Restart", "允许远程重启"), - ("Allow remote restart", "允许远程重启"), - ("Restart Remote Device", "重启远程电脑"), + ("Enable remote restart", "允许远程重启"), + ("Restart remote device", "重启远程电脑"), ("Are you sure you want to restart", "确定要重启"), - ("Restarting Remote Device", "正在重启远程设备"), + ("Restarting remote device", "正在重启远程设备"), ("remote_restarting_tip", "远程设备正在重启, 请关闭当前提示框, 并在一段时间后使用永久密码重新连接"), ("Copied", "已复制"), ("Exit Fullscreen", "退出全屏"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "跟随系统"), ("Enable hardware codec", "使用硬件编解码"), ("Unlock Security Settings", "解锁安全设置"), - ("Enable Audio", "允许传输音频"), + ("Enable audio", "允许传输音频"), ("Unlock Network Settings", "解锁网络设置"), ("Server", "服务器"), ("Direct IP Access", "IP 直接访问"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "更改"), ("Start session recording", "开始录屏"), ("Stop session recording", "结束录屏"), - ("Enable Recording Session", "允许录制会话"), - ("Allow recording session", "允许录制会话"), - ("Enable LAN Discovery", "允许局域网发现"), - ("Deny LAN Discovery", "拒绝局域网发现"), + ("Enable recording session", "允许录制会话"), + ("Enable LAN discovery", "允许局域网发现"), + ("Deny LAN discovery", "拒绝局域网发现"), ("Write a message", "输入聊天消息"), ("Prompt", "提示"), ("Please wait for confirmation of UAC...", "请等待对方确认 UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wayland 支持处于实验阶段,如果你需要使用无人值守访问,请使用 X11。"), ("Right click to select tabs", "右键选择选项卡"), ("Skipped", "已跳过"), - ("Add to Address Book", "添加到地址簿"), + ("Add to address book", "添加到地址簿"), ("Group", "小组"), ("Search", "搜索"), ("Closed manually by web console", "被 web 控制台手动关闭"), @@ -566,11 +561,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", "在单个窗口中打开显示器"), ("Use all my displays for the remote session", "将我的所有显示器用于远程会话"), ("selinux_tip", "SELinux 处于启用状态,RustDesk 可能无法作为被控正常运行。"), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), + ("Change view", "更改视图"), + ("Big tiles", "大磁贴"), + ("Small tiles", "小磁贴"), + ("List", "列表"), ("Virtual display", "虚拟显示器"), ("Plug out all", "拔出所有"), + ("True color (4:4:4)", "真彩模式(4:4:4)"), + ("Enable blocking user input", "允许阻止用户输入"), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 6f809aa41..362b5526b 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Připraveno"), ("Established", "Navázáno"), ("connecting_status", "Připojování k RustDesk síti..."), - ("Enable Service", "Povolit službu"), - ("Start Service", "Spustit službu"), + ("Enable service", "Povolit službu"), + ("Start service", "Spustit službu"), ("Service is running", "Služba je spuštěná"), ("Service is not running", "Služba není spuštěná"), ("not_ready_status", "Nepřipraveno. Zkontrolujte své připojení."), ("Control Remote Desktop", "Ovládat vzdálenou plochu"), - ("Transfer File", "Přenos souborů"), + ("Transfer file", "Přenos souborů"), ("Connect", "Připojit"), - ("Recent Sessions", "Nedávné relace"), - ("Address Book", "Adresář kontaktů"), + ("Recent sessions", "Nedávné relace"), + ("Address book", "Adresář kontaktů"), ("Confirmation", "Potvrzení"), - ("TCP Tunneling", "TCP tunelování"), + ("TCP tunneling", "TCP tunelování"), ("Remove", "Odebrat"), ("Refresh random password", "Vytvořit nové náhodné heslo"), ("Set your own password", "Nastavte si své vlastní heslo"), - ("Enable Keyboard/Mouse", "Povolit klávesnici/myš"), - ("Enable Clipboard", "Povolit schránku"), - ("Enable File Transfer", "Povolit přenos souborů"), - ("Enable TCP Tunneling", "Povolit TCP tunelování"), + ("Enable keyboard/mouse", "Povolit klávesnici/myš"), + ("Enable clipboard", "Povolit schránku"), + ("Enable file transfer", "Povolit přenos souborů"), + ("Enable TCP tunneling", "Povolit TCP tunelování"), ("IP Whitelisting", "Povolování pouze z daných IP adres"), ("ID/Relay Server", "ID/předávací server"), - ("Import Server Config", "Importovat konfiguraci serveru"), + ("Import server config", "Importovat konfiguraci serveru"), ("Export Server Config", "Exportovat konfiguraci serveru"), ("Import server configuration successfully", "Konfigurace serveru úspěšně importována"), ("Export server configuration successfully", "Konfigurace serveru úspěšně exportována"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Přijmout"), ("Dismiss", "Zahodit"), ("Disconnect", "Odpojit"), - ("Allow using keyboard and mouse", "Povolit ovládání klávesnice a myši"), - ("Allow using clipboard", "Povolit použití schránky"), - ("Allow hearing sound", "Povolit slyšet zvuk"), - ("Allow file copy and paste", "Povolit kopírování a vkládání souborů"), + ("Enable file copy and paste", "Povolit kopírování a vkládání souborů"), ("Connected", "Připojeno"), ("Direct and encrypted connection", "Přímé a šifrované spojení"), ("Relayed and encrypted connection", "Předávané a šifrované spojení"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Přihlašování..."), ("Enable RDP session sharing", "Povolit sdílení relace RDP"), ("Auto Login", "Automatické přihlášení"), - ("Enable Direct IP Access", "Povolit přímý přístup k IP"), + ("Enable direct IP access", "Povolit přímý přístup k IP"), ("Rename", "Přejmenovat"), ("Space", "Mezera"), - ("Create Desktop Shortcut", "Vytvořit zástupce na ploše"), + ("Create desktop shortcut", "Vytvořit zástupce na ploše"), ("Change Path", "Změnit umístění"), ("Create Folder", "Vytvořit složku"), ("Please enter the folder name", "Zadejte název pro složku"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Zachovat službu RustDesk na pozadí"), ("Ignore Battery Optimizations", "Ignorovat optimalizaci baterie"), ("android_open_battery_optimizations_tip", "Pokud chcete tuto funkci zakázat, přejděte na další stránku nastavení aplikace RustDesk, najděte a zadejte [Baterie], zrušte zaškrtnutí [Neomezeno]."), - ("Start on Boot", "Spustit při startu systému"), + ("Start on boot", "Spustit při startu systému"), ("Start the screen sharing service on boot, requires special permissions", "Spuštění služby sdílení obrazovky při spuštění systému, vyžaduje zvláštní oprávnění"), ("Connection not allowed", "Připojení není povoleno"), ("Legacy mode", "Režim Legacy"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Použít trvalé heslo"), ("Use both passwords", "Použít obě hesla"), ("Set permanent password", "Nastavit trvalé heslo"), - ("Enable Remote Restart", "Povolit vzdálené restartování"), - ("Allow remote restart", "Povolit vzdálený restart"), - ("Restart Remote Device", "Restartovat vzdálené zařízení"), + ("Enable remote restart", "Povolit vzdálené restartování"), + ("Restart remote device", "Restartovat vzdálené zařízení"), ("Are you sure you want to restart", "Jste si jisti, že chcete restartovat"), - ("Restarting Remote Device", "Restartování vzdáleného zařízení"), + ("Restarting remote device", "Restartování vzdáleného zařízení"), ("remote_restarting_tip", "Vzdálené zařízení se restartuje, zavřete prosím toto okno a po chvíli se znovu připojte pomocí trvalého hesla."), ("Copied", "Zkopírováno"), ("Exit Fullscreen", "Ukončit celou obrazovku"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Podle systému"), ("Enable hardware codec", "Povolit hardwarový kodek"), ("Unlock Security Settings", "Odemknout nastavení zabezpečení"), - ("Enable Audio", "Povolit zvuk"), + ("Enable audio", "Povolit zvuk"), ("Unlock Network Settings", "Odemknout nastavení sítě"), ("Server", "Server"), ("Direct IP Access", "Přímý IP přístup"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Změnit"), ("Start session recording", "Spustit záznam relace"), ("Stop session recording", "Zastavit záznam relace"), - ("Enable Recording Session", "Povolit nahrávání relace"), - ("Allow recording session", "Povolit nahrávání relace"), - ("Enable LAN Discovery", "Povolit zjišťování sítě LAN"), - ("Deny LAN Discovery", "Zakázat zjišťování sítě LAN"), + ("Enable recording session", "Povolit nahrávání relace"), + ("Enable LAN discovery", "Povolit zjišťování sítě LAN"), + ("Deny LAN discovery", "Zakázat zjišťování sítě LAN"), ("Write a message", "Napsat zprávu"), ("Prompt", "Výzva"), ("Please wait for confirmation of UAC...", "Počkejte prosím na potvrzení UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Podpora Waylandu je v experimentální fázi, pokud potřebujete bezobslužný přístup, použijte prosím X11."), ("Right click to select tabs", "Výběr karet kliknutím pravým tlačítkem myši"), ("Skipped", "Vynecháno"), - ("Add to Address Book", "Přidat do adresáře"), + ("Add to address book", "Přidat do adresáře"), ("Group", "Skupina"), ("Search", "Vyhledávání"), ("Closed manually by web console", "Uzavřeno ručně pomocí webové konzole"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", "Seznam"), ("Virtual display", "Virtuální obrazovka"), ("Plug out all", "Odpojit všechny"), + ("True color (4:4:4)", "Skutečné barvy (4:4:4)"), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index bb9c9f06d..173fc7cef 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Klar"), ("Established", "Etableret"), ("connecting_status", "Opretter forbindelse til RustDesk-netværket..."), - ("Enable Service", "Tænd forbindelsesserveren"), - ("Start Service", "Start forbindelsesserveren"), + ("Enable service", "Tænd forbindelsesserveren"), + ("Start service", "Start forbindelsesserveren"), ("Service is running", "Tjenesten kører"), ("Service is not running", "Den tilknyttede tjeneste kører ikke"), ("not_ready_status", "Ikke klar. Tjek venligst din forbindelse"), ("Control Remote Desktop", "Styr fjernskrivebord"), - ("Transfer File", "Overfør fil"), + ("Transfer file", "Overfør fil"), ("Connect", "Forbind"), - ("Recent Sessions", "Seneste sessioner"), - ("Address Book", "Adressebog"), + ("Recent sessions", "Seneste sessioner"), + ("Address book", "Adressebog"), ("Confirmation", "Bekræftelse"), - ("TCP Tunneling", "TCP tunneling"), + ("TCP tunneling", "TCP tunneling"), ("Remove", "Fjern"), ("Refresh random password", "Opdater tilfældig adgangskode"), ("Set your own password", "Indstil din egen adgangskode"), - ("Enable Keyboard/Mouse", "Tænd for tastatur/mus"), - ("Enable Clipboard", "Tænd for udklipsholderen"), - ("Enable File Transfer", "Aktivér filoverførsel"), - ("Enable TCP Tunneling", "Slå TCP-tunneling til"), + ("Enable keyboard/mouse", "Tænd for tastatur/mus"), + ("Enable clipboard", "Tænd for udklipsholderen"), + ("Enable file transfer", "Aktivér filoverførsel"), + ("Enable TCP tunneling", "Slå TCP-tunneling til"), ("IP Whitelisting", "IP whitelisting"), ("ID/Relay Server", "ID/forbindelsesserver"), - ("Import Server Config", "Importér serverkonfiguration"), + ("Import server config", "Importér serverkonfiguration"), ("Export Server Config", "Eksportér serverkonfiguration"), ("Import server configuration successfully", "Importering af serverkonfigurationen lykkedes"), ("Export server configuration successfully", "Eksportering af serverkonfigurationen lykkedes"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Accepter"), ("Dismiss", "Afvis"), ("Disconnect", "Frakobl"), - ("Allow using keyboard and mouse", "Tillad brug af tastatur og mus"), - ("Allow using clipboard", "Tillad brug af udklipsholderen"), - ("Allow hearing sound", "Tillad at høre lyd"), - ("Allow file copy and paste", "Tillad kopiering og indsæt af filer"), + ("Enable file copy and paste", "Tillad kopiering og indsæt af filer"), ("Connected", "Forbundet"), ("Direct and encrypted connection", "Direkte og krypteret forbindelse"), ("Relayed and encrypted connection", "Viderestillet og krypteret forbindelse"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Logger ind..."), ("Enable RDP session sharing", "Aktivér RDP sessiongodkendelse"), ("Auto Login", "Automatisk login (kun gyldigt hvis du har konfigureret \"Lås efter afslutningen af sessionen\")"), - ("Enable Direct IP Access", "Aktivér direkte IP-adgang"), + ("Enable direct IP access", "Aktivér direkte IP-adgang"), ("Rename", "Omdøb"), ("Space", "Plads"), - ("Create Desktop Shortcut", "Opret skrivebords-genvej"), + ("Create desktop shortcut", "Opret skrivebords-genvej"), ("Change Path", "Skift stien"), ("Create Folder", "Opret mappe"), ("Please enter the folder name", "Indtast venligst mappens navn"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Behold RustDesk baggrundstjeneste"), ("Ignore Battery Optimizations", "Ignorér betteri optimeringer"), ("android_open_battery_optimizations_tip", ""), - ("Start on Boot", "Start under opstart"), + ("Start on boot", "Start under opstart"), ("Start the screen sharing service on boot, requires special permissions", "Start skærmdelingstjenesten under opstart, kræver specielle rettigheder"), ("Connection not allowed", "Forbindelse ikke tilladt"), ("Legacy mode", "Bagudkompatibilitetstilstand"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Brug permanent adgangskode"), ("Use both passwords", "Brug begge adgangskoder"), ("Set permanent password", "Sæt permanent adgangskode"), - ("Enable Remote Restart", "Aktivér fjerngenstart"), - ("Allow remote restart", "Tillad fjerngenstart"), - ("Restart Remote Device", "Genstart fjernenhed"), + ("Enable remote restart", "Aktivér fjerngenstart"), + ("Restart remote device", "Genstart fjernenhed"), ("Are you sure you want to restart", "Er du sikker på at du vil genstarte"), - ("Restarting Remote Device", "Genstarter fjernenhed"), + ("Restarting remote device", "Genstarter fjernenhed"), ("remote_restarting_tip", "Enheden genstarter - Lukker denne besked ned, og tilslutter igen om et øjeblik"), ("Copied", "Kopieret"), ("Exit Fullscreen", "Afslut fuldskærm"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Følg System"), ("Enable hardware codec", "Aktivér hardware-codec"), ("Unlock Security Settings", "Lås op for sikkerhedsindstillinger"), - ("Enable Audio", "Aktivér Lyd"), + ("Enable audio", "Aktivér Lyd"), ("Unlock Network Settings", "Lås op for Netværksindstillinger"), ("Server", "Server"), ("Direct IP Access", "Direkte IP Adgang"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Ændr"), ("Start session recording", "Start sessionsoptagelse"), ("Stop session recording", "Stop sessionsoptagelse"), - ("Enable Recording Session", "Aktivér optagelsessession"), - ("Allow recording session", "Tillad optagelsessession"), - ("Enable LAN Discovery", "Aktivér LAN Discovery"), - ("Deny LAN Discovery", "Afvis LAN Discovery"), + ("Enable recording session", "Aktivér optagelsessession"), + ("Enable LAN discovery", "Aktivér LAN Discovery"), + ("Deny LAN discovery", "Afvis LAN Discovery"), ("Write a message", "Skriv en besked"), ("Prompt", "Prompt"), ("Please wait for confirmation of UAC...", "Vent venligst på UAC-bekræftelse..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", "Højreklik for at vælge faner"), ("Skipped", "Sprunget over"), - ("Add to Address Book", "Tilføj til adressebog"), + ("Add to address book", "Tilføj til adressebog"), ("Group", "Gruppe"), ("Search", "Søg"), ("Closed manually by web console", "Lukket ned manuelt af webkonsollen"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index fde9d132c..6dac90ef3 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Bereit"), ("Established", "Verbunden"), ("connecting_status", "Verbinden mit dem RustDesk-Netzwerk …"), - ("Enable Service", "Vermittlungsdienst aktivieren"), - ("Start Service", "Vermittlungsdienst starten"), + ("Enable service", "Vermittlungsdienst aktivieren"), + ("Start service", "Vermittlungsdienst starten"), ("Service is running", "Vermittlungsdienst aktiv"), ("Service is not running", "Vermittlungsdienst deaktiviert"), ("not_ready_status", "Nicht bereit. Bitte überprüfen Sie Ihre Netzwerkverbindung."), ("Control Remote Desktop", "Entfernten Desktop steuern"), - ("Transfer File", "Datei übertragen"), + ("Transfer file", "Datei übertragen"), ("Connect", "Verbinden"), - ("Recent Sessions", "Letzte Sitzungen"), - ("Address Book", "Adressbuch"), + ("Recent sessions", "Letzte Sitzungen"), + ("Address book", "Adressbuch"), ("Confirmation", "Bestätigung"), - ("TCP Tunneling", "TCP-Tunnelung"), + ("TCP tunneling", "TCP-Tunnelung"), ("Remove", "Entfernen"), ("Refresh random password", "Zufälliges Passwort erzeugen"), ("Set your own password", "Eigenes Passwort setzen"), - ("Enable Keyboard/Mouse", "Tastatur und Maus aktivieren"), - ("Enable Clipboard", "Zwischenablage aktivieren"), - ("Enable File Transfer", "Dateiübertragung aktivieren"), - ("Enable TCP Tunneling", "TCP-Tunnelung aktivieren"), + ("Enable keyboard/mouse", "Tastatur und Maus aktivieren"), + ("Enable clipboard", "Zwischenablage aktivieren"), + ("Enable file transfer", "Dateiübertragung aktivieren"), + ("Enable TCP tunneling", "TCP-Tunnelung aktivieren"), ("IP Whitelisting", "IP-Whitelist"), ("ID/Relay Server", "ID/Relay-Server"), - ("Import Server Config", "Serverkonfiguration importieren"), + ("Import server config", "Serverkonfiguration importieren"), ("Export Server Config", "Serverkonfiguration exportieren"), ("Import server configuration successfully", "Serverkonfiguration erfolgreich importiert"), ("Export server configuration successfully", "Serverkonfiguration erfolgreich exportiert"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Akzeptieren"), ("Dismiss", "Ablehnen"), ("Disconnect", "Verbindung trennen"), - ("Allow using keyboard and mouse", "Verwendung von Maus und Tastatur zulassen"), - ("Allow using clipboard", "Verwendung der Zwischenablage zulassen"), - ("Allow hearing sound", "Soundübertragung zulassen"), - ("Allow file copy and paste", "Kopieren und Einfügen von Dateien zulassen"), + ("Enable file copy and paste", "Kopieren und Einfügen von Dateien zulassen"), ("Connected", "Verbunden"), ("Direct and encrypted connection", "Direkte und verschlüsselte Verbindung"), ("Relayed and encrypted connection", "Vermittelte und verschlüsselte Verbindung"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Anmelden …"), ("Enable RDP session sharing", "RDP-Sitzungsfreigabe aktivieren"), ("Auto Login", "Automatisch anmelden (nur gültig, wenn Sie \"Nach Sitzungsende sperren\" aktiviert haben)"), - ("Enable Direct IP Access", "Direkten IP-Zugriff aktivieren"), + ("Enable direct IP access", "Direkten IP-Zugriff aktivieren"), ("Rename", "Umbenennen"), ("Space", "Speicherplatz"), - ("Create Desktop Shortcut", "Desktop-Verknüpfung erstellen"), + ("Create desktop shortcut", "Desktop-Verknüpfung erstellen"), ("Change Path", "Pfad ändern"), ("Create Folder", "Ordner erstellen"), ("Please enter the folder name", "Bitte geben Sie den Ordnernamen ein"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "RustDesk im Hintergrund ausführen"), ("Ignore Battery Optimizations", "Akkuoptimierung ignorieren"), ("android_open_battery_optimizations_tip", "Möchten Sie die Einstellungen zur Akkuoptimierung öffnen?"), - ("Start on Boot", "Beim Booten starten"), + ("Start on boot", "Beim Booten starten"), ("Start the screen sharing service on boot, requires special permissions", "Bildschirmfreigabedienst beim Booten starten, erfordert zusätzliche Berechtigungen"), ("Connection not allowed", "Verbindung abgelehnt"), ("Legacy mode", "Kompatibilitätsmodus"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Permanentes Passwort verwenden"), ("Use both passwords", "Beide Passwörter verwenden"), ("Set permanent password", "Permanentes Passwort setzen"), - ("Enable Remote Restart", "Entfernten Neustart aktivieren"), - ("Allow remote restart", "Entfernten Neustart erlauben"), - ("Restart Remote Device", "Entferntes Gerät neu starten"), + ("Enable remote restart", "Entfernten Neustart aktivieren"), + ("Restart remote device", "Entferntes Gerät neu starten"), ("Are you sure you want to restart", "Möchten Sie das entfernte Gerät wirklich neu starten?"), - ("Restarting Remote Device", "Entferntes Gerät wird neu gestartet"), + ("Restarting remote device", "Entferntes Gerät wird neu gestartet"), ("remote_restarting_tip", "Entferntes Gerät startet neu, bitte schließen Sie diese Meldung und verbinden Sie sich mit dem permanenten Passwort erneut."), ("Copied", "Kopiert"), ("Exit Fullscreen", "Vollbild beenden"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Systemstandard"), ("Enable hardware codec", "Hardware-Codec aktivieren"), ("Unlock Security Settings", "Sicherheitseinstellungen entsperren"), - ("Enable Audio", "Audio aktivieren"), + ("Enable audio", "Audio aktivieren"), ("Unlock Network Settings", "Netzwerkeinstellungen entsperren"), ("Server", "Server"), ("Direct IP Access", "Direkter IP-Zugang"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Ändern"), ("Start session recording", "Sitzungsaufzeichnung starten"), ("Stop session recording", "Sitzungsaufzeichnung beenden"), - ("Enable Recording Session", "Sitzungsaufzeichnung aktivieren"), - ("Allow recording session", "Sitzungsaufzeichnung erlauben"), - ("Enable LAN Discovery", "LAN-Erkennung aktivieren"), - ("Deny LAN Discovery", "LAN-Erkennung verbieten"), + ("Enable recording session", "Sitzungsaufzeichnung aktivieren"), + ("Enable LAN discovery", "LAN-Erkennung aktivieren"), + ("Deny LAN discovery", "LAN-Erkennung verbieten"), ("Write a message", "Nachricht schreiben"), ("Prompt", "Meldung"), ("Please wait for confirmation of UAC...", "Bitte auf die Bestätigung des Nutzers warten …"), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Die Unterstützung von Wayland ist nur experimentell. Bitte nutzen Sie X11, wenn Sie einen unbeaufsichtigten Zugriff benötigen."), ("Right click to select tabs", "Tabs mit rechtem Mausklick auswählen"), ("Skipped", "Übersprungen"), - ("Add to Address Book", "Zum Adressbuch hinzufügen"), + ("Add to address book", "Zum Adressbuch hinzufügen"), ("Group", "Gruppe"), ("Search", "Suchen"), ("Closed manually by web console", "Manuell über die Webkonsole geschlossen"), @@ -570,7 +565,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", "Große Kacheln"), ("Small tiles", "Kleine Kacheln"), ("List", "Liste"), - ("Virtual display", ""), - ("Plug out all", ""), + ("Virtual display", "Virtueller Bildschirm"), + ("Plug out all", "Alle ausschalten"), + ("True color (4:4:4)", "True Color (4:4:4)"), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index e6efe5677..16b9bb508 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Έτοιμο"), ("Established", "Συνδέθηκε"), ("connecting_status", "Σύνδεση στο δίκτυο RustDesk..."), - ("Enable Service", "Ενεργοποίηση υπηρεσίας"), - ("Start Service", "Έναρξη υπηρεσίας"), + ("Enable service", "Ενεργοποίηση υπηρεσίας"), + ("Start service", "Έναρξη υπηρεσίας"), ("Service is running", "Η υπηρεσία εκτελείται"), ("Service is not running", "Η υπηρεσία δεν εκτελείται"), ("not_ready_status", "Δεν είναι έτοιμο. Ελέγξτε τη σύνδεσή σας στο δίκτυο"), ("Control Remote Desktop", "Έλεγχος απομακρυσμένου σταθμού εργασίας"), - ("Transfer File", "Μεταφορά αρχείου"), + ("Transfer file", "Μεταφορά αρχείου"), ("Connect", "Σύνδεση"), - ("Recent Sessions", "Πρόσφατες συνεδρίες"), - ("Address Book", "Βιβλίο διευθύνσεων"), + ("Recent sessions", "Πρόσφατες συνεδρίες"), + ("Address book", "Βιβλίο διευθύνσεων"), ("Confirmation", "Επιβεβαίωση"), - ("TCP Tunneling", "TCP Tunneling"), + ("TCP tunneling", "TCP tunneling"), ("Remove", "Κατάργηση"), ("Refresh random password", "Νέος τυχαίος κωδικός πρόσβασης"), ("Set your own password", "Ορίστε τον δικό σας κωδικό πρόσβασης"), - ("Enable Keyboard/Mouse", "Ενεργοποίηση πληκτρολογίου/ποντικιού"), - ("Enable Clipboard", "Ενεργοποίηση προχείρου"), - ("Enable File Transfer", "Ενεργοποίηση μεταφοράς αρχείων"), - ("Enable TCP Tunneling", "Ενεργοποίηση TCP Tunneling"), + ("Enable keyboard/mouse", "Ενεργοποίηση πληκτρολογίου/ποντικιού"), + ("Enable clipboard", "Ενεργοποίηση προχείρου"), + ("Enable file transfer", "Ενεργοποίηση μεταφοράς αρχείων"), + ("Enable TCP tunneling", "Ενεργοποίηση TCP tunneling"), ("IP Whitelisting", "Λίστα επιτρεπόμενων IP"), ("ID/Relay Server", "Διακομιστής ID/Αναμετάδοσης"), - ("Import Server Config", "Εισαγωγή διαμόρφωσης διακομιστή"), + ("Import server config", "Εισαγωγή διαμόρφωσης διακομιστή"), ("Export Server Config", "Εξαγωγή διαμόρφωσης διακομιστή"), ("Import server configuration successfully", "Επιτυχής εισαγωγή διαμόρφωσης διακομιστή"), ("Export server configuration successfully", "Επιτυχής εξαγωγή διαμόρφωσης διακομιστή"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Αποδοχή"), ("Dismiss", "Απόρριψη"), ("Disconnect", "Αποσύνδεση"), - ("Allow using keyboard and mouse", "Να επιτρέπεται η χρήση πληκτρολογίου και ποντικιού"), - ("Allow using clipboard", "Να επιτρέπεται η χρήση του προχείρου"), - ("Allow hearing sound", "Να επιτρέπεται η αναπαραγωγή ήχου"), - ("Allow file copy and paste", "Να επιτρέπεται η αντιγραφή και επικόλληση αρχείων"), + ("Enable file copy and paste", "Να επιτρέπεται η αντιγραφή και επικόλληση αρχείων"), ("Connected", "Συνδεδεμένο"), ("Direct and encrypted connection", "Άμεση και κρυπτογραφημένη σύνδεση"), ("Relayed and encrypted connection", "Κρυπτογραφημένη σύνδεση με αναμετάδοση"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Γίνεται σύνδεση..."), ("Enable RDP session sharing", "Ενεργοποίηση κοινής χρήσης RDP"), ("Auto Login", "Αυτόματη είσοδος"), - ("Enable Direct IP Access", "Ενεργοποίηση άμεσης πρόσβασης IP"), + ("Enable direct IP access", "Ενεργοποίηση άμεσης πρόσβασης IP"), ("Rename", "Μετονομασία"), ("Space", "Χώρος"), - ("Create Desktop Shortcut", "Δημιουργία συντόμευσης στην επιφάνεια εργασίας"), + ("Create desktop shortcut", "Δημιουργία συντόμευσης στην επιφάνεια εργασίας"), ("Change Path", "Αλλαγή διαδρομής δίσκου"), ("Create Folder", "Δημιουργία φακέλου"), ("Please enter the folder name", "Παρακαλώ εισάγετε το όνομα του φακέλου"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Εκτέλεση του RustDesk στο παρασκήνιο"), ("Ignore Battery Optimizations", "Παράβλεψη βελτιστοποιήσεων μπαταρίας"), ("android_open_battery_optimizations_tip", "Θέλετε να ανοίξετε τις ρυθμίσεις βελτιστοποίησης μπαταρίας;"), - ("Start on Boot", "Έναρξη κατά την εκκίνηση"), + ("Start on boot", "Έναρξη κατά την εκκίνηση"), ("Start the screen sharing service on boot, requires special permissions", "Η έναρξη της υπηρεσίας κοινής χρήσης οθόνης κατά την εκκίνηση, απαιτεί ειδικά δικαιώματα"), ("Connection not allowed", "Η σύνδεση δεν επιτρέπεται"), ("Legacy mode", "Λειτουργία συμβατότητας"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Χρήση μόνιμου κωδικού πρόσβασης"), ("Use both passwords", "Χρήση και των δύο κωδικών πρόσβασης"), ("Set permanent password", "Ορισμός μόνιμου κωδικού πρόσβασης"), - ("Enable Remote Restart", "Ενεργοποίηση απομακρυσμένης επανεκκίνησης"), - ("Allow remote restart", "Να επιτρέπεται η απομακρυσμένη επανεκκίνηση"), - ("Restart Remote Device", "Επανεκκίνηση απομακρυσμένης συσκευής"), + ("Enable remote restart", "Ενεργοποίηση απομακρυσμένης επανεκκίνησης"), + ("Restart remote device", "Επανεκκίνηση απομακρυσμένης συσκευής"), ("Are you sure you want to restart", "Είστε βέβαιοι ότι θέλετε να κάνετε επανεκκίνηση"), - ("Restarting Remote Device", "Γίνεται επανεκκίνηση της απομακρυσμένης συσκευής"), + ("Restarting remote device", "Γίνεται επανεκκίνηση της απομακρυσμένης συσκευής"), ("remote_restarting_tip", "Η απομακρυσμένη συσκευή επανεκκινείται, κλείστε αυτό το μήνυμα και επανασυνδεθείτε χρησιμοποιώντας τον μόνιμο κωδικό πρόσβασης."), ("Copied", "Αντιγράφηκε"), ("Exit Fullscreen", "Έξοδος από πλήρη οθόνη"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Από το σύστημα"), ("Enable hardware codec", "Ενεργοποίηση κωδικοποιητή υλικού"), ("Unlock Security Settings", "Ξεκλείδωμα ρυθμίσεων ασφαλείας"), - ("Enable Audio", "Ενεργοποίηση ήχου"), + ("Enable audio", "Ενεργοποίηση ήχου"), ("Unlock Network Settings", "Ξεκλείδωμα ρυθμίσεων δικτύου"), ("Server", "Διακομιστής"), ("Direct IP Access", "Πρόσβαση με χρήση IP"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Αλλαγή"), ("Start session recording", "Έναρξη εγγραφής συνεδρίας"), ("Stop session recording", "Διακοπή εγγραφής συνεδρίας"), - ("Enable Recording Session", "Ενεργοποίηση εγγραφής συνεδρίας"), - ("Allow recording session", "Να επιτρέπεται η εγγραφή συνεδρίας"), - ("Enable LAN Discovery", "Ενεργοποίηση εντοπισμού LAN"), - ("Deny LAN Discovery", "Απαγόρευση εντοπισμού LAN"), + ("Enable recording session", "Ενεργοποίηση εγγραφής συνεδρίας"), + ("Enable LAN discovery", "Ενεργοποίηση εντοπισμού LAN"), + ("Deny LAN discovery", "Απαγόρευση εντοπισμού LAN"), ("Write a message", "Γράψτε ένα μήνυμα"), ("Prompt", "Υπενθυμίζω"), ("Please wait for confirmation of UAC...", "Παρακαλώ περιμένετε για επιβεβαίωση του UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Η υποστήριξη Wayland βρίσκεται σε πειραματικό στάδιο, χρησιμοποιήστε το X11 εάν χρειάζεστε πρόσβαση χωρίς επίβλεψη."), ("Right click to select tabs", "Κάντε δεξί κλικ για να επιλέξετε καρτέλες"), ("Skipped", "Παράλειψη"), - ("Add to Address Book", "Προσθήκη στο Βιβλίο Διευθύνσεων"), + ("Add to address book", "Προσθήκη στο Βιβλίο Διευθύνσεων"), ("Group", "Ομάδα"), ("Search", "Αναζήτηση"), ("Closed manually by web console", "Κλειστό χειροκίνητα από την κονσόλα web"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index 209772ccc..9e77b6ad5 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -3,21 +3,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ ("desk_tip", "Your desktop can be accessed with this ID and password."), ("connecting_status", "Connecting to the RustDesk network..."), - ("Enable Service", "Enable service"), - ("Start Service", "Enable service"), ("not_ready_status", "Not ready. Please check your connection"), - ("Transfer File", "Transfer file"), - ("Recent Sessions", "Recent sessions"), - ("Address Book", "Address book"), - ("TCP Tunneling", "TCP tunneling"), - ("Enable Keyboard/Mouse", "Enable keyboard/mouse"), - ("Enable Clipboard", "Enable clipboard"), - ("Enable File Transfer", "Enable file transfer"), - ("Enable TCP Tunneling", "Enable TCP tunneling"), - ("IP Whitelisting", "IP whitelisting"), ("ID/Relay Server", "ID/Relay server"), - ("Import Server Config", "Import server config"), - ("Export Server Config", "Export server config"), ("id_change_tip", "Only a-z, A-Z, 0-9 and _ (underscore) characters allowed. The first letter must be a-z, A-Z. Length between 6 and 16."), ("Slogan_tip", "Made with heart in this chaotic world!"), ("Build Date", "Build date"), @@ -61,8 +48,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("setup_server_tip", "For faster connection, please set up your own server"), ("Enter Remote ID", "Enter remote ID"), ("Auto Login", "Auto Login (Only valid if you set \"Lock after session end\")"), - ("Enable Direct IP Access", "Enable direct IP access"), - ("Create Desktop Shortcut", "Create desktop shortcut"), ("Change Path", "Change path"), ("Create Folder", "Create folder"), ("whitelist_tip", "Only whitelisted IP can access me"), @@ -104,15 +89,11 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "Turning on \"Screen Capture\" will automatically start the service, allowing other devices to request a connection to your device."), ("android_stop_service_tip", "Closing the service will automatically close all established connections."), ("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_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"), ("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]"), - ("Start on Boot", "Start on boot"), - ("Enable Remote Restart", "Enable remote restart"), - ("Restart Remote Device", "Restart remote device"), - ("Restarting Remote Device", "Restarting remote device"), ("remote_restarting_tip", "Remote device is restarting, please close this message box and reconnect with permanent password after a while"), ("Exit Fullscreen", "Exit fullscreen"), ("Mobile Actions", "Mobile actions"), @@ -131,16 +112,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Light Theme", "Light theme"), ("Follow System", "Follow system"), ("Unlock Security Settings", "Unlock security settings"), - ("Enable Audio", "Enable audio"), ("Unlock Network Settings", "Unlock network settings"), ("Direct IP Access", "Direct IP access"), ("Audio Input Device", "Audio input device"), ("Use IP Whitelisting", "Use IP whitelisting"), ("Pin Toolbar", "Pin toolbar"), ("Unpin Toolbar", "Unpin toolbar"), - ("Enable Recording Session", "Enable recording session"), - ("Enable LAN Discovery", "Enable LAN discovery"), - ("Deny LAN Discovery", "Deny LAN discovery"), ("elevated_foreground_window_tip", "The current window of the remote desktop requires higher privilege to operate, so it's unable to use the mouse and keyboard temporarily. You can request the remote user to minimize the current window, or click elevation button on the connection management window. To avoid this problem, it is recommended to install the software on the remote device."), ("Keyboard Settings", "Keyboard settings"), ("Full Access", "Full access"), @@ -150,7 +127,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("One-time Password", "One-time password"), ("hide_cm_tip", "Allow hiding only if accepting sessions via password and using permanent password"), ("wayland_experiment_tip", "Wayland support is in experimental stage, please use X11 if you require unattended access."), - ("Add to Address Book", "Add to address book"), ("software_render_tip", "If you're using Nvidia graphics card under Linux and the remote window closes immediately after connecting, switching to the open-source Nouveau driver and choosing to use software rendering may help. A software restart is required."), ("config_input", "In order to control remote desktop with keyboard, you need to grant RustDesk \"Input Monitoring\" permissions."), ("config_microphone", "In order to speak remotely, you need to grant RustDesk \"Record Audio\" permissions."), @@ -226,5 +202,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("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 mode."), ("selinux_tip", "SELinux is enabled on your device, which may prevent RustDesk from running properly as controlled side."), + ("id_input_tip", "You can input an ID, a direct IP, or a domain with a port (:).\nIf you want to access a device on another server, please append the server address (@?key=), for example,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nIf you want to access a device on a public server, please input \"@public\", the key is not needed for public server"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index f65f42aa0..eaf837dad 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Preta"), ("Established", ""), ("connecting_status", "Konektante al la reto RustDesk..."), - ("Enable Service", "Ebligi servon"), - ("Start Service", "Starti servon"), + ("Enable service", "Ebligi servon"), + ("Start service", "Starti servon"), ("Service is running", ""), ("Service is not running", "La servo ne funkcias"), ("not_ready_status", "Ne preta, bonvolu kontroli la retkonekto"), ("Control Remote Desktop", "Kontroli foran aparaton"), - ("Transfer File", "Transigi dosieron"), + ("Transfer file", "Transigi dosieron"), ("Connect", "Konekti al"), - ("Recent Sessions", "Lastaj sesioj"), - ("Address Book", "Adresaro"), + ("Recent sessions", "Lastaj sesioj"), + ("Address book", "Adresaro"), ("Confirmation", "Konfirmacio"), - ("TCP Tunneling", "Tunelado TCP"), + ("TCP tunneling", "Tunelado TCP"), ("Remove", "Forigi"), ("Refresh random password", "Regeneri hazardan pasvorton"), ("Set your own password", "Agordi vian propran pasvorton"), - ("Enable Keyboard/Mouse", "Ebligi klavaro/muso"), - ("Enable Clipboard", "Sinkronigi poŝon"), - ("Enable File Transfer", "Ebligi dosiertransigado"), - ("Enable TCP Tunneling", "Ebligi tunelado TCP"), + ("Enable keyboard/mouse", "Ebligi klavaro/muso"), + ("Enable clipboard", "Sinkronigi poŝon"), + ("Enable file transfer", "Ebligi dosiertransigado"), + ("Enable TCP tunneling", "Ebligi tunelado TCP"), ("IP Whitelisting", "Listo de IP akceptataj"), ("ID/Relay Server", "Identigila/Relajsa servilo"), - ("Import Server Config", "Enporti servilan agordon"), + ("Import server config", "Enporti servilan agordon"), ("Export Server Config", ""), ("Import server configuration successfully", "Importi servilan agordon sukcese"), ("Export server configuration successfully", ""), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Akcepti"), ("Dismiss", "Malakcepti"), ("Disconnect", "Malkonekti"), - ("Allow using keyboard and mouse", "Permesi la uzon de la klavaro kaj muso"), - ("Allow using clipboard", "Permesi la uzon de la poŝo"), - ("Allow hearing sound", "Permesi la uzon de la sono"), - ("Allow file copy and paste", "Permesu kopii kaj alglui dosierojn"), + ("Enable file copy and paste", "Permesu kopii kaj alglui dosierojn"), ("Connected", "Konektata"), ("Direct and encrypted connection", "Konekcio direkta ĉifrata"), ("Relayed and encrypted connection", "Konekcio relajsa ĉifrata"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Konektante..."), ("Enable RDP session sharing", "Ebligi la kundivido de sesio RDP"), ("Auto Login", "Aŭtomata konektado (la ŝloso nur estos ebligita post la malebligado de la unua parametro)"), - ("Enable Direct IP Access", "Permesi direkta eniro per IP"), + ("Enable direct IP access", "Permesi direkta eniro per IP"), ("Rename", "Renomi"), ("Space", "Spaco"), - ("Create Desktop Shortcut", "Krei ligilon sur la labortablon"), + ("Create desktop shortcut", "Krei ligilon sur la labortablon"), ("Change Path", "Ŝanĝi vojon"), ("Create Folder", "Krei dosierujon"), ("Please enter the folder name", "Bonvolu enigi la dosiernomon"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", ""), ("Legacy mode", ""), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", ""), ("Use both passwords", ""), ("Set permanent password", ""), - ("Enable Remote Restart", ""), - ("Allow remote restart", ""), - ("Restart Remote Device", ""), + ("Enable remote restart", ""), + ("Restart remote device", ""), ("Are you sure you want to restart", ""), - ("Restarting Remote Device", ""), + ("Restarting remote device", ""), ("remote_restarting_tip", ""), ("Copied", ""), ("Exit Fullscreen", "Eliru Plenekranon"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", ""), ("Enable hardware codec", ""), ("Unlock Security Settings", ""), - ("Enable Audio", ""), + ("Enable audio", ""), ("Unlock Network Settings", ""), ("Server", ""), ("Direct IP Access", ""), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", ""), ("Start session recording", ""), ("Stop session recording", ""), - ("Enable Recording Session", ""), - ("Allow recording session", ""), - ("Enable LAN Discovery", ""), - ("Deny LAN Discovery", ""), + ("Enable recording session", ""), + ("Enable LAN discovery", ""), + ("Deny LAN discovery", ""), ("Write a message", ""), ("Prompt", ""), ("Please wait for confirmation of UAC...", ""), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index a5ae3699e..0f9faca31 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Listo"), ("Established", "Establecido"), ("connecting_status", "Conexión a la red RustDesk en progreso..."), - ("Enable Service", "Habilitar Servicio"), - ("Start Service", "Iniciar Servicio"), + ("Enable service", "Habilitar Servicio"), + ("Start service", "Iniciar Servicio"), ("Service is running", "El servicio se está ejecutando"), ("Service is not running", "El servicio no se está ejecutando"), ("not_ready_status", "No está listo. Comprueba tu conexión"), ("Control Remote Desktop", "Controlar escritorio remoto"), - ("Transfer File", "Transferir archivo"), + ("Transfer file", "Transferir archivo"), ("Connect", "Conectar"), - ("Recent Sessions", "Sesiones recientes"), - ("Address Book", "Directorio"), + ("Recent sessions", "Sesiones recientes"), + ("Address book", "Directorio"), ("Confirmation", "Confirmación"), - ("TCP Tunneling", "Túnel TCP"), + ("TCP tunneling", "Túnel TCP"), ("Remove", "Quitar"), ("Refresh random password", "Actualizar contraseña aleatoria"), ("Set your own password", "Establece tu propia contraseña"), - ("Enable Keyboard/Mouse", "Habilitar teclado/ratón"), - ("Enable Clipboard", "Habilitar portapapeles"), - ("Enable File Transfer", "Habilitar transferencia de archivos"), - ("Enable TCP Tunneling", "Habilitar túnel TCP"), + ("Enable keyboard/mouse", "Habilitar teclado/ratón"), + ("Enable clipboard", "Habilitar portapapeles"), + ("Enable file transfer", "Habilitar transferencia de archivos"), + ("Enable TCP tunneling", "Habilitar túnel TCP"), ("IP Whitelisting", "Direcciones IP admitidas"), ("ID/Relay Server", "Servidor ID/Relay"), - ("Import Server Config", "Importar configuración de servidor"), + ("Import server config", "Importar configuración de servidor"), ("Export Server Config", "Exportar configuración del servidor"), ("Import server configuration successfully", "Configuración de servidor importada con éxito"), ("Export server configuration successfully", "Configuración de servidor exportada con éxito"), @@ -48,7 +48,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Privacy Statement", "Declaración de privacidad"), ("Mute", "Silenciar"), ("Build Date", "Fecha de compilación"), - ("Version", ""), + ("Version", "¨Versión"), ("Home", "Inicio"), ("Audio Input", "Entrada de audio"), ("Enhancements", "Mejoras"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Aceptar"), ("Dismiss", "Cancelar"), ("Disconnect", "Desconectar"), - ("Allow using keyboard and mouse", "Permitir el uso del teclado y el mouse"), - ("Allow using clipboard", "Permitir usar portapapeles"), - ("Allow hearing sound", "Permitir escuchar sonido"), - ("Allow file copy and paste", "Permitir copiar y pegar archivos"), + ("Enable file copy and paste", "Permitir copiar y pegar archivos"), ("Connected", "Conectado"), ("Direct and encrypted connection", "Conexión directa y cifrada"), ("Relayed and encrypted connection", "Conexión retransmitida y cifrada"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Iniciando sesión..."), ("Enable RDP session sharing", "Habilitar el uso compartido de sesiones RDP"), ("Auto Login", "Inicio de sesión automático"), - ("Enable Direct IP Access", "Habilitar acceso IP directo"), + ("Enable direct IP access", "Habilitar acceso IP directo"), ("Rename", "Renombrar"), ("Space", "Espacio"), - ("Create Desktop Shortcut", "Crear acceso directo en el escritorio"), + ("Create desktop shortcut", "Crear acceso directo en el escritorio"), ("Change Path", "Cambiar ruta"), ("Create Folder", "Crear carpeta"), ("Please enter the folder name", "Por favor introduzca el nombre de la carpeta"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"), ("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"), ("android_open_battery_optimizations_tip", "Si deseas deshabilitar esta característica, por favor, ve a la página siguiente de ajustes, busca y entra en [Batería] y desmarca [Sin restricción]"), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Conexión no disponible"), ("Legacy mode", "Modo heredado"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Usar contraseña permamente"), ("Use both passwords", "Usar ambas contraseñas"), ("Set permanent password", "Establecer contraseña permamente"), - ("Enable Remote Restart", "Habilitar reinicio remoto"), - ("Allow remote restart", "Permitir reinicio remoto"), - ("Restart Remote Device", "Reiniciar dispositivo"), + ("Enable remote restart", "Habilitar reinicio remoto"), + ("Restart remote device", "Reiniciar dispositivo"), ("Are you sure you want to restart", "¿Estás seguro de que deseas reiniciar?"), - ("Restarting Remote Device", "Reiniciando dispositivo remoto"), + ("Restarting remote device", "Reiniciando dispositivo remoto"), ("remote_restarting_tip", "El dispositivo remoto se está reiniciando. Por favor cierre este mensaje y vuelva a conectarse con la contraseña peremanente en unos momentos."), ("Copied", "Copiado"), ("Exit Fullscreen", "Salir de pantalla completa"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Tema del sistema"), ("Enable hardware codec", "Habilitar códec por hardware"), ("Unlock Security Settings", "Desbloquear ajustes de seguridad"), - ("Enable Audio", "Habilitar Audio"), + ("Enable audio", "Habilitar Audio"), ("Unlock Network Settings", "Desbloquear Ajustes de Red"), ("Server", "Servidor"), ("Direct IP Access", "Acceso IP Directo"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Cambiar"), ("Start session recording", "Comenzar grabación de sesión"), ("Stop session recording", "Detener grabación de sesión"), - ("Enable Recording Session", "Habilitar grabación de sesión"), - ("Allow recording session", "Permitir grabación de sesión"), - ("Enable LAN Discovery", "Habilitar descubrimiento de LAN"), - ("Deny LAN Discovery", "Denegar descubrimiento de LAN"), + ("Enable recording session", "Habilitar grabación de sesión"), + ("Enable LAN discovery", "Habilitar descubrimiento de LAN"), + ("Deny LAN discovery", "Denegar descubrimiento de LAN"), ("Write a message", "Escribir un mensaje"), ("Prompt", ""), ("Please wait for confirmation of UAC...", "Por favor, espera confirmación de UAC"), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "El soporte para Wayland está en fase experimental, por favor, use X11 si necesita acceso desatendido."), ("Right click to select tabs", "Clic derecho para seleccionar pestañas"), ("Skipped", "Omitido"), - ("Add to Address Book", "Añadir al directorio"), + ("Add to address book", "Añadir al directorio"), ("Group", "Grupo"), ("Search", "Búsqueda"), ("Closed manually by web console", "Cerrado manualmente por la consola web"), @@ -566,12 +561,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", "Mostrar pantallas como ventanas individuales"), ("Use all my displays for the remote session", "Usar todas mis pantallas para la sesión remota"), ("selinux_tip", "SELinux está activado en tu dispositivo, lo que puede hacer que RustDesk no se ejecute correctamente como lado controlado."), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), - ("selinux_tip", ""), - ("Virtual display", ""), - ("Plug out all", ""), + ("Change view", "Cambiar vista"), + ("Big tiles", "Mosaicos grandes"), + ("Small tiles", "Mosaicos pequeños"), + ("List", "Lista"), + ("Virtual display", "Pantalla virtual"), + ("Plug out all", "Desconectar todo"), + ("True color (4:4:4)", "Color real (4:4:4)"), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 129d41e44..f206b098f 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "آماده به کار"), ("Established", "اتصال برقرار شد"), ("connecting_status", "...در حال برقراری ارتباط با سرور"), - ("Enable Service", "فعالسازی سرویس"), - ("Start Service", "اجرای سرویس"), + ("Enable service", "فعالسازی سرویس"), + ("Start service", "اجرای سرویس"), ("Service is running", "سرویس در حال اجرا است"), ("Service is not running", "سرویس اجرا نشده"), ("not_ready_status", "ارتباط برقرار نشد. لطفا شبکه خود را بررسی کنید"), ("Control Remote Desktop", "کنترل دسکتاپ میزبان"), - ("Transfer File", "انتقال فایل"), + ("Transfer file", "انتقال فایل"), ("Connect", "اتصال"), - ("Recent Sessions", "جلسات اخیر"), - ("Address Book", "دفترچه آدرس"), + ("Recent sessions", "جلسات اخیر"), + ("Address book", "دفترچه آدرس"), ("Confirmation", "تایید"), - ("TCP Tunneling", "TCP تانل"), + ("TCP tunneling", "TCP تانل"), ("Remove", "حذف"), ("Refresh random password", "بروزرسانی رمز عبور تصادفی"), ("Set your own password", "!رمز عبور دلخواه بگذارید"), - ("Enable Keyboard/Mouse", " فعالسازی ماوس/صفحه کلید"), - ("Enable Clipboard", "فعال سازی کلیپبورد"), - ("Enable File Transfer", "انتقال فایل را فعال کنید"), - ("Enable TCP Tunneling", "را فعال کنید TCP تانل"), + ("Enable keyboard/mouse", " فعالسازی ماوس/صفحه کلید"), + ("Enable clipboard", "فعال سازی کلیپبورد"), + ("Enable file transfer", "انتقال فایل را فعال کنید"), + ("Enable TCP tunneling", "را فعال کنید TCP تانل"), ("IP Whitelisting", "های مجاز IP لیست"), ("ID/Relay Server", "ID/Relay سرور"), - ("Import Server Config", "تنظیم سرور با فایل"), + ("Import server config", "تنظیم سرور با فایل"), ("Export Server Config", "ایجاد فایل تظیمات از سرور فعلی"), ("Import server configuration successfully", "تنظیمات سرور با فایل کانفیگ با موفقیت انجام شد"), ("Export server configuration successfully", "ایجاد فایل کانفیگ از تنظیمات فعلی با موفقیت انجام شد"), @@ -172,17 +172,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Local Port", "پورت محلی"), ("Local Address", "آدرس محلی"), ("Change Local Port", "تغییر پورت محلی"), - ("setup_server_tip", "برای اتصال سریعتر، سرور اتصال ضخصی خود را راه اندازی کنید"), + ("setup_server_tip", "برای اتصال سریعتر، سرور اتصال شخصی خود را راه اندازی کنید"), ("Too short, at least 6 characters.", "بسیار کوتاه حداقل 6 کاراکتر مورد نیاز است"), ("The confirmation is not identical.", "تأیید ناموفق بود."), ("Permissions", "دسترسی ها"), ("Accept", "پذیرفتن"), ("Dismiss", "رد کردن"), ("Disconnect", "قطع اتصال"), - ("Allow using keyboard and mouse", "مجاز بودن استفاده از صفحه کلید و ماوس"), - ("Allow using clipboard", "مجاز بودن استفاده از کلیپبورد"), - ("Allow hearing sound", "مجاز بودن شنیدن صدا"), - ("Allow file copy and paste", "مجاز بودن کپی و چسباندن فایل"), + ("Enable file copy and paste", "مجاز بودن کپی و چسباندن فایل"), ("Connected", "متصل شده"), ("Direct and encrypted connection", "اتصال مستقیم و رمزگذاری شده"), ("Relayed and encrypted connection", "و رمزگذاری شده Relay اتصال از طریق"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "...در حال ورود"), ("Enable RDP session sharing", "را فعال کنید RDP اشتراک گذاری جلسه"), ("Auto Login", "ورود خودکار"), - ("Enable Direct IP Access", "را فعال کنید IP دسترسی مستقیم"), + ("Enable direct IP access", "را فعال کنید IP دسترسی مستقیم"), ("Rename", "تغییر نام"), ("Space", "فضا"), - ("Create Desktop Shortcut", "ساخت میانبر روی دسکتاپ"), + ("Create desktop shortcut", "ساخت میانبر روی دسکتاپ"), ("Change Path", "تغییر مسیر"), ("Create Folder", "ایجاد پوشه"), ("Please enter the folder name", "نام پوشه را وارد کنید"), @@ -289,7 +286,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "فعال کردن ضبط صفحه به طور خودکار سرویس را راه اندازی می کند و به دستگاه های دیگر امکان می دهد درخواست اتصال به آن دستگاه را داشته باشند."), ("android_stop_service_tip", "با بستن سرویس، تمام اتصالات برقرار شده به طور خودکار بسته می شود"), ("android_version_audio_tip", "نسخه فعلی اندروید از ضبط صدا پشتیبانی نمی‌کند، لطفاً به اندروید 10 یا بالاتر به‌روزرسانی کنید"), - ("android_start_service_tip", "را فعال کنید [Screen Capture] ضربه بزنید یا مجوز [Start Service] برای شروع سرویس اشتراک ‌گذاری صفحه، روی"), + ("android_start_service_tip", "را فعال کنید [Screen Capture] ضربه بزنید یا مجوز [Start service] برای شروع سرویس اشتراک ‌گذاری صفحه، روی"), ("android_permission_may_not_change_tip", "مجوزهای ایجاد شده یا تغییر یافته برای اتصالات جاری تغییر نخواهد کرد، برای تغییر نیاز است مجددا اتصال برقرار گردد"), ("Account", "حساب کاربری"), ("Overwrite", "بازنویسی"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "را در پس زمینه نگه دارید RustDesk سرویس"), ("Ignore Battery Optimizations", "بهینه سازی باتری نادیده گرفته شود"), ("android_open_battery_optimizations_tip", "به صفحه تنظیمات بعدی بروید"), - ("Start on Boot", "در هنگام بوت شروع شود"), + ("Start on boot", "در هنگام بوت شروع شود"), ("Start the screen sharing service on boot, requires special permissions", "سرویس اشتراک‌گذاری صفحه را در بوت راه‌اندازی کنید، به مجوزهای خاصی نیاز دارد"), ("Connection not allowed", "اتصال مجاز نیست"), ("Legacy mode", "legacy حالت"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "از رمز عبور دائمی استفاده شود"), ("Use both passwords", "از هر دو رمز عبور استفاده شود"), ("Set permanent password", "یک رمز عبور دائمی تنظیم شود"), - ("Enable Remote Restart", "فعال کردن قابلیت ریستارت از راه دور"), - ("Allow remote restart", "مجاز بودن ریستارت از راه دور"), - ("Restart Remote Device", "ریستارت کردن از راه دور"), + ("Enable remote restart", "فعال کردن قابلیت ریستارت از راه دور"), + ("Restart remote device", "ریستارت کردن از راه دور"), ("Are you sure you want to restart", "ایا مطمئن هستید میخواهید راه اندازی مجدد انجام بدید؟"), - ("Restarting Remote Device", "در حال راه اندازی مجدد دستگاه راه دور"), + ("Restarting remote device", "در حال راه اندازی مجدد دستگاه راه دور"), ("remote_restarting_tip", "دستگاه راه دور در حال راه اندازی مجدد است. این پیام را ببندید و پس از مدتی با استفاده از یک رمز عبور دائمی دوباره وصل شوید."), ("Copied", "کپی شده است"), ("Exit Fullscreen", "از حالت تمام صفحه خارج شوید"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "پیروی از سیستم"), ("Enable hardware codec", "فعال سازی کدک سخت افزاری"), ("Unlock Security Settings", "دسترسی کامل به تنظیمات امنیتی"), - ("Enable Audio", "فعال شدن صدا"), + ("Enable audio", "فعال شدن صدا"), ("Unlock Network Settings", "دسترسی کامل به تنظیمات شبکه"), ("Server", "سرور"), ("Direct IP Access", "IP دسترسی مستقیم به"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "تغییر"), ("Start session recording", "شروع ضبط جلسه"), ("Stop session recording", "توقف ضبط جلسه"), - ("Enable Recording Session", "فعالسازی ضبط جلسه"), - ("Allow recording session", "مجومجاز بودن ضبط جلسه"), - ("Enable LAN Discovery", "فعالسازی جستجو در شبکه"), - ("Deny LAN Discovery", "غیر فعالسازی جستجو در شبکه"), + ("Enable recording session", "فعالسازی ضبط جلسه"), + ("Enable LAN discovery", "فعالسازی جستجو در شبکه"), + ("Deny LAN discovery", "غیر فعالسازی جستجو در شبکه"), ("Write a message", "یک پیام بنویسید"), ("Prompt", "سریع"), ("Please wait for confirmation of UAC...", "باشید UAC لطفا منتظر تایید"), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "پشتیبانی Wayland در مرحله آزمایشی است، لطفاً در صورت نیاز به دسترسی بدون مراقبت از X11 استفاده کنید."), ("Right click to select tabs", "برای انتخاب تب ها راست کلیک کنید"), ("Skipped", "رد شد"), - ("Add to Address Book", "افزودن به دفترچه آدرس"), + ("Add to address book", "افزودن به دفترچه آدرس"), ("Group", "گروه"), ("Search", "جستجو"), ("Closed manually by web console", "به صورت دستی توسط کنسول وب بسته شد"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 91e956be0..c457cd75b 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Prêt"), ("Established", "Établi"), ("connecting_status", "Connexion au réseau RustDesk..."), - ("Enable Service", "Autoriser le service"), - ("Start Service", "Démarrer le service"), + ("Enable service", "Autoriser le service"), + ("Start service", "Démarrer le service"), ("Service is running", "Le service est en cours d'exécution"), ("Service is not running", "Le service ne fonctionne pas"), ("not_ready_status", "Pas prêt, veuillez vérifier la connexion réseau"), ("Control Remote Desktop", "Contrôler le bureau à distance"), - ("Transfer File", "Transfert de fichiers"), + ("Transfer file", "Transfert de fichiers"), ("Connect", "Se connecter"), - ("Recent Sessions", "Sessions récentes"), - ("Address Book", "Carnet d'adresses"), + ("Recent sessions", "Sessions récentes"), + ("Address book", "Carnet d'adresses"), ("Confirmation", "Confirmation"), - ("TCP Tunneling", "Tunnel TCP"), + ("TCP tunneling", "Tunnel TCP"), ("Remove", "Supprimer"), ("Refresh random password", "Actualiser le mot de passe aléatoire"), ("Set your own password", "Définir votre propre mot de passe"), - ("Enable Keyboard/Mouse", "Activer le contrôle clavier/souris"), - ("Enable Clipboard", "Activer la synchronisation du presse-papier"), - ("Enable File Transfer", "Activer le transfert de fichiers"), - ("Enable TCP Tunneling", "Activer le tunnel TCP"), + ("Enable keyboard/mouse", "Activer le contrôle clavier/souris"), + ("Enable clipboard", "Activer la synchronisation du presse-papier"), + ("Enable file transfer", "Activer le transfert de fichiers"), + ("Enable TCP tunneling", "Activer le tunnel TCP"), ("IP Whitelisting", "Liste blanche IP"), ("ID/Relay Server", "ID/Serveur Relais"), - ("Import Server Config", "Importer la configuration du serveur"), + ("Import server config", "Importer la configuration du serveur"), ("Export Server Config", "Exporter la configuration du serveur"), ("Import server configuration successfully", "Configuration du serveur importée avec succès"), ("Export server configuration successfully", "Configuration du serveur exportée avec succès"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Accepter"), ("Dismiss", "Rejeter"), ("Disconnect", "Déconnecter"), - ("Allow using keyboard and mouse", "Autoriser l'utilisation du clavier et de la souris"), - ("Allow using clipboard", "Autoriser l'utilisation du presse-papier"), - ("Allow hearing sound", "Autoriser l'envoi du son"), - ("Allow file copy and paste", "Autoriser le copier-coller de fichiers"), + ("Enable file copy and paste", "Autoriser le copier-coller de fichiers"), ("Connected", "Connecté"), ("Direct and encrypted connection", "Connexion directe chiffrée"), ("Relayed and encrypted connection", "Connexion relais chiffrée"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "En cours de connexion ..."), ("Enable RDP session sharing", "Activer le partage de session RDP"), ("Auto Login", "Connexion automatique (le verrouillage ne sera effectif qu'après la désactivation du premier paramètre)"), - ("Enable Direct IP Access", "Autoriser l'accès direct par IP"), + ("Enable direct IP access", "Autoriser l'accès direct par IP"), ("Rename", "Renommer"), ("Space", "Espace"), - ("Create Desktop Shortcut", "Créer un raccourci sur le bureau"), + ("Create desktop shortcut", "Créer un raccourci sur le bureau"), ("Change Path", "Changer de chemin"), ("Create Folder", "Créer un dossier"), ("Please enter the folder name", "Veuillez saisir le nom du dossier"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Gardez le service RustDesk en arrière plan"), ("Ignore Battery Optimizations", "Ignorer les optimisations batterie"), ("android_open_battery_optimizations_tip", "Conseil android d'optimisation de batterie"), - ("Start on Boot", "Lancer au démarrage"), + ("Start on boot", "Lancer au démarrage"), ("Start the screen sharing service on boot, requires special permissions", "Lancer le service de partage d'écran au démarrage, nécessite des autorisations spéciales"), ("Connection not allowed", "Connexion non autorisée"), ("Legacy mode", "Mode hérité"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Utiliser un mot de passe permanent"), ("Use both passwords", "Utiliser les mots de passe unique et permanent"), ("Set permanent password", "Définir le mot de passe permanent"), - ("Enable Remote Restart", "Activer le redémarrage à distance"), - ("Allow remote restart", "Autoriser le redémarrage à distance"), - ("Restart Remote Device", "Redémarrer l'appareil à distance"), + ("Enable remote restart", "Activer le redémarrage à distance"), + ("Restart remote device", "Redémarrer l'appareil à distance"), ("Are you sure you want to restart", "Êtes-vous sûrs de vouloir redémarrer l'appareil ?"), - ("Restarting Remote Device", "Redémarrage de l'appareil distant"), + ("Restarting remote device", "Redémarrage de l'appareil distant"), ("remote_restarting_tip", "L'appareil distant redémarre, veuillez fermer cette boîte de message et vous reconnecter avec un mot de passe permanent après un certain temps"), ("Copied", "Copié"), ("Exit Fullscreen", "Quitter le mode plein écran"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Suivi système"), ("Enable hardware codec", "Activer le transcodage matériel"), ("Unlock Security Settings", "Déverrouiller les configurations de sécurité"), - ("Enable Audio", "Activer l'audio"), + ("Enable audio", "Activer l'audio"), ("Unlock Network Settings", "Déverrouiller les configurations réseau"), ("Server", "Serveur"), ("Direct IP Access", "Accès IP direct"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Modifier"), ("Start session recording", "Commencer l'enregistrement"), ("Stop session recording", "Stopper l'enregistrement"), - ("Enable Recording Session", "Activer l'enregistrement de session"), - ("Allow recording session", "Autoriser l'enregistrement de session"), - ("Enable LAN Discovery", "Activer la découverte sur réseau local"), - ("Deny LAN Discovery", "Interdir la découverte sur réseau local"), + ("Enable recording session", "Activer l'enregistrement de session"), + ("Enable LAN discovery", "Activer la découverte sur réseau local"), + ("Deny LAN discovery", "Interdir la découverte sur réseau local"), ("Write a message", "Ecrire un message"), ("Prompt", "Annonce"), ("Please wait for confirmation of UAC...", "Veuillez attendre la confirmation de l'UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Le support Wayland est en phase expérimentale, veuillez utiliser X11 si vous avez besoin d'un accès sans surveillance."), ("Right click to select tabs", "Clique droit pour selectionner les onglets"), ("Skipped", "Ignoré"), - ("Add to Address Book", "Ajouter au carnet d'adresses"), + ("Add to address book", "Ajouter au carnet d'adresses"), ("Group", "Groupe"), ("Search", "Rechercher"), ("Closed manually by web console", "Fermé manuellement par la console Web"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 90dd4b03e..61f887645 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Kész"), ("Established", "Létrejött"), ("connecting_status", "Csatlakozás folyamatban..."), - ("Enable Service", "Szolgáltatás engedélyezése"), - ("Start Service", "Szolgáltatás indítása"), + ("Enable service", "Szolgáltatás engedélyezése"), + ("Start service", "Szolgáltatás indítása"), ("Service is running", "Szolgáltatás aktív"), ("Service is not running", "Szolgáltatás inaktív"), ("not_ready_status", "Kapcsolódási hiba. Kérlek ellenőrizze a hálózati beállításokat."), ("Control Remote Desktop", "Távoli számítógép vezérlése"), - ("Transfer File", "Fájlátvitel"), + ("Transfer file", "Fájlátvitel"), ("Connect", "Csatlakozás"), - ("Recent Sessions", "Legutóbbi munkamanetek"), - ("Address Book", "Címjegyzék"), + ("Recent sessions", "Legutóbbi munkamanetek"), + ("Address book", "Címjegyzék"), ("Confirmation", "Megerősítés"), - ("TCP Tunneling", "TCP Tunneling"), + ("TCP tunneling", "TCP tunneling"), ("Remove", "Eltávolít"), ("Refresh random password", "Új véletlenszerű jelszó"), ("Set your own password", "Saját jelszó beállítása"), - ("Enable Keyboard/Mouse", "Billentyűzet/egér engedélyezése"), - ("Enable Clipboard", "Megosztott vágólap engedélyezése"), - ("Enable File Transfer", "Fájlátvitel engedélyezése"), - ("Enable TCP Tunneling", "TCP Tunneling engedélyezése"), + ("Enable keyboard/mouse", "Billentyűzet/egér engedélyezése"), + ("Enable clipboard", "Megosztott vágólap engedélyezése"), + ("Enable file transfer", "Fájlátvitel engedélyezése"), + ("Enable TCP tunneling", "TCP tunneling engedélyezése"), ("IP Whitelisting", "IP engedélyezési lista"), ("ID/Relay Server", "Kiszolgáló szerver"), - ("Import Server Config", "Szerver konfiguráció importálása"), + ("Import server config", "Szerver konfiguráció importálása"), ("Export Server Config", "Szerver konfiguráció exportálása"), ("Import server configuration successfully", "Szerver konfiguráció sikeresen importálva"), ("Export server configuration successfully", "Szerver konfiguráció sikeresen exportálva"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Elfogadás"), ("Dismiss", "Elutasítás"), ("Disconnect", "Kapcsolat bontása"), - ("Allow using keyboard and mouse", "Billentyűzet és egér használatának engedélyezése"), - ("Allow using clipboard", "Vágólap használatának engedélyezése"), - ("Allow hearing sound", "Hang átvitelének engedélyezése"), - ("Allow file copy and paste", "Fájlok másolásának és beillesztésének engedélyezése"), + ("Enable file copy and paste", "Fájlok másolásának és beillesztésének engedélyezése"), ("Connected", "Csatlakozva"), ("Direct and encrypted connection", "Közvetlen, és titkosított kapcsolat"), ("Relayed and encrypted connection", "Továbbított, és titkosított kapcsolat"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "A belépés folyamatban..."), ("Enable RDP session sharing", "RDP-munkamenet-megosztás engedélyezése"), ("Auto Login", "Automatikus bejelentkezés"), - ("Enable Direct IP Access", "Közvetlen IP-elérés engedélyezése"), + ("Enable direct IP access", "Közvetlen IP-elérés engedélyezése"), ("Rename", "Átnevezés"), ("Space", ""), - ("Create Desktop Shortcut", "Asztali parancsikon létrehozása"), + ("Create desktop shortcut", "Asztali parancsikon létrehozása"), ("Change Path", "Elérési út módosítása"), ("Create Folder", "Mappa létrehozás"), ("Please enter the folder name", "Kérjük, adja meg a mappa nevét"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "RustDesk futtatása a háttérben"), ("Ignore Battery Optimizations", "Akkumulátorkímélő figyelmen kívűl hagyása"), ("android_open_battery_optimizations_tip", "Ha le szeretné tiltani ezt a funkciót, lépjen a RustDesk alkalmazás beállítási oldalára, keresse meg az [Akkumulátorkímélő] lehetőséget és válassza a nincs korlátozás lehetőséget."), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "A csatlakozás nem engedélyezett"), ("Legacy mode", ""), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Állandó jelszó használata"), ("Use both passwords", "Mindkét jelszó használata"), ("Set permanent password", "Állandó jelszó beállítása"), - ("Enable Remote Restart", "Távoli újraindítás engedélyezése"), - ("Allow remote restart", "Távoli újraindítás engedélyezése"), - ("Restart Remote Device", "Távoli eszköz újraindítása"), + ("Enable remote restart", "Távoli újraindítás engedélyezése"), + ("Restart remote device", "Távoli eszköz újraindítása"), ("Are you sure you want to restart", "Biztos szeretné újraindítani?"), - ("Restarting Remote Device", "Távoli eszköz újraindítása..."), + ("Restarting remote device", "Távoli eszköz újraindítása..."), ("remote_restarting_tip", "A távoli eszköz újraindul, zárja be ezt az üzenetet, csatlakozzon újra, állandó jelszavával"), ("Copied", "Másolva"), ("Exit Fullscreen", "Kilépés teljes képernyős módból"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", ""), ("Enable hardware codec", "Hardveres kodek engedélyezése"), ("Unlock Security Settings", "Biztonsági beállítások feloldása"), - ("Enable Audio", "Hang engedélyezése"), + ("Enable audio", "Hang engedélyezése"), ("Unlock Network Settings", "Hálózati beállítások feloldása"), ("Server", "Szerver"), ("Direct IP Access", "Közvetlen IP hozzáférés"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Változtatás"), ("Start session recording", "Munkamenet rögzítés indítása"), ("Stop session recording", "Munkamenet rögzítés leállítása"), - ("Enable Recording Session", "Munkamenet rögzítés engedélyezése"), - ("Allow recording session", "Munkamenet rögzítés engedélyezése"), - ("Enable LAN Discovery", "Felfedezés enegedélyezése"), - ("Deny LAN Discovery", "Felfedezés tiltása"), + ("Enable recording session", "Munkamenet rögzítés engedélyezése"), + ("Enable LAN discovery", "Felfedezés enegedélyezése"), + ("Deny LAN discovery", "Felfedezés tiltása"), ("Write a message", "Üzenet írása"), ("Prompt", ""), ("Please wait for confirmation of UAC...", ""), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 53114b747..dc4aa7e19 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Sudah siap"), ("Established", "Didirikan"), ("connecting_status", "Menghubungkan ke jaringan RustDesk..."), - ("Enable Service", "Aktifkan Layanan"), - ("Start Service", "Mulai Layanan"), + ("Enable service", "Aktifkan Layanan"), + ("Start service", "Mulai Layanan"), ("Service is running", "Layanan berjalan"), ("Service is not running", "Layanan tidak berjalan"), ("not_ready_status", "Belum siap. Silakan periksa koneksi Anda"), ("Control Remote Desktop", "Kontrol Remote Desktop"), - ("Transfer File", "File Transfer"), + ("Transfer file", "File Transfer"), ("Connect", "Hubungkan"), - ("Recent Sessions", "Sesi Terkini"), - ("Address Book", "Buku Alamat"), + ("Recent sessions", "Sesi Terkini"), + ("Address book", "Buku Alamat"), ("Confirmation", "Konfirmasi"), - ("TCP Tunneling", "Tunneling TCP"), + ("TCP tunneling", "Tunneling TCP"), ("Remove", "Hapus"), ("Refresh random password", "Perbarui kata sandi acak"), ("Set your own password", "Tetapkan kata sandi Anda"), - ("Enable Keyboard/Mouse", "Aktifkan Keyboard/Mouse"), - ("Enable Clipboard", "Aktifkan Papan Klip"), - ("Enable File Transfer", "Aktifkan Transfer File"), - ("Enable TCP Tunneling", "Aktifkan TCP Tunneling"), + ("Enable keyboard/mouse", "Aktifkan Keyboard/Mouse"), + ("Enable clipboard", "Aktifkan Papan Klip"), + ("Enable file transfer", "Aktifkan Transfer file"), + ("Enable TCP tunneling", "Aktifkan TCP tunneling"), ("IP Whitelisting", "Daftar IP yang diizinkan"), ("ID/Relay Server", "ID/Relay Server"), - ("Import Server Config", "Impor Konfigurasi Server"), + ("Import server config", "Impor Konfigurasi Server"), ("Export Server Config", "Ekspor Konfigurasi Server"), ("Import server configuration successfully", "Impor konfigurasi server berhasil"), ("Export server configuration successfully", "Ekspor konfigurasi server berhasil"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Terima"), ("Dismiss", "Hentikan"), ("Disconnect", "Terputus"), - ("Allow using keyboard and mouse", "Izinkan menggunakan keyboard dan mouse"), - ("Allow using clipboard", "Izinkan menggunakan papan klip"), - ("Allow hearing sound", "Izinkan mendengarkan suara"), - ("Allow file copy and paste", "Izinkan salin dan tempel file"), + ("Enable file copy and paste", "Izinkan salin dan tempel file"), ("Connected", "Terhubung"), ("Direct and encrypted connection", "Koneksi langsung dan terenkripsi"), ("Relayed and encrypted connection", "Koneksi relay dan terenkripsi"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Masuk..."), ("Enable RDP session sharing", "Aktifkan berbagi sesi RDP"), ("Auto Login", "Login Otomatis (Hanya berlaku jika Anda mengatur \"Kunci setelah sesi berakhir\")"), - ("Enable Direct IP Access", "Aktifkan Akses IP Langsung"), + ("Enable direct IP access", "Aktifkan Akses IP Langsung"), ("Rename", "Ubah nama"), ("Space", "Spasi"), - ("Create Desktop Shortcut", "Buat Pintasan Desktop"), + ("Create desktop shortcut", "Buat Pintasan Desktop"), ("Change Path", "Ubah Direktori"), ("Create Folder", "Buat Folder"), ("Please enter the folder name", "Silahkan masukkan nama folder"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada service background"), ("Ignore Battery Optimizations", "Abaikan Pengoptimalan Baterai"), ("android_open_battery_optimizations_tip", "Jika anda ingin menonaktifkan fitur ini, buka halam pengaturan, cari dan pilih [Baterai], Uncheck [Tidak dibatasi]"), - ("Start on Boot", "Mulai saat dihidupkan"), + ("Start on boot", "Mulai saat dihidupkan"), ("Start the screen sharing service on boot, requires special permissions", "Mulai layanan berbagi layar saat sistem dinyalakan, memerlukan izin khusus."), ("Connection not allowed", "Koneksi tidak dizinkan"), ("Legacy mode", "Mode lawas"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Gunakan kata sandi permanaen"), ("Use both passwords", "Gunakan kedua kata sandi"), ("Set permanent password", "Setel kata sandi permanen"), - ("Enable Remote Restart", "Aktifkan Restart Secara Remote"), - ("Allow remote restart", "Ijinkan Restart Secara Remote"), - ("Restart Remote Device", "Restart Perangkat Secara Remote"), + ("Enable remote restart", "Aktifkan Restart Secara Remote"), + ("Restart remote device", "Restart Perangkat Secara Remote"), ("Are you sure you want to restart", "Apakah Anda yakin ingin merestart"), - ("Restarting Remote Device", "Merestart Perangkat Remote"), + ("Restarting remote device", "Merestart Perangkat Remote"), ("remote_restarting_tip", "Perangkat remote sedang merestart, harap tutup pesan ini dan sambungkan kembali dengan kata sandi permanen setelah beberapa saat."), ("Copied", "Disalin"), ("Exit Fullscreen", "Keluar dari Layar Penuh"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Ikuti Sistem"), ("Enable hardware codec", "Aktifkan kodek perangkat keras"), ("Unlock Security Settings", "Buka Keamanan Pengaturan"), - ("Enable Audio", "Aktifkan Audio"), + ("Enable audio", "Aktifkan Audio"), ("Unlock Network Settings", "Buka Keamanan Pengaturan Jaringan"), ("Server", "Server"), ("Direct IP Access", "Akses IP Langsung"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Ubah"), ("Start session recording", "Mulai sesi perekaman"), ("Stop session recording", "Hentikan sesi perekaman"), - ("Enable Recording Session", "Aktifkan Sesi Perekaman"), - ("Allow recording session", "Izinkan sesi perekaman"), - ("Enable LAN Discovery", "Aktifkan Pencarian Jaringan Lokal (LAN)"), - ("Deny LAN Discovery", "Tolak Pencarian Jaringan Lokal (LAN)"), + ("Enable recording session", "Aktifkan Sesi Perekaman"), + ("Enable LAN discovery", "Aktifkan Pencarian Jaringan Lokal (LAN)"), + ("Deny LAN discovery", "Tolak Pencarian Jaringan Lokal (LAN)"), ("Write a message", "Tulis pesan"), ("Prompt", ""), ("Please wait for confirmation of UAC...", "Harap tunggu konfirmasi UAC"), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Dukungan Wayland masih dalam tahap percobaan, harap gunakan X11 jika Anda memerlukan akses tanpa pengawasan"), ("Right click to select tabs", "Klik kanan untuk memilih tab"), ("Skipped", "Dilewati"), - ("Add to Address Book", "Tambahkan ke Buku Alamat"), + ("Add to address book", "Tambahkan ke Buku Alamat"), ("Group", "Grup"), ("Search", "Pencarian"), ("Closed manually by web console", "Ditutup secara manual dari konsol web."), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", "Tampilan virtual"), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 87964f39a..f5b651eab 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Pronto"), ("Established", "Stabilita"), ("connecting_status", "Connessione alla rete RustDesk..."), - ("Enable Service", "Abilita servizio"), - ("Start Service", "Avvia servizio"), + ("Enable service", "Abilita servizio"), + ("Start service", "Avvia servizio"), ("Service is running", "Il servizio è in esecuzione"), ("Service is not running", "Il servizio non è in esecuzione"), ("not_ready_status", "Non pronto. Verifica la connessione"), ("Control Remote Desktop", "Controlla desktop remoto"), - ("Transfer File", "Trasferisci file"), + ("Transfer file", "Trasferisci file"), ("Connect", "Connetti"), - ("Recent Sessions", "Sessioni recenti"), - ("Address Book", "Rubrica"), + ("Recent sessions", "Sessioni recenti"), + ("Address book", "Rubrica"), ("Confirmation", "Conferma"), - ("TCP Tunneling", "Tunnel TCP"), + ("TCP tunneling", "Tunnel TCP"), ("Remove", "Rimuovi"), ("Refresh random password", "Nuova password casuale"), ("Set your own password", "Imposta la password"), - ("Enable Keyboard/Mouse", "Abilita tastiera/mouse"), - ("Enable Clipboard", "Abilita appunti"), - ("Enable File Transfer", "Abilita trasferimento file"), - ("Enable TCP Tunneling", "Abilita tunnel TCP"), + ("Enable keyboard/mouse", "Abilita tastiera/mouse"), + ("Enable clipboard", "Abilita appunti"), + ("Enable file transfer", "Abilita trasferimento file"), + ("Enable TCP tunneling", "Abilita tunnel TCP"), ("IP Whitelisting", "IP autorizzati"), ("ID/Relay Server", "Server ID/Relay"), - ("Import Server Config", "Importa configurazione server dagli appunti"), + ("Import server config", "Importa configurazione server dagli appunti"), ("Export Server Config", "Esporta configurazione server negli appunti"), ("Import server configuration successfully", "Configurazione server importata completata"), ("Export server configuration successfully", "Configurazione Server esportata completata"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Accetta"), ("Dismiss", "Rifiuta"), ("Disconnect", "Disconnetti"), - ("Allow using keyboard and mouse", "Consenti uso tastiera e mouse"), - ("Allow using clipboard", "Consenti uso degli appunti"), - ("Allow hearing sound", "Consenti la riproduzione dell'audio"), - ("Allow file copy and paste", "Consenti copia e incolla di file"), + ("Enable file copy and paste", "Consenti copia e incolla di file"), ("Connected", "Connesso"), ("Direct and encrypted connection", "Connessione diretta e cifrata"), ("Relayed and encrypted connection", "Connessione tramite relay e cifrata"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Autenticazione..."), ("Enable RDP session sharing", "Abilita condivisione sessione RDP"), ("Auto Login", "Accesso automatico"), - ("Enable Direct IP Access", "Abilita accesso diretto tramite IP"), + ("Enable direct IP access", "Abilita accesso diretto tramite IP"), ("Rename", "Rinomina"), ("Space", "Spazio"), - ("Create Desktop Shortcut", "Crea collegamento sul desktop"), + ("Create desktop shortcut", "Crea collegamento sul desktop"), ("Change Path", "Modifica percorso"), ("Create Folder", "Crea cartella"), ("Please enter the folder name", "Inserisci il nome della cartella"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Mantieni il servizio di RustDesk in background"), ("Ignore Battery Optimizations", "Ignora le ottimizzazioni della batteria"), ("android_open_battery_optimizations_tip", "Se vuoi disabilitare questa funzione, vai nelle impostazioni dell'applicazione RustDesk, apri la sezione 'Batteria' e deseleziona 'Senza restrizioni'."), - ("Start on Boot", "Avvia all'accensione"), + ("Start on boot", "Avvia all'accensione"), ("Start the screen sharing service on boot, requires special permissions", "L'avvio del servizio di condivisione dello schermo all'accensione richiede autorizzazioni speciali"), ("Connection not allowed", "Connessione non consentita"), ("Legacy mode", "Modalità legacy"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Usa password permanente"), ("Use both passwords", "Usa password monouso e permanente"), ("Set permanent password", "Imposta password permanente"), - ("Enable Remote Restart", "Abilita riavvio da remoto"), - ("Allow remote restart", "Consenti riavvio da remoto"), - ("Restart Remote Device", "Riavvia dispositivo remoto"), + ("Enable remote restart", "Abilita riavvio da remoto"), + ("Restart remote device", "Riavvia dispositivo remoto"), ("Are you sure you want to restart", "Sei sicuro di voler riavviare?"), - ("Restarting Remote Device", "Il dispositivo remoto si sta riavviando"), + ("Restarting remote device", "Il dispositivo remoto si sta riavviando"), ("remote_restarting_tip", "Riavvia il dispositivo remoto"), ("Copied", "Copiato"), ("Exit Fullscreen", "Esci dalla modalità schermo intero"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Sistema"), ("Enable hardware codec", "Abilita codec hardware"), ("Unlock Security Settings", "Sblocca impostazioni sicurezza"), - ("Enable Audio", "Abilita audio"), + ("Enable audio", "Abilita audio"), ("Unlock Network Settings", "Sblocca impostazioni di rete"), ("Server", "Server"), ("Direct IP Access", "Accesso IP diretto"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Modifica"), ("Start session recording", "Inizia registrazione sessione"), ("Stop session recording", "Ferma registrazione sessione"), - ("Enable Recording Session", "Abilita registrazione sessione"), - ("Allow recording session", "Permetti registrazione sessione"), - ("Enable LAN Discovery", "Abilita rilevamento LAN"), - ("Deny LAN Discovery", "Nega rilevamento LAN"), + ("Enable recording session", "Abilita registrazione sessione"), + ("Enable LAN discovery", "Abilita rilevamento LAN"), + ("Deny LAN discovery", "Nega rilevamento LAN"), ("Write a message", "Scrivi un messaggio"), ("Prompt", "Richiedi"), ("Please wait for confirmation of UAC...", "Attendi la conferma dell'UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Il supporto Wayland è in fase sperimentale, se vuoi un accesso stabile usa X11."), ("Right click to select tabs", "Clic con il tasto destro per selezionare le schede"), ("Skipped", "Saltato"), - ("Add to Address Book", "Aggiungi alla rubrica"), + ("Add to address book", "Aggiungi alla rubrica"), ("Group", "Gruppo"), ("Search", "Cerca"), ("Closed manually by web console", "Chiudi manualmente dalla console web"), @@ -570,7 +565,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", "Icone grandi"), ("Small tiles", "Icone piccole"), ("List", "Elenco"), - ("Virtual display", ""), - ("Plug out all", ""), + ("Virtual display", "Scehrmo virtuale"), + ("Plug out all", "Scollega tutto"), + ("True color (4:4:4)", "Colore reale (4:4:4)"), + ("Enable blocking user input", "Abilita blocco input utente"), + ("id_input_tip", "Puoi inserire un ID, un IP diretto o un dominio con una porta (:).\nSe vuoi accedere as un dispositivo in un altro server, aggiungi l'indirizzo del server (@?key=), ad esempio\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nSe vuoi accedere as un dispositivo in un server pubblico, inserisci \"@public\", per il server pubblico la chiave non è necessaria"), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index d5284276e..0014692fd 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "準備完了"), ("Established", "接続完了"), ("connecting_status", "RuskDeskネットワークに接続中..."), - ("Enable Service", "サービスを有効化"), - ("Start Service", "サービスを開始"), + ("Enable service", "サービスを有効化"), + ("Start service", "サービスを開始"), ("Service is running", "サービスは動作中"), ("Service is not running", "サービスは動作していません"), ("not_ready_status", "準備できていません。接続を確認してください。"), ("Control Remote Desktop", "リモートのデスクトップを操作する"), - ("Transfer File", "ファイルを転送"), + ("Transfer file", "ファイルを転送"), ("Connect", "接続"), - ("Recent Sessions", "最近のセッション"), - ("Address Book", "アドレス帳"), + ("Recent sessions", "最近のセッション"), + ("Address book", "アドレス帳"), ("Confirmation", "確認用"), - ("TCP Tunneling", "TCPトンネリング"), + ("TCP tunneling", "TCPトンネリング"), ("Remove", "削除"), ("Refresh random password", "ランダムパスワードを再生成"), ("Set your own password", "自分のパスワードを設定"), - ("Enable Keyboard/Mouse", "キーボード・マウスを有効化"), - ("Enable Clipboard", "クリップボードを有効化"), - ("Enable File Transfer", "ファイル転送を有効化"), - ("Enable TCP Tunneling", "TCPトンネリングを有効化"), + ("Enable keyboard/mouse", "キーボード・マウスを有効化"), + ("Enable clipboard", "クリップボードを有効化"), + ("Enable file transfer", "ファイル転送を有効化"), + ("Enable TCP tunneling", "TCPトンネリングを有効化"), ("IP Whitelisting", "IPホワイトリスト"), ("ID/Relay Server", "認証・中継サーバー"), - ("Import Server Config", "サーバー設定をインポート"), + ("Import server config", "サーバー設定をインポート"), ("Export Server Config", ""), ("Import server configuration successfully", "サーバー設定をインポートしました"), ("Export server configuration successfully", ""), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "承諾"), ("Dismiss", "無視"), ("Disconnect", "切断"), - ("Allow using keyboard and mouse", "キーボード・マウスの使用を許可"), - ("Allow using clipboard", "クリップボードの使用を許可"), - ("Allow hearing sound", "サウンドの受信を許可"), - ("Allow file copy and paste", "ファイルのコピーアンドペーストを許可"), + ("Enable file copy and paste", "ファイルのコピーアンドペーストを許可"), ("Connected", "接続済み"), ("Direct and encrypted connection", "接続は暗号化され、直接つながっている"), ("Relayed and encrypted connection", "接続は暗号化され、中継されている"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "ログイン中..."), ("Enable RDP session sharing", "RDPセッション共有を有効化"), ("Auto Login", "自動ログイン"), - ("Enable Direct IP Access", "直接IPアクセスを有効化"), + ("Enable direct IP access", "直接IPアクセスを有効化"), ("Rename", "名前の変更"), ("Space", "スペース"), - ("Create Desktop Shortcut", "デスクトップにショートカットを作成する"), + ("Create desktop shortcut", "デスクトップにショートカットを作成する"), ("Change Path", "パスを変更"), ("Create Folder", "フォルダを作成"), ("Please enter the folder name", "フォルダ名を入力してください"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"), ("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"), ("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "接続が許可されていません"), ("Legacy mode", ""), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "固定のパスワードを使用"), ("Use both passwords", "どちらのパスワードも使用"), ("Set permanent password", "固定のパスワードを設定"), - ("Enable Remote Restart", "リモートからの再起動を有効化"), - ("Allow remote restart", "リモートからの再起動を許可"), - ("Restart Remote Device", "リモートの端末を再起動"), + ("Enable remote restart", "リモートからの再起動を有効化"), + ("Restart remote device", "リモートの端末を再起動"), ("Are you sure you want to restart", "本当に再起動しますか"), - ("Restarting Remote Device", "リモート端末を再起動中"), + ("Restarting remote device", "リモート端末を再起動中"), ("remote_restarting_tip", "リモート端末は再起動中です。このメッセージボックスを閉じて、しばらくした後に固定のパスワードを使用して再接続してください。"), ("Copied", ""), ("Exit Fullscreen", "全画面表示を終了"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", ""), ("Enable hardware codec", ""), ("Unlock Security Settings", ""), - ("Enable Audio", ""), + ("Enable audio", ""), ("Unlock Network Settings", ""), ("Server", ""), ("Direct IP Access", ""), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", ""), ("Start session recording", ""), ("Stop session recording", ""), - ("Enable Recording Session", ""), - ("Allow recording session", ""), - ("Enable LAN Discovery", ""), - ("Deny LAN Discovery", ""), + ("Enable recording session", ""), + ("Enable LAN discovery", ""), + ("Deny LAN discovery", ""), ("Write a message", ""), ("Prompt", ""), ("Please wait for confirmation of UAC...", ""), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index fc14bace5..45e7e6bec 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -3,61 +3,61 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = [ ("Status", "상태"), ("Your Desktop", "당신의 데스크탑"), - ("desk_tip", "아래 ID와 비밀번호를 통해 당신의 데스크탑으로 접속할 수 있습니다."), + ("desk_tip", "아래의 ID와 비밀번호로 데스크탑에 접속할수 있습니다"), ("Password", "비밀번호"), ("Ready", "준비"), ("Established", "연결됨"), ("connecting_status", "RustDesk 네트워크로 연결중입니다..."), - ("Enable Service", "서비스 활성화"), - ("Start Service", "서비스 시작"), - ("Service is running", "서비스 동작중"), - ("Service is not running", "서비스가 동작하고 있지 않습니다"), + ("Enable service", "서비스 활성화"), + ("Start service", "서비스 시작"), + ("Service is running", "서비스 실행중"), + ("Service is not running", "서비스가 실행되지 않았습니다"), ("not_ready_status", "준비되지 않음. 연결을 확인해주시길 바랍니다."), ("Control Remote Desktop", "원격 데스크탑 제어"), - ("Transfer File", "파일 전송"), + ("Transfer file", "파일 전송"), ("Connect", "접속하기"), - ("Recent Sessions", "최근 세션"), - ("Address Book", "세션 주소록"), + ("Recent sessions", "최근 세션"), + ("Address book", "세션 주소록"), ("Confirmation", "확인"), - ("TCP Tunneling", "TCP 터널링"), + ("TCP tunneling", "TCP 터널링"), ("Remove", "삭제"), ("Refresh random password", "랜덤 비밀번호 새로고침"), ("Set your own password", "개인 비밀번호 설정"), - ("Enable Keyboard/Mouse", "키보드/마우스 활성화"), - ("Enable Clipboard", "클립보드 활성화"), - ("Enable File Transfer", "파일 전송 활성화"), - ("Enable TCP Tunneling", "TCP 터널링 활성화"), + ("Enable keyboard/mouse", "키보드/마우스 활성화"), + ("Enable clipboard", "클립보드 활성화"), + ("Enable file transfer", "파일 전송 활성화"), + ("Enable TCP tunneling", "TCP 터널링 활성화"), ("IP Whitelisting", "IP 화이트리스트"), ("ID/Relay Server", "ID/Relay 서버"), - ("Import Server Config", "서버 설정 가져오기"), - ("Export Server Config", ""), + ("Import server config", "서버 설정 가져오기"), + ("Export Server Config", "서버 설정 내보내기"), ("Import server configuration successfully", "서버 설정 가져오기 성공"), - ("Export server configuration successfully", ""), + ("Export server configuration successfully", "서버 설정 내보내기 성공"), ("Invalid server configuration", "잘못된 서버 설정"), ("Clipboard is empty", "클립보드가 비어있습니다"), ("Stop service", "서비스 중단"), ("Change ID", "ID 변경"), - ("Your new ID", ""), - ("length %min% to %max%", ""), - ("starts with a letter", ""), - ("allowed characters", ""), - ("id_change_tip", "a-z, A-Z, 0-9, _(밑줄 문자)만 입력 가능합니다. 첫 문자는 a-z 혹은 A-Z로 시작해야 합니다. 길이는 6 ~ 16글자가 요구됩니다."), + ("Your new ID", "당신의 새로운 ID"), + ("length %min% to %max%", "길이 %min% ~ %max%"), + ("starts with a letter", "문자로 시작해야 합니다"), + ("allowed characters", "허용되는 문자"), + ("id_change_tip", "a-z, A-Z, 0-9, _(언더바)만 입력 가능합니다. 첫 문자는 a-z 혹은 A-Z로 시작해야 합니다. 길이는 6~16글자가 요구됩니다."), ("Website", "웹사이트"), ("About", "정보"), ("Slogan_tip", ""), - ("Privacy Statement", ""), + ("Privacy Statement", "개인 정보 보호 정책"), ("Mute", "음소거"), - ("Build Date", ""), - ("Version", ""), - ("Home", ""), + ("Build Date", "빌드 날짜"), + ("Version", "버전"), + ("Home", "홈"), ("Audio Input", "오디오 입력"), - ("Enhancements", ""), + ("Enhancements", "향상된 기능"), ("Hardware Codec", "하드웨어 코덱"), ("Adaptive bitrate", "가변 비트레이트"), ("ID Server", "ID 서버"), ("Relay Server", "Relay 서버"), ("API Server", "API 서버"), - ("invalid_http", "다음과 같이 시작해야 합니다. http:// 또는 https://"), + ("invalid_http", "http:// 또는 https:// 로 시작해야합니다"), ("Invalid IP", "유효하지 않은 IP"), ("Invalid format", "유효하지 않은 형식"), ("server_not_support", "해당 서버가 아직 지원하지 않습니다"), @@ -71,7 +71,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Password Required", "비밀번호 입력"), ("Please enter your password", "비밀번호를 입력해주세요"), ("Remember password", "이 비밀번호 기억하기"), - ("Wrong Password", "틀린 비밀번호"), + ("Wrong Password", "비밀번호가 틀렸습니다"), ("Do you want to enter again?", "다시 접속하시겠습니까?"), ("Connection Error", "연결 에러"), ("Error", "에러"), @@ -86,7 +86,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Type", "유형"), ("Modified", "수정됨"), ("Size", "크기"), - ("Show Hidden Files", "숨김 파일 보기"), + ("Show Hidden Files", "숨겨진 파일 표시"), ("Receive", "받기"), ("Send", "보내기"), ("Refresh File", "파일 새로고침"), @@ -98,15 +98,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Delete", "삭제"), ("Properties", "속성"), ("Multi Select", "다중 선택"), - ("Select All", ""), - ("Unselect All", ""), - ("Empty Directory", "빈 디렉터리"), - ("Not an empty directory", "디렉터리가 비어있지 않습니다"), - ("Are you sure you want to delete this file?", "정말로 해당 파일을 삭제하시겠습니까?"), - ("Are you sure you want to delete this empty directory?", "정말로 비어있는 해당 디렉터리를 삭제하시겠습니까?"), - ("Are you sure you want to delete the file of this directory?", "정말로 해당 파일 혹은 디렉터리를 삭제하시겠습니까?"), - ("Do this for all conflicts", "모든 충돌에 대해 해당 작업 수행"), - ("This is irreversible!", "해당 결정은 돌이킬 수 없습니다!"), + ("Select All", "전체 선택"), + ("Unselect All", "전체 선택 해제"), + ("Empty Directory", "폴더가 비어있습니다"), + ("Not an empty directory", "폴더가 비어있지 않습니다"), + ("Are you sure you want to delete this file?", "이 파일을 삭제하시겠습니까?"), + ("Are you sure you want to delete this empty directory?", "이 빈 폴더를 삭제하시겠습니까?"), + ("Are you sure you want to delete the file of this directory?", "이 폴더의 파일을 삭제하시겠습니까?"), + ("Do this for all conflicts", "모든 충돌에 대해 이 작업을 수행합니다"), + ("This is irreversible!", "이 작업은 되돌릴 수 없습니다.!"), ("Deleting", "삭제중"), ("files", "파일"), ("Waiting", "대기중"), @@ -120,12 +120,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Original", "원본"), ("Shrink", "축소"), ("Stretch", "확대"), - ("Scrollbar", ""), - ("ScrollAuto", ""), + ("Scrollbar", "스크롤바"), + ("ScrollAuto", "자동스크롤"), ("Good image quality", "최적 이미지 품질"), ("Balanced", "균형"), ("Optimize reaction time", "반응 시간 최적화"), - ("Custom", ""), + ("Custom", "커스텀"), ("Show remote cursor", "원격 커서 보이기"), ("Show quality monitor", "품질 모니터 띄우기"), ("Disable clipboard", "클립보드 비활성화"), @@ -134,14 +134,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Insert Lock", "입력 잠금"), ("Refresh", "새로고침"), ("ID does not exist", "ID가 존재하지 않습니다"), - ("Failed to connect to rendezvous server", "rendezvous 서버에 접속을 실패하였습니다"), + ("Failed to connect to rendezvous server", "등록 서버에 연결하지 못했습니다."), ("Please try later", "다시 시도해주세요"), ("Remote desktop is offline", "원격 데스크탑이 연결되어 있지 않습니다"), ("Key mismatch", "키가 일치하지 않습니다."), ("Timeout", "시간 초과"), - ("Failed to connect to relay server", "relay 서버에 접속을 실패하였습니다"), - ("Failed to connect via rendezvous server", "rendezvous 서버를 통한 접속에 실패하였습니다"), - ("Failed to connect via relay server", "relay 서버를 통한 접속에 실패하였습니다"), + ("Failed to connect to relay server", "릴레이 서버에 접속을 실패하였습니다"), + ("Failed to connect via rendezvous server", "등록 서버를 통한 접속에 실패하였습니다"), + ("Failed to connect via relay server", "릴레이 서버를 통한 접속에 실패하였습니다"), ("Failed to make direct connection to remote desktop", "원격 데스크탑으로의 직접 연결 생성에 실패하였습니다"), ("Set Password", "비밀번호 설정"), ("OS Password", "OS 비밀번호"), @@ -170,19 +170,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Action", "액션"), ("Add", "추가"), ("Local Port", "로컬 포트"), - ("Local Address", ""), - ("Change Local Port", ""), + ("Local Address", "현재 주소"), + ("Change Local Port", "로컬 포트 변경"), ("setup_server_tip", "빠른 접속을 위해, 당신의 서버를 설정하세요"), ("Too short, at least 6 characters.", "너무 짧습니다, 최소 6글자 이상 입력해주세요."), - ("The confirmation is not identical.", "확인용 입력이 일치하지 않습니다."), + ("The confirmation is not identical.", "두 입력이 일치하지 않습니다."), ("Permissions", "권한"), ("Accept", "수락"), ("Dismiss", "거부"), ("Disconnect", "연결 종료"), - ("Allow using keyboard and mouse", "키보드와 마우스 허용"), - ("Allow using clipboard", "클립보드 허용"), - ("Allow hearing sound", "소리 듣기 허용"), - ("Allow file copy and paste", "파일 복사 및 붙여넣기 허용"), + ("Enable file copy and paste", "파일 복사 및 붙여넣기 허용"), ("Connected", "연결됨"), ("Direct and encrypted connection", "암호화된 직접 연결"), ("Relayed and encrypted connection", "암호화된 릴레이 연결"), @@ -191,12 +188,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enter Remote ID", "원격지 ID를 입력하세요"), ("Enter your password", "비밀번호를 입력하세요"), ("Logging in...", "로그인 중..."), - ("Enable RDP session sharing", "RDP 세션 공유를 활성화하세요"), + ("Enable RDP session sharing", "RDP 세션 공유 활성화"), ("Auto Login", "자동 로그인"), - ("Enable Direct IP Access", "IP 직접 접근 활성화하세요"), + ("Enable direct IP access", "IP 직접 접근 활성화"), ("Rename", "이름 변경"), ("Space", "공간"), - ("Create Desktop Shortcut", "데스크탑 바로가기 생성"), + ("Create desktop shortcut", "데스크탑 바로가기 생성"), ("Change Path", "경로 변경"), ("Create Folder", "폴더 생성"), ("Please enter the folder name", "폴더명을 입력해주세요"), @@ -205,23 +202,23 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Login screen using Wayland is not supported", "Wayland를 사용한 로그인 화면이 지원되지 않습니다"), ("Reboot required", "재부팅이 필요합니다"), ("Unsupported display server", "지원하지 않는 디스플레이 서버"), - ("x11 expected", "x11 예상됨"), - ("Port", ""), + ("x11 expected", "x11로 전환해주세요"), + ("Port", "포트"), ("Settings", "설정"), ("Username", "사용자명"), ("Invalid port", "유효하지 않은 포트"), ("Closed manually by the peer", "다른 사용자에 의해 종료됨"), ("Enable remote configuration modification", "원격 구성 변경 활성화"), ("Run without install", "설치 없이 실행"), - ("Connect via relay", ""), - ("Always connect via relay", "항상 relay를 통해 접속하기"), + ("Connect via relay", "릴레이를 통해 연결"), + ("Always connect via relay", "항상 릴레이를 통해 접속"), ("whitelist_tip", "화이트리스트에 있는 IP만 현 데스크탑에 접속 가능합니다"), ("Login", "로그인"), - ("Verify", ""), - ("Remember me", ""), - ("Trust this device", ""), - ("Verification code", ""), - ("verification_tip", ""), + ("Verify", "확인"), + ("Remember me", "기억하기"), + ("Trust this device", "이 기기 신뢰"), + ("Verification code", "확인 코드"), + ("verification_tip", "확인_팁"), ("Logout", "로그아웃"), ("Tags", "태그"), ("Search ID", "ID 검색"), @@ -233,7 +230,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Username missed", "사용자명 누락"), ("Password missed", "비밀번호 누락"), ("Wrong credentials", "틀린 인증 정보"), - ("The verification code is incorrect or has expired", ""), + ("The verification code is incorrect or has expired", "인증 코드가 잘못되었거나 시간이 초과되었습니다."), ("Edit Tag", "태그 수정"), ("Forget Password", "패스워드 기억하지 않기"), ("Favorites", "즐겨찾기"), @@ -289,9 +286,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "\"화면 캡처\"를 켜면 서비스가 자동으로 시작되어 다른 장치에서 사용자 장치에 대한 연결을 요청할 수 있습니다."), ("android_stop_service_tip", "서비스를 종료하면 모든 연결이 자동으로 닫힙니다."), ("android_version_audio_tip", "현재 Android 버전은 오디오 캡처를 지원하지 않습니다. Android 10 이상으로 업그레이드하십시오."), - ("android_start_service_tip", ""), - ("android_permission_may_not_change_tip", ""), - ("Account", ""), + ("android_start_service_tip", "서비스 시작을 클릭하거나 화면 캡처 권한을 활성화하여 화면 공유 서비스를 시작하세요."), + ("android_permission_may_not_change_tip", "설정된 연결의 경우 연결이 재설정되지 않는 한 권한이 즉시 변경되지 않을 수 있습니다."), + ("Account", "계정"), ("Overwrite", "덮어쓰기"), ("This file exists, skip or overwrite this file?", "해당 파일이 이미 존재합니다, 넘어가거나 덮어쓰시겠습니까?"), ("Quit", "종료"), @@ -311,22 +308,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "RustDesk 백그라운드 서비스로 유지하기"), ("Ignore Battery Optimizations", "배터리 최적화 무시하기"), ("android_open_battery_optimizations_tip", "해당 기능을 비활성화하려면 RustDesk 응용 프로그램 설정 페이지로 이동하여 [배터리]에서 [제한 없음] 선택을 해제하십시오."), - ("Start on Boot", ""), - ("Start the screen sharing service on boot, requires special permissions", ""), + ("Start on boot", "부팅시 시작"), + ("Start the screen sharing service on boot, requires special permissions", "부팅 시 화면 공유 서비스를 시작하려면 특별한 권한이 필요합니다."), ("Connection not allowed", "연결이 허용되지 않음"), - ("Legacy mode", ""), - ("Map mode", ""), - ("Translate mode", ""), + ("Legacy mode", "레거시 모드"), + ("Map mode", "맵 모드"), + ("Translate mode", "번역 모드"), ("Use permanent password", "영구 비밀번호 사용"), - ("Use both passwords", "두 비밀번호 (임시/영구) 사용"), + ("Use both passwords", "(임시/영구) 비밀번호 모두 사용"), ("Set permanent password", "영구 비밀번호 설정"), - ("Enable Remote Restart", "원격지 재시작 활성화"), - ("Allow remote restart", "원격지 재시작 허용"), - ("Restart Remote Device", "원격 기기 재시작"), + ("Enable remote restart", "원격지 재시작 활성화"), + ("Restart remote device", "원격 기기 재시작"), ("Are you sure you want to restart", "정말로 재시작 하시겠습니까"), - ("Restarting Remote Device", "원격 기기를 다시 시작하는중"), + ("Restarting remote device", "원격 기기를 다시 시작하는중"), ("remote_restarting_tip", "원격 장치를 다시 시작하는 중입니다. 이 메시지 상자를 닫고 잠시 후 영구 비밀번호로 다시 연결하십시오."), - ("Copied", ""), + ("Copied", "복사됨"), ("Exit Fullscreen", "전체 화면 종료"), ("Fullscreen", "전체화면"), ("Mobile Actions", "모바일 액션"), @@ -336,241 +332,243 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ratio", "비율"), ("Image Quality", "이미지 품질"), ("Scroll Style", "스크롤 스타일"), - ("Show Toolbar", ""), - ("Hide Toolbar", ""), + ("Show Toolbar", "툴바 보기"), + ("Hide Toolbar", "툴바 숨기기"), ("Direct Connection", "직접 연결"), ("Relay Connection", "릴레이 연결"), ("Secure Connection", "보안 연결"), ("Insecure Connection", "안전하지 않은 연결"), ("Scale original", "원래 크기"), ("Scale adaptive", "맞는 창"), - ("General", ""), - ("Security", ""), - ("Theme", ""), - ("Dark Theme", ""), - ("Light Theme", ""), - ("Dark", ""), - ("Light", ""), - ("Follow System", ""), - ("Enable hardware codec", ""), - ("Unlock Security Settings", ""), - ("Enable Audio", ""), - ("Unlock Network Settings", ""), - ("Server", ""), - ("Direct IP Access", ""), - ("Proxy", ""), - ("Apply", ""), - ("Disconnect all devices?", ""), - ("Clear", ""), - ("Audio Input Device", ""), - ("Use IP Whitelisting", ""), - ("Network", ""), - ("Pin Toolbar", ""), - ("Unpin Toolbar", ""), - ("Recording", ""), - ("Directory", ""), - ("Automatically record incoming sessions", ""), - ("Change", ""), - ("Start session recording", ""), - ("Stop session recording", ""), - ("Enable Recording Session", ""), - ("Allow recording session", ""), - ("Enable LAN Discovery", ""), - ("Deny LAN Discovery", ""), - ("Write a message", ""), - ("Prompt", ""), - ("Please wait for confirmation of UAC...", ""), - ("elevated_foreground_window_tip", ""), - ("Disconnected", ""), - ("Other", ""), - ("Confirm before closing multiple tabs", ""), - ("Keyboard Settings", ""), - ("Full Access", ""), - ("Screen Share", ""), + ("General", "일반적인"), + ("Security", "보안"), + ("Theme", "테마"), + ("Dark Theme", "다크 테마"), + ("Light Theme", "라이트 테마"), + ("Dark", "다크"), + ("Light", "라이트"), + ("Follow System", "시스템 설정에따름"), + ("Enable hardware codec", "하드웨어 코덱 활성화"), + ("Unlock Security Settings", "보안 설정 잠금 해제"), + ("Enable audio", "오디오 활성화"), + ("Unlock Network Settings", "네트워크 설정 잠금 해제"), + ("Server", "서버"), + ("Direct IP Access", "다이렉트 IP 접근"), + ("Proxy", "프록시"), + ("Apply", "적용"), + ("Disconnect all devices?", "모든 기기를 연결 해제하시겠습니까?"), + ("Clear", "지우기"), + ("Audio Input Device", "오디오 입력 장치"), + ("Use IP Whitelisting", "IP 화이트리스트 사용"), + ("Network", "네트워크"), + ("Pin Toolbar", "툴바 고정"), + ("Unpin Toolbar", "툴바 고정 해제"), + ("Recording", "녹화"), + ("Directory", "경로"), + ("Automatically record incoming sessions", "들어오는 세션을 자동으로 녹화"), + ("Change", "변경"), + ("Start session recording", "세션 녹화 시작"), + ("Stop session recording", "세션 녹화 중지"), + ("Enable recording session", "세션 녹화 활성화"), + ("Enable LAN discovery", "LAN 검색 활성화"), + ("Deny LAN discovery", "LAN 검색 거부"), + ("Write a message", "메시지 쓰기"), + ("Prompt", "프롬프트"), + ("Please wait for confirmation of UAC...", "상대방이 UAC를 확인할 때까지 기다려주세요..."), + ("elevated_foreground_window_tip", "원격 데스크톱의 현재 창을 작동하려면 더 높은 권한이 필요하며 마우스와 키보드를 일시적으로 사용할 수 없는 경우 상대방에게 현재 창을 최소화하도록 요청하거나 연결 관리 창에서 권한 상승을 클릭할 수 있습니다. 이 문제를 방지하려면 원격 장치에 이 소프트웨어를 설치하는 것이 좋습니다"), + ("Disconnected", "연결이 끊김"), + ("Other", "기타"), + ("Confirm before closing multiple tabs", "여러 탭을 닫기 전에 확인하세요"), + ("Keyboard Settings", "키보드 설정"), + ("Full Access", "전체 권한"), + ("Screen Share", "화면 공유"), ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland는 Ubuntu 21.04 이상 버전이 필요합니다."), ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland에는 더 높은 버전의 Linux 배포판이 필요합니다. X11 데스크탑을 시도하거나 OS를 변경하십시오."), - ("JumpLink", "View"), + ("JumpLink", "링크연결"), ("Please Select the screen to be shared(Operate on the peer side).", "공유할 화면을 선택하십시오(피어 측에서 작동)."), - ("Show RustDesk", ""), - ("This PC", ""), - ("or", ""), - ("Continue with", ""), - ("Elevate", ""), - ("Zoom cursor", ""), - ("Accept sessions via password", ""), - ("Accept sessions via click", ""), - ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), - ("One-time Password", ""), - ("Use one-time password", ""), - ("One-time password length", ""), - ("Request access to your device", ""), - ("Hide connection management window", ""), - ("hide_cm_tip", ""), - ("wayland_experiment_tip", ""), - ("Right click to select tabs", ""), - ("Skipped", ""), - ("Add to Address Book", ""), - ("Group", ""), - ("Search", ""), - ("Closed manually by web console", ""), - ("Local keyboard type", ""), - ("Select local keyboard type", ""), - ("software_render_tip", ""), - ("Always use software rendering", ""), - ("config_input", ""), - ("config_microphone", ""), - ("request_elevation_tip", ""), - ("Wait", ""), - ("Elevation Error", ""), - ("Ask the remote user for authentication", ""), - ("Choose this if the remote account is administrator", ""), - ("Transmit the username and password of administrator", ""), - ("still_click_uac_tip", ""), - ("Request Elevation", ""), - ("wait_accept_uac_tip", ""), - ("Elevate successfully", ""), - ("uppercase", ""), - ("lowercase", ""), - ("digit", ""), - ("special character", ""), - ("length>=8", ""), - ("Weak", ""), - ("Medium", ""), - ("Strong", ""), - ("Switch Sides", ""), - ("Please confirm if you want to share your desktop?", ""), - ("Display", ""), - ("Default View Style", ""), - ("Default Scroll Style", ""), - ("Default Image Quality", ""), - ("Default Codec", ""), - ("Bitrate", ""), - ("FPS", ""), - ("Auto", ""), - ("Other Default Options", ""), - ("Voice call", ""), - ("Text chat", ""), - ("Stop voice call", ""), - ("relay_hint_tip", ""), - ("Reconnect", ""), - ("Codec", ""), - ("Resolution", ""), - ("No transfers in progress", ""), - ("Set one-time password length", ""), - ("install_cert_tip", ""), - ("confirm_install_cert_tip", ""), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), - ("empty_recent_tip", ""), - ("empty_favorite_tip", ""), - ("empty_lan_tip", ""), - ("empty_address_book_tip", ""), - ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), - ("Me", ""), - ("identical_file_tip", ""), - ("show_monitors_tip", ""), - ("View Mode", ""), - ("login_linux_tip", ""), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), - ("No need to elevate", ""), - ("System Sound", ""), - ("Default", ""), - ("New RDP", ""), - ("Fingerprint", ""), - ("Copy Fingerprint", ""), - ("no fingerprints", ""), - ("Select a peer", ""), - ("Select peers", ""), - ("Plugins", ""), - ("Uninstall", ""), - ("Update", ""), - ("Enable", ""), - ("Disable", ""), - ("Options", ""), - ("resolution_original_tip", ""), - ("resolution_fit_local_tip", ""), - ("resolution_custom_tip", ""), - ("Collapse toolbar", ""), - ("Accept and Elevate", ""), - ("accept_and_elevate_btn_tooltip", ""), - ("clipboard_wait_response_timeout_tip", ""), - ("Incoming connection", ""), - ("Outgoing connection", ""), - ("Exit", ""), - ("Open", ""), - ("logout_tip", ""), - ("Service", ""), - ("Start", ""), - ("Stop", ""), - ("exceed_max_devices", ""), - ("Sync with recent sessions", ""), - ("Sort tags", ""), - ("Open connection in new tab", ""), - ("Move tab to new window", ""), - ("Can not be empty", ""), - ("Already exists", ""), - ("Change Password", ""), - ("Refresh Password", ""), - ("ID", ""), - ("Grid View", ""), - ("List View", ""), - ("Select", ""), - ("Toggle Tags", ""), - ("pull_ab_failed_tip", ""), - ("push_ab_failed_tip", ""), - ("synced_peer_readded_tip", ""), - ("Change Color", ""), - ("Primary Color", ""), - ("HSV Color", ""), - ("Installation Successful!", ""), - ("Installation failed!", ""), - ("Reverse mouse wheel", ""), - ("{} sessions", ""), - ("scam_title", ""), - ("scam_text1", ""), - ("scam_text2", ""), - ("Don't show again", ""), - ("I Agree", ""), - ("Decline", ""), - ("Timeout in minutes", ""), - ("auto_disconnect_option_tip", ""), - ("Connection failed due to inactivity", ""), - ("Check for software update on startup", ""), - ("upgrade_rustdesk_server_pro_to_{}_tip", ""), - ("pull_group_failed_tip", ""), - ("Filter by intersection", ""), - ("Remove wallpaper during incoming sessions", ""), - ("Test", ""), - ("switch_display_elevated_connections_tip", ""), - ("display_is_plugged_out_msg", ""), - ("No displays", ""), - ("elevated_switch_display_msg", ""), - ("Open in new window", ""), - ("Show displays as individual windows", ""), - ("Use all my displays for the remote session", ""), - ("selinux_tip", ""), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), - ("Virtual display", ""), - ("Plug out all", ""), + ("Show RustDesk", "RustDesk 표시"), + ("This PC", "이 PC"), + ("or", "또는"), + ("Continue with", "계속"), + ("Elevate", "권한 상승"), + ("Zoom cursor", "커서 줌"), + ("Accept sessions via password", "비밀번호를 통해 세션 수락"), + ("Accept sessions via click", "클릭을 통해 세션 수락"), + ("Accept sessions via both", "두 가지 모두를 통해 세션을 수락합니다"), + ("Please wait for the remote side to accept your session request...", "원격 측에서 세션 요청을 수락할 때까지 기다리십시오..."), + ("One-time Password", "일회용 비밀번호"), + ("Use one-time password", "일회용 비밀번호 사용"), + ("One-time password length", "일회용 비밀번호 길이"), + ("Request access to your device", "장치에 대한 액세스를 요청하세요"), + ("Hide connection management window", "연결 관리 창 숨기기"), + ("hide_cm_tip", "숨기기는 비밀번호 연결만 허용되고 고정 비밀번호만 사용되는 경우에만 허용됩니다"), + ("wayland_experiment_tip", "Wayland 지원은 실험적입니다. 무인 액세스가 필요한 경우 X11을 사용하십시오"), + ("Right click to select tabs", "마우스 오른쪽 버튼을 클릭하고 탭을 선택하세요"), + ("Skipped", "건너뛰기"), + ("Add to address book", "주소록에 추가"), + ("Group", "그룹"), + ("Search", "검색"), + ("Closed manually by web console", "웹 콘솔에 의해 수동으로 닫힘"), + ("Local keyboard type", "로컬 키보드 유형"), + ("Select local keyboard type", "로컬 키보드 유형 선택"), + ("software_render_tip", "Nvidia 그래픽 카드를 사용하고 세션이 설정된 후 원격 창이 즉시 닫히는 경우 nouveau 드라이버를 설치하고 소프트웨어 렌더링을 사용하도록 선택하는 것이 도움이 될 수 있습니다. 소프트웨어를 다시 시작하면 적용됩니다."), + ("Always use software rendering", "항상 소프트웨어 렌더링 사용"), + ("config_input", "키보드를 통해 원격 데스크톱을 제어할 수 있으려면 RustDesk의 \"입력 모니터링\" 권한을 부여해 주세요"), + ("config_microphone", "마이크를 통한 오디오 전송을 지원하려면 RustDesk에 \"녹음\" 권한을 부여하세요"), + ("request_elevation_tip", "상대방이 있다면 권한 상승을 요청할 수도 있습니다"), + ("Wait", "대기"), + ("Elevation Error", "권한 상승 에러"), + ("Ask the remote user for authentication", "원격 사용자에게 인증 요청"), + ("Choose this if the remote account is administrator", "원격 계정이 관리자인 경우 선택하세요"), + ("Transmit the username and password of administrator", "관리자의 사용자 이름과 비밀번호를 전송합니다"), + ("still_click_uac_tip", "통제된 사용자는 여전히 RustDesk를 실행하는 UAC 창에서 확인을 클릭해야 합니다"), + ("Request Elevation", "권한 상승 요청"), + ("wait_accept_uac_tip", "원격 사용자가 UAC 대화 상자를 확인할 때까지 기다리십시오"), + ("Elevate successfully", "권한 상승이 완료되었습니다"), + ("uppercase", "대문자"), + ("lowercase", "소문자"), + ("digit", "숫자"), + ("special character", "특수문자"), + ("length>=8", "8자 이상"), + ("Weak", "약함"), + ("Medium", "보통"), + ("Strong", "강력"), + ("Switch Sides", "역방향 액세스 전환"), + ("Please confirm if you want to share your desktop?", "데스크탑을 공유하시겠습니까?"), + ("Display", "디스플레이"), + ("Default View Style", "기본 보기 스타일"), + ("Default Scroll Style", "기본 스크롤 스타일"), + ("Default Image Quality", "기본 이미지 품질"), + ("Default Codec", "기본 코덱"), + ("Bitrate", "비트레이트"), + ("FPS", "FPS"), + ("Auto", "자동"), + ("Other Default Options", "기타 기본 옵션"), + ("Voice call", "음성통화"), + ("Text chat", "문자 채팅"), + ("Stop voice call", "음성통화 종료"), + ("relay_hint_tip", "직접 연결이 안될 수도 있으니 릴레이 연결을 시도해보세요. \n또한 릴레이 연결을 직접 사용하고 싶다면 ID 뒤에 /r을 추가하면 되고, 최근 방문에 해당 카드가 존재한다면 카드 옵션에서 릴레이 연결을 강제하도록 선택할 수도 있습니다."), + ("Reconnect", "다시 연결"), + ("Codec", "코덱"), + ("Resolution", "해상도"), + ("No transfers in progress", "진행 중인 전송이 없습니다"), + ("Set one-time password length", "일회용 비밀번호 길이 설정"), + ("install_cert_tip", "RustDesk 인증서 설치"), + ("confirm_install_cert_tip", "이 인증서는 RustDesk 테스트 인증서이므로 신뢰할 수 있습니다. 인증서는 RustDesk 드라이버를 신뢰하고 설치하는 데 사용됩니다"), + ("RDP Settings", "RDP 설정"), + ("Sort by", "정렬 기준"), + ("New Connection", "새로운 연결"), + ("Restore", "복원"), + ("Minimize", "최소화"), + ("Maximize", "최대화"), + ("Your Device", "당신의 장치"), + ("empty_recent_tip", "최근 세션이 없습니다. 새 세션을 시작해보세요"), + ("empty_favorite_tip", "장치 즐겨찾기가 없습니다. 새 즐겨찾기를 추가해보세요"), + ("empty_lan_tip", "제어되는 장치가 발견되지 않았습니다."), + ("empty_address_book_tip", "현재 주소록에 제어되는 클라이언트가 없습니다"), + ("eg: admin", "예: 관리자"), + ("Empty Username", "사용자명이 비어있습니다"), + ("Empty Password", "비밀번호가 비어있습니다"), + ("Me", "나"), + ("identical_file_tip", "이 파일은 상대방의 파일과 일치합니다."), + ("show_monitors_tip", "도구 모음에 모니터 표시"), + ("View Mode", "보기 모드"), + ("login_linux_tip", "X 데스크탑을 활성화하려면 제어되는 터미널의 Linux 계정에 로그인하세요"), + ("verify_rustdesk_password_tip", "RustDesk 비밀번호 확인"), + ("remember_account_tip", "이 계정을 기억하세요"), + ("os_account_desk_tip", "모니터가 없는 환경에서 이 계정은 제어되는 시스템에 로그인하고 데스크탑을 활성화하는 데 사용됩니다"), + ("OS Account", "OS 계정"), + ("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", "데스크탑을 설치해주세요"), + ("No need to elevate", "권한 상승이 필요없습니다."), + ("System Sound", "시스템 사운드"), + ("Default", "기본"), + ("New RDP", "새로운 RDP"), + ("Fingerprint", "지문"), + ("Copy Fingerprint", "지문 복사"), + ("no fingerprints", "지문이 없습니다"), + ("Select a peer", "동료를 선택하세요"), + ("Select peers", "동료 선택"), + ("Plugins", "플러그인"), + ("Uninstall", "제거"), + ("Update", "업데이트"), + ("Enable", "활성화"), + ("Disable", "비활성화"), + ("Options", "옵션"), + ("resolution_original_tip", "기본 해상도"), + ("resolution_fit_local_tip", "로컬 해상도로 변경"), + ("resolution_custom_tip", "맞춤 해상도"), + ("Collapse toolbar", "툴바 접기"), + ("Accept and Elevate", "권한 상승 승인"), + ("accept_and_elevate_btn_tooltip", "연결 수락 및 UAC 권한 상승"), + ("clipboard_wait_response_timeout_tip", "복사 응답을 기다리는 동안 시간이 초과되었습니다."), + ("Incoming connection", "들어오는 연결"), + ("Outgoing connection", "나가는 연결"), + ("Exit", "나가기"), + ("Open", "열기"), + ("logout_tip", "정말로 로그아웃하시겠습니까?"), + ("Service", "서비스"), + ("Start", "시작"), + ("Stop", "중지"), + ("exceed_max_devices", "최대_장치_초과"), + ("Sync with recent sessions", "최근 세션과 동기화"), + ("Sort tags", "태그 정렬"), + ("Open connection in new tab", "새 탭에서 연결 열기"), + ("Move tab to new window", "탭을 새 창으로 이동"), + ("Can not be empty", "비워둘 수 없습니다"), + ("Already exists", "이미 존재 함"), + ("Change Password", "비밀번호 변경"), + ("Refresh Password", "비밀번호 새로고침"), + ("ID", "ID"), + ("Grid View", "그리드 보기"), + ("List View", "리스트 보기"), + ("Select", "선택"), + ("Toggle Tags", "태그 전환"), + ("pull_ab_failed_tip", "주소록을 가져오지 못했습니다."), + ("push_ab_failed_tip", "주소록 업로드 실패"), + ("synced_peer_readded_tip", "최근 세션에 있는 장치는 주소록에 다시 동기화됩니다."), + ("Change Color", "색상 변경"), + ("Primary Color", "기본 색상"), + ("HSV Color", "HSV 색상"), + ("Installation Successful!", "설치 성공!"), + ("Installation failed!", "설치 실패!"), + ("Reverse mouse wheel", "마우스 휠 반전"), + ("{} sessions", "{} 세션"), + ("scam_title", "당신은 사기를 당했을 수도 있습니다"), + ("scam_text1", "모르는 사람과 통화 중이고 그들이 RustDesk를 사용하여 서비스를 시작하라고 요청하는 경우, 계속하지 말고 즉시 전화를 끊으세요"), + ("scam_text2", "그들은 귀하의 돈이나 기타 개인 정보를 훔치려는 사기꾼일 가능성이 높습니다"), + ("Don't show again", "다시 표시하지 않음"), + ("I Agree", "동의"), + ("Decline", "거절"), + ("Timeout in minutes", "시간 초과(분)"), + ("auto_disconnect_option_tip", "비활성 세션 자동 종료"), + ("Connection failed due to inactivity", "장시간 활동이 없어 연결이 자동으로 끊어졌습니다"), + ("Check for software update on startup", "시작 시 소프트웨어 업데이트 확인"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "RustDesk 서버 전문가 버전 {} 으로 업그레이드하세요"), + ("pull_group_failed_tip", "그룹 정보를 가져오지 못했습니다"), + ("Filter by intersection", "교차로로 필터링"), + ("Remove wallpaper during incoming sessions", "세션 수락시 배경화면 제거"), + ("Test", "테스트"), + ("switch_display_elevated_connections_tip", "권한을 승격한 후에는 사용자에게 다중 연결 요금이 부과되며 기본이 아닌 디스플레이로 전환할 수 없습니다. 여러 대의 모니터를 제어하려면 설치 후 다시 시도하세요"), + ("display_is_plugged_out_msg", "모니터의 연결이 끈어지면 첫 번째 모니터로 전환됩니다"), + ("No displays", "디스플레이 없음"), + ("elevated_switch_display_msg", "권한 상승 이후에는 다중 모니터 화면이 지원되지 않으므로 메인 모니터로 전환하세요"), + ("Open in new window", "새 창에서 열기"), + ("Show displays as individual windows", "디스플레이를 개별 창으로 표시"), + ("Use all my displays for the remote session", "원격 세션에 내 모든 디스플레이 사용"), + ("selinux_tip", "SELinux를 활성화하면 RustDesk가 호스트로 제대로 실행되지 않을 수 있습니다"), + ("Change view", "보기 변경"), + ("Big tiles", "큰 타일"), + ("Small tiles", "작은 타일"), + ("List", "리스트"), + ("Virtual display", "가상 디스플레이"), + ("Plug out all", "모두 플러그 아웃"), + ("True color (4:4:4)", "트루컬러(4:4:4)"), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 992ba7cd5..c0364fc8d 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Дайын"), ("Established", "Қосылды"), ("connecting_status", "RustDesk желісіне қосылуда..."), - ("Enable Service", "Сербесті қосу"), - ("Start Service", "Сербесті іске қосу"), + ("Enable service", "Сербесті қосу"), + ("Start service", "Сербесті іске қосу"), ("Service is running", "Сербес істеуде"), ("Service is not running", "Сербес істемеуде"), ("not_ready_status", "Дайын емес. Қосылымды тексеруді өтінеміз"), ("Control Remote Desktop", "Қашықтағы Жұмыс үстелін Басқару"), - ("Transfer File", "Файыл Тасымалдау"), + ("Transfer file", "Файыл Тасымалдау"), ("Connect", "Қосылу"), - ("Recent Sessions", "Соңғы Сештер"), - ("Address Book", "Мекенжай Кітабы"), + ("Recent sessions", "Соңғы Сештер"), + ("Address book", "Мекенжай Кітабы"), ("Confirmation", "Мақұлдау"), - ("TCP Tunneling", "TCP тунелдеу"), + ("TCP tunneling", "TCP тунелдеу"), ("Remove", "Жою"), ("Refresh random password", "Кездейсоқ құпия сөзді жаңарту"), ("Set your own password", "Өз құпия сөзіңізді орнатыңыз"), - ("Enable Keyboard/Mouse", "Пернетақта/Тінтуірді қосу"), - ("Enable Clipboard", "Көшіру-тақтасын қосу"), - ("Enable File Transfer", "Файыл Тасымалдауды қосу"), - ("Enable TCP Tunneling", "TCP тунелдеуді қосу"), + ("Enable keyboard/mouse", "Пернетақта/Тінтуірді қосу"), + ("Enable clipboard", "Көшіру-тақтасын қосу"), + ("Enable file transfer", "Файыл Тасымалдауды қосу"), + ("Enable TCP tunneling", "TCP тунелдеуді қосу"), ("IP Whitelisting", "IP Ақ-тізімі"), ("ID/Relay Server", "ID/Relay сербері"), - ("Import Server Config", "Серверді импорттау"), + ("Import server config", "Серверді импорттау"), ("Export Server Config", ""), ("Import server configuration successfully", "Сервердің конфигурациясы сәтті импортталды"), ("Export server configuration successfully", ""), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Қабылдау"), ("Dismiss", "Босату"), ("Disconnect", "Ажырату"), - ("Allow using keyboard and mouse", "Пернетақта мен тінтуірді қолдануды рұқсат ету"), - ("Allow using clipboard", "Көшіру-тақтасын рұқсат ету"), - ("Allow hearing sound", "Дыбыс естуді рұқсат ету"), - ("Allow file copy and paste", "Файылды көшіру мен қоюды рұқсат ету"), + ("Enable file copy and paste", "Файылды көшіру мен қоюды рұқсат ету"), ("Connected", "Қосылды"), ("Direct and encrypted connection", "Тікелей және кіриптелген қосылым"), ("Relayed and encrypted connection", "Релайданған және кіриптелген қосылым"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Кіруде..."), ("Enable RDP session sharing", "RDP сешті бөлісуді іске қосу"), ("Auto Login", "Ауты Кіру (\"Сеш аяқталған соң құлыптау\"'ды орнатқанда ғана жарамды)"), - ("Enable Direct IP Access", "Тікелей IP Қолжетімді іске қосу"), + ("Enable direct IP access", "Тікелей IP Қолжетімді іске қосу"), ("Rename", "Атын өзгерту"), ("Space", "Орын"), - ("Create Desktop Shortcut", "Жұмыс үстелі Таңбашасын Жасау"), + ("Create desktop shortcut", "Жұмыс үстелі Таңбашасын Жасау"), ("Change Path", "Жолды өзгерту"), ("Create Folder", "Бума жасау"), ("Please enter the folder name", "Буманың атауын еңгізуді өтінеміз"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Артжақтағы RustDesk сербесін сақтап тұру"), ("Ignore Battery Optimizations", "Бәтері Оңтайландыруларын Елемеу"), ("android_open_battery_optimizations_tip", "Егер де бұл ерекшелікті өшіруді қаласаңыз, келесі RustDesk апылқат орнатпалары бетіне барып, [Бәтері]'ні тауып кіріңіз де [Шектеусіз]'ден құсбелгіні алып тастауды өтінеміз"), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Қосылу рұқсат етілмеген"), ("Legacy mode", ""), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Тұрақты құпия сөзді қолдану"), ("Use both passwords", "Қос құпия сөзді қолдану"), ("Set permanent password", "Тұрақты құпия сөзді орнату"), - ("Enable Remote Restart", "Қашықтан қайта-қосуды іске қосу"), - ("Allow remote restart", "Қашықтан қайта-қосуды рұқсат ету"), - ("Restart Remote Device", "Қашықтағы құрылғыны қайта-қосу"), + ("Enable remote restart", "Қашықтан қайта-қосуды іске қосу"), + ("Restart remote device", "Қашықтағы құрылғыны қайта-қосу"), ("Are you sure you want to restart", "Қайта-қосуға сенімдісіз бе?"), - ("Restarting Remote Device", "Қашықтағы Құрылғыны қайта-қосуда"), + ("Restarting remote device", "Қашықтағы Құрылғыны қайта-қосуда"), ("remote_restarting_tip", "Қашықтағы құрылғы қайта-қосылуда, бұл хабар терезесін жабып, біраздан соң тұрақты құпия сөзбен қайта қосылуды өтінеміз"), ("Copied", "Көшірілді"), ("Exit Fullscreen", "Толық екіреннен Шығу"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", ""), ("Enable hardware codec", ""), ("Unlock Security Settings", ""), - ("Enable Audio", ""), + ("Enable audio", ""), ("Unlock Network Settings", ""), ("Server", ""), ("Direct IP Access", ""), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", ""), ("Start session recording", ""), ("Stop session recording", ""), - ("Enable Recording Session", ""), - ("Allow recording session", ""), - ("Enable LAN Discovery", ""), - ("Deny LAN Discovery", ""), + ("Enable recording session", ""), + ("Enable LAN discovery", ""), + ("Deny LAN discovery", ""), ("Write a message", ""), ("Prompt", ""), ("Please wait for confirmation of UAC...", ""), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index fa78425fc..72297cb82 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Pasiruošęs"), ("Established", "Įsteigta"), ("connecting_status", "Prisijungiama prie RustDesk tinklo..."), - ("Enable Service", "Įgalinti paslaugą"), - ("Start Service", "Pradėti paslaugą"), + ("Enable service", "Įgalinti paslaugą"), + ("Start service", "Pradėti paslaugą"), ("Service is running", "Paslauga veikia"), ("Service is not running", "Paslauga neveikia"), ("not_ready_status", "Neprisijungęs. Patikrinkite ryšį."), ("Control Remote Desktop", "Nuotolinio darbalaukio valdymas"), - ("Transfer File", "Perkelti failą"), + ("Transfer file", "Perkelti failą"), ("Connect", "Prisijungti"), - ("Recent Sessions", "Seansų istorija"), - ("Address Book", "Adresų knyga"), + ("Recent sessions", "Seansų istorija"), + ("Address book", "Adresų knyga"), ("Confirmation", "Patvirtinimas"), - ("TCP Tunneling", "TCP tuneliavimas"), + ("TCP tunneling", "TCP tuneliavimas"), ("Remove", "Pašalinti"), ("Refresh random password", "Atnaujinti atsitiktinį slaptažodį"), ("Set your own password", "Nustatykite savo slaptažodį"), - ("Enable Keyboard/Mouse", "Įgalinti klaviatūrą/pelę"), - ("Enable Clipboard", "Įgalinti iškarpinę"), - ("Enable File Transfer", "Įgalinti failų perdavimą"), - ("Enable TCP Tunneling", "Įgalinti TCP tuneliavimą"), + ("Enable keyboard/mouse", "Įgalinti klaviatūrą/pelę"), + ("Enable clipboard", "Įgalinti iškarpinę"), + ("Enable file transfer", "Įgalinti failų perdavimą"), + ("Enable TCP tunneling", "Įgalinti TCP tuneliavimą"), ("IP Whitelisting", "IP baltasis sąrašas"), ("ID/Relay Server", "ID / perdavimo serveris"), - ("Import Server Config", "Importuoti serverio konfigūraciją"), + ("Import server config", "Importuoti serverio konfigūraciją"), ("Export Server Config", "Eksportuoti serverio konfigūraciją"), ("Import server configuration successfully", "Sėkmingai importuoti serverio konfigūraciją"), ("Export server configuration successfully", "Sėkmingai eksportuoti serverio konfigūraciją"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Priimti"), ("Dismiss", "Atmesti"), ("Disconnect", "Atjungti"), - ("Allow using keyboard and mouse", "Leisti naudoti klaviatūrą ir pelę"), - ("Allow using clipboard", "Leisti naudoti mainų sritį"), - ("Allow hearing sound", "Leisti girdėti kompiuterio garsą"), - ("Allow file copy and paste", "Leisti kopijuoti ir įklijuoti failus"), + ("Enable file copy and paste", "Leisti kopijuoti ir įklijuoti failus"), ("Connected", "Prisijungta"), ("Direct and encrypted connection", "Tiesioginis ir šifruotas ryšys"), ("Relayed and encrypted connection", "Perduotas ir šifruotas ryšys"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Prisijungiama..."), ("Enable RDP session sharing", "Įgalinti RDP seansų bendrinimą"), ("Auto Login", "Automatinis prisijungimas"), - ("Enable Direct IP Access", "Įgalinti tiesioginę IP prieigą"), + ("Enable direct IP access", "Įgalinti tiesioginę IP prieigą"), ("Rename", "Pervardyti"), ("Space", "Erdvė"), - ("Create Desktop Shortcut", "Sukurti nuorodą darbalaukyje"), + ("Create desktop shortcut", "Sukurti nuorodą darbalaukyje"), ("Change Path", "Keisti kelią"), ("Create Folder", "Sukurti aplanką"), ("Please enter the folder name", "Įveskite aplanko pavadinimą"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Palikti RustDesk fonine paslauga"), ("Ignore Battery Optimizations", "Ignoruoti akumuliatoriaus optimizavimą"), ("android_open_battery_optimizations_tip", "Eikite į kitą nustatymų puslapį"), - ("Start on Boot", "Pradėti paleidžiant"), + ("Start on boot", "Pradėti paleidžiant"), ("Start the screen sharing service on boot, requires special permissions", "Paleiskite ekrano bendrinimo paslaugą įkrovos metu, reikia specialių leidimų"), ("Connection not allowed", "Ryšys neleidžiamas"), ("Legacy mode", "Senasis režimas"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Naudoti nuolatinį slaptažodį"), ("Use both passwords", "Naudoti abu slaptažodžius"), ("Set permanent password", "Nustatyti nuolatinį slaptažodį"), - ("Enable Remote Restart", "Įgalinti nuotolinį paleidimą iš naujo"), - ("Allow remote restart", "Leisti nuotolinio kompiuterio paleidimą iš naujo"), - ("Restart Remote Device", "Paleisti nuotolinį kompiuterį iš naujo"), + ("Enable remote restart", "Įgalinti nuotolinį paleidimą iš naujo"), + ("Restart remote device", "Paleisti nuotolinį kompiuterį iš naujo"), ("Are you sure you want to restart", "Ar tikrai norite paleisti iš naujo?"), - ("Restarting Remote Device", "Nuotolinio įrenginio paleidimas iš naujo"), + ("Restarting remote device", "Nuotolinio įrenginio paleidimas iš naujo"), ("remote_restarting_tip", "Nuotolinis įrenginys paleidžiamas iš naujo. Uždarykite šį pranešimą ir po kurio laiko vėl prisijunkite naudodami nuolatinį slaptažodį."), ("Copied", "Nukopijuota"), ("Exit Fullscreen", "Išeiti iš pilno ekrano"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Kaip sistemos"), ("Enable hardware codec", "Įgalinti"), ("Unlock Security Settings", "Atrakinti saugos nustatymus"), - ("Enable Audio", "Įgalinti garsą"), + ("Enable audio", "Įgalinti garsą"), ("Unlock Network Settings", "Atrakinti tinklo nustatymus"), ("Server", "Serveris"), ("Direct IP Access", "Tiesioginė IP prieiga"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Keisti"), ("Start session recording", "Pradėti seanso įrašinėjimą"), ("Stop session recording", "Sustabdyti seanso įrašinėjimą"), - ("Enable Recording Session", "Įgalinti seanso įrašinėjimą"), - ("Allow recording session", "Leisti seanso įrašinėjimą"), - ("Enable LAN Discovery", "Įgalinti LAN aptikimą"), - ("Deny LAN Discovery", "Neleisti LAN aptikimo"), + ("Enable recording session", "Įgalinti seanso įrašinėjimą"), + ("Enable LAN discovery", "Įgalinti LAN aptikimą"), + ("Deny LAN discovery", "Neleisti LAN aptikimo"), ("Write a message", "Rašyti žinutę"), ("Prompt", "Užuomina"), ("Please wait for confirmation of UAC...", "Palaukite UAC patvirtinimo..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wayland palaikymas yra eksperimentinis, naudokite X11, jei jums reikalingas automatinis prisijungimas."), ("Right click to select tabs", "Dešiniuoju pelės mygtuku spustelėkite, kad pasirinktumėte skirtukus"), ("Skipped", "Praleisti"), - ("Add to Address Book", "Pridėti prie adresų knygos"), + ("Add to address book", "Pridėti prie adresų knygos"), ("Group", "Grupė"), ("Search", "Paieška"), ("Closed manually by web console", "Uždaryta rankiniu būdu naudojant žiniatinklio konsolę"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 3c6c4d19f..066da1074 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Gatavs"), ("Established", "Izveidots"), ("connecting_status", "Notiek savienojuma izveide ar RustDesk tīklu..."), - ("Enable Service", "Iespējot servisu"), - ("Start Service", "Sākt servisu"), + ("Enable service", "Iespējot servisu"), + ("Start service", "Sākt servisu"), ("Service is running", "Pakalpojums darbojas"), ("Service is not running", "Pakalpojums nedarbojas"), ("not_ready_status", "Nav gatavs. Lūdzu, pārbaudiet savienojumu"), ("Control Remote Desktop", "Vadīt attālo darbvirsmu"), - ("Transfer File", "Pārsūtīt failu"), + ("Transfer file", "Pārsūtīt failu"), ("Connect", "Savienoties"), - ("Recent Sessions", "Pēdējās sesijas"), - ("Address Book", "Adrešu grāmata"), + ("Recent sessions", "Pēdējās sesijas"), + ("Address book", "Adrešu grāmata"), ("Confirmation", "Apstiprinājums"), - ("TCP Tunneling", "TCP tunelēšana"), + ("TCP tunneling", "TCP tunelēšana"), ("Remove", "Noņemt"), ("Refresh random password", "Atsvaidzināt nejaušo paroli"), ("Set your own password", "Iestatiet savu paroli"), - ("Enable Keyboard/Mouse", "Iespējot tastatūru/peli"), - ("Enable Clipboard", "Iespējot starpliktuvi"), - ("Enable File Transfer", "Iespējot failu pārsūtīšanu"), - ("Enable TCP Tunneling", "Iespējot TCP tunelēšanu"), + ("Enable keyboard/mouse", "Iespējot tastatūru/peli"), + ("Enable clipboard", "Iespējot starpliktuvi"), + ("Enable file transfer", "Iespējot failu pārsūtīšanu"), + ("Enable TCP tunneling", "Iespējot TCP tunelēšanu"), ("IP Whitelisting", "IP baltais saraksts"), ("ID/Relay Server", "ID/releja serveris"), - ("Import Server Config", "Importēt servera konfigurāciju"), + ("Import server config", "Importēt servera konfigurāciju"), ("Export Server Config", "Eksportēt servera konfigurāciju"), ("Import server configuration successfully", "Servera konfigurācija veiksmīgi importēta"), ("Export server configuration successfully", "Servera konfigurācija veiksmīgi eksportēta"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Pieņemt"), ("Dismiss", "Noraidīt"), ("Disconnect", "Atvienot"), - ("Allow using keyboard and mouse", "Atļaut izmantot tastatūru un peli"), - ("Allow using clipboard", "Atļaut izmantot starpliktuvi"), - ("Allow hearing sound", "Atļaut klausīties skaņu"), - ("Allow file copy and paste", "Atļaut failu kopēšanu un ielīmēšanu"), + ("Enable file copy and paste", "Atļaut failu kopēšanu un ielīmēšanu"), ("Connected", "Savienots"), ("Direct and encrypted connection", "Tiešs un šifrēts savienojums"), ("Relayed and encrypted connection", "Pārslēgts un šifrēts savienojums"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Ielogoties..."), ("Enable RDP session sharing", "Iespējot RDP sesiju koplietošanu"), ("Auto Login", "Automātiskā pieteikšanās (derīga tikai tad, ja esat iestatījis \"Bloķēt pēc sesijas beigām\")"), - ("Enable Direct IP Access", "Iespējot tiešo IP piekļuvi"), + ("Enable direct IP access", "Iespējot tiešo IP piekļuvi"), ("Rename", "Pārdēvēt"), ("Space", "Vieta"), - ("Create Desktop Shortcut", "Izveidot saīsni uz darbvirsmas"), + ("Create desktop shortcut", "Izveidot saīsni uz darbvirsmas"), ("Change Path", "Mainīt ceļu"), ("Create Folder", "Izveidot mapi"), ("Please enter the folder name", "Lūdzu, ievadiet mapes nosaukumu"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Saglabāt RustDesk fona servisu"), ("Ignore Battery Optimizations", "Ignorēt akumulatora optimizāciju"), ("android_open_battery_optimizations_tip", "Ja vēlaties atspējot šo funkciju, lūdzu, dodieties uz nākamo RustDesk lietojumprogrammas iestatījumu lapu, atrodiet un atveriet [Akumulators], noņemiet atzīmi no [Neierobežots]"), - ("Start on Boot", "Palaist pie ieslēgšanas"), + ("Start on boot", "Palaist pie ieslēgšanas"), ("Start the screen sharing service on boot, requires special permissions", "Startējiet ekrāna koplietošanas pakalpojumu ieslēgšanas laikā, nepieciešamas īpašas atļaujas"), ("Connection not allowed", "Savienojums nav atļauts"), ("Legacy mode", "Novecojis režīms"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Izmantot pastāvīgo paroli"), ("Use both passwords", "Izmantot abas paroles"), ("Set permanent password", "Iestatīt pastāvīgo paroli"), - ("Enable Remote Restart", "Iespējot attālo restartēšanu"), - ("Allow remote restart", "Atļaut attālo restartēšanu"), - ("Restart Remote Device", "Restartēt attālo ierīci"), + ("Enable remote restart", "Iespējot attālo restartēšanu"), + ("Restart remote device", "Restartēt attālo ierīci"), ("Are you sure you want to restart", "Vai tiešām vēlaties restartēt"), - ("Restarting Remote Device", "Attālās ierīces restartēšana"), + ("Restarting remote device", "Attālās ierīces restartēšana"), ("remote_restarting_tip", "Attālā ierīce tiek restartēta, lūdzu, aizveriet šo ziņojuma lodziņu un pēc kāda laika izveidojiet savienojumu ar pastāvīgo paroli"), ("Copied", "Kopēts"), ("Exit Fullscreen", "Iziet no pilnekrāna"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Sekot sistēmai"), ("Enable hardware codec", "Iespējot aparatūras kodeku"), ("Unlock Security Settings", "Atbloķēt drošības iestatījumus"), - ("Enable Audio", "Iespējot audio"), + ("Enable audio", "Iespējot audio"), ("Unlock Network Settings", "Atbloķēt tīkla iestatījumus"), ("Server", "Serveris"), ("Direct IP Access", "Tiešā IP piekļuve"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Mainīt"), ("Start session recording", "Sākt sesijas ierakstīšanu"), ("Stop session recording", "Apturēt sesijas ierakstīšanu"), - ("Enable Recording Session", "Iespējot sesijas ierakstīšanu"), - ("Allow recording session", "Atļaut sesijas ierakstīšanu"), - ("Enable LAN Discovery", "Iespējot LAN atklāšanu"), - ("Deny LAN Discovery", "Liegt LAN atklāšanu"), + ("Enable recording session", "Iespējot sesijas ierakstīšanu"), + ("Enable LAN discovery", "Iespējot LAN atklāšanu"), + ("Deny LAN discovery", "Liegt LAN atklāšanu"), ("Write a message", "Rakstīt ziņojumu"), ("Prompt", "Uzvedne"), ("Please wait for confirmation of UAC...", "Lūdzu, uzgaidiet UAC apstiprinājumu..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wayland atbalsts ir eksperimentālā stadijā. Ja nepieciešama neuzraudzīta piekļuve, lūdzu, izmantojiet X11."), ("Right click to select tabs", "Ar peles labo pogu noklikšķiniet, lai atlasītu cilnes"), ("Skipped", "Izlaists"), - ("Add to Address Book", "Pievienot adrešu grāmatai"), + ("Add to address book", "Pievienot adrešu grāmatai"), ("Group", "Grupa"), ("Search", "Meklēt"), ("Closed manually by web console", "Manuāli aizvērta tīmekļa konsole"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", "Saraksts"), ("Virtual display", "Virtuālais displejs"), ("Plug out all", "Atvienot visu"), + ("True color (4:4:4)", "Īstā krāsa (4:4:4)"), + ("Enable blocking user input", "Iespējot lietotāja ievades bloķēšanu"), + ("id_input_tip", "Varat ievadīt ID, tiešo IP vai domēnu ar portu (:).\nJa vēlaties piekļūt ierīcei citā serverī, lūdzu, pievienojiet servera adresi (@?key=), piemēram,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nJa vēlaties piekļūt ierīcei publiskajā serverī, lūdzu, ievadiet \"@public\", publiskajam serverim atslēga nav nepieciešama"), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 9f1125521..81794843e 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Klaar"), ("Established", "Opgezet"), ("connecting_status", "Verbinding maken met het RustDesk netwerk..."), - ("Enable Service", "Service Inschakelen"), - ("Start Service", "Start Service"), + ("Enable service", "Service Inschakelen"), + ("Start service", "Start service"), ("Service is running", "De service loopt."), ("Service is not running", "De service loopt niet"), ("not_ready_status", "Niet klaar, controleer de netwerkverbinding"), ("Control Remote Desktop", "Beheer Extern Bureaublad"), - ("Transfer File", "Bestand Overzetten"), + ("Transfer file", "Bestand Overzetten"), ("Connect", "Verbinden"), - ("Recent Sessions", "Recente Behandelingen"), - ("Address Book", "Adresboek"), + ("Recent sessions", "Recente Behandelingen"), + ("Address book", "Adresboek"), ("Confirmation", "Bevestiging"), - ("TCP Tunneling", "TCP Tunneling"), + ("TCP tunneling", "TCP tunneling"), ("Remove", "Verwijder"), ("Refresh random password", "Vernieuw willekeurig wachtwoord"), ("Set your own password", "Stel uw eigen wachtwoord in"), - ("Enable Keyboard/Mouse", "Toetsenbord/Muis Inschakelen"), - ("Enable Clipboard", "Klembord Inschakelen"), - ("Enable File Transfer", "Bestandsoverdracht Inschakelen"), - ("Enable TCP Tunneling", "TCP Tunneling Inschakelen"), + ("Enable keyboard/mouse", "Toetsenbord/Muis Inschakelen"), + ("Enable clipboard", "Klembord Inschakelen"), + ("Enable file transfer", "Bestandsoverdracht Inschakelen"), + ("Enable TCP tunneling", "TCP tunneling Inschakelen"), ("IP Whitelisting", "IP Witte Lijst"), ("ID/Relay Server", "ID/Relay Server"), - ("Import Server Config", "Importeer Serverconfiguratie"), + ("Import server config", "Importeer Serverconfiguratie"), ("Export Server Config", "Exporteer Serverconfiguratie"), ("Import server configuration successfully", "Importeren serverconfiguratie succesvol"), ("Export server configuration successfully", "Exporteren serverconfiguratie succesvol"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Accepteren"), ("Dismiss", "Afwijzen"), ("Disconnect", "Verbinding verbreken"), - ("Allow using keyboard and mouse", "Gebruik toetsenbord en muis toestaan"), - ("Allow using clipboard", "Gebruik klembord toestaan"), - ("Allow hearing sound", "Geluidsweergave toestaan"), - ("Allow file copy and paste", "Kopiëren en plakken van bestanden toestaan"), + ("Enable file copy and paste", "Kopiëren en plakken van bestanden toestaan"), ("Connected", "Verbonden"), ("Direct and encrypted connection", "Directe en versleutelde verbinding"), ("Relayed and encrypted connection", "Doorgeschakelde en versleutelde verbinding"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Aanmelden..."), ("Enable RDP session sharing", "Delen van RDP-sessie inschakelen"), ("Auto Login", "Automatisch Aanmelden"), - ("Enable Direct IP Access", "Directe IP-toegang inschakelen"), + ("Enable direct IP access", "Directe IP-toegang inschakelen"), ("Rename", "Naam wijzigen"), ("Space", "Spatie"), - ("Create Desktop Shortcut", "Snelkoppeling op bureaublad maken"), + ("Create desktop shortcut", "Snelkoppeling op bureaublad maken"), ("Change Path", "Pad wijzigen"), ("Create Folder", "Map Maken"), ("Please enter the folder name", "Geef de mapnaam op"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "RustDesk achtergronddienst behouden"), ("Ignore Battery Optimizations", "Negeer Batterij Optimalisaties"), ("android_open_battery_optimizations_tip", "Ga naar de volgende pagina met instellingen"), - ("Start on Boot", "Starten bij Opstarten"), + ("Start on boot", "Starten bij Opstarten"), ("Start the screen sharing service on boot, requires special permissions", "Start de schermdelings service bij het opstarten, vereist speciale rechten"), ("Connection not allowed", "Verbinding niet toegestaan"), ("Legacy mode", "Verouderde modus"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Gebruik permanent wachtwoord"), ("Use both passwords", "Gebruik beide wachtwoorden"), ("Set permanent password", "Stel permanent wachtwoord in"), - ("Enable Remote Restart", "Schakel Herstart op afstand in"), - ("Allow remote restart", "Opnieuw Opstarten op afstand toestaan"), - ("Restart Remote Device", "Apparaat op afstand herstarten"), + ("Enable remote restart", "Schakel Herstart op afstand in"), + ("Restart remote device", "Apparaat op afstand herstarten"), ("Are you sure you want to restart", "Weet u zeker dat u wilt herstarten"), - ("Restarting Remote Device", "Apparaat op afstand herstarten"), + ("Restarting remote device", "Apparaat op afstand herstarten"), ("remote_restarting_tip", "Apparaat op afstand wordt opnieuw opgestart, sluit dit bericht en maak na een ogenblik opnieuw verbinding met het permanente wachtwoord."), ("Copied", "Gekopieerd"), ("Exit Fullscreen", "Volledig Scherm sluiten"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Volg Systeem"), ("Enable hardware codec", "Hardware codec inschakelen"), ("Unlock Security Settings", "Beveiligingsinstellingen vrijgeven"), - ("Enable Audio", "Audio Inschakelen"), + ("Enable audio", "Audio Inschakelen"), ("Unlock Network Settings", "Netwerkinstellingen Vrijgeven"), ("Server", "Server"), ("Direct IP Access", "Directe IP toegang"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Wissel"), ("Start session recording", "Start de sessieopname"), ("Stop session recording", "Stop de sessieopname"), - ("Enable Recording Session", "Opnamesessie Activeren"), - ("Allow recording session", "Opnamesessie toestaan"), - ("Enable LAN Discovery", "LAN-detectie inschakelen"), - ("Deny LAN Discovery", "LAN-detectie Weigeren"), + ("Enable recording session", "Opnamesessie Activeren"), + ("Enable LAN discovery", "LAN-detectie inschakelen"), + ("Deny LAN discovery", "LAN-detectie Weigeren"), ("Write a message", "Schrijf een bericht"), ("Prompt", "Verzoek"), ("Please wait for confirmation of UAC...", "Wacht op bevestiging van UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wayland ondersteuning is slechts experimenteel. Gebruik alsjeblieft X11 als u onbeheerde toegang nodig hebt."), ("Right click to select tabs", "Rechts klikken om tabbladen te selecteren"), ("Skipped", "Overgeslagen"), - ("Add to Address Book", "Toevoegen aan Adresboek"), + ("Add to address book", "Toevoegen aan Adresboek"), ("Group", "Groep"), ("Search", "Zoek"), ("Closed manually by web console", "Handmatig gesloten door webconsole"), @@ -566,11 +561,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show displays as individual windows", "Beeldschermen weergeven als afzonderlijke vensters"), ("Use all my displays for the remote session", "Gebruik al mijn beeldschermen voor de externe sessie"), ("selinux_tip", ""), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), - ("Virtual display", ""), - ("Plug out all", ""), + ("Change view", "Weergave wijzigen"), + ("Big tiles", "Grote tegels"), + ("Small tiles", "Kleine tegels"), + ("List", "Overzicht"), + ("Virtual display", "Virtuele weergave"), + ("Plug out all", "Sluit alle"), + ("True color (4:4:4)", "Ware kleur (4:4:4)"), + ("Enable blocking user input", "Blokkeren van gebruikersinvoer inschakelen"), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 9c4101dfa..79b181bdb 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Gotowe"), ("Established", "Nawiązano"), ("connecting_status", "Łączenie"), - ("Enable Service", "Włącz usługę"), - ("Start Service", "Uruchom usługę"), + ("Enable service", "Włącz usługę"), + ("Start service", "Uruchom usługę"), ("Service is running", "Usługa uruchomiona"), ("Service is not running", "Usługa nie jest uruchomiona"), ("not_ready_status", "Brak gotowości"), ("Control Remote Desktop", "Połącz się z"), - ("Transfer File", "Transfer plików"), + ("Transfer file", "Transfer plików"), ("Connect", "Połącz"), - ("Recent Sessions", "Ostatnie sesje"), - ("Address Book", "Książka adresowa"), + ("Recent sessions", "Ostatnie sesje"), + ("Address book", "Książka adresowa"), ("Confirmation", "Potwierdzenie"), - ("TCP Tunneling", "Tunelowanie TCP"), + ("TCP tunneling", "Tunelowanie TCP"), ("Remove", "Usuń"), ("Refresh random password", "Odśwież losowe hasło"), ("Set your own password", "Ustaw własne hasło"), - ("Enable Keyboard/Mouse", "Włącz klawiaturę/mysz"), - ("Enable Clipboard", "Włącz schowek"), - ("Enable File Transfer", "Włącz transfer pliku"), - ("Enable TCP Tunneling", "Włącz tunelowanie TCP"), + ("Enable keyboard/mouse", "Włącz klawiaturę/mysz"), + ("Enable clipboard", "Włącz schowek"), + ("Enable file transfer", "Włącz transfer pliku"), + ("Enable TCP tunneling", "Włącz tunelowanie TCP"), ("IP Whitelisting", "Biała lista IP"), ("ID/Relay Server", "Serwer ID/Pośredniczący"), - ("Import Server Config", "Importuj konfigurację serwera"), + ("Import server config", "Importuj konfigurację serwera"), ("Export Server Config", "Eksportuj konfigurację serwera"), ("Import server configuration successfully", "Import konfiguracji serwera zakończono pomyślnie"), ("Export server configuration successfully", "Eksport konfiguracji serwera zakończono pomyślnie"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Akceptuj"), ("Dismiss", "Odrzuć"), ("Disconnect", "Rozłącz"), - ("Allow using keyboard and mouse", "Zezwalaj na używanie klawiatury i myszy"), - ("Allow using clipboard", "Zezwalaj na używanie schowka"), - ("Allow hearing sound", "Zezwól na transmisję audio"), - ("Allow file copy and paste", "Zezwalaj na kopiowanie i wklejanie plików"), + ("Enable file copy and paste", "Zezwalaj na kopiowanie i wklejanie plików"), ("Connected", "Połączony"), ("Direct and encrypted connection", "Połączenie bezpośrednie i szyfrowane"), ("Relayed and encrypted connection", "Połączenie pośrednie i szyfrowane"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Trwa logowanie..."), ("Enable RDP session sharing", "Włącz udostępnianie sesji RDP"), ("Auto Login", "Automatyczne logowanie"), - ("Enable Direct IP Access", "Włącz bezpośredni dostęp IP"), + ("Enable direct IP access", "Włącz bezpośredni dostęp IP"), ("Rename", "Zmień nazwę"), ("Space", "Przestrzeń"), - ("Create Desktop Shortcut", "Utwórz skrót na pulpicie"), + ("Create desktop shortcut", "Utwórz skrót na pulpicie"), ("Change Path", "Zmień ścieżkę"), ("Create Folder", "Utwórz folder"), ("Please enter the folder name", "Wpisz nazwę folderu"), @@ -274,7 +271,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Chat", "Czat"), ("Total", "Łącznie"), ("items", "elementów"), - ("Selected", "Zaznaczonych"), + ("Selected", "zaznaczonych"), ("Screen Capture", "Przechwytywanie ekranu"), ("Input Control", "Kontrola wejścia"), ("Audio Capture", "Przechwytywanie dźwięku"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Zachowaj usługę RustDesk w tle"), ("Ignore Battery Optimizations", "Ignoruj optymalizację baterii"), ("android_open_battery_optimizations_tip", "Jeśli chcesz wyłączyć tę funkcję, przejdź do następnej strony ustawień aplikacji RustDesk, znajdź i wprowadź [Bateria], odznacz [Bez ograniczeń]"), - ("Start on Boot", "Autostart"), + ("Start on boot", "Autostart"), ("Start the screen sharing service on boot, requires special permissions", "Uruchom usługę udostępniania ekranu podczas startu, wymaga specjalnych uprawnień"), ("Connection not allowed", "Połączenie niedozwolone"), ("Legacy mode", "Tryb kompatybilności wstecznej (legacy)"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Użyj hasła permanentnego"), ("Use both passwords", "Użyj obu haseł"), ("Set permanent password", "Ustaw hasło permanentne"), - ("Enable Remote Restart", "Włącz zdalne restartowanie"), - ("Allow remote restart", "Zezwól na zdalne restartowanie"), - ("Restart Remote Device", "Zrestartuj zdalne urządzenie"), + ("Enable remote restart", "Włącz zdalne restartowanie"), + ("Restart remote device", "Zrestartuj zdalne urządzenie"), ("Are you sure you want to restart", "Czy na pewno uruchomić ponownie"), - ("Restarting Remote Device", "Trwa restartowanie Zdalnego Urządzenia"), + ("Restarting remote device", "Trwa restartowanie Zdalnego Urządzenia"), ("remote_restarting_tip", "Trwa ponownie uruchomienie zdalnego urządzenia, zamknij ten komunikat i ponownie nawiąż za chwilę połączenie używając hasła permanentnego"), ("Copied", "Skopiowano"), ("Exit Fullscreen", "Wyłączyć tryb pełnoekranowy"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Zgodny z systemem"), ("Enable hardware codec", "Włącz akcelerację sprzętową kodeków"), ("Unlock Security Settings", "Odblokuj ustawienia zabezpieczeń"), - ("Enable Audio", "Włącz dźwięk"), + ("Enable audio", "Włącz dźwięk"), ("Unlock Network Settings", "Odblokuj ustawienia Sieciowe"), ("Server", "Serwer"), ("Direct IP Access", "Bezpośredni adres IP"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Zmień"), ("Start session recording", "Zacznij nagrywać sesję"), ("Stop session recording", "Zatrzymaj nagrywanie sesji"), - ("Enable Recording Session", "Włącz nagrywanie sesji"), - ("Allow recording session", "Zezwól na nagrywanie sesji"), - ("Enable LAN Discovery", "Włącz wykrywanie urządzenia w sieci LAN"), - ("Deny LAN Discovery", "Zablokuj wykrywanie urządzenia w sieci LAN"), + ("Enable recording session", "Włącz nagrywanie sesji"), + ("Enable LAN discovery", "Włącz wykrywanie urządzenia w sieci LAN"), + ("Deny LAN discovery", "Zablokuj wykrywanie urządzenia w sieci LAN"), ("Write a message", "Napisz wiadomość"), ("Prompt", "Monit"), ("Please wait for confirmation of UAC...", "Poczekaj na potwierdzenie uprawnień UAC"), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wsparcie dla Wayland jest niekompletne, użyj X11 jeżeli chcesz korzystać z dostępu nienadzorowanego"), ("Right click to select tabs", "Kliknij prawym przyciskiem myszy, aby wybrać zakładkę"), ("Skipped", "Pominięte"), - ("Add to Address Book", "Dodaj do Książki Adresowej"), + ("Add to address book", "Dodaj do Książki Adresowej"), ("Group", "Grupy"), ("Search", "Szukaj"), ("Closed manually by web console", "Zakończone manualnie z konsoli Web"), @@ -555,7 +550,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Check for software update on startup", "Sprawdź aktualizacje przy starcie programu"), ("upgrade_rustdesk_server_pro_to_{}_tip", "Proszę zaktualizować RustDesk Server Pro do wersji {} lub nowszej!"), ("pull_group_failed_tip", "Błąd odświeżania grup"), - ("Filter by intersection", ""), + ("Filter by intersection", "Filtruj wg przecięcia"), ("Remove wallpaper during incoming sessions", "Usuń tapetę podczas sesji przychodzących"), ("Test", "Test"), ("switch_display_elevated_connections_tip", "Przełączanie na ekran inny niż główny nie jest obsługiwane przy podniesionych uprawnieniach, gdy istnieje wiele połączeń. Jeśli chcesz sterować wieloma ekranami, należy zainstalować program."), @@ -564,13 +559,16 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("elevated_switch_display_msg", "Przełącz się na ekran główny, ponieważ wyświetlanie kilku ekranów nie jest obsługiwane przy podniesionych uprawnieniach."), ("Open in new window", "Otwórz w nowym oknie"), ("Show displays as individual windows", "Pokaż ekrany w osobnych oknach"), - ("Use all my displays for the remote session", ""), - ("selinux_tip", ""), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), - ("Virtual display", ""), - ("Plug out all", ""), + ("Use all my displays for the remote session", "Użyj wszystkich moich ekranów do zdalnej sesji"), + ("selinux_tip", "SELinux jest włączony na Twoim urządzeniu, co może przeszkodzić w uruchomieniu RustDesk po stronie kontrolowanej."), + ("Change view", "Zmień widok"), + ("Big tiles", "Duże kafelki"), + ("Small tiles", "Małe kafelki"), + ("List", "Lista"), + ("Virtual display", "Witualne ekrany"), + ("Plug out all", "Odłącz wszystko"), + ("True color (4:4:4)", "True color (4:4:4)"), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 966a57c04..adff02864 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Pronto"), ("Established", "Estabelecido"), ("connecting_status", "A ligar à rede do RustDesk..."), - ("Enable Service", "Activar Serviço"), - ("Start Service", "Iniciar Serviço"), + ("Enable service", "Activar Serviço"), + ("Start service", "Iniciar Serviço"), ("Service is running", "Serviço está activo"), ("Service is not running", "Serviço não está activo"), ("not_ready_status", "Indisponível. Por favor verifique a sua ligação"), ("Control Remote Desktop", "Controle o Ambiente de Trabalho à distância"), - ("Transfer File", "Transferir Ficheiro"), + ("Transfer file", "Transferir Ficheiro"), ("Connect", "Ligar"), - ("Recent Sessions", "Sessões recentes"), - ("Address Book", "Lista de Endereços"), + ("Recent sessions", "Sessões recentes"), + ("Address book", "Lista de Endereços"), ("Confirmation", "Confirmação"), - ("TCP Tunneling", "Túnel TCP"), + ("TCP tunneling", "Túnel TCP"), ("Remove", "Remover"), ("Refresh random password", "Actualizar palavra-chave"), ("Set your own password", "Configure a sua palavra-passe"), - ("Enable Keyboard/Mouse", "Activar Teclado/Rato"), - ("Enable Clipboard", "Activar Área de Transferência"), - ("Enable File Transfer", "Activar Transferência de Ficheiros"), - ("Enable TCP Tunneling", "Activar Túnel TCP"), + ("Enable keyboard/mouse", "Activar Teclado/Rato"), + ("Enable clipboard", "Activar Área de Transferência"), + ("Enable file transfer", "Activar Transferência de Ficheiros"), + ("Enable TCP tunneling", "Activar Túnel TCP"), ("IP Whitelisting", "Whitelist de IP"), ("ID/Relay Server", "Servidor ID/Relay"), - ("Import Server Config", "Importar Configuração do Servidor"), + ("Import server config", "Importar Configuração do Servidor"), ("Export Server Config", "Exportar Configuração do Servidor"), ("Import server configuration successfully", "Configuração do servidor importada com sucesso"), ("Export server configuration successfully", ""), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Aceitar"), ("Dismiss", "Dispensar"), ("Disconnect", "Desconectar"), - ("Allow using keyboard and mouse", "Permitir o uso de teclado e rato"), - ("Allow using clipboard", "Permitir o uso da área de transferência"), - ("Allow hearing sound", "Permitir ouvir som"), - ("Allow file copy and paste", "Permitir copiar e mover ficheiros"), + ("Enable file copy and paste", "Permitir copiar e mover ficheiros"), ("Connected", "Ligado"), ("Direct and encrypted connection", "Ligação directa e encriptada"), ("Relayed and encrypted connection", "Ligação via relay e encriptada"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "A efectuar Login..."), ("Enable RDP session sharing", "Activar partilha de sessão RDP"), ("Auto Login", "Login Automático (Somente válido se você activou \"Bloquear após o fim da sessão\")"), - ("Enable Direct IP Access", "Activar Acesso IP Directo"), + ("Enable direct IP access", "Activar Acesso IP Directo"), ("Rename", "Renomear"), ("Space", "Espaço"), - ("Create Desktop Shortcut", "Criar Atalho no Ambiente de Trabalho"), + ("Create desktop shortcut", "Criar Atalho no Ambiente de Trabalho"), ("Change Path", "Alterar Caminho"), ("Create Folder", "Criar Diretório"), ("Please enter the folder name", "Por favor introduza o nome do diretório"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Manter o serviço RustDesk em funcionamento"), ("Ignore Battery Optimizations", "Ignorar optimizações de Bateria"), ("android_open_battery_optimizations_tip", ""), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Ligação não autorizada"), ("Legacy mode", ""), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Utilizar palavra-chave permanente"), ("Use both passwords", "Utilizar ambas as palavras-chave"), ("Set permanent password", "Definir palavra-chave permanente"), - ("Enable Remote Restart", "Activar reiniciar remoto"), - ("Allow remote restart", "Permitir reiniciar remoto"), - ("Restart Remote Device", "Reiniciar Dispositivo Remoto"), + ("Enable remote restart", "Activar reiniciar remoto"), + ("Restart remote device", "Reiniciar Dispositivo Remoto"), ("Are you sure you want to restart", "Tem a certeza que pretende reiniciar"), - ("Restarting Remote Device", "A reiniciar sistema remoto"), + ("Restarting remote device", "A reiniciar sistema remoto"), ("remote_restarting_tip", ""), ("Copied", ""), ("Exit Fullscreen", "Sair da tela cheia"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", ""), ("Enable hardware codec", ""), ("Unlock Security Settings", ""), - ("Enable Audio", ""), + ("Enable audio", ""), ("Unlock Network Settings", ""), ("Server", ""), ("Direct IP Access", ""), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", ""), ("Start session recording", ""), ("Stop session recording", ""), - ("Enable Recording Session", ""), - ("Allow recording session", ""), - ("Enable LAN Discovery", ""), - ("Deny LAN Discovery", ""), + ("Enable recording session", ""), + ("Enable LAN discovery", ""), + ("Deny LAN discovery", ""), ("Write a message", ""), ("Prompt", ""), ("Please wait for confirmation of UAC...", ""), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 5caa10268..c1f589113 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Pronto"), ("Established", "Estabelecido"), ("connecting_status", "Conectando à rede do RustDesk..."), - ("Enable Service", "Habilitar Serviço"), - ("Start Service", "Iniciar Serviço"), + ("Enable service", "Habilitar Serviço"), + ("Start service", "Iniciar Serviço"), ("Service is running", "Serviço está em execução"), ("Service is not running", "Serviço não está em execução"), ("not_ready_status", "Não está pronto. Por favor verifique sua conexão"), ("Control Remote Desktop", "Controle um Computador Remoto"), - ("Transfer File", "Transferir Arquivos"), + ("Transfer file", "Transferir Arquivos"), ("Connect", "Conectar"), - ("Recent Sessions", "Sessões Recentes"), - ("Address Book", "Lista de Endereços"), + ("Recent sessions", "Sessões Recentes"), + ("Address book", "Lista de Endereços"), ("Confirmation", "Confirmação"), - ("TCP Tunneling", "Tunelamento TCP"), + ("TCP tunneling", "Tunelamento TCP"), ("Remove", "Remover"), ("Refresh random password", "Atualizar senha aleatória"), ("Set your own password", "Configure sua própria senha"), - ("Enable Keyboard/Mouse", "Habilitar teclado/mouse"), - ("Enable Clipboard", "Habilitar Área de Transferência"), - ("Enable File Transfer", "Habilitar Transferência de Arquivos"), - ("Enable TCP Tunneling", "Habilitar Tunelamento TCP"), + ("Enable keyboard/mouse", "Habilitar teclado/mouse"), + ("Enable clipboard", "Habilitar Área de Transferência"), + ("Enable file transfer", "Habilitar Transferência de Arquivos"), + ("Enable TCP tunneling", "Habilitar Tunelamento TCP"), ("IP Whitelisting", "Lista de IPs Confiáveis"), ("ID/Relay Server", "Servidor ID/Relay"), - ("Import Server Config", "Importar Configuração do Servidor"), + ("Import server config", "Importar Configuração do Servidor"), ("Export Server Config", "Exportar Configuração do Servidor"), ("Import server configuration successfully", "Configuração do servidor importada com sucesso"), ("Export server configuration successfully", "Configuração do servidor exportada com sucesso"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Aceitar"), ("Dismiss", "Dispensar"), ("Disconnect", "Desconectar"), - ("Allow using keyboard and mouse", "Permitir o uso de teclado e mouse"), - ("Allow using clipboard", "Permitir o uso da área de transferência"), - ("Allow hearing sound", "Permitir escutar som"), - ("Allow file copy and paste", "Permitir copiar e colar arquivos"), + ("Enable file copy and paste", "Permitir copiar e colar arquivos"), ("Connected", "Conectado"), ("Direct and encrypted connection", "Conexão direta e criptografada"), ("Relayed and encrypted connection", "Conexão via relay e criptografada"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Fazendo Login..."), ("Enable RDP session sharing", "Habilitar compartilhamento de sessão RDP"), ("Auto Login", "Login Automático (Somente válido se você habilitou \"Bloquear após o fim da sessão\")"), - ("Enable Direct IP Access", "Habilitar Acesso IP Direto"), + ("Enable direct IP access", "Habilitar Acesso IP Direto"), ("Rename", "Renomear"), ("Space", "Espaço"), - ("Create Desktop Shortcut", "Criar Atalho na Área de Trabalho"), + ("Create desktop shortcut", "Criar Atalho na Área de Trabalho"), ("Change Path", "Alterar Caminho"), ("Create Folder", "Criar Diretório"), ("Please enter the folder name", "Por favor informe o nome do diretório"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Manter o serviço do RustDesk executando em segundo plano"), ("Ignore Battery Optimizations", "Ignorar otimizações de bateria"), ("android_open_battery_optimizations_tip", "Abrir otimizações de bateria"), - ("Start on Boot", "Iniciar na Inicialização"), + ("Start on boot", "Iniciar na Inicialização"), ("Start the screen sharing service on boot, requires special permissions", "Inicie o serviço de compartilhamento de tela na inicialização, requer permissões especiais"), ("Connection not allowed", "Conexão não permitida"), ("Legacy mode", "Modo legado"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Utilizar senha permanente"), ("Use both passwords", "Utilizar ambas as senhas"), ("Set permanent password", "Configurar senha permanente"), - ("Enable Remote Restart", "Habilitar Reinicialização Remota"), - ("Allow remote restart", "Permitir reinicialização remota"), - ("Restart Remote Device", "Reiniciar Dispositivo Remoto"), + ("Enable remote restart", "Habilitar Reinicialização Remota"), + ("Restart remote device", "Reiniciar Dispositivo Remoto"), ("Are you sure you want to restart", "Você tem certeza que deseja reiniciar?"), - ("Restarting Remote Device", "Reiniciando dispositivo remoto"), + ("Restarting remote device", "Reiniciando dispositivo remoto"), ("remote_restarting_tip", "O dispositivo remoto está reiniciando, feche esta caixa de mensagem e reconecte com a senha permanente depois de um tempo"), ("Copied", "Copiado"), ("Exit Fullscreen", "Sair da Tela Cheia"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Seguir sistema"), ("Enable hardware codec", "Habilitar codec de hardware"), ("Unlock Security Settings", "Desbloquear configurações de segurança"), - ("Enable Audio", "Habilitar áudio"), + ("Enable audio", "Habilitar áudio"), ("Unlock Network Settings", "Desbloquear configurações de rede"), ("Server", "Servidor"), ("Direct IP Access", "Acesso direto por IP"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Alterar"), ("Start session recording", "Iniciar gravação da sessão"), ("Stop session recording", "Parar gravação da sessão"), - ("Enable Recording Session", "Habilitar gravação da sessão"), - ("Allow recording session", "Permitir gravação da sessão"), - ("Enable LAN Discovery", "Habilitar descoberta da LAN"), - ("Deny LAN Discovery", "Negar descoberta da LAN"), + ("Enable recording session", "Habilitar gravação da sessão"), + ("Enable LAN discovery", "Habilitar descoberta da LAN"), + ("Deny LAN discovery", "Negar descoberta da LAN"), ("Write a message", "Escrever uma mensagem"), ("Prompt", "Prompt de comando"), ("Please wait for confirmation of UAC...", "Favor aguardar a confirmação do UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "O suporte ao Wayland está em estágio experimental, use o X11 se precisar de acesso autônomo."), ("Right click to select tabs", "Clique com o botão direito para selecionar as guias"), ("Skipped", "Ignorado"), - ("Add to Address Book", "Adicionar ao livro de endereços"), + ("Add to address book", "Adicionar ao livro de endereços"), ("Group", "Grupo"), ("Search", "Buscar"), ("Closed manually by web console", "Fechado manualmente pelo console da web"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 1794ccfc0..1e22e09f6 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Pregătit"), ("Established", "Stabilit"), ("connecting_status", "În curs de conectare la rețeaua RustDesk..."), - ("Enable Service", "Activează serviciul"), - ("Start Service", "Pornește serviciul"), + ("Enable service", "Activează serviciul"), + ("Start service", "Pornește serviciul"), ("Service is running", "Serviciul rulează..."), ("Service is not running", "Serviciul nu funcționează"), ("not_ready_status", "Nepregătit. Verifică conexiunea la rețea."), ("Control Remote Desktop", "Controlează desktopul la distanță"), - ("Transfer File", "Transferă fișiere"), + ("Transfer file", "Transferă fișiere"), ("Connect", "Conectează-te"), - ("Recent Sessions", "Sesiuni recente"), - ("Address Book", "Agendă"), + ("Recent sessions", "Sesiuni recente"), + ("Address book", "Agendă"), ("Confirmation", "Confirmare"), - ("TCP Tunneling", "Tunel TCP"), + ("TCP tunneling", "Tunel TCP"), ("Remove", "Elimină"), ("Refresh random password", "Actualizează parola aleatorie"), ("Set your own password", "Setează propria parolă"), - ("Enable Keyboard/Mouse", "Activează control tastatură/mouse"), - ("Enable Clipboard", "Activează clipboard"), - ("Enable File Transfer", "Activează transferul de fișiere"), - ("Enable TCP Tunneling", "Activează tunelul TCP"), + ("Enable keyboard/mouse", "Activează control tastatură/mouse"), + ("Enable clipboard", "Activează clipboard"), + ("Enable file transfer", "Activează transferul de fișiere"), + ("Enable TCP tunneling", "Activează tunelul TCP"), ("IP Whitelisting", "Listă de IP-uri autorizate"), ("ID/Relay Server", "Server de ID/retransmisie"), - ("Import Server Config", "Importă configurație server"), + ("Import server config", "Importă configurație server"), ("Export Server Config", "Exportă configurație server"), ("Import server configuration successfully", "Configurație server importată cu succes"), ("Export server configuration successfully", "Configurație server exportată cu succes"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Acceptă"), ("Dismiss", "Respinge"), ("Disconnect", "Deconectează-te"), - ("Allow using keyboard and mouse", "Permite utilizarea tastaturii și a mouse-ului"), - ("Allow using clipboard", "Permite utilizarea clipboardului"), - ("Allow hearing sound", "Permite auzirea sunetului"), - ("Allow file copy and paste", "Permite copierea și lipirea fișierelor"), + ("Enable file copy and paste", "Permite copierea și lipirea fișierelor"), ("Connected", "Conectat"), ("Direct and encrypted connection", "Conexiune directă criptată"), ("Relayed and encrypted connection", "Conexiune retransmisă criptată"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Se conectează..."), ("Enable RDP session sharing", "Activează partajarea sesiunii RDP"), ("Auto Login", "Conectare automată (validă doar dacă opțiunea Blocare după deconectare este selectată)"), - ("Enable Direct IP Access", "Activează accesul direct cu IP"), + ("Enable direct IP access", "Activează accesul direct cu IP"), ("Rename", "Redenumește"), ("Space", "Spațiu"), - ("Create Desktop Shortcut", "Creează comandă rapidă de desktop"), + ("Create desktop shortcut", "Creează comandă rapidă de desktop"), ("Change Path", "Schimbă calea"), ("Create Folder", "Creează folder"), ("Please enter the folder name", "Introdu numele folderului"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Rulează serviciul RustDesk în fundal"), ("Ignore Battery Optimizations", "Ignoră optimizările de baterie"), ("android_open_battery_optimizations_tip", "Pentru dezactivarea acestei funcții, accesează setările aplicației RustDesk, deschide secțiunea [Baterie] și deselectează [Fără restricții]."), - ("Start on Boot", "Pornește la boot"), + ("Start on boot", "Pornește la boot"), ("Start the screen sharing service on boot, requires special permissions", "Pornește serviciul de partajare a ecranului la boot; necesită permisiuni speciale"), ("Connection not allowed", "Conexiune neautoriztă"), ("Legacy mode", "Mod legacy"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Folosește parola permanentă"), ("Use both passwords", "Folosește ambele programe"), ("Set permanent password", "Setează parola permanentă"), - ("Enable Remote Restart", "Activează repornirea la distanță"), - ("Allow remote restart", "Permite repornirea la distanță"), - ("Restart Remote Device", "Repornește dispozivul la distanță"), + ("Enable remote restart", "Activează repornirea la distanță"), + ("Restart remote device", "Repornește dispozivul la distanță"), ("Are you sure you want to restart", "Sigur vrei să repornești dispozitivul?"), - ("Restarting Remote Device", "Se repornește dispozitivul la distanță"), + ("Restarting remote device", "Se repornește dispozitivul la distanță"), ("remote_restarting_tip", "Dispozitivul este în curs de repornire. Închide acest mesaj și reconectează-te cu parola permanentă după un timp."), ("Copied", "Copiat"), ("Exit Fullscreen", "Ieși din modul ecran complet"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Temă sistem"), ("Enable hardware codec", "Activează codec hardware"), ("Unlock Security Settings", "Deblochează setările de securitate"), - ("Enable Audio", "Activează audio"), + ("Enable audio", "Activează audio"), ("Unlock Network Settings", "Deblochează setările de rețea"), ("Server", "Server"), ("Direct IP Access", "Acces direct IP"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Modifică"), ("Start session recording", "Începe înregistrarea"), ("Stop session recording", "Oprește înregistrarea"), - ("Enable Recording Session", "Activează înregistrarea sesiunii"), - ("Allow recording session", "Permite înregistrarea sesiunii"), - ("Enable LAN Discovery", "Activează descoperirea LAN"), - ("Deny LAN Discovery", "Interzice descoperirea LAN"), + ("Enable recording session", "Activează înregistrarea sesiunii"), + ("Enable LAN discovery", "Activează descoperirea LAN"), + ("Deny LAN discovery", "Interzice descoperirea LAN"), ("Write a message", "Scrie un mesaj"), ("Prompt", "Prompt"), ("Please wait for confirmation of UAC...", "Așteaptă confirmarea CCU..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wayland este acceptat doar într-o formă experimentală. Folosește X11 dacă nu ai nevoie de acces supravegheat."), ("Right click to select tabs", "Dă clic dreapta pentru a selecta file"), ("Skipped", "Ignorat"), - ("Add to Address Book", "Adaugă la agendă"), + ("Add to address book", "Adaugă la agendă"), ("Group", "Grup"), ("Search", "Caută"), ("Closed manually by web console", "Conexiune închisă manual de consola web"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index f43df683c..dc3b9ba34 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Готово"), ("Established", "Установлено"), ("connecting_status", "Подключение к сети RustDesk..."), - ("Enable Service", "Включить службу"), - ("Start Service", "Запустить службу"), + ("Enable service", "Включить службу"), + ("Start service", "Запустить службу"), ("Service is running", "Служба запущена"), ("Service is not running", "Служба не запущена"), ("not_ready_status", "Не подключено. Проверьте соединение."), ("Control Remote Desktop", "Управление удалённым рабочим столом"), - ("Transfer File", "Передача файла"), + ("Transfer file", "Передача файла"), ("Connect", "Подключиться"), - ("Recent Sessions", "Последние сеансы"), - ("Address Book", "Адресная книга"), + ("Recent sessions", "Последние сеансы"), + ("Address book", "Адресная книга"), ("Confirmation", "Подтверждение"), - ("TCP Tunneling", "TCP-туннелирование"), + ("TCP tunneling", "TCP-туннелирование"), ("Remove", "Удалить"), ("Refresh random password", "Обновить случайный пароль"), ("Set your own password", "Установить свой пароль"), - ("Enable Keyboard/Mouse", "Включить клавиатуру/мышь"), - ("Enable Clipboard", "Включить буфер обмена"), - ("Enable File Transfer", "Включить передачу файлов"), - ("Enable TCP Tunneling", "Включить туннелирование TCP"), + ("Enable keyboard/mouse", "Включить клавиатуру/мышь"), + ("Enable clipboard", "Включить буфер обмена"), + ("Enable file transfer", "Включить передачу файлов"), + ("Enable TCP tunneling", "Включить туннелирование TCP"), ("IP Whitelisting", "Список разрешённых IP-адресов"), ("ID/Relay Server", "ID/Ретранслятор"), - ("Import Server Config", "Импортировать конфигурацию сервера"), + ("Import server config", "Импортировать конфигурацию сервера"), ("Export Server Config", "Экспортировать конфигурацию сервера"), ("Import server configuration successfully", "Конфигурация сервера успешно импортирована"), ("Export server configuration successfully", "Конфигурация сервера успешно экспортирована"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Принять"), ("Dismiss", "Отклонить"), ("Disconnect", "Отключить"), - ("Allow using keyboard and mouse", "Разрешить использование клавиатуры и мыши"), - ("Allow using clipboard", "Разрешить использование буфера обмена"), - ("Allow hearing sound", "Разрешить передачу звука"), - ("Allow file copy and paste", "Разрешить копирование и вставку файлов"), + ("Enable file copy and paste", "Разрешить копирование и вставку файлов"), ("Connected", "Подключено"), ("Direct and encrypted connection", "Прямое и зашифрованное подключение"), ("Relayed and encrypted connection", "Ретранслируемое и зашифрованное подключение"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Вход..."), ("Enable RDP session sharing", "Включить общий доступ к сеансу RDP"), ("Auto Login", "Автоматический вход (действителен только если вы установили \"Завершение пользовательского сеанса после завершения удалённого подключения\""), - ("Enable Direct IP Access", "Включить прямой IP-доступ"), + ("Enable direct IP access", "Включить прямой IP-доступ"), ("Rename", "Переименовать"), ("Space", "Место"), - ("Create Desktop Shortcut", "Создать ярлык на рабочем столе"), + ("Create desktop shortcut", "Создать ярлык на рабочем столе"), ("Change Path", "Изменить путь"), ("Create Folder", "Создать папку"), ("Please enter the folder name", "Введите имя папки"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Держать в фоне службу RustDesk"), ("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"), ("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек"), - ("Start on Boot", "Начинать при загрузке"), + ("Start on boot", "Начинать при загрузке"), ("Start the screen sharing service on boot, requires special permissions", "Запускать службу демонстрации экрана при загрузке (требуются специальные разрешения)"), ("Connection not allowed", "Подключение не разрешено"), ("Legacy mode", "Устаревший режим"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Использовать постоянный пароль"), ("Use both passwords", "Использовать оба пароля"), ("Set permanent password", "Установить постоянный пароль"), - ("Enable Remote Restart", "Включить удалённый перезапуск"), - ("Allow remote restart", "Разрешить удалённый перезапуск"), - ("Restart Remote Device", "Перезапустить удалённое устройство"), + ("Enable remote restart", "Включить удалённый перезапуск"), + ("Restart remote device", "Перезапустить удалённое устройство"), ("Are you sure you want to restart", "Вы уверены, что хотите выполнить перезапуск?"), - ("Restarting Remote Device", "Перезагрузка удалённого устройства"), + ("Restarting remote device", "Перезагрузка удалённого устройства"), ("remote_restarting_tip", "Удалённое устройство перезапускается. Закройте это сообщение и через некоторое время переподключитесь, используя постоянный пароль."), ("Copied", "Скопировано"), ("Exit Fullscreen", "Выйти из полноэкранного режима"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Системная"), ("Enable hardware codec", "Использовать аппаратный кодек"), ("Unlock Security Settings", "Разблокировать настройки безопасности"), - ("Enable Audio", "Включить звук"), + ("Enable audio", "Включить звук"), ("Unlock Network Settings", "Разблокировать сетевые настройки"), ("Server", "Сервер"), ("Direct IP Access", "Прямой IP-доступ"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Изменить"), ("Start session recording", "Начать запись сеанса"), ("Stop session recording", "Остановить запись сеанса"), - ("Enable Recording Session", "Включить запись сеанса"), - ("Allow recording session", "Разрешить запись сеанса"), - ("Enable LAN Discovery", "Включить обнаружение в локальной сети"), - ("Deny LAN Discovery", "Запретить обнаружение в локальной сети"), + ("Enable recording session", "Включить запись сеанса"), + ("Enable LAN discovery", "Включить обнаружение в локальной сети"), + ("Deny LAN discovery", "Запретить обнаружение в локальной сети"), ("Write a message", "Написать сообщение"), ("Prompt", "Подсказка"), ("Please wait for confirmation of UAC...", "Дождитесь подтверждения UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Поддержка Wayland находится на экспериментальной стадии, используйте X11, если вам требуется автоматический доступ."), ("Right click to select tabs", "Выбор вкладок щелчком правой кнопки мыши"), ("Skipped", "Пропущено"), - ("Add to Address Book", "Добавить в адресную книгу"), + ("Add to address book", "Добавить в адресную книгу"), ("Group", "Группа"), ("Search", "Поиск"), ("Closed manually by web console", "Закрыто вручную через веб-консоль"), @@ -570,7 +565,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Big tiles", "Большие значки"), ("Small tiles", "Маленькие значки"), ("List", "Список"), - ("Virtual display", ""), - ("Plug out all", ""), + ("Virtual display", "Виртуальный дисплей"), + ("Plug out all", "Отключить все"), + ("True color (4:4:4)", "Истинный цвет (4:4:4)"), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 9f5b3084e..37900069f 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -8,52 +8,52 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Pripravené"), ("Established", "Nadviazané"), ("connecting_status", "Pripájam sa na RusDesk server..."), - ("Enable Service", "Povoliť službu"), - ("Start Service", "Spustiť službu"), + ("Enable service", "Povoliť službu"), + ("Start service", "Spustiť službu"), ("Service is running", "Služba je aktívna"), ("Service is not running", "Služba je vypnutá"), ("not_ready_status", "Nepripravené. Skontrolujte svoje sieťové pripojenie."), ("Control Remote Desktop", "Ovládať vzdialenú plochu"), - ("Transfer File", "Prenos súborov"), + ("Transfer file", "Prenos súborov"), ("Connect", "Pripojiť"), - ("Recent Sessions", "Nedávne pripojenie"), - ("Address Book", "Adresár kontaktov"), + ("Recent sessions", "Nedávne pripojenie"), + ("Address book", "Adresár kontaktov"), ("Confirmation", "Potvrdenie"), - ("TCP Tunneling", "TCP tunelovanie"), + ("TCP tunneling", "TCP tunelovanie"), ("Remove", "Odstrániť"), ("Refresh random password", "Aktualizovať náhodné heslo"), ("Set your own password", "Nastavte si svoje vlastné heslo"), - ("Enable Keyboard/Mouse", "Povoliť klávesnicu/myš"), - ("Enable Clipboard", "Povoliť schránku"), - ("Enable File Transfer", "Povoliť prenos súborov"), - ("Enable TCP Tunneling", "Povoliť TCP tunelovanie"), + ("Enable keyboard/mouse", "Povoliť klávesnicu/myš"), + ("Enable clipboard", "Povoliť schránku"), + ("Enable file transfer", "Povoliť prenos súborov"), + ("Enable TCP tunneling", "Povoliť TCP tunelovanie"), ("IP Whitelisting", "Zoznam povolených IP adries"), ("ID/Relay Server", "ID/Prepojovací server"), - ("Import Server Config", "Importovať konfiguráciu servera"), - ("Export Server Config", ""), + ("Import server config", "Importovať konfiguráciu servera"), + ("Export Server Config", "Exportovať konfiguráciu servera"), ("Import server configuration successfully", "Konfigurácia servera bola úspešne importovaná"), - ("Export server configuration successfully", ""), + ("Export server configuration successfully", "Konfigurácia servera bola úspešne exportovaná"), ("Invalid server configuration", "Neplatná konfigurácia servera"), ("Clipboard is empty", "Schránka je prázdna"), ("Stop service", "Zastaviť službu"), ("Change ID", "Zmeniť ID"), - ("Your new ID", ""), - ("length %min% to %max%", ""), - ("starts with a letter", ""), - ("allowed characters", ""), + ("Your new ID", "Vaše nové ID"), + ("length %min% to %max%", "dĺžka medzi %min% a %max%"), + ("starts with a letter", "začína písmenom"), + ("allowed characters", "povolené znaky"), ("id_change_tip", "Povolené sú len znaky a-z, A-Z, 0-9 a _ (podčiarkovník). Prvý znak musí byť a-z, A-Z. Dĺžka musí byť medzi 6 a 16 znakmi."), ("Website", "Webová stránka"), ("About", "O RustDesk"), - ("Slogan_tip", ""), - ("Privacy Statement", ""), + ("Slogan_tip", "Stvorené srdcom v tomto chaotickom svete!"), + ("Privacy Statement", "Vyhlásenie o ochrane osobných údajov"), ("Mute", "Stíšiť"), - ("Build Date", ""), - ("Version", ""), - ("Home", ""), + ("Build Date", "Dátum zostavenia"), + ("Version", "Verzia"), + ("Home", "Domov"), ("Audio Input", "Zvukový vstup"), - ("Enhancements", ""), - ("Hardware Codec", ""), - ("Adaptive bitrate", ""), + ("Enhancements", "Vylepšenia"), + ("Hardware Codec", "Hardvérový kodek"), + ("Adaptive bitrate", "Adaptívny dátový tok"), ("ID Server", "ID server"), ("Relay Server", "Prepojovací server"), ("API Server", "API server"), @@ -98,8 +98,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Delete", "Zmazať"), ("Properties", "Vlastnosti"), ("Multi Select", "Viacnásobný výber"), - ("Select All", ""), - ("Unselect All", ""), + ("Select All", "Vybrať všetko"), + ("Unselect All", "Zrušiť výber všetkého"), ("Empty Directory", "Prázdny adresár"), ("Not an empty directory", "Nie prázdny adresár"), ("Are you sure you want to delete this file?", "Ste si istý, že chcete zmazať tento súbor?"), @@ -125,9 +125,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Good image quality", "Dobrá kvalita obrazu"), ("Balanced", "Vyvážené"), ("Optimize reaction time", "Optimalizované pre čas odozvy"), - ("Custom", ""), + ("Custom", "Vlastné"), ("Show remote cursor", "Zobrazovať vzdialený ukazovateľ myši"), - ("Show quality monitor", ""), + ("Show quality monitor", "Zobraziť monitor kvality"), ("Disable clipboard", "Vypnúť schránku"), ("Lock after session end", "Po skončení uzamknúť plochu"), ("Insert", "Vložiť"), @@ -170,8 +170,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Action", "Akcia"), ("Add", "Pridať"), ("Local Port", "Lokálny port"), - ("Local Address", ""), - ("Change Local Port", ""), + ("Local Address", "Lokálna adresa"), + ("Change Local Port", "Zmena lokálneho portu"), ("setup_server_tip", "Pre zrýchlenie pripojenia si nainštalujte svoj vlastný server"), ("Too short, at least 6 characters.", "Príliš krátke, vyžaduje sa aspoň 6 znakov."), ("The confirmation is not identical.", "Potvrdenie nie je zhodné."), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Prijať"), ("Dismiss", "Odmietnuť"), ("Disconnect", "Odpojiť"), - ("Allow using keyboard and mouse", "Povoliť používanie klávesnice a myši"), - ("Allow using clipboard", "Povoliť používanie schránky"), - ("Allow hearing sound", "Povoliť zvuky"), - ("Allow file copy and paste", "Povoliť kopírovanie a vkladanie súborov"), + ("Enable file copy and paste", "Povoliť kopírovanie a vkladanie súborov"), ("Connected", "Pripojené"), ("Direct and encrypted connection", "Priame a šifrované spojenie"), ("Relayed and encrypted connection", "Sprostredkované a šifrované spojenie"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Prihlasovanie sa...."), ("Enable RDP session sharing", "Povoliť zdieľanie RDP relácie"), ("Auto Login", "Automatické prihlásenie"), - ("Enable Direct IP Access", "Povoliť priame pripojenie cez IP"), + ("Enable direct IP access", "Povoliť priame pripojenie cez IP"), ("Rename", "Premenovať"), ("Space", "Medzera"), - ("Create Desktop Shortcut", "Vytvoriť zástupcu na ploche"), + ("Create desktop shortcut", "Vytvoriť zástupcu na ploche"), ("Change Path", "Zmeniť adresár"), ("Create Folder", "Vytvoriť adresár"), ("Please enter the folder name", "Zadajte názov adresára"), @@ -206,22 +203,22 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Reboot required", "Vyžaduje sa reštart"), ("Unsupported display server", "Nepodporovaný zobrazovací (display) server"), ("x11 expected", "očakáva sa x11"), - ("Port", ""), + ("Port", "Port"), ("Settings", "Nastavenia"), ("Username", "Uživateľské meno"), ("Invalid port", "Neplatný port"), ("Closed manually by the peer", "Manuálne ukončené opačnou stranou pripojenia"), ("Enable remote configuration modification", "Povoliť zmeny konfigurácie zo vzdialeného PC"), ("Run without install", "Spustiť bez inštalácie"), - ("Connect via relay", ""), + ("Connect via relay", "Pripojenie prostredníctvom relay servera"), ("Always connect via relay", "Vždy pripájať cez prepájací server"), ("whitelist_tip", "Len vymenované IP adresy majú oprávnenie sa pripojiť k vzdialenej správe"), ("Login", "Prihlásenie"), - ("Verify", ""), - ("Remember me", ""), - ("Trust this device", ""), - ("Verification code", ""), - ("verification_tip", ""), + ("Verify", "Overiť"), + ("Remember me", "Zapamätať si"), + ("Trust this device", "Dôverovať tomuto zariadeniu"), + ("Verification code", "Overovací kód"), + ("verification_tip", "Na vašu registrovanú e-mailovú adresu bol odoslaný overovací kód, zadajte ho a pokračujte v prihlasovaní."), ("Logout", "Odhlásenie"), ("Tags", "Štítky"), ("Search ID", "Hľadať ID"), @@ -233,7 +230,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Username missed", "Chýba užívateľské meno"), ("Password missed", "Chýba heslo"), ("Wrong credentials", "Nesprávne prihlasovacie údaje"), - ("The verification code is incorrect or has expired", ""), + ("The verification code is incorrect or has expired", "Overovací kód je nesprávny alebo jeho platnosť vypršala"), ("Edit Tag", "Upraviť štítok"), ("Forget Password", "Zabudnúť heslo"), ("Favorites", "Obľúbené"), @@ -289,9 +286,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("android_service_will_start_tip", "Zapnutie \"Zachytávanie obsahu obrazovky\" automaticky spistí službu, čo iným zariadeniam umožní požiadať o pripojenie k tomuto zariadeniu."), ("android_stop_service_tip", "Zastavenie služby automaticky ukončí všetky naviazané spojenia."), ("android_version_audio_tip", "Vaša verzia Androidu neumožňuje zaznamenávanie zvuku. Prejdite na verziu Android 10 alebo vyššiu."), - ("android_start_service_tip", ""), - ("android_permission_may_not_change_tip", ""), - ("Account", ""), + ("android_start_service_tip", "Ťuknutím na položku [Spustiť službu] alebo povolením povolenia [Snímanie obrazovky] spustite službu zdieľania obrazovky."), + ("android_permission_may_not_change_tip", "Oprávnenia pre vytvorené pripojenia možno zmeniť až po opätovnom pripojení."), + ("Account", "Účet"), ("Overwrite", "Prepísať"), ("This file exists, skip or overwrite this file?", "Preskočiť alebo prepísať existujúci súbor?"), ("Quit", "Ukončiť"), @@ -307,26 +304,25 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Turned off", "Vypnutý"), ("In privacy mode", "V režime súkromia"), ("Out privacy mode", "Mimo režimu súkromia"), - ("Language", ""), - ("Keep RustDesk background service", ""), - ("Ignore Battery Optimizations", ""), - ("android_open_battery_optimizations_tip", ""), - ("Start on Boot", ""), - ("Start the screen sharing service on boot, requires special permissions", ""), - ("Connection not allowed", ""), - ("Legacy mode", ""), - ("Map mode", ""), - ("Translate mode", ""), - ("Use permanent password", ""), - ("Use both passwords", ""), - ("Set permanent password", ""), - ("Enable Remote Restart", ""), - ("Allow remote restart", ""), - ("Restart Remote Device", ""), - ("Are you sure you want to restart", ""), - ("Restarting Remote Device", ""), - ("remote_restarting_tip", ""), - ("Copied", ""), + ("Language", "Jazyk"), + ("Keep RustDesk background service", "Ponechať službu RustDesk na pozadí"), + ("Ignore Battery Optimizations", "Ignorovať optimalizácie batérie"), + ("android_open_battery_optimizations_tip", "Ak chcete túto funkciu vypnúť, prejdite na ďalšiu stránku nastavení RustDesku, vyhľadajte a zadajte položku [Batéria], zrušte začiarknutie položky [Neobmedzené]."), + ("Start on boot", "Spustenie po štarte"), + ("Start the screen sharing service on boot, requires special permissions", "Spustenie služby zdieľania obrazovky pri štarte systému, vyžaduje špeciálne oprávnenia"), + ("Connection not allowed", "Spojenie nie je povolené"), + ("Legacy mode", "Režim Legacy"), + ("Map mode", "Režim mapovania"), + ("Translate mode", "Režim prekladania"), + ("Use permanent password", "Použitie trvalého hesla"), + ("Use both passwords", "Používanie oboch hesiel"), + ("Set permanent password", "Nastaviť trvalé heslo"), + ("Enable remote restart", "Povoliť vzdialený reštart"), + ("Restart remote device", "Reštartovať vzdialené zariadenie"), + ("Are you sure you want to restart", "Ste si istý, že chcete reštartovať"), + ("Restarting remote device", "Reštartovanie vzdialeného zariadenia"), + ("remote_restarting_tip", "Vzdialené zariadenie sa reštartuje, zatvorte toto okno a po chvíli sa znovu pripojte pomocou trvalého hesla."), + ("Copied", "Skopírované"), ("Exit Fullscreen", "Ukončiť celú obrazovku"), ("Fullscreen", "Celá obrazovka"), ("Mobile Actions", "Mobilné akcie"), @@ -336,241 +332,243 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ratio", "Pomer"), ("Image Quality", "Kvalita obrazu"), ("Scroll Style", "Štýl posúvania"), - ("Show Toolbar", ""), - ("Hide Toolbar", ""), + ("Show Toolbar", "Zobrazenie panela nástrojov"), + ("Hide Toolbar", "Skrytie panela nástrojov"), ("Direct Connection", "Priame pripojenie"), ("Relay Connection", "Reléové pripojenie"), ("Secure Connection", "Zabezpečené pripojenie"), ("Insecure Connection", "Nezabezpečené pripojenie"), ("Scale original", "Pôvodná mierka"), ("Scale adaptive", "Prispôsobivá mierka"), - ("General", ""), - ("Security", ""), - ("Theme", ""), - ("Dark Theme", ""), - ("Light Theme", ""), - ("Dark", ""), - ("Light", ""), - ("Follow System", ""), - ("Enable hardware codec", ""), - ("Unlock Security Settings", ""), - ("Enable Audio", ""), - ("Unlock Network Settings", ""), - ("Server", ""), - ("Direct IP Access", ""), - ("Proxy", ""), - ("Apply", ""), - ("Disconnect all devices?", ""), - ("Clear", ""), - ("Audio Input Device", ""), - ("Use IP Whitelisting", ""), - ("Network", ""), - ("Pin Toolbar", ""), - ("Unpin Toolbar", ""), - ("Recording", ""), - ("Directory", ""), - ("Automatically record incoming sessions", ""), - ("Change", ""), - ("Start session recording", ""), - ("Stop session recording", ""), - ("Enable Recording Session", ""), - ("Allow recording session", ""), - ("Enable LAN Discovery", ""), - ("Deny LAN Discovery", ""), - ("Write a message", ""), - ("Prompt", ""), - ("Please wait for confirmation of UAC...", ""), - ("elevated_foreground_window_tip", ""), - ("Disconnected", ""), - ("Other", ""), - ("Confirm before closing multiple tabs", ""), - ("Keyboard Settings", ""), - ("Full Access", ""), - ("Screen Share", ""), + ("General", "Všeobecné"), + ("Security", "Zabezpečenie"), + ("Theme", "Motív"), + ("Dark Theme", "Tmavý motív"), + ("Light Theme", "Svetlý motív"), + ("Dark", "Tmavý"), + ("Light", "Svetlý"), + ("Follow System", "Podľa systému"), + ("Enable hardware codec", "Povoliť hardwarový kodek"), + ("Unlock Security Settings", "Odomknúť nastavenie zabezpečenia"), + ("Enable audio", "Povoliť zvuk"), + ("Unlock Network Settings", "Odomknúť nastavenie siete"), + ("Server", "Server"), + ("Direct IP Access", "Priamy IP prístup"), + ("Proxy", "Proxy"), + ("Apply", "Použiť"), + ("Disconnect all devices?", "Odpojiť všetky zariadenia?"), + ("Clear", "Zmazať"), + ("Audio Input Device", "Vstupné zvukové zariadenie"), + ("Use IP Whitelisting", "Použiť IP whitelisting"), + ("Network", "Sieť"), + ("Pin Toolbar", "Pripnúť panel nástrojov"), + ("Unpin Toolbar", "Odpojiť panel nástrojov"), + ("Recording", "Nahrávanie"), + ("Directory", "Adresár"), + ("Automatically record incoming sessions", "Automaticky nahrávať prichádzajúce relácie"), + ("Change", "Zmeniť"), + ("Start session recording", "Spustiť záznam relácie"), + ("Stop session recording", "Zastaviť záznam relácie"), + ("Enable recording session", "Povoliť nahrávanie relácie"), + ("Enable LAN discovery", "Povolenie zisťovania siete LAN"), + ("Deny LAN discovery", "Zakázať zisťovania siete LAN"), + ("Write a message", "Napísať správu"), + ("Prompt", "Výzva"), + ("Please wait for confirmation of UAC...", "Počkajte, prosím, na potvrdenie UAC..."), + ("elevated_foreground_window_tip", "Aktuálne okno vzdialenej plochy vyžaduje vyššie oprávnenia, takže dočasne nemôže používať myš a klávesnicu. Môžete požiadať vzdialeného používateľa, aby minimalizoval aktuálne okno, alebo kliknúť na tlačidlo povýšiť v okne správy pripojenia. Ak sa chcete vyhnúť tomuto problému, odporúčame nainštalovať softvér na vzdialené zariadenie."), + ("Disconnected", "Odpojené"), + ("Other", "Iné"), + ("Confirm before closing multiple tabs", "Potvrdiť pred zatvorením viacerých kariet"), + ("Keyboard Settings", "Nastavenia klávesnice"), + ("Full Access", "Úplný prístup"), + ("Screen Share", "Zdielanie obrazovky"), ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland vyžaduje Ubuntu 21.04 alebo vyššiu verziu."), ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland vyžaduje vyššiu verziu linuxovej distribúcie. Skúste X11 desktop alebo zmeňte OS."), ("JumpLink", "View"), ("Please Select the screen to be shared(Operate on the peer side).", "Vyberte obrazovku, ktorú chcete zdieľať (Ovládajte na strane partnera)."), - ("Show RustDesk", ""), - ("This PC", ""), - ("or", ""), - ("Continue with", ""), - ("Elevate", ""), - ("Zoom cursor", ""), - ("Accept sessions via password", ""), - ("Accept sessions via click", ""), - ("Accept sessions via both", ""), - ("Please wait for the remote side to accept your session request...", ""), - ("One-time Password", ""), - ("Use one-time password", ""), - ("One-time password length", ""), - ("Request access to your device", ""), - ("Hide connection management window", ""), - ("hide_cm_tip", ""), - ("wayland_experiment_tip", ""), - ("Right click to select tabs", ""), - ("Skipped", ""), - ("Add to Address Book", ""), - ("Group", ""), - ("Search", ""), - ("Closed manually by web console", ""), - ("Local keyboard type", ""), - ("Select local keyboard type", ""), - ("software_render_tip", ""), - ("Always use software rendering", ""), - ("config_input", ""), - ("config_microphone", ""), - ("request_elevation_tip", ""), - ("Wait", ""), - ("Elevation Error", ""), - ("Ask the remote user for authentication", ""), - ("Choose this if the remote account is administrator", ""), - ("Transmit the username and password of administrator", ""), - ("still_click_uac_tip", ""), - ("Request Elevation", ""), - ("wait_accept_uac_tip", ""), - ("Elevate successfully", ""), - ("uppercase", ""), - ("lowercase", ""), - ("digit", ""), - ("special character", ""), - ("length>=8", ""), - ("Weak", ""), - ("Medium", ""), - ("Strong", ""), - ("Switch Sides", ""), - ("Please confirm if you want to share your desktop?", ""), - ("Display", ""), - ("Default View Style", ""), - ("Default Scroll Style", ""), - ("Default Image Quality", ""), - ("Default Codec", ""), - ("Bitrate", ""), - ("FPS", ""), - ("Auto", ""), - ("Other Default Options", ""), - ("Voice call", ""), - ("Text chat", ""), - ("Stop voice call", ""), - ("relay_hint_tip", ""), - ("Reconnect", ""), - ("Codec", ""), - ("Resolution", ""), - ("No transfers in progress", ""), - ("Set one-time password length", ""), - ("install_cert_tip", ""), - ("confirm_install_cert_tip", ""), - ("RDP Settings", ""), - ("Sort by", ""), - ("New Connection", ""), - ("Restore", ""), - ("Minimize", ""), - ("Maximize", ""), - ("Your Device", ""), - ("empty_recent_tip", ""), - ("empty_favorite_tip", ""), - ("empty_lan_tip", ""), - ("empty_address_book_tip", ""), - ("eg: admin", ""), - ("Empty Username", ""), - ("Empty Password", ""), - ("Me", ""), - ("identical_file_tip", ""), - ("show_monitors_tip", ""), - ("View Mode", ""), - ("login_linux_tip", ""), - ("verify_rustdesk_password_tip", ""), - ("remember_account_tip", ""), - ("os_account_desk_tip", ""), - ("OS Account", ""), - ("another_user_login_title_tip", ""), - ("another_user_login_text_tip", ""), - ("xorg_not_found_title_tip", ""), - ("xorg_not_found_text_tip", ""), - ("no_desktop_title_tip", ""), - ("no_desktop_text_tip", ""), - ("No need to elevate", ""), - ("System Sound", ""), - ("Default", ""), - ("New RDP", ""), - ("Fingerprint", ""), - ("Copy Fingerprint", ""), - ("no fingerprints", ""), - ("Select a peer", ""), - ("Select peers", ""), - ("Plugins", ""), - ("Uninstall", ""), - ("Update", ""), - ("Enable", ""), - ("Disable", ""), - ("Options", ""), - ("resolution_original_tip", ""), - ("resolution_fit_local_tip", ""), - ("resolution_custom_tip", ""), - ("Collapse toolbar", ""), - ("Accept and Elevate", ""), - ("accept_and_elevate_btn_tooltip", ""), - ("clipboard_wait_response_timeout_tip", ""), - ("Incoming connection", ""), - ("Outgoing connection", ""), - ("Exit", ""), - ("Open", ""), - ("logout_tip", ""), - ("Service", ""), - ("Start", ""), - ("Stop", ""), - ("exceed_max_devices", ""), - ("Sync with recent sessions", ""), - ("Sort tags", ""), - ("Open connection in new tab", ""), - ("Move tab to new window", ""), - ("Can not be empty", ""), - ("Already exists", ""), - ("Change Password", ""), - ("Refresh Password", ""), - ("ID", ""), - ("Grid View", ""), - ("List View", ""), - ("Select", ""), - ("Toggle Tags", ""), - ("pull_ab_failed_tip", ""), - ("push_ab_failed_tip", ""), - ("synced_peer_readded_tip", ""), - ("Change Color", ""), - ("Primary Color", ""), - ("HSV Color", ""), - ("Installation Successful!", ""), - ("Installation failed!", ""), - ("Reverse mouse wheel", ""), - ("{} sessions", ""), - ("scam_title", ""), - ("scam_text1", ""), - ("scam_text2", ""), - ("Don't show again", ""), - ("I Agree", ""), - ("Decline", ""), - ("Timeout in minutes", ""), - ("auto_disconnect_option_tip", ""), - ("Connection failed due to inactivity", ""), - ("Check for software update on startup", ""), - ("upgrade_rustdesk_server_pro_to_{}_tip", ""), - ("pull_group_failed_tip", ""), - ("Filter by intersection", ""), - ("Remove wallpaper during incoming sessions", ""), - ("Test", ""), - ("switch_display_elevated_connections_tip", ""), - ("display_is_plugged_out_msg", ""), - ("No displays", ""), - ("elevated_switch_display_msg", ""), - ("Open in new window", ""), - ("Show displays as individual windows", ""), - ("Use all my displays for the remote session", ""), - ("selinux_tip", ""), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), - ("Virtual display", ""), - ("Plug out all", ""), + ("Show RustDesk", "Zobraziť RustDesk"), + ("This PC", "Tento počítač"), + ("or", "alebo"), + ("Continue with", "Pokračovať s"), + ("Elevate", "Zvýšiť"), + ("Zoom cursor", "Kurzor priblíženia"), + ("Accept sessions via password", "Prijímanie relácií pomocou hesla"), + ("Accept sessions via click", "Prijímanie relácií kliknutím"), + ("Accept sessions via both", "Prijímanie relácií prostredníctvom oboch"), + ("Please wait for the remote side to accept your session request...", "Počkajte, kým vzdialená strana prijme vašu žiadosť o reláciu..."), + ("One-time Password", "Jednorázové heslo"), + ("Use one-time password", "Použiť jednorázové heslo"), + ("One-time password length", "Dĺžka jednorázového hesla"), + ("Request access to your device", "Žiadosť o prístup k vášmu zariadeniu"), + ("Hide connection management window", "Skryť okno správy pripojenia"), + ("hide_cm_tip", "Skrývanie povoľte len vtedy, ak relácie prijímate pomocou hesla a používate trvalé heslo."), + ("wayland_experiment_tip", "Podpora Waylandu je v experimentálnej fáze, ak potrebujete bezobslužný prístup, použite X11."), + ("Right click to select tabs", "Výber karty kliknutím pravým tlačidlom myši"), + ("Skipped", "Vynechané"), + ("Add to address book", "Pridať do adresára"), + ("Group", "Skupina"), + ("Search", "Vyhľadávanie"), + ("Closed manually by web console", "Zatvorené ručne pomocou webovej konzoly"), + ("Local keyboard type", "Typ lokálnej klávesnice"), + ("Select local keyboard type", "Výber typu lokálnej klávesnice"), + ("software_render_tip", "Ak používate grafickú kartu Nvidia v systéme Linux a vzdialené okno sa po pripojení okamžite zatvorí, môže vám pomôcť prepnutie na ovládač Nouveau s otvoreným zdrojovým kódom a výber softvérového vykresľovania. Vyžaduje sa softvérový reštart."), + ("Always use software rendering", "Vždy použiť softvérové vykresľovanie"), + ("config_input", "Ak chcete ovládať vzdialenú plochu pomocou klávesnice, musíte udeliť oprávnenie RustDesk \"Sledovanie vstupu\"."), + ("config_microphone", "Ak chcete hovoriť na diaľku, musíte RustDesku udeliť povolenie \"Nahrávať zvuk\"."), + ("request_elevation_tip", "Môžete tiež požiadať o zvýšenie, ak je niekto na vzdialenej strane."), + ("Wait", "Počkajte"), + ("Elevation Error", "Chyba navýšenia"), + ("Ask the remote user for authentication", "Požiadať vzdialeného používateľa o overenie"), + ("Choose this if the remote account is administrator", "Túto možnosť vyberte, ak je účet vzdialeného správcu"), + ("Transmit the username and password of administrator", "Prenos používateľského mena a hesla správcu"), + ("still_click_uac_tip", "Stále sa vyžaduje, aby vzdialený používateľ klikol na tlačidlo OK v okne UAC spusteného programu RustDesk."), + ("Request Elevation", "Žiadosť o navýšenie"), + ("wait_accept_uac_tip", "Počkajte, kým vzdialený používateľ prijme dialógové okno UAC."), + ("Elevate successfully", "Úspešné navýšenie"), + ("uppercase", "velké písmená"), + ("lowercase", "malé písmená"), + ("digit", "číslice"), + ("special character", "špeciálny znak"), + ("length>=8", "dĺžka>=8"), + ("Weak", "Slabé"), + ("Medium", "Stredné"), + ("Strong", "Silné"), + ("Switch Sides", "Prepínanie strán"), + ("Please confirm if you want to share your desktop?", "Potvrďte, prosím, či chcete zdieľať svoju plochu?"), + ("Display", "Obrazovka"), + ("Default View Style", "Predvolený štýl zobrazenia"), + ("Default Scroll Style", "Predvolený štýl posúvania"), + ("Default Image Quality", "Predvolená kvalita obrazu"), + ("Default Codec", "Predvolený kodek"), + ("Bitrate", "Dátový tok"), + ("FPS", "FPS"), + ("Auto", "Auto"), + ("Other Default Options", "Ďalšie predvolené možnosti"), + ("Voice call", "Hlasový hovor"), + ("Text chat", "Textový chat"), + ("Stop voice call", "Zastaviť hlasový hovor"), + ("relay_hint_tip", "Priame pripojenie nemusí byť možné, môžete sa pokúsiť pripojiť prostredníctvom presmerovacieho servera. Okrem toho, ak chcete pri prvom pokuse použiť presmerovací server, môžete k ID pridať príponu \"/r\" alebo vybrať možnosť \"Vždy sa pripájať cez bránu\" na karte posledných relácií, ak existuje."), + ("Reconnect", "Znovu pripojiť"), + ("Codec", "Kodek"), + ("Resolution", "Rozlíšenie"), + ("No transfers in progress", "Žiadne prebiehajúce presuny"), + ("Set one-time password length", "Nastaviť dĺžku jednorazového hesla"), + ("install_cert_tip", "Inštalácia certifikátu RustDesk"), + ("confirm_install_cert_tip", "Ide o testovací certifikát RustDesk, ktorému možno dôverovať. Certifikát sa v prípade potreby použije na dôveryhodnosť a inštaláciu ovládačov RustDesk."), + ("RDP Settings", "Nastavenia RDP"), + ("Sort by", "Usporiadať podľa"), + ("New Connection", "Nové pripojenie"), + ("Restore", "Obnoviť"), + ("Minimize", "Minimalizovať"), + ("Maximize", "Maximalizovať"), + ("Your Device", "Vaše zariadenie"), + ("empty_recent_tip", "Ups, žiadna nedávna relácia!\nČas naplánovať novú."), + ("empty_favorite_tip", "Ešte nemáte obľúbeného partnera?\nNájdite niekoho, s kým sa môžete spojiť, a pridajte si ho do obľúbených!"), + ("empty_lan_tip", "Ale nie, zdá sa, že sme zatiaľ neobjavili žiadnu protistranu."), + ("empty_address_book_tip", "Ach bože, zdá sa, že vo vašom adresári momentálne nie sú uvedení žiadni kolegovia."), + ("eg: admin", "napr. admin"), + ("Empty Username", "Prázdne používateľské meno"), + ("Empty Password", "Prázdne heslo"), + ("Me", "Ja"), + ("identical_file_tip", "Tento súbor je identický so súborom partnera."), + ("show_monitors_tip", "Zobraziť monitory na paneli nástrojov"), + ("View Mode", "Režim zobrazenia"), + ("login_linux_tip", "Ak chcete povoliť reláciu Desktop X, musíte sa prihlásiť do vzdialeného konta Linuxu."), + ("verify_rustdesk_password_tip", "Overenie hesla RustDesk"), + ("remember_account_tip", "Zapamätať si tento účet"), + ("os_account_desk_tip", "Toto konto sa používa na prihlásenie do vzdialeného operačného systému a na povolenie relácie pracovnej plochy v režime headless."), + ("OS Account", "Účet operačného systému"), + ("another_user_login_title_tip", "Ďalší používateľ je už prihlásený"), + ("another_user_login_text_tip", "Odpojiť"), + ("xorg_not_found_title_tip", "Xorg nebol nájdený"), + ("xorg_not_found_text_tip", "Prosím, nainštalujte Xorg"), + ("no_desktop_title_tip", "Nie je k dispozícii žiadna plocha"), + ("no_desktop_text_tip", "Nainštalujte si prostredie GNOME"), + ("No need to elevate", "Navýšenie nie je potrebné"), + ("System Sound", "Systémový zvuk"), + ("Default", "Predvolené"), + ("New RDP", "Nové RDP"), + ("Fingerprint", "Odtlačok prsta"), + ("Copy Fingerprint", "Kopírovať odtlačok prsta"), + ("no fingerprints", "žiadne odtlačky prstov"), + ("Select a peer", "Výber partnera"), + ("Select peers", "Výber partnerov"), + ("Plugins", "Pluginy"), + ("Uninstall", "Odinštalovať"), + ("Update", "Aktualizovať"), + ("Enable", "Povoliť"), + ("Disable", "Zakázať"), + ("Options", "Možnosti"), + ("resolution_original_tip", "Pôvodné rozlíšenie"), + ("resolution_fit_local_tip", "Prispôsobiť miestne rozlíšenie"), + ("resolution_custom_tip", "Vlastné rozlíšenie"), + ("Collapse toolbar", "Zbaliť panel nástrojov"), + ("Accept and Elevate", "Prijať navýšenie"), + ("accept_and_elevate_btn_tooltip", "Prijmite pripojenie a zvýšte oprávnenia UAC."), + ("clipboard_wait_response_timeout_tip", "Čas na čakanie na kópiu odpovede uplynul."), + ("Incoming connection", "Prichádzajúce pripojenie"), + ("Outgoing connection", "Odchádzajúce pripojenie"), + ("Exit", "Ukončiť"), + ("Open", "Otvoriť"), + ("logout_tip", "Naozaj sa chcete odhlásiť?"), + ("Service", "Služba"), + ("Start", "Spustiť"), + ("Stop", "Zastaviť"), + ("exceed_max_devices", "Dosiahli ste maximálny počet spravovaných zariadení."), + ("Sync with recent sessions", "Synchronizovať s poslednými reláciami"), + ("Sort tags", "Zoradiť štítky"), + ("Open connection in new tab", "Otvoriť pripojenie v novej karte"), + ("Move tab to new window", "Presunúť kartu do nového okna"), + ("Can not be empty", "Nemôže byť prázdne"), + ("Already exists", "Už existuje"), + ("Change Password", "Zmeniť heslo"), + ("Refresh Password", "Obnoviť heslo"), + ("ID", "ID"), + ("Grid View", "Mriežka"), + ("List View", "Zoznam"), + ("Select", "Vybrať"), + ("Toggle Tags", "Prepnúť štítky"), + ("pull_ab_failed_tip", "Nepodarilo sa obnoviť adresár"), + ("push_ab_failed_tip", "Nepodarilo sa synchronizovať adresár so serverom"), + ("synced_peer_readded_tip", "Zariadenia, ktoré boli prítomné v posledných reláciách, budú synchronizované späť do adresára."), + ("Change Color", "Zmeniť farbu"), + ("Primary Color", "Hlavná farba"), + ("HSV Color", "HSV farba"), + ("Installation Successful!", "Inštalácia úspešná!"), + ("Installation failed!", "Inštalácia zlyhala!"), + ("Reverse mouse wheel", "Reverzné koliesko myši"), + ("{} sessions", "{} relácií"), + ("scam_title", "Možno ste boli oklamaní!"), + ("scam_text1", "Ak telefonujete s niekým, koho nepoznáte a komu nedôverujete a kto vás požiadal o použitie a spustenie aplikácie RustDesk, nepokračujte v hovore a okamžite zaveste."), + ("scam_text2", "Pravdepodobne ide o podvodníka, ktorý sa snaží ukradnúť vaše peniaze alebo iné súkromné informácie."), + ("Don't show again", "Nezobrazovať znova"), + ("I Agree", "Súhlasím"), + ("Decline", "Odmietnuť"), + ("Timeout in minutes", "Časový limit v minútach"), + ("auto_disconnect_option_tip", "Automatické ukončenie prichádzajúcich relácií, keď je používateľ nečinný"), + ("Connection failed due to inactivity", "Pripojenie zlyhalo z dôvodu nečinnosti"), + ("Check for software update on startup", "Kontrola aktualizácií softvéru pri spustení"), + ("upgrade_rustdesk_server_pro_to_{}_tip", "Aktualizujte RustDesk Server Pro na verziu {} alebo novšiu!"), + ("pull_group_failed_tip", "Nepodarilo sa obnoviť skupinu"), + ("Filter by intersection", "Filtrovať podľa križovatky"), + ("Remove wallpaper during incoming sessions", "Odstrániť tapetu počas prichádzajúcich relácií"), + ("Test", "Test"), + ("switch_display_elevated_connections_tip", "Prepínanie na inú ako primárnu obrazovku nie je podporované vo zvýšenom režime, ak existuje viacero pripojení. Ak chcete ovládať viacero obrazoviek, skúste to po inštalácii znova."), + ("display_is_plugged_out_msg", "Obrazovka je odpojená, prepnite na prvú obrazovku."), + ("No displays", "Žiadne obrazovky"), + ("elevated_switch_display_msg", "Prepnite na primárnu obrazovku, pretože viacero obrazoviek nie je podporovaných vo zvýšenom režime."), + ("Open in new window", "Otvoriť v novom okne"), + ("Show displays as individual windows", "Zobraziť obrazovky ako jednotlivé okná"), + ("Use all my displays for the remote session", "Použiť všetky moje obrazovky pre vzdialenú reláciu"), + ("selinux_tip", "Na vašom zariadení je povolený SELinux, čo môže brániť správnemu spusteniu RustDesku ako spravovanej strany."), + ("Change view", "Zmeniť pohľad"), + ("Big tiles", "Veľké dlaždice"), + ("Small tiles", "Malé dlaždice"), + ("List", "Zoznam"), + ("Virtual display", "Virtuálny displej"), + ("Plug out all", "Odpojiť všetky"), + ("True color (4:4:4)", "Skutočná farba (4:4:4)"), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 71300b6c6..39b694909 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Pripravljen"), ("Established", "Povezava vzpostavljena"), ("connecting_status", "Vzpostavljanje povezave z omrežjem RustDesk..."), - ("Enable Service", "Omogoči storitev"), - ("Start Service", "Zaženi storitev"), + ("Enable service", "Omogoči storitev"), + ("Start service", "Zaženi storitev"), ("Service is running", "Storitev se izvaja"), ("Service is not running", "Storitev se ne izvaja"), ("not_ready_status", "Ni pripravljeno, preverite vašo mrežno povezavo"), ("Control Remote Desktop", "Nadzoruj oddaljeno namizje"), - ("Transfer File", "Prenos datotek"), + ("Transfer file", "Prenos datotek"), ("Connect", "Poveži"), - ("Recent Sessions", "Nedavne seje"), - ("Address Book", "Adresar"), + ("Recent sessions", "Nedavne seje"), + ("Address book", "Adresar"), ("Confirmation", "Potrditev"), - ("TCP Tunneling", "TCP tuneliranje"), + ("TCP tunneling", "TCP tuneliranje"), ("Remove", "Odstrani"), ("Refresh random password", "Osveži naključno geslo"), ("Set your own password", "Nastavi lastno geslo"), - ("Enable Keyboard/Mouse", "Omogoči tipkovnico in miško"), - ("Enable Clipboard", "Omogoči odložišče"), - ("Enable File Transfer", "Omogoči prenos datotek"), - ("Enable TCP Tunneling", "Omogoči TCP tuneliranje"), + ("Enable keyboard/mouse", "Omogoči tipkovnico in miško"), + ("Enable clipboard", "Omogoči odložišče"), + ("Enable file transfer", "Omogoči prenos datotek"), + ("Enable TCP tunneling", "Omogoči TCP tuneliranje"), ("IP Whitelisting", "Omogoči seznam dovoljenih IPjev"), ("ID/Relay Server", "Strežnik za ID/posredovanje"), - ("Import Server Config", "Uvozi nastavitve strežnika"), + ("Import server config", "Uvozi nastavitve strežnika"), ("Export Server Config", "Izvozi nastavitve strežnika"), ("Import server configuration successfully", "Nastavitve strežnika uspešno uvožene"), ("Export server configuration successfully", "Nastavitve strežnika uspešno izvožene"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Sprejmi"), ("Dismiss", "Opusti"), ("Disconnect", "Prekini povezavo"), - ("Allow using keyboard and mouse", "Dovoli uporabo tipkovnice in miške"), - ("Allow using clipboard", "Dovoli uporabo odložišča"), - ("Allow hearing sound", "Dovoli prenos zvoka"), - ("Allow file copy and paste", "Dovoli kopiranje in lepljenje datotek"), + ("Enable file copy and paste", "Dovoli kopiranje in lepljenje datotek"), ("Connected", "Povezan"), ("Direct and encrypted connection", "Neposredna šifrirana povezava"), ("Relayed and encrypted connection", "Posredovana šifrirana povezava"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Prijavljanje..."), ("Enable RDP session sharing", "Omogoči deljenje RDP seje"), ("Auto Login", "Samodejna prijava"), - ("Enable Direct IP Access", "Omogoči neposredni dostop preko IP"), + ("Enable direct IP access", "Omogoči neposredni dostop preko IP"), ("Rename", "Preimenuj"), ("Space", "Prazno"), - ("Create Desktop Shortcut", "Ustvari bližnjico na namizju"), + ("Create desktop shortcut", "Ustvari bližnjico na namizju"), ("Change Path", "Spremeni pot"), ("Create Folder", "Ustvari mapo"), ("Please enter the folder name", "Vnesite ime mape"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Ohrani RustDeskovo storitev v ozadju"), ("Ignore Battery Optimizations", "Prezri optimizacije baterije"), ("android_open_battery_optimizations_tip", "Če želite izklopiti to možnost, pojdite v nastavitve aplikacije RustDesk, poiščite »Baterija« in izklopite »Neomejeno«"), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Povezava ni dovoljena"), ("Legacy mode", "Stari način"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Uporabi stalno geslo"), ("Use both passwords", "Uporabi obe gesli"), ("Set permanent password", "Nastavi stalno geslo"), - ("Enable Remote Restart", "Omogoči oddaljeni ponovni zagon"), - ("Allow remote restart", "Dovoli oddaljeni ponovni zagon"), - ("Restart Remote Device", "Znova zaženi oddaljeno napravo"), + ("Enable remote restart", "Omogoči oddaljeni ponovni zagon"), + ("Restart remote device", "Znova zaženi oddaljeno napravo"), ("Are you sure you want to restart", "Ali ste prepričani, da želite znova zagnati"), - ("Restarting Remote Device", "Ponovni zagon oddaljene naprave"), + ("Restarting remote device", "Ponovni zagon oddaljene naprave"), ("remote_restarting_tip", "Oddaljena naprava se znova zaganja, prosim zaprite to sporočilo in se čez nekaj časa povežite s stalnim geslom."), ("Copied", "Kopirano"), ("Exit Fullscreen", "Izhod iz celozaslonskega načina"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Sistemska"), ("Enable hardware codec", "Omogoči strojno pospeševanje"), ("Unlock Security Settings", "Odkleni varnostne nastavitve"), - ("Enable Audio", "Omogoči zvok"), + ("Enable audio", "Omogoči zvok"), ("Unlock Network Settings", "Odkleni mrežne nastavitve"), ("Server", "Strežnik"), ("Direct IP Access", "Neposredni dostop preko IPja"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Spremeni"), ("Start session recording", "Začni snemanje seje"), ("Stop session recording", "Ustavi snemanje seje"), - ("Enable Recording Session", "Omogoči snemanje seje"), - ("Allow recording session", "Dovoli snemanje seje"), - ("Enable LAN Discovery", "Omogoči odkrivanje lokalnega omrežja"), - ("Deny LAN Discovery", "Onemogoči odkrivanje lokalnega omrežja"), + ("Enable recording session", "Omogoči snemanje seje"), + ("Enable LAN discovery", "Omogoči odkrivanje lokalnega omrežja"), + ("Deny LAN discovery", "Onemogoči odkrivanje lokalnega omrežja"), ("Write a message", "Napiši spoorčilo"), ("Prompt", "Poziv"), ("Please wait for confirmation of UAC...", "Počakajte za potrditev nadzora uporabniškega računa"), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Podpora za Wayland je v preizkusni fazi. Uporabite X11, če rabite nespremljan dostop."), ("Right click to select tabs", "Desno-kliknite za izbiro zavihkov"), ("Skipped", "Izpuščeno"), - ("Add to Address Book", "Dodaj v adresar"), + ("Add to address book", "Dodaj v adresar"), ("Group", "Skupina"), ("Search", "Iskanje"), ("Closed manually by web console", "Ročno zaprto iz spletne konzole"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 52e9a2fdd..7bc044116 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Gati"), ("Established", "I themeluar"), ("connecting_status", "statusi_i_lidhjes"), - ("Enable Service", "Aktivizo Shërbimin"), - ("Start Service", "Nis Shërbimin"), + ("Enable service", "Aktivizo Shërbimin"), + ("Start service", "Nis Shërbimin"), ("Service is running", "Shërbimi është duke funksionuar"), ("Service is not running", "Shërbimi nuk është duke funksionuar"), ("not_ready_status", "Jo gati.Ju lutem kontolloni lidhjen tuaj."), ("Control Remote Desktop", "Kontrolli i desktopit në distancë"), - ("Transfer File", "Transfero dosje"), + ("Transfer file", "Transfero dosje"), ("Connect", "Lidh"), - ("Recent Sessions", "Sessioni i fundit"), - ("Address Book", "Libër adresash"), + ("Recent sessions", "Sessioni i fundit"), + ("Address book", "Libër adresash"), ("Confirmation", "Konfirmimi"), - ("TCP Tunneling", "TCP tunel"), + ("TCP tunneling", "TCP tunel"), ("Remove", "Hiqni"), ("Refresh random password", "Rifreskoni fjalëkalimin e rastësishëm"), ("Set your own password", "Vendosni fjalëkalimin tuaj"), - ("Enable Keyboard/Mouse", "Aktivizoni Tastierën/Mousin"), - ("Enable Clipboard", "Aktivizo"), - ("Enable File Transfer", "Aktivizoni transferimin e skedarëve"), - ("Enable TCP Tunneling", "Aktivizoni TCP Tunneling"), + ("Enable keyboard/mouse", "Aktivizoni Tastierën/Mousin"), + ("Enable clipboard", "Aktivizo"), + ("Enable file transfer", "Aktivizoni transferimin e skedarëve"), + ("Enable TCP tunneling", "Aktivizoni TCP tunneling"), ("IP Whitelisting", ""), ("ID/Relay Server", "ID/server rele"), - ("Import Server Config", "Konfigurimi i severit të importit"), + ("Import server config", "Konfigurimi i severit të importit"), ("Export Server Config", "Konfigurimi i severit të eksportit"), ("Import server configuration successfully", "Konfigurimi i severit të importit i suksesshëm"), ("Export server configuration successfully", "Konfigurimi i severit të eksprotit i suksesshëm"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Prano"), ("Dismiss", "Hiq"), ("Disconnect", "Shkëput"), - ("Allow using keyboard and mouse", "Lejoni përdorimin e Tastierës dhe Mousit"), - ("Allow using clipboard", "Lejoni përdorimin e clipboard"), - ("Allow hearing sound", "Lejoni dëgjimin e zërit"), - ("Allow file copy and paste", "Lejoni kopjimin dhe pastimin e skedarëve"), + ("Enable file copy and paste", "Lejoni kopjimin dhe pastimin e skedarëve"), ("Connected", "I lidhur"), ("Direct and encrypted connection", "Lidhje direkte dhe enkriptuar"), ("Relayed and encrypted connection", "Lidhje transmetuese dhe e enkriptuar"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Duke u loguar"), ("Enable RDP session sharing", "Aktivizoni shpërndarjen e sesionit RDP"), ("Auto Login", "Hyrje automatike"), - ("Enable Direct IP Access", "Aktivizoni aksesimin e IP direkte"), + ("Enable direct IP access", "Aktivizoni aksesimin e IP direkte"), ("Rename", "Riemërto"), ("Space", "Hapërsirë"), - ("Create Desktop Shortcut", "Krijoni shortcut desktop"), + ("Create desktop shortcut", "Krijoni shortcut desktop"), ("Change Path", "Ndrysho rrugëzimin"), ("Create Folder", "Krijoni një folder"), ("Please enter the folder name", "Ju lutem vendosni emrin e folderit"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Mbaje shërbimin e sfondit të RustDesk"), ("Ignore Battery Optimizations", "Injoro optimizimet e baterisë"), ("android_open_battery_optimizations_tip", "Nëse dëshironi ta çaktivizoni këtë veçori, ju lutemi shkoni te faqja tjetër e cilësimeve të aplikacionit RustDesk, gjeni dhe shtypni [Batteri], hiqni zgjedhjen [Te pakufizuara]"), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Lidhja nuk lejohet"), ("Legacy mode", "Modaliteti i trashëgimisë"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Përdor fjalëkalim të përhershëm"), ("Use both passwords", "Përdor të dy fjalëkalimet"), ("Set permanent password", "Vendos fjalëkalimin e përhershëm"), - ("Enable Remote Restart", "Aktivizo rinisjen në distancë"), - ("Allow remote restart", "Lejo rinisjen në distancë"), - ("Restart Remote Device", "Rinisni pajisjen në distancë"), + ("Enable remote restart", "Aktivizo rinisjen në distancë"), + ("Restart remote device", "Rinisni pajisjen në distancë"), ("Are you sure you want to restart", "A jeni i sigurt që dëshironi të rinisni"), - ("Restarting Remote Device", "Rinisja e pajisjes në distancë"), + ("Restarting remote device", "Rinisja e pajisjes në distancë"), ("remote_restarting_tip", "Pajisja në distancë po riniset, ju lutemi mbyllni këtë kuti mesazhi dhe lidheni përsëri me fjalëkalim të përhershëm pas një kohe"), ("Copied", "Kopjuar"), ("Exit Fullscreen", "Dil nga ekrani i plotë"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Ndiq sistemin"), ("Enable hardware codec", "Aktivizo kodekun e harduerit"), ("Unlock Security Settings", "Zhbllokoni cilësimet e sigurisë"), - ("Enable Audio", "Aktivizo audio"), + ("Enable audio", "Aktivizo audio"), ("Unlock Network Settings", "Zhbllokoni cilësimet e rrjetit"), ("Server", "Server"), ("Direct IP Access", "Qasje e drejtpërdrejtë IP"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Ndrysho"), ("Start session recording", "Fillo regjistrimin e sesionit"), ("Stop session recording", "Ndalo regjistrimin e sesionit"), - ("Enable Recording Session", "Aktivizo seancën e regjistrimit"), - ("Allow recording session", "Lejo regjistrimin e sesionit"), - ("Enable LAN Discovery", "Aktivizo zbulimin e LAN"), - ("Deny LAN Discovery", "Mohoni zbulimin e LAN"), + ("Enable recording session", "Aktivizo seancën e regjistrimit"), + ("Enable LAN discovery", "Aktivizo zbulimin e LAN"), + ("Deny LAN discovery", "Mohoni zbulimin e LAN"), ("Write a message", "Shkruani një mesazh"), ("Prompt", "Prompt"), ("Please wait for confirmation of UAC...", "Ju lutemi prisni për konfirmimin e UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index f82b5bdec..8df9454e2 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Spremno"), ("Established", "Uspostavljeno"), ("connecting_status", "Spajanje na RustDesk mrežu..."), - ("Enable Service", "Dozvoli servis"), - ("Start Service", "Pokreni servis"), + ("Enable service", "Dozvoli servis"), + ("Start service", "Pokreni servis"), ("Service is running", "Servis je pokrenut"), ("Service is not running", "Servis nije pokrenut"), ("not_ready_status", "Nije spremno. Proverite konekciju."), ("Control Remote Desktop", "Upravljanje udaljenom radnom površinom"), - ("Transfer File", "Prenos fajla"), + ("Transfer file", "Prenos fajla"), ("Connect", "Spajanje"), - ("Recent Sessions", "Poslednje sesije"), - ("Address Book", "Adresar"), + ("Recent sessions", "Poslednje sesije"), + ("Address book", "Adresar"), ("Confirmation", "Potvrda"), - ("TCP Tunneling", "TCP tunel"), + ("TCP tunneling", "TCP tunel"), ("Remove", "Ukloni"), ("Refresh random password", "Osveži slučajnu lozinku"), ("Set your own password", "Postavi lozinku"), - ("Enable Keyboard/Mouse", "Dozvoli tastaturu/miša"), - ("Enable Clipboard", "Dozvoli clipboard"), - ("Enable File Transfer", "Dozvoli prenos fajlova"), - ("Enable TCP Tunneling", "Dozvoli TCP tunel"), + ("Enable keyboard/mouse", "Dozvoli tastaturu/miša"), + ("Enable clipboard", "Dozvoli clipboard"), + ("Enable file transfer", "Dozvoli prenos fajlova"), + ("Enable TCP tunneling", "Dozvoli TCP tunel"), ("IP Whitelisting", "IP pouzdana lista"), ("ID/Relay Server", "ID/Posredni server"), - ("Import Server Config", "Import server konfiguracije"), + ("Import server config", "Import server konfiguracije"), ("Export Server Config", "Eksport server konfiguracije"), ("Import server configuration successfully", "Import server konfiguracije uspešan"), ("Export server configuration successfully", "Eksport server konfiguracije uspešan"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Prihvati"), ("Dismiss", "Odbaci"), ("Disconnect", "Raskini konekciju"), - ("Allow using keyboard and mouse", "Dozvoli korišćenje tastature i miša"), - ("Allow using clipboard", "Dozvoli korišćenje clipboard-a"), - ("Allow hearing sound", "Dozvoli da se čuje zvuk"), - ("Allow file copy and paste", "Dozvoli kopiranje i lepljenje fajlova"), + ("Enable file copy and paste", "Dozvoli kopiranje i lepljenje fajlova"), ("Connected", "Spojeno"), ("Direct and encrypted connection", "Direktna i kriptovana konekcija"), ("Relayed and encrypted connection", "Posredna i kriptovana konekcija"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Prijava..."), ("Enable RDP session sharing", "Dozvoli deljenje RDP sesije"), ("Auto Login", "Auto prijavljivanje (Važeće samo ako ste postavili \"Lock after session end\")"), - ("Enable Direct IP Access", "Dozvoli direktan pristup preko IP"), + ("Enable direct IP access", "Dozvoli direktan pristup preko IP"), ("Rename", "Preimenuj"), ("Space", "Prazno"), - ("Create Desktop Shortcut", "Kreiraj prečicu na radnoj površini"), + ("Create desktop shortcut", "Kreiraj prečicu na radnoj površini"), ("Change Path", "Promeni putanju"), ("Create Folder", "Kreiraj direktorijum"), ("Please enter the folder name", "Unesite ime direktorijuma"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Zadrži RustDesk kao pozadinski servis"), ("Ignore Battery Optimizations", "Zanemari optimizacije baterije"), ("android_open_battery_optimizations_tip", "Ako želite da onemogućite ovu funkciju, molimo idite na sledeću stranicu za podešavanje RustDesk aplikacije, pronađite i uđite u [Battery], isključite [Unrestricted]"), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Konekcija nije dozvoljena"), ("Legacy mode", "Zastareli mod"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Koristi trajnu lozinku"), ("Use both passwords", "Koristi obe lozinke"), ("Set permanent password", "Postavi trajnu lozinku"), - ("Enable Remote Restart", "Omogući daljinsko restartovanje"), - ("Allow remote restart", "Dozvoli daljinsko restartovanje"), - ("Restart Remote Device", "Restartuj daljinski uređaj"), + ("Enable remote restart", "Omogući daljinsko restartovanje"), + ("Restart remote device", "Restartuj daljinski uređaj"), ("Are you sure you want to restart", "Da li ste sigurni da želite restart"), - ("Restarting Remote Device", "Restartovanje daljinskog uređaja"), + ("Restarting remote device", "Restartovanje daljinskog uređaja"), ("remote_restarting_tip", "Udaljeni uređaj se restartuje, molimo zatvorite ovu poruku i ponovo se kasnije povežite trajnom šifrom"), ("Copied", "Kopirano"), ("Exit Fullscreen", "Napusti mod celog ekrana"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Prema sistemu"), ("Enable hardware codec", "Omogući hardverski kodek"), ("Unlock Security Settings", "Otključaj postavke bezbednosti"), - ("Enable Audio", "Dozvoli zvuk"), + ("Enable audio", "Dozvoli zvuk"), ("Unlock Network Settings", "Otključaj postavke mreže"), ("Server", "Server"), ("Direct IP Access", "Direktan IP pristup"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Promeni"), ("Start session recording", "Započni snimanje sesije"), ("Stop session recording", "Zaustavi snimanje sesije"), - ("Enable Recording Session", "Omogući snimanje sesije"), - ("Allow recording session", "Dozvoli snimanje sesije"), - ("Enable LAN Discovery", "Omogući LAN otkrivanje"), - ("Deny LAN Discovery", "Zabrani LAN otkrivanje"), + ("Enable recording session", "Omogući snimanje sesije"), + ("Enable LAN discovery", "Omogući LAN otkrivanje"), + ("Deny LAN discovery", "Zabrani LAN otkrivanje"), ("Write a message", "Napiši poruku"), ("Prompt", "Prompt"), ("Please wait for confirmation of UAC...", "Molimo sačekajte UAC potvrdu..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wayland eksperiment savet"), ("Right click to select tabs", "Desni klik za izbor kartica"), ("Skipped", ""), - ("Add to Address Book", "Dodaj u adresar"), + ("Add to address book", "Dodaj u adresar"), ("Group", "Grupa"), ("Search", "Pretraga"), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index b73836e05..d110f6c2d 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Redo"), ("Established", "Uppkopplad"), ("connecting_status", "Ansluter till RustDesk..."), - ("Enable Service", "Sätt på tjänsten"), - ("Start Service", "Starta tjänsten"), + ("Enable service", "Sätt på tjänsten"), + ("Start service", "Starta tjänsten"), ("Service is running", "Tjänsten är startad"), ("Service is not running", "Tjänsten är ej startad"), ("not_ready_status", "Ej redo. Kontrollera din nätverksanslutning"), ("Control Remote Desktop", "Kontrollera fjärrskrivbord"), - ("Transfer File", "Överför fil"), + ("Transfer file", "Överför fil"), ("Connect", "Anslut"), - ("Recent Sessions", "Dina senaste sessioner"), - ("Address Book", "Addressbok"), + ("Recent sessions", "Dina senaste sessioner"), + ("Address book", "Addressbok"), ("Confirmation", "Bekräftelse"), - ("TCP Tunneling", "TCP Tunnel"), + ("TCP tunneling", "TCP Tunnel"), ("Remove", "Ta bort"), ("Refresh random password", "Skapa nytt slumpmässigt lösenord"), ("Set your own password", "Skapa ditt eget lösenord"), - ("Enable Keyboard/Mouse", "Tillåt tangentbord/mus"), - ("Enable Clipboard", "Tillåt urklipp"), - ("Enable File Transfer", "Tillåt filöverföring"), - ("Enable TCP Tunneling", "Tillåt TCP tunnel"), + ("Enable keyboard/mouse", "Tillåt tangentbord/mus"), + ("Enable clipboard", "Tillåt urklipp"), + ("Enable file transfer", "Tillåt filöverföring"), + ("Enable TCP tunneling", "Tillåt TCP tunnel"), ("IP Whitelisting", "IP Vitlisting"), ("ID/Relay Server", "ID/Relay Server"), - ("Import Server Config", "Importera Server config"), + ("Import server config", "Importera Server config"), ("Export Server Config", "Exportera Server config"), ("Import server configuration successfully", "Importering lyckades"), ("Export server configuration successfully", "Exportering lyckades"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Acceptera"), ("Dismiss", "Tillåt inte"), ("Disconnect", "Koppla ifrån"), - ("Allow using keyboard and mouse", "Tillåt tangentbord och mus"), - ("Allow using clipboard", "Tillåt urklipp"), - ("Allow hearing sound", "Tillåt att höra ljud"), - ("Allow file copy and paste", "Tillåt kopiering av filer"), + ("Enable file copy and paste", "Tillåt kopiering av filer"), ("Connected", "Ansluten"), ("Direct and encrypted connection", "Direkt och krypterad anslutning"), ("Relayed and encrypted connection", "Vidarebefodrad och krypterad anslutning"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Loggar in..."), ("Enable RDP session sharing", "Tillåt RDP sessionsdelning"), ("Auto Login", "Auto Login (Bara giltigt om du sätter \"Lås efter sessionens slut\")"), - ("Enable Direct IP Access", "Tillåt direkt IP anslutningar"), + ("Enable direct IP access", "Tillåt direkt IP anslutningar"), ("Rename", "Byt namn"), ("Space", "Mellanslag"), - ("Create Desktop Shortcut", "Skapa skrivbordsgenväg"), + ("Create desktop shortcut", "Skapa skrivbordsgenväg"), ("Change Path", "Ändra plats"), ("Create Folder", "Skapa mapp"), ("Please enter the folder name", "Skriv in namnet på mappen"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Behåll RustDesk i bakgrunden"), ("Ignore Battery Optimizations", "Ignorera batterioptimering"), ("android_open_battery_optimizations_tip", "Om du vill stänga av denna funktion, gå till nästa RustDesk programs inställningar, hitta [Batteri], Checka ur [Obegränsad]"), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "Anslutning ej tillåten"), ("Legacy mode", "Legacy mode"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Använd permanent lösenord"), ("Use both passwords", "Använd båda lösenorden"), ("Set permanent password", "Ställ in permanent lösenord"), - ("Enable Remote Restart", "Sätt på fjärromstart"), - ("Allow remote restart", "Tillåt fjärromstart"), - ("Restart Remote Device", "Starta om fjärrenheten"), + ("Enable remote restart", "Sätt på fjärromstart"), + ("Restart remote device", "Starta om fjärrenheten"), ("Are you sure you want to restart", "Är du säker att du vill starta om?"), - ("Restarting Remote Device", "Startar om fjärrenheten"), + ("Restarting remote device", "Startar om fjärrenheten"), ("remote_restarting_tip", "Enheten startar om, stäng detta meddelande och anslut igen om en liten stund"), ("Copied", "Kopierad"), ("Exit Fullscreen", "Gå ur fullskärmsläge"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Följ system"), ("Enable hardware codec", "Aktivera hårdvarucodec"), ("Unlock Security Settings", "Lås upp säkerhetsinställningar"), - ("Enable Audio", "Sätt på ljud"), + ("Enable audio", "Sätt på ljud"), ("Unlock Network Settings", "Lås upp nätverksinställningar"), ("Server", "Server"), ("Direct IP Access", "Direkt IP åtkomst"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Byt"), ("Start session recording", "Starta inspelning"), ("Stop session recording", "Avsluta inspelning"), - ("Enable Recording Session", "Sätt på sessionsinspelning"), - ("Allow recording session", "Tillåt sessionsinspelning"), - ("Enable LAN Discovery", "Sätt på LAN upptäckt"), - ("Deny LAN Discovery", "Neka LAN upptäckt"), + ("Enable recording session", "Sätt på sessionsinspelning"), + ("Enable LAN discovery", "Sätt på LAN upptäckt"), + ("Deny LAN discovery", "Neka LAN upptäckt"), ("Write a message", "Skriv ett meddelande"), ("Prompt", "Prompt"), ("Please wait for confirmation of UAC...", "Var god vänta för UAC bekräftelse..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index f14767127..f6ca3f7c4 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", ""), ("Established", ""), ("connecting_status", ""), - ("Enable Service", ""), - ("Start Service", ""), + ("Enable service", ""), + ("Start service", ""), ("Service is running", ""), ("Service is not running", ""), ("not_ready_status", ""), ("Control Remote Desktop", ""), - ("Transfer File", ""), + ("Transfer file", ""), ("Connect", ""), - ("Recent Sessions", ""), - ("Address Book", ""), + ("Recent sessions", ""), + ("Address book", ""), ("Confirmation", ""), - ("TCP Tunneling", ""), + ("TCP tunneling", ""), ("Remove", ""), ("Refresh random password", ""), ("Set your own password", ""), - ("Enable Keyboard/Mouse", ""), - ("Enable Clipboard", ""), - ("Enable File Transfer", ""), - ("Enable TCP Tunneling", ""), + ("Enable keyboard/mouse", ""), + ("Enable clipboard", ""), + ("Enable file transfer", ""), + ("Enable TCP tunneling", ""), ("IP Whitelisting", ""), ("ID/Relay Server", ""), - ("Import Server Config", ""), + ("Import server config", ""), ("Export Server Config", ""), ("Import server configuration successfully", ""), ("Export server configuration successfully", ""), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", ""), ("Dismiss", ""), ("Disconnect", ""), - ("Allow using keyboard and mouse", ""), - ("Allow using clipboard", ""), - ("Allow hearing sound", ""), - ("Allow file copy and paste", ""), + ("Enable file copy and paste", ""), ("Connected", ""), ("Direct and encrypted connection", ""), ("Relayed and encrypted connection", ""), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", ""), ("Enable RDP session sharing", ""), ("Auto Login", ""), - ("Enable Direct IP Access", ""), + ("Enable direct IP access", ""), ("Rename", ""), ("Space", ""), - ("Create Desktop Shortcut", ""), + ("Create desktop shortcut", ""), ("Change Path", ""), ("Create Folder", ""), ("Please enter the folder name", ""), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", ""), ("Ignore Battery Optimizations", ""), ("android_open_battery_optimizations_tip", ""), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", ""), ("Legacy mode", ""), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", ""), ("Use both passwords", ""), ("Set permanent password", ""), - ("Enable Remote Restart", ""), - ("Allow remote restart", ""), - ("Restart Remote Device", ""), + ("Enable remote restart", ""), + ("Restart remote device", ""), ("Are you sure you want to restart", ""), - ("Restarting Remote Device", ""), + ("Restarting remote device", ""), ("remote_restarting_tip", ""), ("Copied", ""), ("Exit Fullscreen", ""), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", ""), ("Enable hardware codec", ""), ("Unlock Security Settings", ""), - ("Enable Audio", ""), + ("Enable audio", ""), ("Unlock Network Settings", ""), ("Server", ""), ("Direct IP Access", ""), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", ""), ("Start session recording", ""), ("Stop session recording", ""), - ("Enable Recording Session", ""), - ("Allow recording session", ""), - ("Enable LAN Discovery", ""), - ("Deny LAN Discovery", ""), + ("Enable recording session", ""), + ("Enable LAN discovery", ""), + ("Deny LAN discovery", ""), ("Write a message", ""), ("Prompt", ""), ("Please wait for confirmation of UAC...", ""), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", ""), ("Right click to select tabs", ""), ("Skipped", ""), - ("Add to Address Book", ""), + ("Add to address book", ""), ("Group", ""), ("Search", ""), ("Closed manually by web console", ""), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 223d14d29..2bce9eb36 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "พร้อม"), ("Established", "เชื่อมต่อแล้ว"), ("connecting_status", "กำลังเชื่อมต่อไปยังเครือข่าย RustDesk..."), - ("Enable Service", "เปิดใช้การงานเซอร์วิส"), - ("Start Service", "เริ่มต้นใช้งานเซอร์วิส"), + ("Enable service", "เปิดใช้การงานเซอร์วิส"), + ("Start service", "เริ่มต้นใช้งานเซอร์วิส"), ("Service is running", "เซอร์วิสกำลังทำงาน"), ("Service is not running", "เซอร์วิสไม่ทำงาน"), ("not_ready_status", "ไม่พร้อมใช้งาน กรุณาตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณ"), ("Control Remote Desktop", "การควบคุมเดสก์ท็อปปลายทาง"), - ("Transfer File", "การถ่ายโอนไฟล์"), + ("Transfer file", "การถ่ายโอนไฟล์"), ("Connect", "เชื่อมต่อ"), - ("Recent Sessions", "เซสชันล่าสุด"), - ("Address Book", "สมุดรายชื่อ"), + ("Recent sessions", "เซสชันล่าสุด"), + ("Address book", "สมุดรายชื่อ"), ("Confirmation", "การยืนยัน"), - ("TCP Tunneling", "อุโมงค์การเชื่อมต่อ TCP"), + ("TCP tunneling", "อุโมงค์การเชื่อมต่อ TCP"), ("Remove", "ลบ"), ("Refresh random password", "รีเฟรชรหัสผ่านใหม่แบบสุ่ม"), ("Set your own password", "ตั้งรหัสผ่านของคุณเอง"), - ("Enable Keyboard/Mouse", "เปิดการใช้งาน คีย์บอร์ด/เมาส์"), - ("Enable Clipboard", "เปิดการใช้งาน คลิปบอร์ด"), - ("Enable File Transfer", "เปิดการใช้งาน การถ่ายโอนไฟล์"), - ("Enable TCP Tunneling", "เปิดการใช้งาน อุโมงค์การเชื่อมต่อ TCP"), + ("Enable keyboard/mouse", "เปิดการใช้งาน คีย์บอร์ด/เมาส์"), + ("Enable clipboard", "เปิดการใช้งาน คลิปบอร์ด"), + ("Enable file transfer", "เปิดการใช้งาน การถ่ายโอนไฟล์"), + ("Enable TCP tunneling", "เปิดการใช้งาน อุโมงค์การเชื่อมต่อ TCP"), ("IP Whitelisting", "IP ไวท์ลิสต์"), ("ID/Relay Server", "เซิร์ฟเวอร์ ID/Relay"), - ("Import Server Config", "นำเข้าการตั้งค่าเซิร์ฟเวอร์"), + ("Import server config", "นำเข้าการตั้งค่าเซิร์ฟเวอร์"), ("Export Server Config", "ส่งออกการตั้งค่าเซิร์ฟเวอร์"), ("Import server configuration successfully", "นำเข้าการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"), ("Export server configuration successfully", "ส่งออกการตั้งค่าเซิร์ฟเวอร์เสร็จสมบูรณ์"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "ยอมรับ"), ("Dismiss", "ปิด"), ("Disconnect", "ยกเลิกการเชื่อมต่อ"), - ("Allow using keyboard and mouse", "อนุญาตให้ใช้งานคีย์บอร์ดและเมาส์"), - ("Allow using clipboard", "อนุญาตให้ใช้คลิปบอร์ด"), - ("Allow hearing sound", "อนุญาตให้ได้ยินเสียง"), - ("Allow file copy and paste", "อนุญาตให้มีการคัดลอกและวางไฟล์"), + ("Enable file copy and paste", "อนุญาตให้มีการคัดลอกและวางไฟล์"), ("Connected", "เชื่อมต่อแล้ว"), ("Direct and encrypted connection", "การเชื่อมต่อตรงที่มีการเข้ารหัส"), ("Relayed and encrypted connection", "การเชื่อมต่อแบบ Relay ที่มีการเข้ารหัส"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "กำลังเข้าสู่ระบบ..."), ("Enable RDP session sharing", "เปิดการใช้งานการแชร์เซสชัน RDP"), ("Auto Login", "เข้าสู่ระบอัตโนมัติ"), - ("Enable Direct IP Access", "เปิดการใช้งาน IP ตรง"), + ("Enable direct IP access", "เปิดการใช้งาน IP ตรง"), ("Rename", "ปลายทาง"), ("Space", "พื้นที่ว่าง"), - ("Create Desktop Shortcut", "สร้างทางลัดบนเดสก์ท็อป"), + ("Create desktop shortcut", "สร้างทางลัดบนเดสก์ท็อป"), ("Change Path", "เปลี่ยนตำแหน่ง"), ("Create Folder", "สร้างโฟลเดอร์"), ("Please enter the folder name", "กรุณาใส่ชื่อโฟลเดอร์"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "คงสถานะการทำงานเบื้องหลังของเซอร์วิส RustDesk"), ("Ignore Battery Optimizations", "เพิกเฉยการตั้งค่าการใช้งาน Battery Optimization"), ("android_open_battery_optimizations_tip", "หากคุณต้องการปิดการใช้งานฟีเจอร์นี้ กรุณาไปยังหน้าตั้งค่าในแอปพลิเคชัน RustDesk ค้นหาหัวข้อ [Battery] และยกเลิกการเลือกรายการ [Unrestricted]"), - ("Start on Boot", "เริ่มต้นเมื่อเปิดเครื่อง"), + ("Start on boot", "เริ่มต้นเมื่อเปิดเครื่อง"), ("Start the screen sharing service on boot, requires special permissions", "เริ่มต้นใช้งานเซอร์วิสสำหรับการแบ่งปันหน้าจอเมื่อเปิดเครื่อง (ต้องมีการให้สิทธิ์การใช้งานพิเศษเพิ่มเติม)"), ("Connection not allowed", "การเชื่อมต่อไม่อนุญาต"), ("Legacy mode", "โหมดดั้งเดิม"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "ใช้รหัสผ่านถาวร"), ("Use both passwords", "ใช้รหัสผ่านทั้งสองแบบ"), ("Set permanent password", "ตั้งค่ารหัสผ่านถาวร"), - ("Enable Remote Restart", "เปิดการใช้งานการรีสตาร์ทระบบทางไกล"), - ("Allow remote restart", "อนุญาตการรีสตาร์ทระบบทางไกล"), - ("Restart Remote Device", "รีสตาร์ทอุปกรณ์ปลายทาง"), + ("Enable remote restart", "เปิดการใช้งานการรีสตาร์ทระบบทางไกล"), + ("Restart remote device", "รีสตาร์ทอุปกรณ์ปลายทาง"), ("Are you sure you want to restart", "คุณแน่ใจหรือไม่ที่จะรีสตาร์ท"), - ("Restarting Remote Device", "กำลังรีสตาร์ทระบบปลายทาง"), + ("Restarting remote device", "กำลังรีสตาร์ทระบบปลายทาง"), ("remote_restarting_tip", "ระบบปลายทางกำลังรีสตาร์ท กรุณาปิดกล่องข้อความนี้และดำเนินการเขื่อมต่อใหม่อีกครั้งด้วยรหัสผ่านถาวรหลังจากผ่านไปซักครู่"), ("Copied", "คัดลอกแล้ว"), ("Exit Fullscreen", "ออกจากเต็มหน้าจอ"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "ตามระบบ"), ("Enable hardware codec", "เปิดการใช้งานฮาร์ดแวร์ codec"), ("Unlock Security Settings", "ปลดล็อคการตั้งค่าความปลอดภัย"), - ("Enable Audio", "เปิดการใช้งานเสียง"), + ("Enable audio", "เปิดการใช้งานเสียง"), ("Unlock Network Settings", "ปลดล็อคการตั้งค่าเครือข่าย"), ("Server", "เซิร์ฟเวอร์"), ("Direct IP Access", "การเข้าถึง IP ตรง"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "เปลี่ยน"), ("Start session recording", "เริ่มต้นการบันทึกเซสชัน"), ("Stop session recording", "หยุดการบันทึกเซสซัน"), - ("Enable Recording Session", "เปิดใช้งานการบันทึกเซสชัน"), - ("Allow recording session", "อนุญาตการบันทึกเซสชัน"), - ("Enable LAN Discovery", "เปิดการใช้งานการค้นหาในวง LAN"), - ("Deny LAN Discovery", "ปฏิเสธการใช้งานการค้นหาในวง LAN"), + ("Enable recording session", "เปิดใช้งานการบันทึกเซสชัน"), + ("Enable LAN discovery", "เปิดการใช้งานการค้นหาในวง LAN"), + ("Deny LAN discovery", "ปฏิเสธการใช้งานการค้นหาในวง LAN"), ("Write a message", "เขียนข้อความ"), ("Prompt", ""), ("Please wait for confirmation of UAC...", "กรุณารอการยืนยันจาก UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "การสนับสนุน Wayland ยังอยู่ในขั้นตอนการทดลอง กรุณาใช้ X11 หากคุณต้องการใช้งานการเข้าถึงแบบไม่มีผู้ดูแล"), ("Right click to select tabs", "คลิกขวาเพื่อเลือกแท็บ"), ("Skipped", "ข้าม"), - ("Add to Address Book", "เพิ่มไปยังสมุดรายชื่อ"), + ("Add to address book", "เพิ่มไปยังสมุดรายชื่อ"), ("Group", "กลุ่ม"), ("Search", "ค้นหา"), ("Closed manually by web console", "ถูกปิดโดยเว็บคอนโซล"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 263df3741..6dcab0848 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Hazır"), ("Established", "Bağlantı sağlandı"), ("connecting_status", "Bağlanılıyor "), - ("Enable Service", "Servisi aktif et"), - ("Start Service", "Servisi başlat"), + ("Enable service", "Servisi aktif et"), + ("Start service", "Servisi başlat"), ("Service is running", "Servis çalışıyor"), ("Service is not running", "Servis çalışmıyor"), ("not_ready_status", "Hazır değil. Bağlantınızı kontrol edin"), ("Control Remote Desktop", "Bağlanılacak Uzak Bağlantı ID"), - ("Transfer File", "Dosya transferi"), + ("Transfer file", "Dosya transferi"), ("Connect", "Bağlan"), - ("Recent Sessions", "Son Bağlanılanlar"), - ("Address Book", "Adres Defteri"), + ("Recent sessions", "Son Bağlanılanlar"), + ("Address book", "Adres Defteri"), ("Confirmation", "Onayla"), - ("TCP Tunneling", "TCP Tünelleri"), + ("TCP tunneling", "TCP Tünelleri"), ("Remove", "Kaldır"), ("Refresh random password", "Yeni rastgele şifre oluştur"), ("Set your own password", "Kendi şifreni oluştur"), - ("Enable Keyboard/Mouse", "Klavye ve Fareye izin ver"), - ("Enable Clipboard", "Kopyalanan geçici veriye izin ver"), - ("Enable File Transfer", "Dosya Transferine izin ver"), - ("Enable TCP Tunneling", "TCP Tüneline izin ver"), + ("Enable keyboard/mouse", "Klavye ve Fareye izin ver"), + ("Enable clipboard", "Kopyalanan geçici veriye izin ver"), + ("Enable file transfer", "Dosya Transferine izin ver"), + ("Enable TCP tunneling", "TCP Tüneline izin ver"), ("IP Whitelisting", "İzinli IP listesi"), ("ID/Relay Server", "ID/Relay Sunucusu"), - ("Import Server Config", "Sunucu ayarlarını içe aktar"), + ("Import server config", "Sunucu ayarlarını içe aktar"), ("Export Server Config", "Sunucu Yapılandırmasını Dışa Aktar"), ("Import server configuration successfully", "Sunucu ayarları başarıyla içe aktarıldı"), ("Export server configuration successfully", "Sunucu yapılandırmasını başarıyla dışa aktar"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Kabul Et"), ("Dismiss", "Reddet"), ("Disconnect", "Bağlanıyı kes"), - ("Allow using keyboard and mouse", "Klavye ve fare kullanımına izin ver"), - ("Allow using clipboard", "Pano kullanımına izin ver"), - ("Allow hearing sound", "Sesi duymaya izin ver"), - ("Allow file copy and paste", "Dosya kopyalamaya ve yapıştırmaya izin ver"), + ("Enable file copy and paste", "Dosya kopyalamaya ve yapıştırmaya izin ver"), ("Connected", "Bağlandı"), ("Direct and encrypted connection", "Doğrudan ve şifreli bağlantı"), ("Relayed and encrypted connection", "Aktarmalı ve şifreli bağlantı"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Giriş yapılıyor..."), ("Enable RDP session sharing", "RDP oturum paylaşımını etkinleştir"), ("Auto Login", "Otomatik giriş"), - ("Enable Direct IP Access", "Doğrudan IP Erişimini Etkinleştir"), + ("Enable direct IP access", "Doğrudan IP Erişimini Etkinleştir"), ("Rename", "Yeniden adlandır"), ("Space", "Boşluk"), - ("Create Desktop Shortcut", "Masaüstü kısayolu oluşturun"), + ("Create desktop shortcut", "Masaüstü kısayolu oluşturun"), ("Change Path", "Yolu değiştir"), ("Create Folder", "Klasör oluşturun"), ("Please enter the folder name", "Lütfen klasör adını girin"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "RustDesk arka plan hizmetini sürdürün"), ("Ignore Battery Optimizations", "Pil Optimizasyonlarını Yoksay"), ("android_open_battery_optimizations_tip", ""), - ("Start on Boot", ""), + ("Start on boot", ""), ("Start the screen sharing service on boot, requires special permissions", ""), ("Connection not allowed", "bağlantıya izin verilmedi"), ("Legacy mode", "Eski mod"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Kalıcı şifre kullan"), ("Use both passwords", "İki şifreyide kullan"), ("Set permanent password", "Kalıcı şifre oluştur"), - ("Enable Remote Restart", "Uzaktan yeniden başlatmayı aktif et"), - ("Allow remote restart", "Uzaktan yeniden başlatmaya izin ver"), - ("Restart Remote Device", "Uzaktaki cihazı yeniden başlat"), + ("Enable remote restart", "Uzaktan yeniden başlatmayı aktif et"), + ("Restart remote device", "Uzaktaki cihazı yeniden başlat"), ("Are you sure you want to restart", "Yeniden başlatmak istediğinize emin misin?"), - ("Restarting Remote Device", "Uzaktan yeniden başlatılıyor"), + ("Restarting remote device", "Uzaktan yeniden başlatılıyor"), ("remote_restarting_tip", "remote_restarting_tip"), ("Copied", "Kopyalandı"), ("Exit Fullscreen", "Tam ekrandan çık"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Sisteme Uy"), ("Enable hardware codec", "Donanımsal codec aktif et"), ("Unlock Security Settings", "Güvenlik Ayarlarını Aç"), - ("Enable Audio", "Sesi Aktif Et"), + ("Enable audio", "Sesi Aktif Et"), ("Unlock Network Settings", "Ağ Ayarlarını Aç"), ("Server", "Sunucu"), ("Direct IP Access", "Direk IP Erişimi"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Değiştir"), ("Start session recording", "Oturum kaydını başlat"), ("Stop session recording", "Oturum kaydını sonlandır"), - ("Enable Recording Session", "Kayıt Oturumunu Aktif Et"), - ("Allow recording session", "Oturum kaydına izin ver"), - ("Enable LAN Discovery", "Yerel Ağ Keşfine İzin Ver"), - ("Deny LAN Discovery", "Yerl Ağ Keşfine İzin Verme"), + ("Enable recording session", "Kayıt Oturumunu Aktif Et"), + ("Enable LAN discovery", "Yerel Ağ Keşfine İzin Ver"), + ("Deny LAN discovery", "Yerl Ağ Keşfine İzin Verme"), ("Write a message", "Bir mesaj yazın"), ("Prompt", "İstem"), ("Please wait for confirmation of UAC...", "UAC onayı için lütfen bekleyiniz..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wayland desteği deneysel aşamada olduğundan, gerektiğinde X11'i kullanmanız önerilir"), ("Right click to select tabs", "Sekmeleri seçmek için sağ tıklayın"), ("Skipped", "Atlandı"), - ("Add to Address Book", "Adres Defterine Ekle"), + ("Add to address book", "Adres Defterine Ekle"), ("Group", "Grup"), ("Search", "Ara"), ("Closed manually by web console", "Web konsoluyla manuel olarak kapatıldı"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 86116b6ce..33583f339 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "就緒"), ("Established", "已建立"), ("connecting_status", "正在連線到 RustDesk 網路 ..."), - ("Enable Service", "啟用服務"), - ("Start Service", "啟動服務"), + ("Enable service", "啟用服務"), + ("Start service", "啟動服務"), ("Service is running", "服務正在執行"), ("Service is not running", "服務尚未執行"), ("not_ready_status", "尚未就緒,請檢查您的網路連線。"), ("Control Remote Desktop", "控制遠端桌面"), - ("Transfer File", "傳輸檔案"), + ("Transfer file", "傳輸檔案"), ("Connect", "連線"), - ("Recent Sessions", "近期的工作階段"), - ("Address Book", "通訊錄"), + ("Recent sessions", "近期的工作階段"), + ("Address book", "通訊錄"), ("Confirmation", "確認"), - ("TCP Tunneling", "TCP 通道"), + ("TCP tunneling", "TCP 通道"), ("Remove", "移除"), ("Refresh random password", "重新產生隨機密碼"), ("Set your own password", "自行設定密碼"), - ("Enable Keyboard/Mouse", "啟用鍵盤和滑鼠"), - ("Enable Clipboard", "啟用剪貼簿"), - ("Enable File Transfer", "啟用檔案傳輸"), - ("Enable TCP Tunneling", "啟用 TCP 通道"), + ("Enable keyboard/mouse", "啟用鍵盤和滑鼠"), + ("Enable clipboard", "啟用剪貼簿"), + ("Enable file transfer", "啟用檔案傳輸"), + ("Enable TCP tunneling", "啟用 TCP 通道"), ("IP Whitelisting", "IP 白名單"), ("ID/Relay Server", "ID / 中繼伺服器"), - ("Import Server Config", "匯入伺服器設定"), + ("Import server config", "匯入伺服器設定"), ("Export Server Config", "匯出伺服器設定"), ("Import server configuration successfully", "匯入伺服器設定成功"), ("Export server configuration successfully", "匯出伺服器設定成功"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "接受"), ("Dismiss", "關閉"), ("Disconnect", "中斷連線"), - ("Allow using keyboard and mouse", "允許使用鍵盤和滑鼠"), - ("Allow using clipboard", "允許使用剪貼簿"), - ("Allow hearing sound", "允許分享音訊"), - ("Allow file copy and paste", "允許檔案複製和貼上"), + ("Enable file copy and paste", "允許檔案複製和貼上"), ("Connected", "已連線"), ("Direct and encrypted connection", "加密直接連線"), ("Relayed and encrypted connection", "加密中繼連線"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "正在登入 ..."), ("Enable RDP session sharing", "啟用 RDP 工作階段共享"), ("Auto Login", "自動登入 (只在您設定「工作階段結束後鎖定」時有效)"), - ("Enable Direct IP Access", "啟用 IP 直接存取"), + ("Enable direct IP access", "啟用 IP 直接存取"), ("Rename", "重新命名"), ("Space", "空白"), - ("Create Desktop Shortcut", "新增桌面捷徑"), + ("Create desktop shortcut", "新增桌面捷徑"), ("Change Path", "更改路徑"), ("Create Folder", "新增資料夾"), ("Please enter the folder name", "請輸入資料夾名稱"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "保持 RustDesk 後台服務"), ("Ignore Battery Optimizations", "忽略電池最佳化"), ("android_open_battery_optimizations_tip", "如果您想要停用此功能,請前往下一個 RustDesk 應用程式設定頁面,找到並進入「電池」,取消勾選「不受限制」"), - ("Start on Boot", "開機時啟動"), + ("Start on boot", "開機時啟動"), ("Start the screen sharing service on boot, requires special permissions", "開機時啟動螢幕分享服務,需要特殊權限。"), ("Connection not allowed", "不允許連線"), ("Legacy mode", "傳統模式"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "使用固定密碼"), ("Use both passwords", "同時使用兩種密碼"), ("Set permanent password", "設定固定密碼"), - ("Enable Remote Restart", "啟用遠端重新啟動"), - ("Allow remote restart", "允許遠端重新啟動"), - ("Restart Remote Device", "重新啟動遠端裝置"), + ("Enable remote restart", "啟用遠端重新啟動"), + ("Restart remote device", "重新啟動遠端裝置"), ("Are you sure you want to restart", "確定要重新啟動嗎?"), - ("Restarting Remote Device", "正在重新啟動遠端裝置"), + ("Restarting remote device", "正在重新啟動遠端裝置"), ("remote_restarting_tip", "遠端裝置正在重新啟動,請關閉此對話框,並在一段時間後使用永久密碼重新連線"), ("Copied", "已複製"), ("Exit Fullscreen", "退出全螢幕"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "跟隨系統"), ("Enable hardware codec", "啟用硬體編解碼器"), ("Unlock Security Settings", "解鎖安全設定"), - ("Enable Audio", "啟用音訊"), + ("Enable audio", "啟用音訊"), ("Unlock Network Settings", "解鎖網路設定"), ("Server", "伺服器"), ("Direct IP Access", "IP 直接連線"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "變更"), ("Start session recording", "開始錄影"), ("Stop session recording", "停止錄影"), - ("Enable Recording Session", "啟用錄製工作階段"), - ("Allow recording session", "允許錄製工作階段"), - ("Enable LAN Discovery", "允許區域網路探索"), - ("Deny LAN Discovery", "拒絕區域網路探索"), + ("Enable recording session", "啟用錄製工作階段"), + ("Enable LAN discovery", "允許區域網路探索"), + ("Deny LAN discovery", "拒絕區域網路探索"), ("Write a message", "輸入聊天訊息"), ("Prompt", "提示"), ("Please wait for confirmation of UAC...", "請等待對方確認 UAC ..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Wayland 支援處於實驗階段,如果您需要使用無人值守存取,請使用 X11。"), ("Right click to select tabs", "右鍵選擇分頁"), ("Skipped", "已跳過"), - ("Add to Address Book", "新增到通訊錄"), + ("Add to address book", "新增到通訊錄"), ("Group", "群組"), ("Search", "搜尋"), ("Closed manually by web console", "被 Web 控制台手動關閉"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 3bfae65df..5ec923d60 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Готово"), ("Established", "Встановлено"), ("connecting_status", "Підключення до мережі RustDesk..."), - ("Enable Service", "Включити службу"), - ("Start Service", "Запустити службу"), + ("Enable service", "Включити службу"), + ("Start service", "Запустити службу"), ("Service is running", "Служба працює"), ("Service is not running", "Служба не запущена"), ("not_ready_status", "Не готово. Будь ласка, перевірте ваше підключення"), ("Control Remote Desktop", "Керування віддаленою стільницею"), - ("Transfer File", "Надіслати файл"), + ("Transfer file", "Надіслати файл"), ("Connect", "Підключитися"), - ("Recent Sessions", "Нещодавні сеанси"), - ("Address Book", "Адресна книга"), + ("Recent sessions", "Нещодавні сеанси"), + ("Address book", "Адресна книга"), ("Confirmation", "Підтвердження"), - ("TCP Tunneling", "TCP-тунелювання"), + ("TCP tunneling", "TCP-тунелювання"), ("Remove", "Видалити"), ("Refresh random password", "Оновити випадковий пароль"), ("Set your own password", "Встановити свій пароль"), - ("Enable Keyboard/Mouse", "Увімкнути клавіатуру/мишу"), - ("Enable Clipboard", "Увімкнути буфер обміну"), - ("Enable File Transfer", "Увімкнути передачу файлів"), - ("Enable TCP Tunneling", "Увімкнути тунелювання TCP"), + ("Enable keyboard/mouse", "Увімкнути клавіатуру/мишу"), + ("Enable clipboard", "Увімкнути буфер обміну"), + ("Enable file transfer", "Увімкнути передачу файлів"), + ("Enable TCP tunneling", "Увімкнути тунелювання TCP"), ("IP Whitelisting", "Список дозволених IP-адрес"), ("ID/Relay Server", "ID/Сервер ретрансляції"), - ("Import Server Config", "Імпортувати конфігурацію сервера"), + ("Import server config", "Імпортувати конфігурацію сервера"), ("Export Server Config", "Експортувати конфігурацію сервера"), ("Import server configuration successfully", "Конфігурацію сервера успішно імпортовано"), ("Export server configuration successfully", "Конфігурацію сервера успішно експортовано"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Прийняти"), ("Dismiss", "Відхилити"), ("Disconnect", "Відʼєднати"), - ("Allow using keyboard and mouse", "Дозволити використання клавіатури та миші"), - ("Allow using clipboard", "Дозволити використання буфера обміну"), - ("Allow hearing sound", "Дозволити передачу звуку"), - ("Allow file copy and paste", "Дозволити копіювання та вставку файлів"), + ("Enable file copy and paste", "Дозволити копіювання та вставку файлів"), ("Connected", "Підключено"), ("Direct and encrypted connection", "Пряме та зашифроване підключення"), ("Relayed and encrypted connection", "Релейне та зашифроване підключення"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Вхід..."), ("Enable RDP session sharing", "Включити загальний доступ до сеансу RDP"), ("Auto Login", "Автоматичний вхід (дійсний, тільки якщо ви встановили \"Завершення користувацького сеансу після завершення віддаленого підключення\")"), - ("Enable Direct IP Access", "Увімкнути прямий IP-доступ"), + ("Enable direct IP access", "Увімкнути прямий IP-доступ"), ("Rename", "Перейменувати"), ("Space", "Місце"), - ("Create Desktop Shortcut", "Створити ярлик на стільниці"), + ("Create desktop shortcut", "Створити ярлик на стільниці"), ("Change Path", "Змінити шлях"), ("Create Folder", "Створити теку"), ("Please enter the folder name", "Будь ласка, введіть назву для теки"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Зберегти фонову службу RustDesk"), ("Ignore Battery Optimizations", "Ігнорувати оптимізації батареї"), ("android_open_battery_optimizations_tip", "Перейдіть на наступну сторінку налаштувань"), - ("Start on Boot", "Автозапуск"), + ("Start on boot", "Автозапуск"), ("Start the screen sharing service on boot, requires special permissions", "Запустити службу службу спільного доступу до екрана під час завантаження, потребує спеціальних дозволів"), ("Connection not allowed", "Підключення не дозволено"), ("Legacy mode", "Застарілий режим"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Використовувати постійний пароль"), ("Use both passwords", "Використовувати обидва паролі"), ("Set permanent password", "Встановити постійний пароль"), - ("Enable Remote Restart", "Увімкнути віддалений перезапуск"), - ("Allow remote restart", "Дозволити віддалений перезапуск"), - ("Restart Remote Device", "Перезапустити віддалений пристрій"), + ("Enable remote restart", "Увімкнути віддалений перезапуск"), + ("Restart remote device", "Перезапустити віддалений пристрій"), ("Are you sure you want to restart", "Ви впевнені, що хочете виконати перезапуск?"), - ("Restarting Remote Device", "Перезавантаження віддаленого пристрою"), + ("Restarting remote device", "Перезавантаження віддаленого пристрою"), ("remote_restarting_tip", "Віддалений пристрій перезапускається. Будь ласка, закрийте це повідомлення та через деякий час перепідʼєднайтесь, використовуючи постійний пароль."), ("Copied", "Скопійовано"), ("Exit Fullscreen", "Вийти з повноекранного режиму"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Як в системі"), ("Enable hardware codec", "Увімкнути апаратний кодек"), ("Unlock Security Settings", "Розблокувати налаштування безпеки"), - ("Enable Audio", "Увімкнути аудіо"), + ("Enable audio", "Увімкнути аудіо"), ("Unlock Network Settings", "Розблокувати мережеві налаштування"), ("Server", "Сервер"), ("Direct IP Access", "Прямий IP доступ"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Змінити"), ("Start session recording", "Розпочати запис сесії"), ("Stop session recording", "Закінчити запис сесії"), - ("Enable Recording Session", "Увімкнути запис сесії"), - ("Allow recording session", "Дозволити запис сеансу"), - ("Enable LAN Discovery", "Увімкнути пошук локальної мережі"), - ("Deny LAN Discovery", "Заборонити виявлення локальної мережі"), + ("Enable recording session", "Увімкнути запис сесії"), + ("Enable LAN discovery", "Увімкнути пошук локальної мережі"), + ("Deny LAN discovery", "Заборонити виявлення локальної мережі"), ("Write a message", "Написати повідомлення"), ("Prompt", "Підказка"), ("Please wait for confirmation of UAC...", "Будь ласка, зачекайте підтвердження UAC..."), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Підтримка Wayland на експериментальній стадії, будь ласка, використовуйте X11, якщо необхідний автоматичний доступ."), ("Right click to select tabs", "Правий клік для вибору вкладки"), ("Skipped", "Пропущено"), - ("Add to Address Book", "Додати IP до Адресної книги"), + ("Add to address book", "Додати IP до Адресної книги"), ("Group", "Група"), ("Search", "Пошук"), ("Closed manually by web console", "Закрито вручну з веб-консолі"), @@ -565,12 +560,15 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Open in new window", "Відкрити в новому вікні"), ("Show displays as individual windows", "Відображати дисплеї в якості окремих вікон"), ("Use all my displays for the remote session", "Використовувати всі мої дисплеї для віддаленого сеансу"), - ("selinux_tip", ""), - ("Change view", ""), - ("Big tiles", ""), - ("Small tiles", ""), - ("List", ""), - ("Virtual display", ""), - ("Plug out all", ""), + ("selinux_tip", "SELinux увімкнено на вашому пристрої, що може ускладнити для іншої сторони віддалене керування за допомогою RustDesk."), + ("Change view", "Режим перегляду"), + ("Big tiles", "Великі плитки"), + ("Small tiles", "Маленькі плитки"), + ("List", "Список"), + ("Virtual display", "Віртуальний дисплей"), + ("Plug out all", "Відключити все"), + ("True color (4:4:4)", "Спражній колір (4:4:4)"), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index eacd49b85..c2e37cf99 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -8,28 +8,28 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Ready", "Sẵn sàng"), ("Established", "Đã đuợc thiết lập"), ("connecting_status", "Đang kết nối đến mạng lưới RustDesk..."), - ("Enable Service", "Bật dịch vụ"), - ("Start Service", "Bắt đầu dịch vụ"), + ("Enable service", "Bật dịch vụ"), + ("Start service", "Bắt đầu dịch vụ"), ("Service is running", "Dịch vụ hiện đang chạy"), ("Service is not running", "Dịch vụ hiện đang dừng"), ("not_ready_status", "Hiện chưa sẵn sàng. Hãy kiểm tra kết nối của bạn"), ("Control Remote Desktop", "Điều khiển Desktop Từ Xa"), - ("Transfer File", "Truyền Tệp Tin"), + ("Transfer file", "Truyền Tệp Tin"), ("Connect", "Kết nối"), - ("Recent Sessions", "Các session gần đây"), - ("Address Book", "Quyển địa chỉ"), + ("Recent sessions", "Các session gần đây"), + ("Address book", "Quyển địa chỉ"), ("Confirmation", "Xác nhận"), - ("TCP Tunneling", "TCP Tunneling"), + ("TCP tunneling", "TCP tunneling"), ("Remove", "Loại bỏ"), ("Refresh random password", "Làm mới mật khẩu ngẫu nhiên"), ("Set your own password", "Đặt mật khẩu riêng"), - ("Enable Keyboard/Mouse", "Cho phép sử dụng bàn phím/chuột"), - ("Enable Clipboard", "Cho phép sử dụng clipboard"), - ("Enable File Transfer", "Cho phép truyền tệp tin"), - ("Enable TCP Tunneling", "Cho phép TCP Tunneling"), + ("Enable keyboard/mouse", "Cho phép sử dụng bàn phím/chuột"), + ("Enable clipboard", "Cho phép sử dụng clipboard"), + ("Enable file transfer", "Cho phép truyền tệp tin"), + ("Enable TCP tunneling", "Cho phép TCP tunneling"), ("IP Whitelisting", "Cho phép IP"), ("ID/Relay Server", "Máy chủ ID/chuyển tiếp"), - ("Import Server Config", "Nhập cấu hình máy chủ"), + ("Import server config", "Nhập cấu hình máy chủ"), ("Export Server Config", "Xuất cấu hình máy chủ"), ("Import server configuration successfully", "Nhập cấu hình máy chủ thành công"), ("Export server configuration successfully", "Xuất cấu hình máy chủ thành công"), @@ -179,10 +179,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Accept", "Chấp nhận"), ("Dismiss", "Bỏ qua"), ("Disconnect", "Ngắt kết nối"), - ("Allow using keyboard and mouse", "Cho phép sử dụng bàn phím và chuột"), - ("Allow using clipboard", "Cho phép sử dụng clipboard"), - ("Allow hearing sound", "Cho phép nghe âm thanh"), - ("Allow file copy and paste", "Cho phép sao chép và dán tệp tin"), + ("Enable file copy and paste", "Cho phép sao chép và dán tệp tin"), ("Connected", "Đã kết nối"), ("Direct and encrypted connection", "Kết nối trực tiếp và đuợc mã hóa"), ("Relayed and encrypted connection", "Kết nối chuyển tiếp và mã hóa"), @@ -193,10 +190,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Logging in...", "Đang đăng nhập"), ("Enable RDP session sharing", "Cho phép chia sẻ phiên kết nối RDP"), ("Auto Login", "Tự động đăng nhập"), - ("Enable Direct IP Access", "Cho phép truy cập trực tiếp qua IP"), + ("Enable direct IP access", "Cho phép truy cập trực tiếp qua IP"), ("Rename", "Đổi tên"), ("Space", "Dấu cách"), - ("Create Desktop Shortcut", "Tạo shortcut trên desktop"), + ("Create desktop shortcut", "Tạo shortcut trên desktop"), ("Change Path", "Đổi địa điểm"), ("Create Folder", "Tạo thư mục"), ("Please enter the folder name", "Hãy nhập tên thư mục"), @@ -311,7 +308,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Keep RustDesk background service", "Giữ dịch vụ nền RustDesk"), ("Ignore Battery Optimizations", "Bỏ qua các tối ưu pin"), ("android_open_battery_optimizations_tip", "Nếu bạn muốn tắt tính năng này, vui lòng chuyển đến trang cài đặt ứng dụng RustDesk tiếp theo, tìm và nhập [Pin], Bỏ chọn [Không hạn chế]"), - ("Start on Boot", "Chạy khi khởi động"), + ("Start on boot", "Chạy khi khởi động"), ("Start the screen sharing service on boot, requires special permissions", "Chạy dịch vụ chia sẻ màn hình khi khởi động, yêu cầu quyền đặc biệt"), ("Connection not allowed", "Kết nối không đuợc phép"), ("Legacy mode", "Chế độ cũ"), @@ -320,11 +317,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Use permanent password", "Sử dụng mật khẩu vĩnh viễn"), ("Use both passwords", "Sử dụng cả hai mật khẩu"), ("Set permanent password", "Đặt mật khẩu vĩnh viễn"), - ("Enable Remote Restart", "Bật khởi động lại từ xa"), - ("Allow remote restart", "Cho phép khởi động lại từ xa"), - ("Restart Remote Device", "Khởi động lại thiết bị từ xa"), + ("Enable remote restart", "Bật khởi động lại từ xa"), + ("Restart remote device", "Khởi động lại thiết bị từ xa"), ("Are you sure you want to restart", "Bạn có chắc bạn muốn khởi động lại không"), - ("Restarting Remote Device", "Đang khởi động lại thiết bị từ xa"), + ("Restarting remote device", "Đang khởi động lại thiết bị từ xa"), ("remote_restarting_tip", "Thiết bị từ xa đang khởi động lại, hãy đóng cửa sổ tin nhắn này và kết nối lại với mật khẩu vĩnh viễn sau một khoảng thời gian"), ("Copied", "Đã sao chép"), ("Exit Fullscreen", "Thoát toàn màn hình"), @@ -354,7 +350,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Follow System", "Theo hệ thống"), ("Enable hardware codec", "Bật codec phần cứng"), ("Unlock Security Settings", "Mở khóa cài đặt bảo mật"), - ("Enable Audio", "Bật âm thanh"), + ("Enable audio", "Bật âm thanh"), ("Unlock Network Settings", "Mở khóa cài đặt mạng"), ("Server", "Máy chủ"), ("Direct IP Access", "Truy cập trực tiếp qua IP"), @@ -373,10 +369,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Change", "Thay đổi"), ("Start session recording", "Bắt đầu ghi hình phiên kết nối"), ("Stop session recording", "Dừng ghi hình phiên kết nối"), - ("Enable Recording Session", "Bật ghi hình phiên kết nối"), - ("Allow recording session", "Cho phép ghi hình phiên kết nối"), - ("Enable LAN Discovery", "Bật phát hiện mạng nội bộ (LAN)"), - ("Deny LAN Discovery", "Từ chối phát hiện mạng nội bộ (LAN)"), + ("Enable recording session", "Bật ghi hình phiên kết nối"), + ("Enable LAN discovery", "Bật phát hiện mạng nội bộ (LAN)"), + ("Deny LAN discovery", "Từ chối phát hiện mạng nội bộ (LAN)"), ("Write a message", "Viết một tin nhắn"), ("Prompt", ""), ("Please wait for confirmation of UAC...", "Vui lòng chờ cho phép UAC"), @@ -410,7 +405,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("wayland_experiment_tip", "Hỗ trợ cho Wayland đang trong giai đoạn thử nghiệm, vui lòng dùng DX11 nếu bạn muốn sử dụng kết nối không giám sát."), ("Right click to select tabs", "Chuột phải để chọn cửa sổ"), ("Skipped", "Đã bỏ qua"), - ("Add to Address Book", "Thêm vào Quyển địa chỉ"), + ("Add to address book", "Thêm vào Quyển địa chỉ"), ("Group", "Nhóm"), ("Search", "Tìm"), ("Closed manually by web console", "Đã đóng thủ công bằng bảng điều khiển web"), @@ -572,5 +567,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("List", ""), ("Virtual display", ""), ("Plug out all", ""), + ("True color (4:4:4)", ""), + ("Enable blocking user input", ""), + ("id_input_tip", ""), ].iter().cloned().collect(); } diff --git a/src/lib.rs b/src/lib.rs index beeb6e46a..8aa520579 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,7 @@ mod ui_session_interface; mod hbbs_http; -#[cfg(windows)] +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] pub mod clipboard_file; #[cfg(windows)] diff --git a/src/platform/macos.rs b/src/platform/macos.rs index 0731ae87a..bc4949678 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -126,7 +126,7 @@ pub fn is_can_screen_recording(prompt: bool) -> bool { if !can_record_screen && prompt { use scrap::{Capturer, Display}; if let Ok(d) = Display::primary() { - Capturer::new(d, true).ok(); + Capturer::new(d).ok(); } } can_record_screen diff --git a/src/server.rs b/src/server.rs index 0e1abfd55..e019f1889 100644 --- a/src/server.rs +++ b/src/server.rs @@ -95,7 +95,7 @@ pub fn new() -> ServerPtr { id_count: hbb_common::rand::random::() % 1000 + 1000, // ensure positive }; server.add_service(Box::new(audio_service::new())); - #[cfg(not(any(target_os = "android", target_os = "ios")))] + #[cfg(not(target_os = "ios"))] server.add_service(Box::new(display_service::new())); server.add_service(Box::new(video_service::new( *display_service::PRIMARY_DISPLAY_IDX, diff --git a/src/server/connection.rs b/src/server/connection.rs index 972884260..29235ab73 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1,5 +1,5 @@ use super::{input_service::*, *}; -#[cfg(windows)] +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::update_clipboard; @@ -44,12 +44,14 @@ use hbb_common::{ use scrap::android::{call_main_service_pointer_input, call_main_service_key_event}; #[cfg(target_os = "android")] use crate::keyboard::client::map_key_to_control_key; +use serde_derive::Serialize; use serde_json::{json, value::Value}; use sha2::{Digest, Sha256}; #[cfg(not(any(target_os = "android", target_os = "ios")))] use std::sync::atomic::Ordering; use std::{ num::NonZeroI64, + path::PathBuf, sync::{atomic::AtomicI64, mpsc as std_mpsc}, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -181,6 +183,7 @@ pub struct Connection { file: bool, restart: bool, recording: bool, + block_input: bool, last_test_delay: i64, network_delay: Option, lock_after_session_end: bool, @@ -194,7 +197,7 @@ pub struct Connection { // by peer disable_audio: bool, // by peer - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] enable_file_transfer: bool, // by peer audio_sender: Option, @@ -225,6 +228,7 @@ pub struct Connection { start_cm_ipc_para: Option, auto_disconnect_timer: Option<(Instant, u64)>, authed_conn_id: Option, + file_remove_log_control: FileRemoveLogControl, } impl ConnInner { @@ -326,13 +330,14 @@ impl Connection { file: Connection::permission("enable-file-transfer"), restart: Connection::permission("enable-remote-restart"), recording: Connection::permission("enable-record-session"), + block_input: Connection::permission("enable-block-input"), last_test_delay: 0, network_delay: None, lock_after_session_end: false, show_remote_cursor: false, ip: "".to_owned(), disable_audio: false, - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] enable_file_transfer: false, disable_clipboard: false, disable_keyboard: false, @@ -367,6 +372,7 @@ impl Connection { }), auto_disconnect_timer: None, authed_conn_id: None, + file_remove_log_control: FileRemoveLogControl::new(id), }; let addr = hbb_common::try_into_v4(addr); if !conn.on_open(addr).await { @@ -395,6 +401,9 @@ impl Connection { if !conn.recording { conn.send_permission(Permission::Recording, false).await; } + if !conn.block_input { + conn.send_permission(Permission::BlockInput, false).await; + } let mut test_delay_timer = time::interval_at(Instant::now() + TEST_DELAY_TIMEOUT, TEST_DELAY_TIMEOUT); let mut last_recv_time = Instant::now(); @@ -476,12 +485,15 @@ impl Connection { } else if &name == "recording" { conn.recording = enabled; conn.send_permission(Permission::Recording, enabled).await; + } else if &name == "block_input" { + conn.block_input = enabled; + conn.send_permission(Permission::BlockInput, enabled).await; } } ipc::Data::RawMessage(bytes) => { allow_err!(conn.stream.send_raw(bytes).await); } - #[cfg(windows)] + #[cfg(any(target_os="windows", target_os="linux", target_os = "macos"))] ipc::Data::ClipboardFile(clip) => { allow_err!(conn.stream.send(&clip_2_msg(clip)).await); } @@ -558,11 +570,11 @@ impl Connection { }, _ = conn.file_timer.tick() => { if !conn.read_jobs.is_empty() { - conn.send_to_cm(ipc::Data::FileTransferLog(fs::serialize_transfer_jobs(&conn.read_jobs))); + conn.send_to_cm(ipc::Data::FileTransferLog(("transfer".to_string(), fs::serialize_transfer_jobs(&conn.read_jobs)))); match fs::handle_read_jobs(&mut conn.read_jobs, &mut conn.stream).await { Ok(log) => { if !log.is_empty() { - conn.send_to_cm(ipc::Data::FileTransferLog(log)); + conn.send_to_cm(ipc::Data::FileTransferLog(("transfer".to_string(), log))); } } Err(err) => { @@ -634,6 +646,7 @@ impl Connection { break; } } + conn.file_remove_log_control.on_timer().drain(..).map(|x| conn.send_to_cm(x)).count(); } _ = test_delay_timer.tick() => { if last_recv_time.elapsed() >= SEC30 { @@ -672,7 +685,6 @@ impl Connection { conn.lr.my_id.clone(), ); video_service::notify_video_frame_fetched(id, None); - scrap::codec::Encoder::update(id, scrap::codec::EncodingUpdate::Remove); if conn.authorized { password::update_temporary_password(); } @@ -1034,7 +1046,7 @@ impl Connection { pi.hostname = DEVICE_NAME.lock().unwrap().clone(); pi.platform = "Android".into(); } - #[cfg(any(target_os = "linux", target_os = "windows"))] + #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] let mut platform_additions = serde_json::Map::new(); #[cfg(target_os = "linux")] { @@ -1064,7 +1076,18 @@ impl Connection { } } - #[cfg(any(target_os = "linux", target_os = "windows"))] + #[cfg(any( + target_os = "windows", + all( + any(target_os = "linux", target_os = "macos"), + feature = "unix-file-copy-paste" + ) + ))] + { + platform_additions.insert("has_file_clipboard".into(), json!(true)); + } + + #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] if !platform_additions.is_empty() { pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into()); } @@ -1164,7 +1187,7 @@ impl Connection { sub_service = true; } } - Self::on_remote_authorized(); + self.on_remote_authorized(); } let mut msg_out = Message::new(); msg_out.set_login_response(res); @@ -1203,9 +1226,10 @@ impl Connection { } } - fn on_remote_authorized() { + fn on_remote_authorized(&self) { use std::sync::Once; - static ONCE: Once = Once::new(); + static _ONCE: Once = Once::new(); + self.update_codec_on_login(); #[cfg(any(target_os = "windows", target_os = "linux"))] if !Config::get_option("allow-remove-wallpaper").is_empty() { // multi connections set once @@ -1214,7 +1238,7 @@ impl Connection { match crate::platform::WallPaperRemover::new() { Ok(remover) => { *wallpaper = Some(remover); - ONCE.call_once(|| { + _ONCE.call_once(|| { shutdown_hooks::add_shutdown_hook(shutdown_hook); }); } @@ -1238,7 +1262,7 @@ impl Connection { self.audio && !self.disable_audio } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] fn file_transfer_enabled(&self) -> bool { self.file && self.enable_file_transfer } @@ -1258,6 +1282,7 @@ impl Connection { file_transfer_enabled: self.file, restart: self.restart, recording: self.recording, + block_input: self.block_input, from_switch: self.from_switch, }); } @@ -1403,8 +1428,8 @@ impl Connection { return Config::get_option(enable_prefix_option).is_empty(); } - fn update_codec_on_login(&self, lr: &LoginRequest) { - if let Some(o) = lr.option.as_ref() { + fn update_codec_on_login(&self) { + if let Some(o) = self.lr.clone().option.as_ref() { if let Some(q) = o.supported_decoding.clone().take() { scrap::codec::Encoder::update( self.inner.id(), @@ -1429,9 +1454,6 @@ impl Connection { if let Some(o) = lr.option.as_ref() { self.options_in_login = Some(o.clone()); } - if lr.union.is_none() { - self.update_codec_on_login(&lr); - } self.video_ack_required = lr.video_ack_required; } @@ -1856,8 +1878,9 @@ impl Connection { } Some(message::Union::Cliprdr(_clip)) => { - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] if let Some(clip) = msg_2_clip(_clip) { + log::debug!("got clipfile from client peer"); self.send_to_cm(ipc::Data::ClipboardFile(clip)) } } @@ -1952,30 +1975,43 @@ impl Connection { } Some(file_action::Union::RemoveDir(d)) => { self.send_fs(ipc::FS::RemoveDir { - path: d.path, + path: d.path.clone(), id: d.id, recursive: d.recursive, }); + self.file_remove_log_control.on_remove_dir(d); } Some(file_action::Union::RemoveFile(f)) => { self.send_fs(ipc::FS::RemoveFile { - path: f.path, + path: f.path.clone(), id: f.id, file_num: f.file_num, }); + self.file_remove_log_control.on_remove_file(f); } Some(file_action::Union::Create(c)) => { self.send_fs(ipc::FS::CreateDir { - path: c.path, + path: c.path.clone(), id: c.id, }); + self.send_to_cm(ipc::Data::FileTransferLog(( + "create_dir".to_string(), + serde_json::to_string(&FileActionLog { + id: c.id, + conn_id: self.inner.id(), + path: c.path, + dir: true, + }) + .unwrap_or_default(), + ))); } Some(file_action::Union::Cancel(c)) => { self.send_fs(ipc::FS::CancelWrite { id: c.id }); if let Some(job) = fs::get_job_immutable(c.id, &self.read_jobs) { - self.send_to_cm(ipc::Data::FileTransferLog( + self.send_to_cm(ipc::Data::FileTransferLog(( + "transfer".to_string(), fs::serialize_transfer_job(job, false, true, ""), - )); + ))); } fs::remove_job(c.id, &mut self.read_jobs); } @@ -2215,8 +2251,9 @@ impl Connection { } // Send display changed message. - // For compatibility with old versions ( < 1.2.4 ). - // sciter need it in new version + // 1. For compatibility with old versions ( < 1.2.4 ). + // 2. Sciter version. + // 3. Update `SupportedResolutions`. if let Some(msg_out) = video_service::make_display_changed_msg(self.display_idx, None) { self.send(msg_out).await; } @@ -2232,7 +2269,11 @@ impl Connection { lock.add_service(Box::new(video_service::new(display_idx))); } } - lock.subscribe(&old_service_name, self.inner.clone(), false); + // For versions greater than 1.2.4, a `CaptureDisplays` message will be sent immediately. + // Unnecessary capturers will be removed then. + if !crate::common::is_support_multi_ui_session(&self.lr.version) { + lock.subscribe(&old_service_name, self.inner.clone(), false); + } lock.subscribe(&new_service_name, self.inner.clone(), true); self.display_idx = display_idx; } @@ -2439,7 +2480,7 @@ impl Connection { } } } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] if let Ok(q) = o.enable_file_transfer.enum_value() { if q != BoolOption::NotSet { self.enable_file_transfer = q == BoolOption::Yes; @@ -2544,8 +2585,8 @@ impl Connection { } } } - if self.keyboard { - if let Ok(q) = o.block_input.enum_value() { + if let Ok(q) = o.block_input.enum_value() { + if self.keyboard && self.block_input { match q { BoolOption::Yes => { self.tx_input.send(MessageInput::BlockOn).ok(); @@ -2555,6 +2596,17 @@ impl Connection { } _ => {} } + } else { + if q != BoolOption::NotSet { + let state = if q == BoolOption::Yes { + back_notification::BlockInputState::BlkOnFailed + } else { + back_notification::BlockInputState::BlkOffFailed + }; + if let Some(tx) = &self.inner.tx { + Self::send_block_input_error(tx, state, "No permission".to_string()); + } + } } } } @@ -2909,6 +2961,114 @@ pub enum FileAuditType { RemoteReceive = 1, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct FileActionLog { + id: i32, + conn_id: i32, + path: String, + dir: bool, +} + +struct FileRemoveLogControl { + conn_id: i32, + instant: Instant, + removed_files: Vec, + removed_dirs: Vec, +} + +impl FileRemoveLogControl { + fn new(conn_id: i32) -> Self { + FileRemoveLogControl { + conn_id, + instant: Instant::now(), + removed_files: vec![], + removed_dirs: vec![], + } + } + + fn on_remove_file(&mut self, f: FileRemoveFile) -> Option { + self.instant = Instant::now(); + self.removed_files.push(f.clone()); + Some(ipc::Data::FileTransferLog(( + "remove".to_string(), + serde_json::to_string(&FileActionLog { + id: f.id, + conn_id: self.conn_id, + path: f.path, + dir: false, + }) + .unwrap_or_default(), + ))) + } + + fn on_remove_dir(&mut self, d: FileRemoveDir) -> Option { + self.instant = Instant::now(); + let direct_child = |parent: &str, child: &str| { + PathBuf::from(child).parent().map(|x| x.to_path_buf()) == Some(PathBuf::from(parent)) + }; + self.removed_files + .retain(|f| !direct_child(&f.path, &d.path)); + self.removed_dirs + .retain(|x| !direct_child(&d.path, &x.path)); + if !self + .removed_dirs + .iter() + .any(|x| direct_child(&x.path, &d.path)) + { + self.removed_dirs.push(d.clone()); + } + Some(ipc::Data::FileTransferLog(( + "remove".to_string(), + serde_json::to_string(&FileActionLog { + id: d.id, + conn_id: self.conn_id, + path: d.path, + dir: true, + }) + .unwrap_or_default(), + ))) + } + + fn on_timer(&mut self) -> Vec { + if self.instant.elapsed().as_secs() < 1 { + return vec![]; + } + let mut v: Vec = vec![]; + self.removed_files + .drain(..) + .map(|f| { + v.push(ipc::Data::FileTransferLog(( + "remove".to_string(), + serde_json::to_string(&FileActionLog { + id: f.id, + conn_id: self.conn_id, + path: f.path, + dir: false, + }) + .unwrap_or_default(), + ))); + }) + .count(); + self.removed_dirs + .drain(..) + .map(|d| { + v.push(ipc::Data::FileTransferLog(( + "remove".to_string(), + serde_json::to_string(&FileActionLog { + id: d.id, + conn_id: self.conn_id, + path: d.path, + dir: true, + }) + .unwrap_or_default(), + ))); + }) + .count(); + v + } +} + #[cfg(windows)] pub struct PortableState { pub last_uac: bool, @@ -3002,18 +3162,6 @@ mod raii { fn drop(&mut self) { let mut active_conns_lock = ALIVE_CONNS.lock().unwrap(); active_conns_lock.retain(|&c| c != self.0); - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if active_conns_lock.is_empty() { - display_service::reset_resolutions(); - } - #[cfg(all(windows, feature = "virtual_display_driver"))] - if active_conns_lock.is_empty() { - let _ = virtual_display_manager::reset_all(); - } - #[cfg(all(windows))] - if active_conns_lock.is_empty() { - crate::privacy_win_mag::stop(); - } video_service::VIDEO_QOS .lock() .unwrap() @@ -3021,17 +3169,20 @@ mod raii { } } - pub struct AuthedConnID(i32); + pub struct AuthedConnID(i32, AuthConnType); impl AuthedConnID { pub fn new(id: i32, conn_type: AuthConnType) -> Self { AUTHED_CONNS.lock().unwrap().push((id, conn_type)); - Self(id) + Self(id, conn_type) } } impl Drop for AuthedConnID { fn drop(&mut self) { + if self.1 == AuthConnType::Remote { + scrap::codec::Encoder::update(self.0, scrap::codec::EncodingUpdate::Remove); + } let mut lock = AUTHED_CONNS.lock().unwrap(); lock.retain(|&c| c.0 != self.0); if lock.iter().filter(|c| c.1 == AuthConnType::Remote).count() == 0 { @@ -3039,6 +3190,12 @@ mod raii { { *WALLPAPER_REMOVER.lock().unwrap() = None; } + #[cfg(not(any(target_os = "android", target_os = "ios")))] + display_service::reset_resolutions(); + #[cfg(all(windows, feature = "virtual_display_driver"))] + let _ = virtual_display_manager::reset_all(); + #[cfg(all(windows))] + crate::privacy_win_mag::stop(); } } } diff --git a/src/server/display_service.rs b/src/server/display_service.rs index 30225e491..a9a49818c 100644 --- a/src/server/display_service.rs +++ b/src/server/display_service.rs @@ -12,6 +12,7 @@ use scrap::Display; pub const NAME: &'static str = "display"; +#[cfg(all(windows, feature = "virtual_display_driver"))] const DUMMY_DISPLAY_SIDE_MAX_SIZE: usize = 1024; struct ChangedResolution { @@ -349,7 +350,7 @@ pub fn try_get_displays() -> ResultType> { #[cfg(all(windows, feature = "virtual_display_driver"))] pub fn try_get_displays() -> ResultType> { let mut displays = Display::all()?; - if no_displays(&displays) { + if crate::platform::is_installed() && no_displays(&displays) { log::debug!("no displays, create virtual display"); if let Err(e) = virtual_display_manager::plug_in_headless() { log::error!("plug in headless failed {}", e); diff --git a/src/server/portable_service.rs b/src/server/portable_service.rs index 7b77c1516..51d665d0a 100644 --- a/src/server/portable_service.rs +++ b/src/server/portable_service.rs @@ -8,7 +8,7 @@ use hbb_common::{ tokio::{self, sync::mpsc}, ResultType, }; -use scrap::{Capturer, Frame, TraitCapturer}; +use scrap::{Capturer, Frame, TraitCapturer, TraitFrame}; use shared_memory::*; use std::{ mem::size_of, @@ -300,7 +300,6 @@ pub mod server { fn run_capture(shmem: Arc) { let mut c = None; let mut last_current_display = usize::MAX; - let mut last_use_yuv = false; let mut last_timeout_ms: i32 = 33; let mut spf = Duration::from_millis(last_timeout_ms as _); let mut first_frame_captured = false; @@ -316,14 +315,7 @@ pub mod server { let para = para_ptr as *const CapturerPara; let recreate = (*para).recreate; let current_display = (*para).current_display; - let use_yuv = (*para).use_yuv; - let use_yuv_set = (*para).use_yuv_set; let timeout_ms = (*para).timeout_ms; - if !use_yuv_set { - c = None; - std::thread::sleep(spf); - continue; - } if c.is_none() { let Ok(mut displays) = display_service::try_get_displays() else { log::error!("Failed to get displays"); @@ -338,11 +330,10 @@ pub mod server { let display = displays.remove(current_display); display_width = display.width(); display_height = display.height(); - match Capturer::new(display, use_yuv) { + match Capturer::new(display) { Ok(mut v) => { c = { last_current_display = current_display; - last_use_yuv = use_yuv; first_frame_captured = false; if dxgi_failed_times > MAX_DXGI_FAIL_TIME { dxgi_failed_times = 0; @@ -353,8 +344,6 @@ pub mod server { CapturerPara { recreate: false, current_display: (*para).current_display, - use_yuv: (*para).use_yuv, - use_yuv_set: (*para).use_yuv_set, timeout_ms: (*para).timeout_ms, }, ); @@ -368,16 +357,11 @@ pub mod server { } } } else { - if recreate - || current_display != last_current_display - || use_yuv != last_use_yuv - { + if recreate || current_display != last_current_display { log::info!( - "create capturer, display:{}->{}, use_yuv:{}->{}", + "create capturer, display:{}->{}", last_current_display, current_display, - last_use_yuv, - use_yuv ); c = None; continue; @@ -401,12 +385,12 @@ pub mod server { utils::set_frame_info( &shmem, FrameInfo { - length: f.0.len(), + length: f.data().len(), width: display_width, height: display_height, }, ); - shmem.write(ADDR_CAPTURE_FRAME, f.0); + shmem.write(ADDR_CAPTURE_FRAME, f.data()); shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE)); utils::increase_counter(shmem.as_ptr().add(ADDR_CAPTURE_FRAME_COUNTER)); first_frame_captured = true; @@ -651,7 +635,7 @@ pub mod client { } impl CapturerPortable { - pub fn new(current_display: usize, use_yuv: bool) -> Self + pub fn new(current_display: usize) -> Self where Self: Sized, { @@ -665,8 +649,6 @@ pub mod client { CapturerPara { recreate: true, current_display, - use_yuv, - use_yuv_set: false, timeout_ms: 33, }, ); @@ -684,26 +666,6 @@ pub mod client { } impl TraitCapturer for CapturerPortable { - fn set_use_yuv(&mut self, use_yuv: bool) { - let mut option = SHMEM.lock().unwrap(); - if let Some(shmem) = option.as_mut() { - unsafe { - let para_ptr = shmem.as_ptr().add(ADDR_CAPTURER_PARA); - let para = para_ptr as *const CapturerPara; - utils::set_para( - shmem, - CapturerPara { - recreate: (*para).recreate, - current_display: (*para).current_display, - use_yuv, - use_yuv_set: true, - timeout_ms: (*para).timeout_ms, - }, - ); - } - } - } - fn frame<'a>(&'a mut self, timeout: Duration) -> std::io::Result> { let mut lock = SHMEM.lock().unwrap(); let shmem = lock.as_mut().ok_or(std::io::Error::new( @@ -720,8 +682,6 @@ pub mod client { CapturerPara { recreate: (*para).recreate, current_display: (*para).current_display, - use_yuv: (*para).use_yuv, - use_yuv_set: (*para).use_yuv_set, timeout_ms: timeout.as_millis() as _, }, ); @@ -744,7 +704,7 @@ pub mod client { } let frame_ptr = base.add(ADDR_CAPTURE_FRAME); let data = slice::from_raw_parts(frame_ptr, (*frame_info).length); - Ok(Frame(data)) + Ok(Frame::new(data, self.width, self.height)) } else { let ptr = base.add(ADDR_CAPTURE_WOULDBLOCK); let wouldblock = utils::ptr_to_i32(ptr); @@ -910,7 +870,6 @@ pub mod client { pub fn create_capturer( current_display: usize, display: scrap::Display, - use_yuv: bool, portable_service_running: bool, ) -> ResultType> { if portable_service_running != RUNNING.lock().unwrap().clone() { @@ -919,7 +878,7 @@ pub mod client { if portable_service_running { log::info!("Create shared memory capturer"); if current_display == *display_service::PRIMARY_DISPLAY_IDX { - return Ok(Box::new(CapturerPortable::new(current_display, use_yuv))); + return Ok(Box::new(CapturerPortable::new(current_display))); } else { bail!( "Ignore capture display index: {}, the primary display index is: {}", @@ -930,7 +889,7 @@ pub mod client { } else { log::debug!("Create capturer dxgi|gdi"); return Ok(Box::new( - Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?, + Capturer::new(display).with_context(|| "Failed to create capturer")?, )); } } @@ -981,8 +940,6 @@ pub mod client { pub struct CapturerPara { recreate: bool, current_display: usize, - use_yuv: bool, - use_yuv_set: bool, timeout_ms: i32, } diff --git a/src/server/video_service.rs b/src/server/video_service.rs index b8c6343c2..22fe7a89f 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -42,9 +42,10 @@ use scrap::Capturer; use scrap::{ aom::AomEncoderConfig, codec::{Encoder, EncoderCfg, HwEncoderConfig, Quality}, + convert_to_yuv, record::{Recorder, RecorderContext}, vpxcodec::{VpxEncoderConfig, VpxVideoCodecId}, - CodecName, Display, TraitCapturer, + CodecName, Display, Frame, TraitCapturer, TraitFrame, }; #[cfg(windows)] use std::sync::Once; @@ -171,7 +172,6 @@ pub fn new(idx: usize) -> GenericService { fn create_capturer( privacy_mode_id: i32, display: Display, - use_yuv: bool, _current: usize, _portable_service_running: bool, ) -> ResultType> { @@ -182,12 +182,7 @@ fn create_capturer( if privacy_mode_id > 0 { #[cfg(windows)] { - match scrap::CapturerMag::new( - display.origin(), - display.width(), - display.height(), - use_yuv, - ) { + match scrap::CapturerMag::new(display.origin(), display.width(), display.height()) { Ok(mut c1) => { let mut ok = false; let check_begin = Instant::now(); @@ -236,12 +231,11 @@ fn create_capturer( return crate::portable_service::client::create_capturer( _current, display, - use_yuv, _portable_service_running, ); #[cfg(not(windows))] return Ok(Box::new( - Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?, + Capturer::new(display).with_context(|| "Failed to create capturer")?, )); } }; @@ -265,7 +259,7 @@ pub fn test_create_capturer( ) } else { let display = displays.remove(display_idx); - match create_capturer(privacy_mode_id, display, true, display_idx, false) { + match create_capturer(privacy_mode_id, display, display_idx, false) { Ok(_) => return "".to_owned(), Err(e) => e, } @@ -320,11 +314,7 @@ impl DerefMut for CapturerInfo { } } -fn get_capturer( - current: usize, - use_yuv: bool, - portable_service_running: bool, -) -> ResultType { +fn get_capturer(current: usize, portable_service_running: bool) -> ResultType { #[cfg(target_os = "linux")] { if !is_x11() { @@ -382,7 +372,6 @@ fn get_capturer( let capturer = create_capturer( capturer_privacy_mode_id, display, - use_yuv, current, portable_service_running, )?; @@ -424,7 +413,7 @@ fn run(vs: VideoService) -> ResultType<()> { let display_idx = vs.idx; let sp = vs.sp; - let mut c = get_capturer(display_idx, true, last_portable_service_running)?; + let mut c = get_capturer(display_idx, last_portable_service_running)?; let mut video_qos = VIDEO_QOS.lock().unwrap(); video_qos.refresh(None); @@ -439,11 +428,11 @@ fn run(vs: VideoService) -> ResultType<()> { let encoder_cfg = get_encoder_config(&c, quality, last_recording); let mut encoder; - match Encoder::new(encoder_cfg) { + let use_i444 = Encoder::use_i444(&encoder_cfg); + match Encoder::new(encoder_cfg.clone(), use_i444) { Ok(x) => encoder = x, Err(err) => bail!("Failed to create encoder: {}", err), } - c.set_use_yuv(encoder.use_yuv()); VIDEO_QOS.lock().unwrap().store_bitrate(encoder.bitrate()); if sp.is_option_true(OPTION_REFRESH) { @@ -463,6 +452,8 @@ fn run(vs: VideoService) -> ResultType<()> { #[cfg(target_os = "linux")] let mut would_block_count = 0u32; + let mut yuv = Vec::new(); + let mut mid_data = Vec::new(); while sp.ok() { #[cfg(windows)] @@ -493,6 +484,9 @@ fn run(vs: VideoService) -> ResultType<()> { if last_portable_service_running != crate::portable_service::client::running() { bail!("SWITCH"); } + if Encoder::use_i444(&encoder_cfg) != use_i444 { + bail!("SWITCH"); + } check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] { @@ -512,40 +506,23 @@ fn run(vs: VideoService) -> ResultType<()> { frame_controller.reset(); - #[cfg(any(target_os = "android", target_os = "ios"))] let res = match c.frame(spf) { Ok(frame) => { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; - match frame { - scrap::Frame::RAW(data) => { - if data.len() != 0 { - let send_conn_ids = handle_one_frame( - display_idx, - &sp, - data, - ms, - &mut encoder, - recorder.clone(), - )?; - frame_controller.set_send(now, send_conn_ids); - } - } - _ => {} - }; - Ok(()) - } - Err(err) => Err(err), - }; - - #[cfg(not(any(target_os = "android", target_os = "ios")))] - let res = match c.frame(spf) { - Ok(frame) => { - let time = now - start; - let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; - let send_conn_ids = - handle_one_frame(display_idx, &sp, &frame, ms, &mut encoder, recorder.clone())?; - frame_controller.set_send(now, send_conn_ids); + if frame.data().len() != 0 { + let send_conn_ids = handle_one_frame( + display_idx, + &sp, + frame, + &mut yuv, + &mut mid_data, + ms, + &mut encoder, + recorder.clone(), + )?; + frame_controller.set_send(now, send_conn_ids); + } #[cfg(windows)] { try_gdi = 0; @@ -718,7 +695,9 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu fn handle_one_frame( display: usize, sp: &GenericService, - frame: &[u8], + frame: Frame, + yuv: &mut Vec, + mid_data: &mut Vec, ms: i64, encoder: &mut Encoder, recorder: Arc>>, @@ -732,7 +711,8 @@ fn handle_one_frame( })?; let mut send_conn_ids: HashSet = Default::default(); - if let Ok(mut vf) = encoder.encode_to_message(frame, ms) { + convert_to_yuv(&frame, encoder.yuvfmt(), yuv, mid_data)?; + if let Ok(mut vf) = encoder.encode_to_message(yuv, ms) { vf.display = display as _; let mut msg = Message::new(); msg.set_video_frame(vf); diff --git a/src/server/wayland.rs b/src/server/wayland.rs index f869266bc..eeed118dc 100644 --- a/src/server/wayland.rs +++ b/src/server/wayland.rs @@ -76,12 +76,6 @@ impl TraitCapturer for CapturerPtr { fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { unsafe { (*self.0).frame(timeout) } } - - fn set_use_yuv(&mut self, use_yuv: bool) { - unsafe { - (*self.0).set_use_yuv(use_yuv); - } - } } struct CapDisplayInfo { @@ -192,7 +186,8 @@ pub(super) async fn check_init() -> ResultType<()> { maxy = max_height; let capturer = Box::into_raw(Box::new( - Capturer::new(display, true).with_context(|| "Failed to create capturer")?, + Capturer::new(display) + .with_context(|| "Failed to create capturer")?, )); let capturer = CapturerPtr(capturer); let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo { diff --git a/src/ui.rs b/src/ui.rs index 43d2aee8d..fb1badf88 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -589,7 +589,7 @@ impl UI { } fn handle_relay_id(&self, id: String) -> String { - handle_relay_id(id) + handle_relay_id(&id).to_owned() } fn get_login_device_info(&self) -> String { diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 70f46e932..2c8724750 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -325,15 +325,15 @@ class SessionList: Reactor.Component {
  • {translate('Connect')}
  • -
  • {translate('Transfer File')}
  • -
  • {translate('TCP Tunneling')}
  • +
  • {translate('Transfer file')}
  • +
  • {translate('TCP tunneling')}
  • {svg_checkmark}{translate('Always connect via relay')}
  • RDP
  • {translate('WOL')}
  • {this.type != "lan" &&
  • {translate('Rename')}
  • } {this.type != "fav" &&
  • {translate('Remove')}
  • } - {is_win &&
  • {translate('Create Desktop Shortcut')}
  • } + {is_win &&
  • {translate('Create desktop shortcut')}
  • }
  • {translate('Forget Password')}
  • {(!this.type || this.type == "fav") &&
  • {translate('Add to Favorites')}
  • } {(!this.type || this.type == "fav") &&
  • {translate('Remove from Favorites')}
  • } @@ -540,10 +540,10 @@ class MultipleSessions: Reactor.Component { return
    - {translate('Recent Sessions')} + {translate('Recent sessions')} {translate('Favorites')} {handler.is_installed() && {translate('Discovered')}} - {translate('Address Book')} + {translate('Address book')}
    {!this.hidden && } {!this.hidden && } @@ -578,7 +578,7 @@ class MultipleSessions: Reactor.Component { function onSize() { var w = this.$(.sessions-bar .sessions-tab).box(#width); - var len = translate('Recent Sessions').length; + var len = translate('Recent sessions').length; var totalChars = 0; var nEle = 0; for (var el in this.$$(#sessions-type span)) { diff --git a/src/ui/cm.css b/src/ui/cm.css index 960c8b567..d817f40a0 100644 --- a/src/ui/cm.css +++ b/src/ui/cm.css @@ -112,6 +112,10 @@ icon.recording { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAANpJREFUWEftltENAiEMhtsJ1NcynG6gI+gGugEOR591gppeQoIYSDBILxEeydH/57u2FMF4obE+TAOTwLoIhBDOAHBExG2n6rgR0akW640AM0sn4SWMiDycc7s8JjN7Ijro/k8NqAAR5RoeAPZxv2ggP9hCJiWZxtGbq3hqbJiBVHy4gVx8qAER8Yi4JFy6huVAKXemgb8icI+1b5KEitq0DOO/Nm1EEX1TK27p/bVvv36MOhl4EtHHbFF7jq8AoG1z08OAiFycczrkFNe6RrIet26NMQlMAuYEXiayryF/QQktAAAAAElFTkSuQmCC'); } +icon.block_input { + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAjdJREFUWEe1V8tNAzEQfXOHAx2QG0UgQSqBFIIgHdABoQqOhBq4cCMlcMh90FvZq/HEXtvJxlKUZNceP783no+gY6jqNYBHAHcA+JufXTDBb37eRWTbalZqE82mz7W55v0ABMBGRCLA7PJJAKr6AiC3sT11NHyf2SEyQjvtAMKp3wBYo9VTGbYegjxxU65d5tg4YEBVbwF8ALgw2lLX4in80QqyZUEkAMLCb7P5n4hcdWifTA32Pg0bByA8AE4+oL3n9A1s7ERkEeeNAJzD/QC4OVaCAgjrU7wdK86zAHREJSKqyvvORRxVb67JFOT4NfYGpxwAqCo34oYcKxHZhOdzg7D2BhYigHj6RJ+5QbjrPezlqR61sZTOKYfztSUBWPoXpdA5FwjnC2sCGK+eiNRC8yw+oap0RiayLQHEPwf65zx7DibMoXcEEB0wq/85QJQAbEVkWbvP8f0pTFi/65ZgjtuRyJ7QYWL0OZnwTmiLDobH5nLqGDlUlcmON49jQwnsg/Wxma/VJ1zcGQIR7+OYJGyqbJWhhwlDPxh3JpNRL4Ba7nAsJckoYaFUv7UCyslBvQ3TNDWEfVsPJGH2FCkKTPAxD8ox+poFwJfZqqX15H6eYyK+TgJeriidLCJ7wAQHZ4Udy7u9iFxaG7mynEx4EF1leZDANzV7AE8i8joJICz2cvBxbExIYTZYTTQmxTxTzP+VnvC8rZlLOLEj7m5OW6JqtTs2US6247Hvy7XnX0OV05FP/gHde5fLZaGS8AAAAABJRU5ErkJggg=='); +} + div.outer_buttons { flow:vertical; border-spacing:8; diff --git a/src/ui/cm.html b/src/ui/cm.html index aabaa0294..86641907e 100644 --- a/src/ui/cm.html +++ b/src/ui/cm.html @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/src/ui/cm.rs b/src/ui/cm.rs index b827b76b1..c8c8c657f 100644 --- a/src/ui/cm.rs +++ b/src/ui/cm.rs @@ -28,7 +28,8 @@ impl InvokeUiCM for SciterHandler { client.audio, client.file, client.restart, - client.recording + client.recording, + client.block_input ), ); } @@ -63,7 +64,7 @@ impl InvokeUiCM for SciterHandler { ); } - fn file_transfer_log(&self, _log: String) {} + fn file_transfer_log(&self, _action: &str, _log: &str) {} } impl SciterHandler { diff --git a/src/ui/cm.tis b/src/ui/cm.tis index 4e46e217f..38f5c5c2d 100644 --- a/src/ui/cm.tis +++ b/src/ui/cm.tis @@ -50,13 +50,14 @@ class Body: Reactor.Component
    {c.is_file_transfer || c.port_forward || disconnected ? "" :
    {translate('Permissions')}
    } {c.is_file_transfer || c.port_forward || disconnected ? "" :
    -
    -
    -
    -
    -
    +
    +
    +
    +
    +
    -
    +
    +
    } {c.port_forward ?
    Port Forwarding: {c.port_forward}
    : ""} @@ -143,6 +144,15 @@ class Body: Reactor.Component }); } + event click $(icon.block_input) { + var { cid, connection } = this; + checkClickTime(function() { + connection.block_input = !connection.block_input; + body.update(); + handler.switch_permission(cid, "block_input", connection.block_input); + }); + } + event click $(button#accept) { var { cid, connection } = this; checkClickTime(function() { @@ -346,7 +356,7 @@ function bring_to_top(idx=-1) { } } -handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording) { +handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input) { stdout.println("new connection #" + id + ": " + peer_id); var conn; connections.map(function(c) { @@ -368,6 +378,7 @@ handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, na name: name, authorized: authorized, time: new Date(), now: new Date(), keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0, audio: audio, file: file, restart: restart, recording: recording, + block_input:block_input, disconnected: false }; if (idx < 0) { diff --git a/src/ui/header.tis b/src/ui/header.tis index 029dd2629..2350aff5f 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -196,11 +196,12 @@ class Header: Reactor.Component { {!cursor_embedded &&
  • {svg_checkmark}{translate('Show remote cursor')}
  • }
  • {svg_checkmark}{translate('Show quality monitor')}
  • {audio_enabled ?
  • {svg_checkmark}{translate('Mute')}
  • : ""} - {is_win && pi.platform == 'Windows' && file_enabled ?
  • {svg_checkmark}{translate('Allow file copy and paste')}
  • : ""} + {(is_win && pi.platform == "Windows") && file_enabled ?
  • {svg_checkmark}{translate('Allow file copy and paste')}
  • : ""} {keyboard_enabled && clipboard_enabled ?
  • {svg_checkmark}{translate('Disable clipboard')}
  • : ""} {keyboard_enabled ?
  • {svg_checkmark}{translate('Lock after session end')}
  • : ""} {keyboard_enabled && pi.platform == "Windows" ?
  • {svg_checkmark}{translate('Privacy mode')}
  • : ""} {keyboard_enabled && ((is_osx && pi.platform != "Mac OS") || (!is_osx && pi.platform == "Mac OS")) ?
  • {svg_checkmark}{translate('Swap control-command key')}
  • : ""} + {handler.version_cmp(pi.version, '1.2.4') >= 0 ?
  • {svg_checkmark}{translate('True color (4:4:4)')}
  • : ""} ; } @@ -209,12 +210,12 @@ class Header: Reactor.Component { return {keyboard_enabled ?
  • {translate('OS Password')}
  • : ""} -
  • {translate('Transfer File')}
  • -
  • {translate('TCP Tunneling')}
  • +
  • {translate('Transfer file')}
  • +
  • {translate('TCP tunneling')}
  • {handler.get_audit_server("conn") &&
  • {translate('Note')}
  • }
    {keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ?
  • {translate('Insert')} Ctrl + Alt + Del
  • : ""} - {restart_enabled && (pi.platform == "Linux" || pi.platform == "Windows" || pi.platform == "Mac OS") ?
  • {translate('Restart Remote Device')}
  • : ""} + {restart_enabled && (pi.platform == "Linux" || pi.platform == "Windows" || pi.platform == "Mac OS") ?
  • {translate('Restart remote device')}
  • : ""} {keyboard_enabled ?
  • {translate('Insert Lock')}
  • : ""} {keyboard_enabled && pi.platform == "Windows" && pi.sas_enabled ?
  • {translate("Block user input")}
  • : ""}
  • {translate('Refresh')}
  • @@ -365,7 +366,7 @@ class Header: Reactor.Component { event click $(#restart_remote_device) { msgbox( "restart-confirmation", - translate("Restart Remote Device"), + translate("Restart remote device"), translate("Are you sure you want to restart") + " " + pi.username + "@" + pi.hostname + "(" + get_id() + ") ?", "", function(res=null) { @@ -402,6 +403,8 @@ class Header: Reactor.Component { togglePrivacyMode(me.id); } else if (me.id == "show-quality-monitor") { toggleQualityMonitor(me.id); + } else if (me.id == "i444") { + toggleI444(me.id); } else if (me.attributes.hasClass("toggle-option")) { handler.toggle_option(me.id); toggleMenuState(); @@ -476,7 +479,7 @@ function toggleMenuState() { for (var el in $$(menu#keyboard-options>li)) { el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0); } - for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end", "allow_swap_key"]) { + for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end", "allow_swap_key", "i444"]) { var el = self.select('#' + id); if (el) { var value = handler.get_toggle_option(id); @@ -563,6 +566,12 @@ function toggleQualityMonitor(name) { toggleMenuState(); } +function toggleI444(name) { + handler.toggle_option(name); + handler.change_prefer_codec(); + toggleMenuState(); +} + handler.updateBlockInputState = function(input_blocked) { if (!input_blocked) { handler.toggle_option("block-input"); diff --git a/src/ui/index.tis b/src/ui/index.tis index 7a46e3b3f..35be086e0 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -33,7 +33,7 @@ class ConnectStatus: Reactor.Component {
    {this.getConnectStatusStr()} - {service_stopped ? {translate('Start Service')} : ""} + {service_stopped ? {translate('Start service')} : ""}
    ; } @@ -93,7 +93,7 @@ class DirectServer: Reactor.Component { } function render() { - var text = translate("Enable Direct IP Access"); + var text = translate("Enable direct IP access"); var enabled = handler.get_option("direct-server") == "Y"; var cls = enabled ? "selected" : "line-through"; return
  • {svg_checkmark}{text}{enabled && }
  • ; @@ -249,7 +249,7 @@ class Enhancements: Reactor.Component { var ts1 = handler.get_option("allow-auto-record-incoming") == 'Y' ? { checked: true } : {}; msgbox("custom-recording", translate('Recording'),
    -
    {translate('Enable Recording Session')}
    +
    {translate('Enable recording session')}
    {translate('Automatically record incoming sessions')}
    {translate("Directory")}:  {dir}
    @@ -301,12 +301,13 @@ class MyIdMenu: Reactor.Component { var username = handler.get_local_option("access_token") ? getUserName() : ''; return -
  • {svg_checkmark}{translate('Enable Keyboard/Mouse')}
  • -
  • {svg_checkmark}{translate('Enable Clipboard')}
  • -
  • {svg_checkmark}{translate('Enable File Transfer')}
  • -
  • {svg_checkmark}{translate('Enable Remote Restart')}
  • -
  • {svg_checkmark}{translate('Enable TCP Tunneling')}
  • -
  • {svg_checkmark}{translate('Enable LAN Discovery')}
  • +
  • {svg_checkmark}{translate('Enable keyboard/mouse')}
  • +
  • {svg_checkmark}{translate('Enable clipboard')}
  • +
  • {svg_checkmark}{translate('Enable file transfer')}
  • +
  • {svg_checkmark}{translate('Enable remote restart')}
  • +
  • {svg_checkmark}{translate('Enable TCP tunneling')}
  • + {is_win ?
  • {svg_checkmark}{translate('Enable blocking user input')}
  • : ""} +
  • {svg_checkmark}{translate('Enable LAN discovery')}
  • {svg_checkmark}{translate('Enable remote configuration modification')}
  • @@ -315,7 +316,7 @@ class MyIdMenu: Reactor.Component {
  • {translate('IP Whitelisting')}
  • {translate('Socks5 Proxy')}
  • -
  • {svg_checkmark}{translate("Enable Service")}
  • +
  • {svg_checkmark}{translate("Enable service")}
  • {handler.is_rdp_service_open() ? : ""} {false && handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • } @@ -578,7 +579,7 @@ class App: Reactor.Component
    {translate('Control Remote Desktop')}
    - +
    @@ -1016,7 +1017,7 @@ updatePasswordArea(); class ID: Reactor.Component { function render() { - return ; } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 2138fc458..f6be03f39 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -131,7 +131,8 @@ impl InvokeUiSession for SciterHandler { status.target_bitrate.map_or(Value::null(), |it| it.into()), status .codec_format - .map_or(Value::null(), |it| it.to_string().into()) + .map_or(Value::null(), |it| it.to_string().into()), + status.chroma.map_or(Value::null(), |it| it.into()) ), ); } @@ -481,8 +482,7 @@ impl sciter::EventHandler for SciterSession { impl SciterSession { pub fn new(cmd: String, id: String, password: String, args: Vec) -> Self { let force_relay = args.contains(&"--relay".to_string()); - let session: Session = Session { - id: id.clone(), + let mut session: Session = Session { password: password.clone(), args, server_keyboard_enabled: Arc::new(RwLock::new(true)), diff --git a/src/ui/remote.tis b/src/ui/remote.tis index a99cd188b..8f106e78e 100644 --- a/src/ui/remote.tis +++ b/src/ui/remote.tis @@ -510,17 +510,21 @@ class QualityMonitor: Reactor.Component
    Codec: {qualityMonitorData[4]}
    +
    + Chroma: {qualityMonitorData[5]} +
    ; } } $(#quality-monitor).content(); -handler.updateQualityStatus = function(speed, fps, delay, bitrate, codec_format) { +handler.updateQualityStatus = function(speed, fps, delay, bitrate, codec_format, chroma) { speed ? qualityMonitorData[0] = speed:null; fps ? qualityMonitorData[1] = fps:null; delay ? qualityMonitorData[2] = delay:null; bitrate ? qualityMonitorData[3] = bitrate:null; codec_format ? qualityMonitorData[4] = codec_format:null; + chroma ? qualityMonitorData[5] = chroma:null; qualityMonitor.update(); } diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index da36646e6..b8de4fd82 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -1,6 +1,6 @@ #[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))] use std::iter::FromIterator; -#[cfg(windows)] +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] use std::sync::Arc; use std::{ collections::HashMap, @@ -15,11 +15,11 @@ use std::{ use crate::ipc::Connection; #[cfg(not(any(target_os = "ios")))] use crate::ipc::{self, Data}; -#[cfg(windows)] -use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, ContextSend}; +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] +use clipboard::ContextSend; #[cfg(not(any(target_os = "android", target_os = "ios")))] use hbb_common::tokio::sync::mpsc::unbounded_channel; -#[cfg(windows)] +#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] use hbb_common::tokio::sync::Mutex as TokioMutex; use hbb_common::{ allow_err, @@ -34,6 +34,7 @@ use hbb_common::{ sync::mpsc::{self, UnboundedSender}, task::spawn_blocking, }, + ResultType, }; use serde_derive::Serialize; @@ -52,6 +53,7 @@ pub struct Client { pub file: bool, pub restart: bool, pub recording: bool, + pub block_input: bool, pub from_switch: bool, pub in_voice_call: bool, pub incoming_voice_call: bool, @@ -69,9 +71,9 @@ struct IpcTaskRunner { close: bool, running: bool, conn_id: i32, - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] file_transfer_enabled: bool, - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] file_transfer_enabled_peer: bool, } @@ -100,7 +102,7 @@ pub trait InvokeUiCM: Send + Clone + 'static + Sized { fn update_voice_call_state(&self, client: &Client); - fn file_transfer_log(&self, log: String); + fn file_transfer_log(&self, action: &str, log: &str); } impl Deref for ConnectionManager { @@ -132,6 +134,7 @@ impl ConnectionManager { file: bool, restart: bool, recording: bool, + block_input: bool, from_switch: bool, #[cfg(not(any(target_os = "ios")))] tx: mpsc::UnboundedSender, ) { @@ -149,6 +152,7 @@ impl ConnectionManager { file, restart, recording, + block_input, from_switch, #[cfg(not(any(target_os = "ios")))] tx, @@ -164,7 +168,7 @@ impl ConnectionManager { } #[inline] - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] fn is_authorized(&self, id: i32) -> bool { CLIENTS .read() @@ -185,11 +189,11 @@ impl ConnectionManager { .map(|c| c.disconnected = true); } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] { - ContextSend::proc(|context: &mut CliprdrClientContext| -> u32 { - empty_clipboard(context, id); - 0 + let _ = ContextSend::proc(|context| -> ResultType<()> { + context.empty_clipboard(id)?; + Ok(()) }); } @@ -328,31 +332,35 @@ impl IpcTaskRunner { // for tmp use, without real conn id let mut write_jobs: Vec = Vec::new(); - #[cfg(windows)] + + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] let is_authorized = self.cm.is_authorized(self.conn_id); - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] let rx_clip1; let mut rx_clip; let _tx_clip; - #[cfg(windows)] - if is_authorized { + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] + if self.conn_id > 0 && is_authorized { + log::debug!("Clipboard is enabled from client peer: type 1"); rx_clip1 = clipboard::get_rx_cliprdr_server(self.conn_id); rx_clip = rx_clip1.lock().await; } else { + log::debug!("Clipboard is enabled from client peer, actually useless: type 2"); let rx_clip2; (_tx_clip, rx_clip2) = unbounded_channel::(); rx_clip1 = Arc::new(TokioMutex::new(rx_clip2)); rx_clip = rx_clip1.lock().await; } - #[cfg(not(windows))] + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] { (_tx_clip, rx_clip) = unbounded_channel::(); } - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] { if ContextSend::is_enabled() { + log::debug!("Clipboard is enabled"); allow_err!( self.stream .send(&Data::ClipboardFile(clipboard::ClipboardFile::MonitorReady)) @@ -373,11 +381,11 @@ impl IpcTaskRunner { } Ok(Some(data)) => { match data { - Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, from_switch} => { + Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, from_switch} => { log::debug!("conn_id: {}", id); - self.cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, from_switch,self.tx.clone()); + self.cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, from_switch, self.tx.clone()); self.conn_id = id; - #[cfg(windows)] + #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))] { self.file_transfer_enabled = _file_transfer_enabled; } @@ -413,14 +421,14 @@ impl IpcTaskRunner { handle_fs(fs, &mut write_jobs, &self.tx, Some(&tx_log)).await; } let log = fs::serialize_transfer_jobs(&write_jobs); - self.cm.ui_handler.file_transfer_log(log); + self.cm.ui_handler.file_transfer_log("transfer", &log); } - Data::FileTransferLog(log) => { - self.cm.ui_handler.file_transfer_log(log); + Data::FileTransferLog((action, log)) => { + self.cm.ui_handler.file_transfer_log(&action, &log); } #[cfg(not(any(target_os = "android", target_os = "ios")))] Data::ClipboardFile(_clip) => { - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os="linux", target_os = "macos"))] { let is_stopping_allowed = _clip.is_stopping_allowed_from_peer(); let is_clipboard_enabled = ContextSend::is_enabled(); @@ -437,14 +445,15 @@ impl IpcTaskRunner { continue; } let conn_id = self.conn_id; - ContextSend::proc(|context: &mut CliprdrClientContext| -> u32 { - clipboard::server_clip_file(context, conn_id, _clip) + let _ = ContextSend::proc(|context| -> ResultType<()> { + context.server_clip_file(conn_id, _clip) + .map_err(|e| e.into()) }); } } } Data::ClipboardFileEnabled(_enabled) => { - #[cfg(windows)] + #[cfg(any(target_os= "windows",target_os ="linux", target_os = "macos"))] { self.file_transfer_enabled_peer = _enabled; } @@ -477,12 +486,13 @@ impl IpcTaskRunner { } } Some(data) = self.rx.recv() => { - if self.stream.send(&data).await.is_err() { + if let Err(e) = self.stream.send(&data).await { + log::error!("error encountered in IPC task, quitting: {}", e); break; } match &data { Data::SwitchPermission{name: _name, enabled: _enabled} => { - #[cfg(windows)] + #[cfg(any(target_os="linux", target_os="windows", target_os = "macos"))] if _name == "file" { self.file_transfer_enabled = *_enabled; } @@ -497,7 +507,7 @@ impl IpcTaskRunner { }, clip_file = rx_clip.recv() => match clip_file { Some(_clip) => { - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os ="linux", target_os = "macos"))] { let is_stopping_allowed = _clip.is_stopping_allowed(); let is_clipboard_enabled = ContextSend::is_enabled(); @@ -505,7 +515,7 @@ impl IpcTaskRunner { let file_transfer_enabled_peer = self.file_transfer_enabled_peer; let stop = is_stopping_allowed && !(is_clipboard_enabled && file_transfer_enabled && file_transfer_enabled_peer); log::debug!( - "Process clipboard message from cm, stop: {}, is_stopping_allowed: {}, is_clipboard_enabled: {}, file_transfer_enabled: {}, file_transfer_enabled_peer: {}", + "Process clipboard message from clip, stop: {}, is_stopping_allowed: {}, is_clipboard_enabled: {}, file_transfer_enabled: {}, file_transfer_enabled_peer: {}", stop, is_stopping_allowed, is_clipboard_enabled, file_transfer_enabled, file_transfer_enabled_peer); if stop { ContextSend::set_is_stopped(); @@ -519,7 +529,7 @@ impl IpcTaskRunner { } }, Some(job_log) = rx_log.recv() => { - self.cm.ui_handler.file_transfer_log(job_log); + self.cm.ui_handler.file_transfer_log("transfer", &job_log); } } } @@ -536,9 +546,9 @@ impl IpcTaskRunner { close: true, running: true, conn_id: 0, - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] file_transfer_enabled: false, - #[cfg(windows)] + #[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))] file_transfer_enabled_peer: false, }; @@ -568,7 +578,13 @@ pub async fn start_ipc(cm: ConnectionManager) { } }); - #[cfg(target_os = "windows")] + #[cfg(any( + target_os = "windows", + all( + any(target_os = "linux", target_os = "macos"), + feature = "unix-file-copy-paste" + ), + ))] ContextSend::enable(Config::get_option("enable-file-transfer").is_empty()); match ipc::new_listener("_cm").await { @@ -619,6 +635,7 @@ pub async fn start_listen( file, restart, recording, + block_input, from_switch, .. }) => { @@ -636,6 +653,7 @@ pub async fn start_listen( file, restart, recording, + block_input, from_switch, tx.clone(), ); diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 7ce79f366..d681de830 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -1007,7 +1007,8 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver String { +pub fn handle_relay_id(id: &str) -> &str { if id.ends_with(r"\r") || id.ends_with(r"/r") { - id[0..id.len() - 2].to_string() + &id[0..id.len() - 2] } else { id } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index fc765131e..c82b91029 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -32,8 +32,8 @@ use hbb_common::{ use crate::client::io_loop::Remote; use crate::client::{ check_if_retry, handle_hash, handle_login_error, handle_login_from_ui, handle_test_delay, - input_os_password, load_config, send_mouse, send_pointer_device_event, - start_video_audio_threads, FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, + input_os_password, send_mouse, send_pointer_device_event, start_video_audio_threads, + FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP, }; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::GrabState; @@ -47,7 +47,6 @@ const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15; #[derive(Clone, Default)] pub struct Session { - pub id: String, // peer id pub password: String, pub args: Vec, pub lc: Arc>, @@ -365,7 +364,7 @@ impl Session { display as usize, w, h, - self.id.clone(), + self.get_id(), )); } @@ -470,7 +469,7 @@ impl Session { pub fn send_note(&self, note: String) { let url = self.get_audit_server("conn".to_string()); - let id = self.id.clone(); + let id = self.get_id(); let session_id = self.lc.read().unwrap().session_id; std::thread::spawn(move || { send_note(url, id, session_id, note); @@ -514,11 +513,6 @@ impl Session { self.send(Data::AddPortForward(pf)); } - #[cfg(not(feature = "flutter"))] - pub fn get_id(&self) -> String { - self.id.clone() - } - pub fn get_option(&self, k: String) -> String { if k.eq("remote_dir") { return self.lc.read().unwrap().get_remote_dir(); @@ -536,7 +530,7 @@ impl Session { #[inline] pub fn load_config(&self) -> PeerConfig { - load_config(&self.id) + self.lc.read().unwrap().load_config() } #[inline] @@ -1110,7 +1104,7 @@ impl Session { match crate::ipc::connect(1000, "").await { Ok(mut conn) => { if conn - .send(&crate::ipc::Data::SwitchSidesRequest(self.id.to_string())) + .send(&crate::ipc::Data::SwitchSidesRequest(self.get_id())) .await .is_ok() { @@ -1286,7 +1280,7 @@ impl FileManager for Session {} #[async_trait] impl Interface for Session { - fn get_login_config_handler(&self) -> Arc> { + fn get_lch(&self) -> Arc> { return self.lc.clone(); } @@ -1361,7 +1355,7 @@ impl Interface for Session { #[cfg(windows)] { let mut path = std::env::temp_dir(); - path.push(&self.id); + path.push(self.get_id()); let path = path.with_extension(crate::get_app_name().to_lowercase()); std::fs::File::create(&path).ok(); if let Some(path) = path.to_str() { @@ -1438,7 +1432,13 @@ impl Session { #[tokio::main(flavor = "current_thread")] pub async fn io_loop(handler: Session, round: u32) { // It is ok to call this function multiple times. - #[cfg(target_os = "windows")] + #[cfg(any( + target_os = "windows", + all( + any(target_os = "linux", target_os = "macos"), + feature = "unix-file-copy-paste" + ) + ))] if !handler.is_file_transfer() && !handler.is_port_forward() { clipboard::ContextSend::enable(true); } @@ -1539,16 +1539,17 @@ pub async fn io_loop(handler: Session, round: u32) { let frame_count_map: Arc>> = Default::default(); let frame_count_map_cl = frame_count_map.clone(); let ui_handler = handler.ui_handler.clone(); - let (video_sender, audio_sender, video_queue_map, decode_fps_map) = start_video_audio_threads( - handler.clone(), - move |display: usize, data: &mut scrap::ImageRgb| { - let mut write_lock = frame_count_map_cl.write().unwrap(); - let count = write_lock.get(&display).unwrap_or(&0) + 1; - write_lock.insert(display, count); - drop(write_lock); - ui_handler.on_rgba(display, data); - }, - ); + let (video_sender, audio_sender, video_queue_map, decode_fps_map, chroma) = + start_video_audio_threads( + handler.clone(), + move |display: usize, data: &mut scrap::ImageRgb| { + let mut write_lock = frame_count_map_cl.write().unwrap(); + let count = write_lock.get(&display).unwrap_or(&0) + 1; + write_lock.insert(display, count); + drop(write_lock); + ui_handler.on_rgba(display, data); + }, + ); let mut remote = Remote::new( handler, @@ -1559,6 +1560,7 @@ pub async fn io_loop(handler: Session, round: u32) { sender, frame_count_map, decode_fps_map, + chroma, ); remote.io_loop(&key, &token, round).await; remote.sync_jobs_status_to_local().await; @@ -1575,7 +1577,7 @@ async fn start_one_port_forward( token: &str, ) { if let Err(err) = crate::port_forward::listen( - handler.id.clone(), + handler.get_id(), handler.password.clone(), port, handler.clone(), diff --git a/src/virtual_display_manager.rs b/src/virtual_display_manager.rs index 1c53a964f..fc9af6dea 100644 --- a/src/virtual_display_manager.rs +++ b/src/virtual_display_manager.rs @@ -139,10 +139,10 @@ pub fn plug_in_index_modes( } pub fn reset_all() -> ResultType<()> { - let mut manager = VIRTUAL_DISPLAY_MANAGER.lock().unwrap(); - if !manager.peer_index_name.is_empty() || manager.headless_index_name.is_some() { - manager.install_update_driver()?; + if let Err(e) = plug_out_peer_request(&get_virtual_displays()) { + log::error!("Failed to plug out virtual displays: {}", e); } + let _ = plug_out_headless(); Ok(()) }