diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd8282187..aa1f5595b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,7 +78,7 @@ jobs: shell: bash run: | case ${{ matrix.job.target }} in - x86_64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake ;; + x86_64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev ;; # arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;; # aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;; esac diff --git a/.gitignore b/.gitignore index 5b26711c5..53bd9cf94 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .vscode .idea .DS_Store +libsciter-gtk.so src/ui/inline.rs extractor __pycache__ diff --git a/Cargo.lock b/Cargo.lock index 6a32a7a89..107c4400a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ab_glyph_rasterizer" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e" - [[package]] name = "addr2line" version = "0.17.0" @@ -29,6 +23,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -69,19 +74,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "andrew" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf" -dependencies = [ - "bitflags", - "rusttype", - "walkdir", - "xdg", - "xml-rs", -] - [[package]] name = "android_log-sys" version = "0.2.0" @@ -195,9 +187,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" @@ -218,7 +210,7 @@ checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" dependencies = [ "atk-sys", "bitflags", - "glib 0.15.11", + "glib 0.15.12", "libc", ] @@ -277,9 +269,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", @@ -325,6 +317,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1489fcb93a5bb47da0462ca93ad252ad6af2145cce58d10d46a83931ba9f016b" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block" version = "0.1.6" @@ -362,9 +366,9 @@ checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "bytemuck" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +checksum = "c53dfa917ec274df8ed3c572698f381a24eef2efba9492d797301b72b6db408a" [[package]] name = "byteorder" @@ -386,13 +390,13 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cairo-rs" -version = "0.15.11" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62be3562254e90c1c6050a72aa638f6315593e98c5cdaba9017cedbabf0a5dee" +checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" dependencies = [ "bitflags", "cairo-sys-rs", - "glib 0.15.11", + "glib 0.15.12", "libc", "thiserror", ] @@ -410,12 +414,12 @@ dependencies = [ [[package]] name = "calloop" -version = "0.6.5" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" +checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" dependencies = [ "log", - "nix 0.18.0", + "nix 0.22.3", ] [[package]] @@ -424,7 +428,7 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" dependencies = [ - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -433,7 +437,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" dependencies = [ - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -444,25 +448,25 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.10", - "serde 1.0.137", - "serde_json 1.0.81", + "semver 1.0.12", + "serde 1.0.139", + "serde_json 1.0.82", ] [[package]] name = "cbindgen" -version = "0.24.3" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" +checksum = "5b6d248e3ca02f3fbfabcb9284464c596baec223a26d91bbf44a5a62ddb0d900" dependencies = [ - "clap 3.2.6", + "clap 3.2.12", "heck 0.4.0", "indexmap", "log", "proc-macro2", "quote", - "serde 1.0.137", - "serde_json 1.0.81", + "serde 1.0.139", + "serde_json 1.0.82", "syn", "tempfile", "toml", @@ -533,7 +537,7 @@ checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" dependencies = [ "glob", "libc", - "libloading 0.7.3", + "libloading", ] [[package]] @@ -553,24 +557,39 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.6" +version = "3.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1fe12880bae935d142c8702d500c63a4e8634b6c3c57ad72bf978fc7b6249a" +checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" dependencies = [ "atty", "bitflags", + "clap_derive", "clap_lex", "indexmap", + "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.15.0", ] [[package]] -name = "clap_lex" -version = "0.2.3" +name = "clap_derive" +version = "3.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87eba3c8c7f42ef17f6c659fc7416d0f4758cd3e58861ee63c5fa4a4dde649e4" +checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] @@ -582,16 +601,16 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", "thiserror", ] [[package]] name = "clipboard-win" -version = "4.4.1" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3e1238132dc01f081e1cbb9dace14e5ef4c3a51ee244bd982275fb514605db" +checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219" dependencies = [ "error-code", "str-buf", @@ -680,9 +699,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.2" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "83827793632c72fa4f73c2edb31e7a997527dd8ffe7077344621fc62c5478157" dependencies = [ "cache-padded", ] @@ -693,7 +712,7 @@ version = "0.4.0" source = "git+https://github.com/open-trade/confy#630cc28a396cb7d01eefdd9f3824486fe4d8554b" dependencies = [ "directories-next", - "serde 1.0.137", + "serde 1.0.139", "thiserror", "toml", ] @@ -860,20 +879,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "crossbeam" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - [[package]] name = "crossbeam-channel" version = "0.5.5" @@ -931,9 +936,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -955,7 +960,7 @@ version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865" dependencies = [ - "nix 0.24.1", + "nix 0.24.2", "winapi 0.3.9", ] @@ -965,38 +970,14 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core 0.10.2", - "darling_macro 0.10.2", -] - [[package]] name = "darling" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", -] - -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.9.3", - "syn", + "darling_core", + "darling_macro", ] [[package]] @@ -1013,24 +994,13 @@ dependencies = [ "syn", ] -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core 0.10.2", - "quote", - "syn", -] - [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core 0.13.4", + "darling_core", "quote", "syn", ] @@ -1156,15 +1126,27 @@ dependencies = [ [[package]] name = "dbus" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0a745c25b32caa56b82a3950f5fec7893a960f4c10ca3b02060b0c38d8c2ce" +checksum = "6f8bcdd56d2e5c4ed26a529c5a9029f5db8290d433497506f958eae3be148eb6" dependencies = [ "libc", "libdbus-sys", "winapi 0.3.9", ] +[[package]] +name = "default-net" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e70d471b0ba4e722c85651b3bb04b6880dfdb1224a43ade80c1295314db646" +dependencies = [ + "libc", + "memalloc", + "system-configuration", + "windows", +] + [[package]] name = "deflate" version = "0.8.6" @@ -1195,15 +1177,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -1214,17 +1187,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi 0.3.9", -] - [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -1242,22 +1204,13 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "dlib" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" -dependencies = [ - "libloading 0.6.7", -] - [[package]] name = "dlib" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" dependencies = [ - "libloading 0.7.3", + "libloading", ] [[package]] @@ -1268,7 +1221,7 @@ checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f" dependencies = [ "lazy_static", "regex", - "serde 1.0.137", + "serde 1.0.139", "strsim 0.10.0", ] @@ -1295,9 +1248,9 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" [[package]] name = "encoding_rs" @@ -1313,12 +1266,13 @@ name = "enigo" version = "0.0.14" dependencies = [ "core-graphics 0.22.3", + "hbb_common", "libc", "log", "objc", "pkg-config", "rdev", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", "unicode-segmentation", "winapi 0.3.9", @@ -1403,6 +1357,16 @@ dependencies = [ "str-buf", ] +[[package]] +name = "evdev" +version = "0.11.5" +source = "git+https://github.com/fufesou/evdev#cec616e37790293d2cd2aa54a96601ed6b1b35a9" +dependencies = [ + "bitvec", + "libc", + "nix 0.23.1", +] + [[package]] name = "event-listener" version = "2.5.2" @@ -1439,14 +1403,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall", - "winapi 0.3.9", + "windows-sys 0.36.1", ] [[package]] @@ -1481,9 +1445,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge" -version = "1.34.2" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56ac4e92d08407968b7efba9cda734935f6ebbffd521f9255b8f516d8c2cede" +checksum = "b7e7e4af55d6a36aad9573737a12fba774999e4d6dd5e668e29c25bb473f85f3" dependencies = [ "allo-isolate", "anyhow", @@ -1495,9 +1459,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_codegen" -version = "1.34.2" +version = "1.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bd4b68e21ce08b9afe3332c37c1eef2799bc36c0521890f5aaa303942b7df2" +checksum = "3209735fd687b06b8d770ec008874119b91f7f46b4a73d17226d5c337435bb74" dependencies = [ "anyhow", "cargo_metadata", @@ -1510,7 +1474,7 @@ dependencies = [ "pathdiff", "quote", "regex", - "serde 1.0.137", + "serde 1.0.139", "serde_yaml", "structopt", "syn", @@ -1521,9 +1485,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" -version = "1.34.2" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d4540ab97380ed5af0212f8b18ff84a5f32acc8247f8f731311516dd105363f" +checksum = "13652b9b71bc3bf4ea3bbb5cadc9bc2350fe0fba5145f6a949309fc452576d6d" [[package]] name = "fnv" @@ -1578,6 +1542,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.21" @@ -1702,7 +1672,7 @@ dependencies = [ "gdk-pixbuf", "gdk-sys", "gio", - "glib 0.15.11", + "glib 0.15.12", "libc", "pango", ] @@ -1716,7 +1686,7 @@ dependencies = [ "bitflags", "gdk-pixbuf-sys", "gio", - "glib 0.15.11", + "glib 0.15.12", "libc", ] @@ -1783,22 +1753,22 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" [[package]] name = "gio" -version = "0.15.11" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f132be35e05d9662b9fa0fee3f349c6621f7782e0105917f4cc73c1bf47eceb" +checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-io", "gio-sys", - "glib 0.15.11", + "glib 0.15.12", "libc", "once_cell", "thiserror", @@ -1838,9 +1808,9 @@ dependencies = [ [[package]] name = "glib" -version = "0.15.11" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124026a2fa8c33a3d17a3fe59c103f2d9fa5bd92c19e029e037736729abeab" +checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" dependencies = [ "bitflags", "futures-channel", @@ -2082,7 +2052,7 @@ dependencies = [ "gdk", "gdk-pixbuf", "gio", - "glib 0.15.11", + "glib 0.15.12", "gtk-sys", "gtk3-macros", "libc", @@ -2138,15 +2108,18 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.3", + "tokio-util", "tracing", ] [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "hbb_common" @@ -2164,19 +2137,21 @@ dependencies = [ "lazy_static", "log", "mac_address", + "machine-uid", "protobuf", - "protobuf-codegen-pure", + "protobuf-codegen", "quinn", "rand 0.8.5", "regex", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", - "serde_json 1.0.81", + "serde_json 1.0.82", + "serde_with", "socket2 0.3.19", "sodiumoxide", "tokio", "tokio-socks", - "tokio-util 0.6.10", + "tokio-util", "toml", "winapi 0.3.9", "zstd", @@ -2255,21 +2230,21 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" version = "0.1.0" -source = "git+https://github.com/21pages/hwcodec#bfc558d2375928b0a59336cfc72336415db27066" +source = "git+https://github.com/21pages/hwcodec#91d1cd327c88490f917457072aeef0676ddb2be7" dependencies = [ "bindgen", "cc", "log", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", - "serde_json 1.0.81", + "serde_json 1.0.82", ] [[package]] name = "hyper" -version = "0.14.19" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -2371,6 +2346,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -2477,11 +2455,11 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libappindicator" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b29fab3280d59f3d06725f75da9ef9a1b001b2c748b1abfebd1c966c61d7de" +checksum = "db2d3cb96d092b4824cb306c9e544c856a4cb6210c1081945187f7f1924b47e8" dependencies = [ - "glib 0.15.11", + "glib 0.15.12", "gtk", "gtk-sys", "libappindicator-sys", @@ -2490,12 +2468,12 @@ dependencies = [ [[package]] name = "libappindicator-sys" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0e019ae1a736a858f4c52b58af2ca6e797f27d7fe534e8a56776d74a8f2535" +checksum = "f1b3b6681973cea8cc3bce7391e6d7d5502720b80a581c9a95c9cbaf592826aa" dependencies = [ "gtk-sys", - "libloading 0.7.3", + "libloading", "once_cell", ] @@ -2514,16 +2492,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "libloading" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" -dependencies = [ - "cfg-if 1.0.0", - "winapi 0.3.9", -] - [[package]] name = "libloading" version = "0.7.3" @@ -2680,6 +2648,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" +[[package]] +name = "memalloc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df39d232f5c40b0891c10216992c2f250c054105cb1e56f0fc9032db6203ecc1" + [[package]] name = "memchr" version = "2.5.0" @@ -2688,9 +2662,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" +checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" dependencies = [ "libc", ] @@ -2763,19 +2737,6 @@ dependencies = [ "winapi 0.2.8", ] -[[package]] -name = "mio" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" -dependencies = [ - "libc", - "log", - "miow 0.3.7", - "ntapi", - "winapi 0.3.9", -] - [[package]] name = "mio" version = "0.8.4" @@ -2788,18 +2749,6 @@ dependencies = [ "windows-sys 0.36.1", ] -[[package]] -name = "mio-misc" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c" -dependencies = [ - "crossbeam", - "crossbeam-queue", - "log", - "mio 0.7.14", -] - [[package]] name = "mio-named-pipes" version = "0.1.7" @@ -2842,6 +2791,15 @@ dependencies = [ "windows-sys 0.28.0", ] +[[package]] +name = "mouce" +version = "0.2.1" +source = "git+https://github.com/fufesou/mouce.git#26da8d4b0009b7f96996799c2a5c0990a8dbf08b" +dependencies = [ + "glob", + "libc", +] + [[package]] name = "muldiv" version = "0.2.1" @@ -2850,10 +2808,11 @@ checksum = "0419348c027fa7be448d2ae7ea0e4e04c2334c31dc4e74ab29f00a2a7ca69204" [[package]] name = "ndk" -version = "0.3.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab" +checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" dependencies = [ + "bitflags", "jni-sys", "ndk-sys 0.2.2", "num_enum", @@ -2881,15 +2840,16 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-glue" -version = "0.3.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385" +checksum = "c71bee8ea72d685477e28bd004cfe1bf99c754d688cd78cad139eae4089484d4" dependencies = [ "lazy_static", "libc", "log", - "ndk 0.3.0", - "ndk-macro 0.2.0", + "ndk 0.5.0", + "ndk-context", + "ndk-macro", "ndk-sys 0.2.2", ] @@ -2904,30 +2864,17 @@ dependencies = [ "log", "ndk 0.6.0", "ndk-context", - "ndk-macro 0.3.0", + "ndk-macro", "ndk-sys 0.3.0", ] -[[package]] -name = "ndk-macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" -dependencies = [ - "darling 0.10.2", - "proc-macro-crate 0.1.5", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "ndk-macro" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ - "darling 0.13.4", + "darling", "proc-macro-crate 1.1.3", "proc-macro2", "quote", @@ -2960,30 +2907,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "nix" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" -dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", - "libc", -] - -[[package]] -name = "nix" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", -] - [[package]] name = "nix" version = "0.22.3" @@ -3012,9 +2935,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9" +checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -3181,9 +3104,9 @@ dependencies = [ [[package]] name = "object" -version = "0.28.4" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "memchr", ] @@ -3213,9 +3136,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "openssl-probe" @@ -3225,18 +3148,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "os_str_bytes" -version = "6.1.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" - -[[package]] -name = "owned_ttf_parser" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" -dependencies = [ - "ttf-parser", -] +checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" [[package]] name = "padlock" @@ -3251,7 +3165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" dependencies = [ "bitflags", - "glib 0.15.11", + "glib 0.15.12", "libc", "once_cell", "pango-sys", @@ -3271,8 +3185,8 @@ dependencies = [ [[package]] name = "parity-tokio-ipc" -version = "0.7.3" -source = "git+https://github.com/open-trade/parity-tokio-ipc#64d5b6b11464d01bfe75b3b79a49bd455b79e352" +version = "0.7.3-1" +source = "git+https://github.com/open-trade/parity-tokio-ipc#20b2895910161605210657f3e751edd55321f698" dependencies = [ "futures", "libc", @@ -3411,18 +3325,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" dependencies = [ "proc-macro2", "quote", @@ -3547,60 +3461,56 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.0.0-alpha.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5ef59c35c7472ce5e1b6c5924b87585143d1fc2cf39eae0009bba6c4df62f1" +checksum = "4ee4a7d8b91800c8f167a6268d1a1026607368e1adc84e98fe044aeb905302f7" +dependencies = [ + "bytes", + "once_cell", + "protobuf-support", + "thiserror", +] [[package]] name = "protobuf-codegen" -version = "3.0.0-alpha.2" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89100ee819f69b77a4cab389fec9dd155a305af4c615e6413ec1ef9341f333ef" +checksum = "07b893e5e7d3395545d5244f8c0d33674025bd566b26c03bfda49b82c6dec45e" dependencies = [ "anyhow", + "once_cell", "protobuf", "protobuf-parse", - "thiserror", -] - -[[package]] -name = "protobuf-codegen-pure" -version = "3.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79453e74d08190551e821533ee42c447f9e21ca26f83520e120e6e8af27f6879" -dependencies = [ - "anyhow", - "protobuf", - "protobuf-codegen", - "protobuf-parse", - "thiserror", -] - -[[package]] -name = "protobuf-parse" -version = "3.0.0-alpha.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c265ffc69976efc3056955b881641add3186ad0be893ef10622482d80d1d2b68" -dependencies = [ - "anyhow", - "protobuf", - "protoc", + "regex", "tempfile", "thiserror", ] [[package]] -name = "protoc" -version = "3.0.0-alpha.2" +name = "protobuf-parse" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1f8b318a54d18fbe542513331e058f4f8ce6502e542e057c50c7e5e803fdab" +checksum = "9b1447dd751c434cc1b415579837ebd0411ed7d67d465f38010da5d7cd33af4d" dependencies = [ "anyhow", + "indexmap", "log", + "protobuf", + "protobuf-support", + "tempfile", "thiserror", "which 4.2.5", ] +[[package]] +name = "protobuf-support" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca157fe12fc7ee2e315f2f735e27df41b3d97cdd70ea112824dac1ffb08ee1c" +dependencies = [ + "thiserror", +] + [[package]] name = "quest" version = "0.3.0" @@ -3676,6 +3586,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.6.5" @@ -3812,16 +3728,6 @@ dependencies = [ "rand_core 0.3.1", ] -[[package]] -name = "raw-window-handle" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" -dependencies = [ - "libc", - "raw-window-handle 0.4.3", -] - [[package]] name = "raw-window-handle" version = "0.4.3" @@ -3911,9 +3817,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.6" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -3922,9 +3828,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -3970,8 +3876,8 @@ dependencies = [ "pin-project-lite", "rustls", "rustls-pemfile 1.0.0", - "serde 1.0.137", - "serde_json 1.0.81", + "serde 1.0.139", + "serde_json 1.0.82", "serde_urlencoded", "tokio", "tokio-rustls", @@ -4012,13 +3918,11 @@ dependencies = [ [[package]] name = "rpassword" -version = "6.0.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956" +checksum = "26b763cb66df1c928432cc35053f8bd4cec3335d8559fc16010017d16b3c1680" dependencies = [ "libc", - "serde 1.0.137", - "serde_json 1.0.81", "winapi 0.3.9", ] @@ -4084,7 +3988,7 @@ dependencies = [ "base64", "cc", "cfg-if 1.0.0", - "clap 3.2.6", + "clap 3.2.12", "clipboard", "cocoa 0.24.0", "core-foundation 0.9.3", @@ -4092,8 +3996,10 @@ dependencies = [ "cpal", "ctrlc", "dasp", + "default-net", "dispatch", "enigo", + "evdev", "flexi_logger", "flutter_rust_bridge", "flutter_rust_bridge_codegen", @@ -4108,22 +4014,23 @@ dependencies = [ "mac_address", "machine-uid", "magnum-opus", + "mouce", "num_cpus", "objc", "parity-tokio-ipc", "rdev", "repng", "reqwest", - "rpassword 6.0.1", + "rpassword 7.0.0", "rubato", "runas", "rust-pulsectl", "samplerate", "sciter-rs", "scrap", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", - "serde_json 1.0.81", + "serde_json 1.0.82", "sha2", "simple_rc", "sys-locale", @@ -4138,6 +4045,7 @@ dependencies = [ "winit", "winreg 0.10.1", "winres", + "wol-rs", ] [[package]] @@ -4196,21 +4104,11 @@ dependencies = [ "base64", ] -[[package]] -name = "rusttype" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" -dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", -] - [[package]] name = "rustversion" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" +checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8" [[package]] name = "ryu" @@ -4291,8 +4189,8 @@ dependencies = [ "num_cpus", "quest", "repng", - "serde 1.0.137", - "serde_json 1.0.81", + "serde 1.0.139", + "serde_json 1.0.82", "target_build_utils", "tracing", "webm", @@ -4343,11 +4241,11 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" +checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" dependencies = [ - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -4367,18 +4265,18 @@ checksum = "34b623917345a631dc9608d5194cc206b3fe6c3554cd1c75b937e55e285254af" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" dependencies = [ "proc-macro2", "quote", @@ -4399,13 +4297,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ "itoa 1.0.2", "ryu", - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -4417,18 +4315,40 @@ dependencies = [ "form_urlencoded", "itoa 1.0.2", "ryu", - "serde 1.0.137", + "serde 1.0.139", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde 1.0.139", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "serde_yaml" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ "indexmap", "ryu", - "serde 1.0.137", + "serde 1.0.139", "yaml-rust", ] @@ -4480,7 +4400,7 @@ version = "0.1.0" dependencies = [ "confy", "hbb_common", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", "walkdir", ] @@ -4499,24 +4419,24 @@ checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "smallvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smithay-client-toolkit" -version = "0.12.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" +checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3" dependencies = [ - "andrew", "bitflags", "calloop", - "dlib 0.4.2", + "dlib", "lazy_static", "log", "memmap2", - "nix 0.18.0", + "nix 0.22.3", + "pkg-config", "wayland-client", "wayland-cursor", "wayland-protocols", @@ -4552,7 +4472,7 @@ dependencies = [ "ed25519", "libc", "libsodium-sys", - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -4585,12 +4505,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - [[package]] name = "strsim" version = "0.10.0" @@ -4679,9 +4593,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.23.13" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3977ec2e0520829be45c8a2df70db2bf364714d8a748316a10c3c35d4d2b01c9" +checksum = "0b6e19da72a8d75be4d40e4dd4686afca31507f26c3ffdf6bd3073278d9de0a0" dependencies = [ "cfg-if 1.0.0", "core-foundation-sys 0.8.3", @@ -4692,6 +4606,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "system-configuration" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75182f12f490e953596550b65ee31bda7c8e043d9386174b353bda50838c3fd" +dependencies = [ + "bitflags", + "core-foundation 0.9.3", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys 0.8.3", + "libc", +] + [[package]] name = "system-deps" version = "1.3.2" @@ -4720,6 +4655,12 @@ dependencies = [ "version-compare 0.1.0", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "target_build_utils" version = "0.3.1" @@ -4853,10 +4794,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e" dependencies = [ + "autocfg 1.1.0", "bytes", "libc", "memchr", @@ -4895,8 +4837,8 @@ dependencies = [ [[package]] name = "tokio-socks" -version = "0.5.1" -source = "git+https://github.com/open-trade/tokio-socks#c34272f219b24dc6508f13fa81eff9850e616ce2" +version = "0.5.1-1" +source = "git+https://github.com/open-trade/tokio-socks#7034e79263ce25c348be072808d7601d82cd892d" dependencies = [ "bytes", "either", @@ -4906,23 +4848,7 @@ dependencies = [ "pin-project", "thiserror", "tokio", - "tokio-util 0.6.10", -] - -[[package]] -name = "tokio-util" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" -dependencies = [ - "bytes", - "futures-core", - "futures-io", - "futures-sink", - "log", - "pin-project-lite", - "slab", - "tokio", + "tokio-util", ] [[package]] @@ -4933,8 +4859,12 @@ checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", + "futures-util", + "hashbrown", "pin-project-lite", + "slab", "tokio", "tracing", ] @@ -4945,7 +4875,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ - "serde 1.0.137", + "serde 1.0.139", ] [[package]] @@ -4968,9 +4898,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" dependencies = [ "proc-macro2", "quote", @@ -5016,9 +4946,8 @@ dependencies = [ [[package]] name = "trayicon" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c367fd7cdcdf19234aa104f7e03abe1be526018e4282af9f275bf436b9c9ad23" +version = "0.1.3-1" +source = "git+https://github.com/open-trade/trayicon-rs#8d9c4489287752cc5be4a35c103198f7111112f9" dependencies = [ "winapi 0.3.9", "winit", @@ -5030,12 +4959,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" -[[package]] -name = "ttf-parser" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" - [[package]] name = "typenum" version = "1.15.0" @@ -5044,9 +4967,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ucd-trie" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" +checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c" [[package]] name = "unicode-bidi" @@ -5056,15 +4979,15 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" [[package]] name = "unicode-normalization" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" dependencies = [ "tinyvec", ] @@ -5145,7 +5068,7 @@ dependencies = [ "cc", "hbb_common", "lazy_static", - "serde 1.0.137", + "serde 1.0.139", "serde_derive", "thiserror", ] @@ -5251,14 +5174,14 @@ checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" [[package]] name = "wayland-client" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" +checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f" dependencies = [ "bitflags", "downcast-rs", "libc", - "nix 0.20.0", + "nix 0.22.3", "scoped-tls", "wayland-commons", "wayland-scanner", @@ -5267,11 +5190,11 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" +checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" dependencies = [ - "nix 0.20.0", + "nix 0.22.3", "once_cell", "smallvec", "wayland-sys", @@ -5279,20 +5202,20 @@ dependencies = [ [[package]] name = "wayland-cursor" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" +checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" dependencies = [ - "nix 0.20.0", + "nix 0.22.3", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" +checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741" dependencies = [ "bitflags", "wayland-client", @@ -5302,9 +5225,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" +checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0" dependencies = [ "proc-macro2", "quote", @@ -5313,11 +5236,11 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" +checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4" dependencies = [ - "dlib 0.5.0", + "dlib", "lazy_static", "pkg-config", ] @@ -5362,18 +5285,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.3" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" +checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" dependencies = [ "webpki", ] [[package]] name = "weezl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "wepoll-ffi" @@ -5473,6 +5396,19 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0" +dependencies = [ + "windows_aarch64_msvc 0.30.0", + "windows_i686_gnu 0.30.0", + "windows_i686_msvc 0.30.0", + "windows_x86_64_gnu 0.30.0", + "windows_x86_64_msvc 0.30.0", +] + [[package]] name = "windows-service" version = "0.4.0" @@ -5517,6 +5453,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" +[[package]] +name = "windows_aarch64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -5529,6 +5471,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" +[[package]] +name = "windows_i686_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -5541,6 +5489,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" +[[package]] +name = "windows_i686_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -5553,6 +5507,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" +[[package]] +name = "windows_x86_64_gnu" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -5565,6 +5525,12 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" +[[package]] +name = "windows_x86_64_msvc" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -5573,9 +5539,9 @@ checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "winit" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8" +checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" dependencies = [ "bitflags", "cocoa 0.24.0", @@ -5587,18 +5553,19 @@ dependencies = [ "lazy_static", "libc", "log", - "mio 0.7.14", - "mio-misc", - "ndk 0.3.0", - "ndk-glue 0.3.0", + "mio 0.8.4", + "ndk 0.5.0", + "ndk-glue 0.5.2", "ndk-sys 0.2.2", "objc", "parking_lot 0.11.2", "percent-encoding", - "raw-window-handle 0.3.4", - "scopeguard", + "raw-window-handle", "smithay-client-toolkit", + "wasm-bindgen", "wayland-client", + "wayland-protocols", + "web-sys", "winapi 0.3.9", "x11-dl", ] @@ -5630,6 +5597,15 @@ dependencies = [ "toml", ] +[[package]] +name = "wol-rs" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f97e69b28b256ccfb02472c25057132e234aa8368fea3bb0268def564ce1f2" +dependencies = [ + "clap 3.2.12", +] + [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -5640,6 +5616,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b31594f29d27036c383b53b59ed3476874d518f0efb151b27a4c275141390e" +dependencies = [ + "tap", +] + [[package]] name = "x11" version = "2.19.1" @@ -5682,15 +5667,6 @@ dependencies = [ "nom", ] -[[package]] -name = "xdg" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" -dependencies = [ - "dirs", -] - [[package]] name = "xml-rs" version = "0.8.4" diff --git a/Cargo.toml b/Cargo.toml index 264c82940..61005f304 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ default = ["use_dasp"] [dependencies] whoami = "1.2" -scrap = { path = "libs/scrap" } +scrap = { path = "libs/scrap", features = ["wayland"] } hbb_common = { path = "libs/hbb_common" } serde_derive = "1.0" serde = "1.0" @@ -51,10 +51,12 @@ samplerate = { version = "0.2", optional = true } async-trait = "0.1" uuid = { version = "1.0", features = ["v4"] } clap = "3.0" -rpassword = "6.0" +rpassword = "7.0" base64 = "0.13" -sysinfo = "0.23" +sysinfo = "0.24" num_cpus = "1.13" +default-net = "0.11.0" +wol-rs = "0.9.1" [target.'cfg(not(target_os = "linux"))'.dependencies] reqwest = { version = "0.11", features = ["json", "rustls-tls"], default-features=false } @@ -67,7 +69,7 @@ machine-uid = "0.2" mac_address = "1.1" sciter-rs = { git = "https://github.com/open-trade/rust-sciter", branch = "dyn" } sys-locale = "0.2" -enigo = { path = "libs/enigo" } +enigo = { path = "libs/enigo", features = [ "with_serde" ] } clipboard = { path = "libs/clipboard" } rdev = { git = "https://github.com/asur4s/rdev" } ctrlc = "3.2" @@ -76,9 +78,8 @@ arboard = "2.0" [target.'cfg(target_os = "windows")'.dependencies] #systray = { git = "https://github.com/open-trade/systray-rs" } -trayicon = { version = "0.1", features = ["winit"] } -# > 0.25 not work with trayicon -winit = "0.25" +trayicon = { git = "https://github.com/open-trade/trayicon-rs", features = ["winit"] } +winit = "0.26" winapi = { version = "0.3", features = ["winuser"] } winreg = "0.10" windows-service = "0.4" @@ -98,13 +99,15 @@ psimple = { package = "libpulse-simple-binding", version = "2.25" } pulse = { package = "libpulse-binding", version = "2.26" } rust-pulsectl = { git = "https://github.com/open-trade/pulsectl" } async-process = "1.3" +mouce = { git="https://github.com/fufesou/mouce.git" } +evdev = { git="https://github.com/fufesou/evdev" } [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.11" -jni = "0.19.0" +jni = "0.19" [target.'cfg(any(target_os = "android", target_os = "ios"))'.dependencies] -flutter_rust_bridge = "1.30.0" +flutter_rust_bridge = "=1.30.0" [workspace] members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/simple_rc"] @@ -122,7 +125,7 @@ winapi = { version = "0.3", features = [ "winnt" ] } cc = "1.0" hbb_common = { path = "libs/hbb_common" } simple_rc = { path = "libs/simple_rc", optional = true } -flutter_rust_bridge_codegen = "1.30.0" +flutter_rust_bridge_codegen = "=1.30.0" [dev-dependencies] hound = "3.4" diff --git a/DEBIAN/postinst b/DEBIAN/postinst old mode 100644 new mode 100755 index 5899bd4df..1c7697acc --- a/DEBIAN/postinst +++ b/DEBIAN/postinst @@ -8,16 +8,20 @@ if [ "$1" = configure ]; then if [ "systemd" == "$INITSYS" ]; then if [ -e /etc/systemd/system/rustdesk.service ]; then - rm /etc/systemd/system/rustdesk.service + rm /etc/systemd/system/rustdesk.service /usr/lib/systemd/system/rustdesk.service /usr/lib/systemd/user/rustdesk.service >/dev/null 2>&1 fi version=$(python3 -V 2>&1 | grep -Po '(?<=Python )(.+)') parsedVersion=$(echo "${version//./}") if [[ "$parsedVersion" -gt "360" ]]; then sudo -H pip3 install pynput fi - cp /usr/share/rustdesk/files/systemd/rustdesk.service /etc/systemd/system/rustdesk.service + cp /usr/share/rustdesk/files/systemd/rustdesk.service /usr/lib/systemd/system/rustdesk.service systemctl daemon-reload systemctl enable rustdesk systemctl start rustdesk + + cp /usr/share/rustdesk/files/systemd/rustdesk.service.user /usr/lib/systemd/user/rustdesk.service + curUser=$(who | awk '{print $1}' | head -1) + systemctl --machine=${curUser}@.host --user daemon-reload fi fi diff --git a/DEBIAN/postrm b/DEBIAN/postrm old mode 100644 new mode 100755 diff --git a/DEBIAN/preinst b/DEBIAN/preinst old mode 100644 new mode 100755 index 8b73e9962..7fbedca4a --- a/DEBIAN/preinst +++ b/DEBIAN/preinst @@ -7,6 +7,13 @@ case $1 in INITSYS=$(ls -al /proc/1/exe | awk -F' ' '{print $NF}' | awk -F'/' '{print $NF}') if [ "systemd" == "${INITSYS}" ]; then service rustdesk stop || true + + serverUser=$(ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1) + if [ "$serverUser" != "" ] && [ "$serverUser" != "root" ] + then + systemctl --machine=${serverUser}@.host --user stop rustdesk || true + fi + sleep 1 rm -rf /usr/bin/libsciter-gtk.so fi diff --git a/DEBIAN/prerm b/DEBIAN/prerm old mode 100644 new mode 100755 index 865b689ab..3bb453198 --- a/DEBIAN/prerm +++ b/DEBIAN/prerm @@ -8,7 +8,14 @@ case $1 in if [ "systemd" == "${INITSYS}" ]; then systemctl stop rustdesk || true systemctl disable rustdesk || true - rm /etc/systemd/system/rustdesk.service || true + + serverUser=$(ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1) + if [ "$serverUser" != "" ] && [ "$serverUser" != "root" ] + then + systemctl --machine=${serverUser}@.host --user stop rustdesk || true + fi + + rm /etc/systemd/system/rustdesk.service /usr/lib/systemd/system/rustdesk.service /usr/lib/systemd/user/rustdesk.service || true fi ;; esac diff --git a/README-AR.md b/README-AR.md index 055a654d2..636d2611e 100644 --- a/README-AR.md +++ b/README-AR.md @@ -5,7 +5,7 @@ DockerStructureSnapshot
- [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
لغتك الأم, Doc و RustDesk UI, README نحن بحاجة إلى مساعدتك لترجمة هذا

diff --git a/README-CS.md b/README-CS.md index 1dd5463a1..ac4567a0f 100644 --- a/README-CS.md +++ b/README-CS.md @@ -5,7 +5,7 @@ DockerStrukturaUkázky
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Potřebujeme Vaši pomoc s překláním textů tohoto ČTIMNE, uživatelského rozhraní aplikace RustDesk a dokumentace k ní do vašeho jazyka

diff --git a/README-DE.md b/README-DE.md index 3f770d226..0df001e3f 100644 --- a/README-DE.md +++ b/README-DE.md @@ -5,11 +5,11 @@ DockerDateistrukturScreenshots
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Wir brauchen deine Hilfe um diese README Datei zu verbessern und aktualisieren

-Rede mit uns: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Rede mit uns: [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) diff --git a/README-EO.md b/README-EO.md index 21a4f9521..b532aa4de 100644 --- a/README-EO.md +++ b/README-EO.md @@ -5,11 +5,11 @@ DockerStrukturoEkrankopio
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Ni bezonas helpon traduki tiun README kaj la interfacon al via denaska lingvo

-Babili kun ni: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Babili kun ni: [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) diff --git a/README-ES.md b/README-ES.md index ce8601fa0..3d8f019a4 100644 --- a/README-ES.md +++ b/README-ES.md @@ -5,7 +5,7 @@ DockerEstructuraCaptura de pantalla
- [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Necesitamos tu ayuda para traducir este README a tu idioma

diff --git a/README-FA.md b/README-FA.md index 0f7ca1a95..0aac205a8 100644 --- a/README-FA.md +++ b/README-FA.md @@ -5,11 +5,11 @@ داکرساختسرور
- [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
برای ترجمه این RustDesk UI ،README و Doc به زبان مادری شما به کمکتون نیاز داریم

-با ما گپ بزنید: [Reddit](https://www.reddit.com/r/rustdesk) | [Twitter](https://twitter.com/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) +با ما گپ بزنید: [Reddit](https://www.reddit.com/r/rustdesk) | [Twitter](https://twitter.com/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) diff --git a/README-FI.md b/README-FI.md index a2d7534e0..ca846007f 100644 --- a/README-FI.md +++ b/README-FI.md @@ -5,11 +5,11 @@ DockerRakenneTilannevedos
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi

-Juttele meidän kanssa: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Juttele meidän kanssa: [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) diff --git a/README-FR.md b/README-FR.md index b1f8e3670..5d421b97d 100644 --- a/README-FR.md +++ b/README-FR.md @@ -5,11 +5,11 @@ Docker - Structure - Images
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle.

-Chattez avec nous : [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Chattez avec nous : [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) diff --git a/README-HU.md b/README-HU.md new file mode 100644 index 000000000..eeeaaa37d --- /dev/null +++ b/README-HU.md @@ -0,0 +1,182 @@ +

+ RustDesk - Your remote desktop
+ Szerverek • + Építés • + Docker • + Struktúra • + Képernyőképek
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ Kell a segítséged, hogy lefordítsuk ezt a README-t, a RustDesk UI-t és a Dokumentációt az anyanyelvedre +

+ +Beszélgess velünk: [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) + +A RustDesk egy távoli elérésű asztali szoftver, Rust-ban írva. Működik mindenféle konfiguráció nélkül, feltelepítéssel, vagy anélkül. Az adataidat teljesen te kezeled, nincs szükség aggódásra a harmadik felek miatt. Használhatod a RustDesk punblikus randevú/relay szervereit, [hostolhatsz sajátot](https://rustdesk.com/server), vagy akár [írhatsz is egyet](https://github.com/rustdesk/rustdesk-server-demo). + +![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) + +A RustDesk szívesen fogad minden contributiont, támogatást mindenkitől. Lásd a [`CONTRIBUTING.md`](CONTRIBUTING.md) fájlt a kezdéshez. + +[**Hogyan működik a RustDesk?**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F) + +[**BINARY LELTÖLTÉS**](https://github.com/rustdesk/rustdesk/releases) + +[Get it on F-Droid](https://f-droid.org/en/packages/com.carriez.flutter_hbb) + +## Ingyenes publikus szerverek + +Ezalatt az üzenet alatt találhatóak azok a publikus szerverek, amelyeket ingyen használhatsz. Ezek a szerverek változhatnak a jövőben, illetve a hálózatuk lehet hogy lassú lehet. +| Hely | Host | Specifikáció | +| --------- | ------------- | ------------------ | +| Seoul | AWS lightsail | 1 VCPU / 0.5GB RAM | +| Singapore | Vultr | 1 VCPU / 1GB RAM | +| Dallas | Vultr | 1 VCPU / 1GB RAM | | + +## Dependencies + +Az asztali verziók [sciter](https://sciter.com/)-t használnak a GUI-hoz, kérlek telepítsd a dynamikus könyvtárat magad. + +[Windows](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.win/x64/sciter.dll) | +[Linux](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so) | +[MacOS](https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.osx/libsciter.dylib) + +A telefonos verziók Flutter-t hasznának. Később lehetséges hogy Sciterről Flutterre migrálunk az asztali verziókban is. + +## Építési pontok + +- Készítsd elő a Rust, C++ fejlesztői környezetet (env) + +- Telepítsd a [vcpkg](https://github.com/microsoft/vcpkg)-t, és állítsd be a `VCPKG_ROOT` környezeti változót helyesen + + - Windows: vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static + - Linux/MacOS: vcpkg install libvpx libyuv opus + +- Futtasd a `cargo run` parancsot + +## [Építés](https://rustdesk.com/docs/hu/dev/build/) + +## Hogyan építs Linuxon + +### Ubuntu 18 (Debian 10) + +```sh +sudo apt install -y g++ gcc git curl wget nasm yasm libgtk-3-dev clang libxcb-randr0-dev libxdo-dev libxfixes-dev libxcb-shape0-dev libxcb-xfixes0-dev libasound2-dev libpulse-dev cmake +``` + +### Fedora 28 (CentOS 8) + +```sh +sudo yum -y install gcc-c++ git curl wget nasm yasm gcc gtk3-devel clang libxcb-devel libxdo-devel libXfixes-devel pulseaudio-libs-devel cmake alsa-lib-devel +``` + +### Arch (Manjaro) + +```sh +sudo pacman -Syu --needed unzip git cmake gcc curl wget yasm nasm zip make pkg-config clang gtk3 xdotool libxcb libxfixes alsa-lib pulseaudio +``` + +### Telepítsd a pynput csomagot + +```sh +pip3 install pynput +``` + +### Telepítsd a vcpkg-t + +```sh +git clone https://github.com/microsoft/vcpkg +cd vcpkg +git checkout 2021.12.01 +cd .. +vcpkg/bootstrap-vcpkg.sh +export VCPKG_ROOT=$HOME/vcpkg +vcpkg/vcpkg install libvpx libyuv opus +``` + +### Fixeld a libvpx-t (Fedora-n csak) + +```sh +cd vcpkg/buildtrees/libvpx/src +cd * +./configure +sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile +sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile +make +cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/ +cd +``` + +### Építés + +```sh +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +source $HOME/.cargo/env +git clone https://github.com/rustdesk/rustdesk +cd rustdesk +mkdir -p target/debug +wget https://raw.githubusercontent.com/c-smile/sciter-sdk/master/bin.lnx/x64/libsciter-gtk.so +mv libsciter-gtk.so target/debug +VCPKG_ROOT=$HOME/vcpkg cargo run +``` + +### Válts Wayland-ról X11-re (Xorg) + +A RustDesk nem támogatja a Waylendet. [Itt](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) található egy tutorial amelynek segítségével beállíthatod a Xorg-ot mint alap GNOME session. + +## Hogyan építs Dockerrel + +Kezdjünk a repo clónozásával, majd pedig a Docker container megépítésével: + +```sh +git clone https://github.com/rustdesk/rustdesk +cd rustdesk +docker build -t "rustdesk-builder" . +``` + +Ezután, minden egyes alkalommal amikor meg kell építened a RustDesk-et, futtasd a kövezkező parancsot: + +```sh +docker run --rm -it -v $PWD:/home/user/rustdesk -v rustdesk-git-cache:/home/user/.cargo/git -v rustdesk-registry-cache:/home/user/.cargo/registry -e PUID="$(id -u)" -e PGID="$(id -g)" rustdesk-builder +``` + +Fontos, hogy az első építés lehet hogy több ideig fog tartani mint a következőek, mivel a dependenciek még nincsenek cachelve. Emelett, ha esetleg szeretnél valamilyen argumentumot hozzáadni az építő parancshoz, akkor megteheted a paracssor végén, a `` argumentum használatával. Például ha egy optimalizált release éptést szeretnél megépíteni, akkor add hozzá a fenti parancsorhoz a `--release` opciót. A futtatható binary elérhető lesz a target mappában a rendszereden, futtatni a következőképpen tudod: + +```sh +target/debug/rustdesk +``` + +Vagy ha release binary, akkor: + +```sh +target/release/rustdesk +``` + +Kérlek mindenképpen nézd meg hogy ezeket a parancsokat a root RustDesk mappában futtatod e, különben a RustDesk lehet hogy nem fogja megtalálni az építéshez szükséges elemeket. Fontos az is, hogy jelenleg más cargo subparancsok, például `install`vagy `run` nem támogatottak, mivel egy Dockeres építés esetén elindítanák a programot a containeren belül. + + +## Fájl Struktúra + +- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: video codec, config, tcp/udp wrapper, protobuf, fs functions for file transfer, and some other utility functions +- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: screen capture +- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: platform specific keyboard/mouse control +- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: GUI +- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: audio/clipboard/input/video services, and network connections +- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: start a peer connection +- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Communicate with [rustdesk-server](https://github.com/rustdesk/rustdesk-server), wait for remote direct (TCP hole punching) or relayed connection +- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: platform specific code +- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter code for mobile +- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Javascript for Flutter web client + +## Képernyőképek + +![image](https://user-images.githubusercontent.com/71636191/113112362-ae4deb80-923b-11eb-957d-ff88daad4f06.png) + +![image](https://user-images.githubusercontent.com/71636191/113112619-f705a480-923b-11eb-911d-97e984ef52b6.png) + +![image](https://user-images.githubusercontent.com/71636191/113112857-3fbd5d80-923c-11eb-9836-768325faf906.png) + +![image](https://user-images.githubusercontent.com/71636191/135385039-38fdbd72-379a-422d-b97f-33df71fb1cec.png) diff --git a/README-ID.md b/README-ID.md index 624336f45..6dc00f6fd 100644 --- a/README-ID.md +++ b/README-ID.md @@ -5,11 +5,11 @@ DockerStructureSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Kami membutuhkan bantuan Anda untuk menerjemahkan README ini dan RustDesk UI ke bahasa asli anda

-Birbincang bersama kami: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Birbincang bersama kami: [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) diff --git a/README-IT.md b/README-IT.md index 7eba7860a..6d3aaf5ee 100644 --- a/README-IT.md +++ b/README-IT.md @@ -5,11 +5,11 @@ DockerStrutturaScreenshots
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Abbiamo bisogno del tuo aiuto per tradurre questo README e la RustDesk UI nella tua lingua nativa

-Chatta con noi: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Chatta con noi: [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) diff --git a/README-JP.md b/README-JP.md index 60816a5d5..a912d3cf3 100644 --- a/README-JP.md +++ b/README-JP.md @@ -5,7 +5,7 @@ DockerStructureSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
このREADMEをあなたの母国語に翻訳するために、あなたの助けが必要です。

diff --git a/README-KR.md b/README-KR.md index 750cf91bd..11eed8ab2 100644 --- a/README-KR.md +++ b/README-KR.md @@ -5,7 +5,7 @@ DockerStructureSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
README를 모국어로 번역하기 위한 당신의 도움의 필요합니다.

diff --git a/README-ML.md b/README-ML.md index c479d0496..d72d14c02 100644 --- a/README-ML.md +++ b/README-ML.md @@ -5,11 +5,11 @@ DockerStructureSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
ഈ README നിങ്ങളുടെ മാതൃഭാഷയിലേക്ക് വിവർത്തനം ചെയ്യാൻ ഞങ്ങൾക്ക് നിങ്ങളുടെ സഹായം ആവശ്യമാണ്

-ഞങ്ങളുമായി ചാറ്റ് ചെയ്യുക: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +ഞങ്ങളുമായി ചാറ്റ് ചെയ്യുക: [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) diff --git a/README-NL.md b/README-NL.md index 2d87504db..cce863b6d 100644 --- a/README-NL.md +++ b/README-NL.md @@ -5,11 +5,11 @@ DockerStructuurSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
We hebben je hulp nodig om deze README te vertalen naar jouw moedertaal

-Praat met ons: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Praat met ons: [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) diff --git a/README-PL.md b/README-PL.md index 162ca7648..d21461fee 100644 --- a/README-PL.md +++ b/README-PL.md @@ -5,11 +5,11 @@ DockerStrukturaSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Potrzebujemy twojej pomocy w tłumaczeniu README na twój ojczysty język

-Porozmawiaj z nami na: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Porozmawiaj z nami na: [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) diff --git a/README-PTBR.md b/README-PTBR.md index 76b360283..11986df55 100644 --- a/README-PTBR.md +++ b/README-PTBR.md @@ -5,11 +5,11 @@ DockerEstruturaScreenshots
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Precisamos de sua ajuda para traduzir este README e a UI do RustDesk para sua língua nativa

-Converse conosco: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Converse conosco: [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) diff --git a/README-RU.md b/README-RU.md index 755d91ca3..3b01b8749 100644 --- a/README-RU.md +++ b/README-RU.md @@ -5,7 +5,7 @@ DockerStructureSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Нам нужна ваша помощь для перевода этого README и RustDesk UI на ваш родной язык

diff --git a/README-ZH.md b/README-ZH.md index cd1a332c3..cce3841ee 100644 --- a/README-ZH.md +++ b/README-ZH.md @@ -5,7 +5,7 @@ Docker结构截图
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]

Chat with us: [知乎](https://www.zhihu.com/people/rustdesk) | [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) diff --git a/README.md b/README.md index 2166073a7..c00212991 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ DockerStructureSnapshot
- [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ [česky] | [中文] | | [Magyar] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
We need your help to translate this README, RustDesk UI and Doc to your native language

diff --git a/build.py b/build.py index 2b7cd3f27..efa6f7831 100644 --- a/build.py +++ b/build.py @@ -209,12 +209,15 @@ rcodesign notarize --api-issuer 69a6de7d-2907-47e3-e053-5b8c7c11a4d1 --api-key 9 os.system('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/') os.system( 'cp rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/') + os.system( + 'cp rustdesk.service.user tmpdeb/usr/share/rustdesk/files/systemd/') os.system('cp pynput_service.py tmpdeb/usr/share/rustdesk/files/') - os.system('cp DEBIAN/* tmpdeb/DEBIAN/') + os.system('cp -a DEBIAN/* tmpdeb/DEBIAN/') os.system('strip tmpdeb/usr/bin/rustdesk') os.system('mkdir -p tmpdeb/usr/lib/rustdesk') os.system('cp libsciter-gtk.so tmpdeb/usr/lib/rustdesk/') md5_file('usr/share/rustdesk/files/systemd/rustdesk.service') + md5_file('usr/share/rustdesk/files/systemd/rustdesk.service.user') md5_file('usr/share/rustdesk/files/pynput_service.py') md5_file('usr/lib/rustdesk/libsciter-gtk.so') os.system('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/') diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml index 1759a1ac0..04b2ccc9a 100644 --- a/flutter/android/app/src/main/AndroidManifest.xml +++ b/flutter/android/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.carriez.flutter_hbb"> + diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 76068eee5..905a2734d 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -8,14 +8,14 @@ package com.carriez.flutter_hbb import android.accessibilityservice.AccessibilityService import android.accessibilityservice.GestureDescription -import android.content.Context import android.graphics.Path import android.os.Build import android.util.Log import android.view.accessibility.AccessibilityEvent -import androidx.annotation.Keep import androidx.annotation.RequiresApi import java.util.* +import kotlin.math.abs +import kotlin.math.max const val LIFT_DOWN = 9 const val LIFT_MOVE = 8 @@ -49,28 +49,40 @@ class InputService : AccessibilityService() { private val wheelActionsQueue = LinkedList() private var isWheelActionsPolling = false + private var isWaitingLongPress = false @RequiresApi(Build.VERSION_CODES.N) fun onMouseInput(mask: Int, _x: Int, _y: Int) { - val x = if (_x < 0) { - 0 - } else { - _x - } - - val y = if (_y < 0) { - 0 - } else { - _y - } + val x = max(0, _x) + val y = max(0, _y) if (mask == 0 || mask == LIFT_MOVE) { + val oldX = mouseX + val oldY = mouseY mouseX = x * SCREEN_INFO.scale mouseY = y * SCREEN_INFO.scale + if (isWaitingLongPress) { + val delta = abs(oldX - mouseX) + abs(oldY - mouseY) + Log.d(logTag,"delta:$delta") + if (delta > 8) { + isWaitingLongPress = false + } + } } // left button down ,was up if (mask == LIFT_DOWN) { + isWaitingLongPress = true + timer.schedule(object : TimerTask() { + override fun run() { + if (isWaitingLongPress) { + isWaitingLongPress = false + leftIsDown = false + endGesture(mouseX, mouseY) + } + } + }, LONG_TAP_DELAY * 4) + leftIsDown = true startGesture(mouseX, mouseY) return @@ -83,9 +95,12 @@ class InputService : AccessibilityService() { // left up ,was down if (mask == LIFT_UP) { - leftIsDown = false - endGesture(mouseX, mouseY) - return + if (leftIsDown) { + leftIsDown = false + isWaitingLongPress = false + endGesture(mouseX, mouseY) + return + } } if (mask == RIGHT_UP) { diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt index 3cc105bfa..fd340f7ed 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt @@ -192,7 +192,6 @@ class MainActivity : FlutterActivity() { override fun onResume() { super.onResume() val inputPer = InputService.isOpen - Log.d(logTag, "onResume inputPer:$inputPer") activity.runOnUiThread { flutterMethodChannel.invokeMethod( "on_state_changed", diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt index 7ce7d3ecc..4bf244a06 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt @@ -2,20 +2,26 @@ package com.carriez.flutter_hbb import android.annotation.SuppressLint import android.content.Context +import android.content.Intent import android.media.AudioRecord import android.media.AudioRecord.READ_BLOCKING import android.media.MediaCodecList import android.media.MediaFormat +import android.net.Uri import android.os.Build import android.os.Handler import android.os.Looper -import android.util.Log +import android.os.PowerManager +import android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS +import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat.getSystemService import com.hjq.permissions.Permission import com.hjq.permissions.XXPermissions import java.nio.ByteBuffer import java.util.* + @SuppressLint("ConstantLocale") val LOCAL_NAME = Locale.getDefault().toString() val SCREEN_INFO = Info(0, 0, 1, 200) @@ -38,8 +44,31 @@ fun testVP9Support(): Boolean { return res != null } +@RequiresApi(Build.VERSION_CODES.M) fun requestPermission(context: Context, type: String) { val permission = when (type) { + "ignore_battery_optimizations" -> { + try { + context.startActivity(Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply { + data = Uri.parse("package:" + context.packageName) + }) + } catch (e:Exception) { + e.printStackTrace() + } + return + } + "application_details_settings" -> { + try { + context.startActivity(Intent().apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + action = "android.settings.APPLICATION_DETAILS_SETTINGS" + data = Uri.parse("package:" + context.packageName) + }) + } catch (e:Exception) { + e.printStackTrace() + } + return + } "audio" -> { Permission.RECORD_AUDIO } @@ -52,7 +81,7 @@ fun requestPermission(context: Context, type: String) { } XXPermissions.with(context) .permission(permission) - .request { permissions, all -> + .request { _, all -> if (all) { Handler(Looper.getMainLooper()).post { MainActivity.flutterMethodChannel.invokeMethod( @@ -64,8 +93,13 @@ fun requestPermission(context: Context, type: String) { } } +@RequiresApi(Build.VERSION_CODES.M) fun checkPermission(context: Context, type: String): Boolean { val permission = when (type) { + "ignore_battery_optimizations" -> { + val pw = context.getSystemService(Context.POWER_SERVICE) as PowerManager + return pw.isIgnoringBatteryOptimizations(context.packageName) + } "audio" -> { Permission.RECORD_AUDIO } diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index d6be51986..a7c5dfea0 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -260,7 +260,12 @@ class PermissionManager { static Timer? _timer; static var _current = ""; - static final permissions = ["audio", "file"]; + static final permissions = [ + "audio", + "file", + "ignore_battery_optimizations", + "application_details_settings" + ]; static bool isWaitingFile() { if (_completer != null) { @@ -279,9 +284,12 @@ class PermissionManager { if (!permissions.contains(type)) return Future.error("Wrong permission!$type"); + FFI.invokeMethod("request_permission", type); + if (type == "ignore_battery_optimizations") { + return Future.value(false); + } _current = type; _completer = Completer(); - FFI.invokeMethod("request_permission", type); // timeout _timer?.cancel(); diff --git a/flutter/lib/pages/remote_page.dart b/flutter/lib/pages/remote_page.dart index c383bc361..7a3e489b0 100644 --- a/flutter/lib/pages/remote_page.dart +++ b/flutter/lib/pages/remote_page.dart @@ -262,7 +262,6 @@ class _RemotePageState extends State { : SafeArea(child: OrientationBuilder(builder: (ctx, orientation) { if (_currentOrientation != orientation) { - debugPrint("on orientation changed"); Timer(Duration(milliseconds: 200), () { resetMobileActionsOverlay(); _currentOrientation = orientation; @@ -1061,6 +1060,8 @@ void showOptions() { getRadio('Optimize reaction time', 'low', quality, setQuality), Divider(color: MyTheme.border), getToggle(setState, 'show-remote-cursor', 'Show remote cursor'), + getToggle( + setState, 'show-quality-monitor', 'Show quality monitor'), ] + more), actions: [], diff --git a/flutter/lib/pages/settings_page.dart b/flutter/lib/pages/settings_page.dart index 2c8b7fe9a..30eb88b7b 100644 --- a/flutter/lib/pages/settings_page.dart +++ b/flutter/lib/pages/settings_page.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:settings_ui/settings_ui.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -24,13 +26,100 @@ class SettingsPage extends StatefulWidget implements PageShape { _SettingsState createState() => _SettingsState(); } -class _SettingsState extends State { +class _SettingsState extends State with WidgetsBindingObserver { static const url = 'https://rustdesk.com/'; + final _hasIgnoreBattery = androidVersion >= 26; + var _ignoreBatteryOpt = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + if (_hasIgnoreBattery) { + updateIgnoreBatteryStatus(); + } + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + updateIgnoreBatteryStatus(); + } + } + + Future updateIgnoreBatteryStatus() async { + final res = await PermissionManager.check("ignore_battery_optimizations"); + if (_ignoreBatteryOpt != res) { + setState(() { + _ignoreBatteryOpt = res; + }); + return true; + } else { + return false; + } + } @override Widget build(BuildContext context) { Provider.of(context); final username = getUsername(); + final enableAbr = FFI.getByName("option", "enable-abr") != 'N'; + final enhancementsTiles = [ + SettingsTile.switchTile( + title: Text(translate('Adaptive Bitrate') + '(beta)'), + initialValue: enableAbr, + onToggle: (v) { + final msg = Map() + ..["name"] = "enable-abr" + ..["value"] = ""; + if (!v) { + msg["value"] = "N"; + } + FFI.setByName("option", json.encode(msg)); + setState(() {}); + }, + ) + ]; + if (_hasIgnoreBattery) { + enhancementsTiles.insert( + 0, + SettingsTile.switchTile( + initialValue: _ignoreBatteryOpt, + title: Text(translate('Keep RustDesk background service')), + description: + Text('* ${translate('Ignore Battery Optimizations')}'), + onToggle: (v) async { + if (v) { + PermissionManager.request("ignore_battery_optimizations"); + } else { + final res = await DialogManager.show( + (setState, close) => CustomAlertDialog( + title: Text(translate("Open System Setting")), + content: Text(translate( + "android_open_battery_optimizations_tip")), + actions: [ + TextButton( + onPressed: () => close(), + child: Text(translate("Cancel"))), + ElevatedButton( + onPressed: () => close(true), + child: + Text(translate("Open System Setting"))), + ], + )); + if (res == true) { + PermissionManager.request("application_details_settings"); + } + } + })); + } + return SettingsList( sections: [ SettingsSection( @@ -51,17 +140,17 @@ class _SettingsState extends State { ), ], ), - SettingsSection( - title: Text(translate("Settings")), - tiles: [ - SettingsTile.navigation( + SettingsSection(title: Text(translate("Settings")), tiles: [ + SettingsTile.navigation( title: Text(translate('ID/Relay Server')), leading: Icon(Icons.cloud), onPressed: (context) { showServerSettings(); - }, - ), - ], + }) + ]), + SettingsSection( + title: Text(translate("Enhancements")), + tiles: enhancementsTiles, ), SettingsSection( title: Text(translate("About")), diff --git a/libs/enigo/Cargo.toml b/libs/enigo/Cargo.toml index e97f000a6..fec03d34e 100644 --- a/libs/enigo/Cargo.toml +++ b/libs/enigo/Cargo.toml @@ -23,6 +23,7 @@ serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } log = "0.4" rdev = { git = "https://github.com/asur4s/rdev" } +hbb_common = { path = "../hbb_common" } [features] with_serde = ["serde", "serde_derive"] diff --git a/libs/enigo/src/lib.rs b/libs/enigo/src/lib.rs index 10cde9cbe..164fb1c17 100644 --- a/libs/enigo/src/lib.rs +++ b/libs/enigo/src/lib.rs @@ -249,7 +249,7 @@ pub trait MouseControllable { /// For alphabetical keys, use Key::Layout for a system independent key. /// If a key is missing, you can use the raw keycode with Key::Raw. #[cfg_attr(feature = "with_serde", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Key { /// alt key on Linux and Windows (option key on macOS) Alt, diff --git a/libs/enigo/src/linux/mod.rs b/libs/enigo/src/linux/mod.rs new file mode 100644 index 000000000..42e1dfebf --- /dev/null +++ b/libs/enigo/src/linux/mod.rs @@ -0,0 +1,5 @@ +mod nix_impl; +mod pynput; +mod xdo; + +pub use self::nix_impl::Enigo; diff --git a/libs/enigo/src/linux/nix_impl.rs b/libs/enigo/src/linux/nix_impl.rs new file mode 100644 index 000000000..840290b2b --- /dev/null +++ b/libs/enigo/src/linux/nix_impl.rs @@ -0,0 +1,178 @@ +use super::{pynput::EnigoPynput, xdo::EnigoXdo}; +use crate::{Key, KeyboardControllable, MouseButton, MouseControllable}; + +/// The main struct for handling the event emitting +// #[derive(Default)] +pub struct Enigo { + xdo: EnigoXdo, + pynput: EnigoPynput, + is_x11: bool, + uinput_keyboard: Option>, + uinput_mouse: Option>, +} + +impl Enigo { + /// Get delay of xdo implementation. + pub fn delay(&self) -> u64 { + self.xdo.delay() + } + /// Set delay of xdo implemetation. + pub fn set_delay(&mut self, delay: u64) { + self.xdo.set_delay(delay) + } + /// Reset pynput. + pub fn reset(&mut self) { + self.pynput.reset(); + } + /// Set uinput keyboard. + pub fn set_uinput_keyboard( + &mut self, + uinput_keyboard: Option>, + ) { + self.uinput_keyboard = uinput_keyboard + } + /// Set uinput mouse. + pub fn set_uinput_mouse(&mut self, uinput_mouse: Option>) { + self.uinput_mouse = uinput_mouse + } +} + +impl Default for Enigo { + fn default() -> Self { + Self { + is_x11: "x11" == hbb_common::platform::linux::get_display_server(), + uinput_keyboard: None, + uinput_mouse: None, + xdo: EnigoXdo::default(), + pynput: EnigoPynput::default(), + } + } +} + +impl MouseControllable for Enigo { + fn mouse_move_to(&mut self, x: i32, y: i32) { + if self.is_x11 { + self.xdo.mouse_move_to(x, y); + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_move_to(x, y) + } + } + } + fn mouse_move_relative(&mut self, x: i32, y: i32) { + if self.is_x11 { + self.xdo.mouse_move_relative(x, y); + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_move_relative(x, y) + } + } + } + fn mouse_down(&mut self, button: MouseButton) -> crate::ResultType { + if self.is_x11 { + self.xdo.mouse_down(button) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_down(button) + } else { + Ok(()) + } + } + } + fn mouse_up(&mut self, button: MouseButton) { + if self.is_x11 { + self.xdo.mouse_up(button) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_up(button) + } + } + } + fn mouse_click(&mut self, button: MouseButton) { + if self.is_x11 { + self.xdo.mouse_click(button) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_click(button) + } + } + } + fn mouse_scroll_x(&mut self, length: i32) { + if self.is_x11 { + self.xdo.mouse_scroll_x(length) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_scroll_x(length) + } + } + } + fn mouse_scroll_y(&mut self, length: i32) { + if self.is_x11 { + self.xdo.mouse_scroll_y(length) + } else { + if let Some(mouse) = &mut self.uinput_mouse { + mouse.mouse_scroll_y(length) + } + } + } +} + +impl KeyboardControllable for Enigo { + fn get_key_state(&mut self, key: Key) -> bool { + if self.is_x11 { + self.xdo.get_key_state(key) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.get_key_state(key) + } else { + false + } + } + } + + fn key_sequence(&mut self, sequence: &str) { + if self.is_x11 { + self.xdo.key_sequence(sequence) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.key_sequence(sequence) + } + } + } + + fn key_down(&mut self, key: Key) -> crate::ResultType { + if self.is_x11 { + if self.pynput.send_pynput(&key, true) { + return Ok(()); + } + self.xdo.key_down(key) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.key_down(key) + } else { + Ok(()) + } + } + } + fn key_up(&mut self, key: Key) { + if self.is_x11 { + if self.pynput.send_pynput(&key, false) { + return; + } + self.xdo.key_up(key) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.key_up(key) + } + } + } + fn key_click(&mut self, key: Key) { + if self.is_x11 { + self.xdo.key_click(key) + } else { + if let Some(keyboard) = &mut self.uinput_keyboard { + keyboard.key_click(key) + } + } + } +} diff --git a/libs/enigo/src/linux/pynput.rs b/libs/enigo/src/linux/pynput.rs new file mode 100644 index 000000000..748b30105 --- /dev/null +++ b/libs/enigo/src/linux/pynput.rs @@ -0,0 +1,280 @@ +use crate::Key; +use std::{io::prelude::*, sync::mpsc}; + +enum PyMsg { + Char(char), + Str(&'static str), +} + +/// The main struct for handling the event emitting +pub(super) struct EnigoPynput { + tx: mpsc::Sender<(PyMsg, bool)>, +} + +impl Default for EnigoPynput { + fn default() -> Self { + let (tx, rx) = mpsc::channel(); + start_pynput_service(rx); + Self { tx } + } +} +impl EnigoPynput { + pub(super) fn reset(&mut self) { + self.tx.send((PyMsg::Char('\0'), true)).ok(); + } + + #[inline] + pub(super) fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool { + if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } { + return false; + } + if let Key::Layout(c) = key { + return self.tx.send((PyMsg::Char(*c), is_press)).is_ok(); + } + if let Key::Raw(_) = key { + return false; + } + #[allow(deprecated)] + let s = match key { + Key::Alt => "Alt_L", + Key::Backspace => "BackSpace", + Key::CapsLock => "Caps_Lock", + Key::Control => "Control_L", + Key::Delete => "Delete", + Key::DownArrow => "Down", + Key::End => "End", + Key::Escape => "Escape", + Key::F1 => "F1", + Key::F10 => "F10", + Key::F11 => "F11", + Key::F12 => "F12", + Key::F2 => "F2", + Key::F3 => "F3", + Key::F4 => "F4", + Key::F5 => "F5", + Key::F6 => "F6", + Key::F7 => "F7", + Key::F8 => "F8", + Key::F9 => "F9", + Key::Home => "Home", + Key::LeftArrow => "Left", + Key::Option => "Option", + Key::PageDown => "Page_Down", + Key::PageUp => "Page_Up", + Key::Return => "Return", + Key::RightArrow => "Right", + Key::Shift => "Shift_L", + Key::Space => "space", + Key::Tab => "Tab", + Key::UpArrow => "Up", + Key::Numpad0 => "0", + Key::Numpad1 => "1", + Key::Numpad2 => "2", + Key::Numpad3 => "3", + Key::Numpad4 => "4", + Key::Numpad5 => "5", + Key::Numpad6 => "6", + Key::Numpad7 => "7", + Key::Numpad8 => "8", + Key::Numpad9 => "9", + Key::Decimal => "KP_Decimal", + Key::Cancel => "Cancel", + Key::Clear => "Clear", + Key::Pause => "Pause", + Key::Kana => "Kana", + Key::Hangul => "Hangul", + Key::Hanja => "Hanja", + Key::Kanji => "Kanji", + Key::Select => "Select", + Key::Print => "Print", + Key::Execute => "Execute", + Key::Snapshot => "3270_PrintScreen", + Key::Insert => "Insert", + Key::Help => "Help", + Key::Separator => "KP_Separator", + Key::Scroll => "Scroll_Lock", + Key::NumLock => "Num_Lock", + Key::RWin => "Super_R", + Key::Apps => "Menu", + Key::Multiply => "KP_Multiply", + Key::Add => "KP_Add", + Key::Subtract => "KP_Subtract", + Key::Divide => "KP_Divide", + Key::Equals => "KP_Equal", + Key::NumpadEnter => "KP_Enter", + Key::RightShift => "Shift_R", + Key::RightControl => "Control_R", + Key::RightAlt => "Mode_switch", + Key::Command | Key::Super | Key::Windows | Key::Meta => "Super_L", + _ => { + return true; + } + }; + log::info!("send pynput: {:?}", &s); + return self.tx.send((PyMsg::Str(s), is_press)).is_ok(); + } +} + +// impl MouseControllable for EnigoPynput { +// fn mouse_move_to(&mut self, _x: i32, _y: i32) { +// unimplemented!() +// } +// fn mouse_move_relative(&mut self, _x: i32, _y: i32) { +// unimplemented!() +// } +// fn mouse_down(&mut self, _button: MouseButton) -> crate::ResultType { +// unimplemented!() +// } +// fn mouse_up(&mut self, _button: MouseButton) { +// unimplemented!() +// } +// fn mouse_click(&mut self, _button: MouseButton) { +// unimplemented!() +// } +// fn mouse_scroll_x(&mut self, _length: i32) { +// unimplemented!() +// } +// fn mouse_scroll_y(&mut self, _length: i32) { +// unimplemented!() +// } +// } + +// impl KeyboardControllable for EnigoPynput { +// fn get_key_state(&mut self, _key: Key) -> bool { +// unimplemented!() +// } + +// fn key_sequence(&mut self, _sequence: &str) { +// unimplemented!() +// } +// fn key_down(&mut self, key: Key) -> crate::ResultType { +// let _ = self.send_pynput(&key, true); +// Ok(()) +// } +// fn key_up(&mut self, key: Key) { +// let _ = self.send_pynput(&key, false); +// } +// fn key_click(&mut self, _key: Key) { +// unimplemented!() +// } +// } + +static mut PYNPUT_EXIT: bool = false; +static mut PYNPUT_REDAY: bool = false; +static IPC_FILE: &'static str = "/tmp/RustDesk/pynput_service"; + +fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) { + let mut py = "./pynput_service.py".to_owned(); + if !std::path::Path::new(&py).exists() { + py = "/usr/share/rustdesk/files/pynput_service.py".to_owned(); + if !std::path::Path::new(&py).exists() { + py = "/usr/lib/rustdesk/pynput_service.py".to_owned(); + if !std::path::Path::new(&py).exists() { + log::error!("{} not exits", py); + } + } + } + log::info!("pynput service: {}", py); + std::thread::spawn(move || { + let username = std::env::var("PYNPUT_USERNAME").unwrap_or("".to_owned()); + let userid = std::env::var("PYNPUT_USERID").unwrap_or("".to_owned()); + let status = if username.is_empty() { + std::process::Command::new("python3") + .arg(&py) + .arg(IPC_FILE) + .status() + .map(|x| x.success()) + } else { + let mut status = Ok(true); + for i in 0..100 { + if i % 10 == 0 { + log::info!("#{} try to start pynput server", i); + } + status = std::process::Command::new("sudo") + .args(vec![ + "-E", + &format!("XDG_RUNTIME_DIR=/run/user/{}", userid) as &str, + "-u", + &username, + "python3", + &py, + IPC_FILE, + ]) + .status() + .map(|x| x.success()); + match status { + Ok(true) => break, + _ => {} + } + std::thread::sleep(std::time::Duration::from_millis(100)); + } + status + }; + log::info!( + "pynput server exit with username/id {}/{}: {:?}", + username, + userid, + status + ); + unsafe { + PYNPUT_EXIT = true; + } + }); + std::thread::spawn(move || { + for i in 0..300 { + std::thread::sleep(std::time::Duration::from_millis(100)); + let mut conn = match std::os::unix::net::UnixStream::connect(IPC_FILE) { + Ok(conn) => conn, + Err(err) => { + if i % 15 == 0 { + log::warn!("Failed to connect to {}: {}", IPC_FILE, err); + } + continue; + } + }; + if let Err(err) = conn.set_nonblocking(true) { + log::error!("Failed to set ipc nonblocking: {}", err); + return; + } + log::info!("Conntected to pynput server"); + let d = std::time::Duration::from_millis(30); + unsafe { + PYNPUT_REDAY = true; + } + let mut buf = [0u8; 1024]; + loop { + if unsafe { PYNPUT_EXIT } { + break; + } + match rx.recv_timeout(d) { + Ok((msg, is_press)) => { + let msg = match msg { + PyMsg::Char(chr) => { + format!("{}{}", if is_press { 'p' } else { 'r' }, chr) + } + PyMsg::Str(s) => format!("{}{}", if is_press { 'p' } else { 'r' }, s), + }; + let n = msg.len(); + buf[0] = n as _; + buf[1..(n + 1)].copy_from_slice(msg.as_bytes()); + if let Err(err) = conn.write_all(&buf[..n + 1]) { + log::error!("Failed to write to ipc: {}", err); + break; + } + } + Err(err) => match err { + mpsc::RecvTimeoutError::Disconnected => { + log::error!("pynput sender disconnecte"); + break; + } + _ => {} + }, + } + } + unsafe { + PYNPUT_REDAY = false; + } + break; + } + }); +} diff --git a/libs/enigo/src/linux.rs b/libs/enigo/src/linux/xdo.rs similarity index 56% rename from libs/enigo/src/linux.rs rename to libs/enigo/src/linux/xdo.rs index de06923f3..541dbe81f 100644 --- a/libs/enigo/src/linux.rs +++ b/libs/enigo/src/linux/xdo.rs @@ -4,6 +4,7 @@ use crate::{Key, KeyboardControllable, MouseButton, MouseControllable}; use self::libc::{c_char, c_int, c_void, useconds_t}; use std::{borrow::Cow, ffi::CString, io::prelude::*, ptr, sync::mpsc}; + const CURRENT_WINDOW: c_int = 0; const DEFAULT_DELAY: u64 = 12000; type Window = c_int; @@ -59,34 +60,25 @@ fn mousebutton(button: MouseButton) -> c_int { } } -enum PyMsg { - Char(char), - Str(&'static str), -} - /// The main struct for handling the event emitting -pub struct Enigo { +pub(super) struct EnigoXdo { xdo: Xdo, delay: u64, - tx: mpsc::Sender<(PyMsg, bool)>, } // This is safe, we have a unique pointer. // TODO: use Unique once stable. -unsafe impl Send for Enigo {} +unsafe impl Send for EnigoXdo {} -impl Default for Enigo { - /// Create a new Enigo instance +impl Default for EnigoXdo { + /// Create a new EnigoXdo instance fn default() -> Self { - let (tx, rx) = mpsc::channel(); - start_pynput_service(rx); Self { xdo: unsafe { xdo_new(ptr::null()) }, delay: DEFAULT_DELAY, - tx, } } } -impl Enigo { +impl EnigoXdo { /// Get the delay per keypress. /// Default value is 12000. /// This is Linux-specific. @@ -98,102 +90,8 @@ impl Enigo { pub fn set_delay(&mut self, delay: u64) { self.delay = delay; } - /// - pub fn reset(&mut self) { - self.tx.send((PyMsg::Char('\0'), true)).ok(); - } - - #[inline] - fn send_pynput(&mut self, key: &Key, is_press: bool) -> bool { - if unsafe { PYNPUT_EXIT || !PYNPUT_REDAY } { - return false; - } - if let Key::Layout(c) = key { - return self.tx.send((PyMsg::Char(*c), is_press)).is_ok(); - } - if let Key::Raw(_) = key { - return false; - } - #[allow(deprecated)] - let s = match key { - Key::Alt => "Alt_L", - Key::Backspace => "BackSpace", - Key::CapsLock => "Caps_Lock", - Key::Control => "Control_L", - Key::Delete => "Delete", - Key::DownArrow => "Down", - Key::End => "End", - Key::Escape => "Escape", - Key::F1 => "F1", - Key::F10 => "F10", - Key::F11 => "F11", - Key::F12 => "F12", - Key::F2 => "F2", - Key::F3 => "F3", - Key::F4 => "F4", - Key::F5 => "F5", - Key::F6 => "F6", - Key::F7 => "F7", - Key::F8 => "F8", - Key::F9 => "F9", - Key::Home => "Home", - Key::LeftArrow => "Left", - Key::Option => "Option", - Key::PageDown => "Page_Down", - Key::PageUp => "Page_Up", - Key::Return => "Return", - Key::RightArrow => "Right", - Key::Shift => "Shift_L", - Key::Space => "space", - Key::Tab => "Tab", - Key::UpArrow => "Up", - Key::Numpad0 => "0", - Key::Numpad1 => "1", - Key::Numpad2 => "2", - Key::Numpad3 => "3", - Key::Numpad4 => "4", - Key::Numpad5 => "5", - Key::Numpad6 => "6", - Key::Numpad7 => "7", - Key::Numpad8 => "8", - Key::Numpad9 => "9", - Key::Decimal => "KP_Decimal", - Key::Cancel => "Cancel", - Key::Clear => "Clear", - Key::Pause => "Pause", - Key::Kana => "Kana", - Key::Hangul => "Hangul", - Key::Hanja => "Hanja", - Key::Kanji => "Kanji", - Key::Select => "Select", - Key::Print => "Print", - Key::Execute => "Execute", - Key::Snapshot => "3270_PrintScreen", - Key::Insert => "Insert", - Key::Help => "Help", - Key::Separator => "KP_Separator", - Key::Scroll => "Scroll_Lock", - Key::NumLock => "Num_Lock", - Key::RWin => "Super_R", - Key::Apps => "Menu", - Key::Multiply => "KP_Multiply", - Key::Add => "KP_Add", - Key::Subtract => "KP_Subtract", - Key::Divide => "KP_Divide", - Key::Equals => "KP_Equal", - Key::NumpadEnter => "KP_Enter", - Key::RightShift => "Shift_R", - Key::RightControl => "Control_R", - Key::RightAlt => "Mode_switch", - Key::Command | Key::Super | Key::Windows | Key::Meta => "Super_L", - _ => { - return true; - } - }; - return self.tx.send((PyMsg::Str(s), is_press)).is_ok(); - } } -impl Drop for Enigo { +impl Drop for EnigoXdo { fn drop(&mut self) { if self.xdo.is_null() { return; @@ -203,7 +101,7 @@ impl Drop for Enigo { } } } -impl MouseControllable for Enigo { +impl MouseControllable for EnigoXdo { fn mouse_move_to(&mut self, x: i32, y: i32) { if self.xdo.is_null() { return; @@ -378,7 +276,7 @@ fn keysequence<'a>(key: Key) -> Cow<'a, str> { _ => "", }) } -impl KeyboardControllable for Enigo { +impl KeyboardControllable for EnigoXdo { fn get_key_state(&mut self, key: Key) -> bool { if self.xdo.is_null() { return false; @@ -431,9 +329,6 @@ impl KeyboardControllable for Enigo { if self.xdo.is_null() { return Ok(()); } - if self.send_pynput(&key, true) { - return Ok(()); - } let string = CString::new(&*keysequence(key))?; unsafe { xdo_send_keysequence_window_down( @@ -449,9 +344,6 @@ impl KeyboardControllable for Enigo { if self.xdo.is_null() { return; } - if self.send_pynput(&key, false) { - return; - } if let Ok(string) = CString::new(&*keysequence(key)) { unsafe { xdo_send_keysequence_window_up( @@ -494,128 +386,3 @@ impl KeyboardControllable for Enigo { crate::dsl::eval(self, sequence) } } - -static mut PYNPUT_EXIT: bool = false; -static mut PYNPUT_REDAY: bool = false; -static IPC_FILE: &'static str = "/tmp/RustDesk/pynput_service"; - -fn start_pynput_service(rx: mpsc::Receiver<(PyMsg, bool)>) { - let mut py = "./pynput_service.py".to_owned(); - if !std::path::Path::new(&py).exists() { - py = "/usr/share/rustdesk/files/pynput_service.py".to_owned(); - if !std::path::Path::new(&py).exists() { - py = "/usr/lib/rustdesk/pynput_service.py".to_owned(); - if !std::path::Path::new(&py).exists() { - // enigo libs, not rustdesk root project, so skip using appimage features - py = std::env::var("APPDIR").unwrap_or("".to_string()) - + "/usr/lib/rustdesk/pynput_service.py"; - if !std::path::Path::new(&py).exists() { - log::error!("{} not exists", py); - } - } - } - } - log::info!("pynput service: {}", py); - std::thread::spawn(move || { - let username = std::env::var("PYNPUT_USERNAME").unwrap_or("".to_owned()); - let userid = std::env::var("PYNPUT_USERID").unwrap_or("".to_owned()); - let status = if username.is_empty() { - std::process::Command::new("python3") - .arg(&py) - .arg(IPC_FILE) - .status() - .map(|x| x.success()) - } else { - let mut status = Ok(true); - for i in 0..100 { - if i % 10 == 0 { - log::info!("#{} try to start pynput server", i); - } - status = std::process::Command::new("sudo") - .args(vec![ - "-E", - &format!("XDG_RUNTIME_DIR=/run/user/{}", userid) as &str, - "-u", - &username, - "python3", - &py, - IPC_FILE, - ]) - .status() - .map(|x| x.success()); - match status { - Ok(true) => break, - _ => {} - } - std::thread::sleep(std::time::Duration::from_millis(100)); - } - status - }; - log::info!( - "pynput server exit with username/id {}/{}: {:?}", - username, - userid, - status - ); - unsafe { - PYNPUT_EXIT = true; - } - }); - std::thread::spawn(move || { - for i in 0..300 { - std::thread::sleep(std::time::Duration::from_millis(100)); - let mut conn = match std::os::unix::net::UnixStream::connect(IPC_FILE) { - Ok(conn) => conn, - Err(err) => { - if i % 15 == 0 { - log::warn!("Failed to connect to {}: {}", IPC_FILE, err); - } - continue; - } - }; - if let Err(err) = conn.set_nonblocking(true) { - log::error!("Failed to set ipc nonblocking: {}", err); - return; - } - log::info!("Conntected to pynput server"); - let d = std::time::Duration::from_millis(30); - unsafe { - PYNPUT_REDAY = true; - } - let mut buf = [0u8; 1024]; - loop { - if unsafe { PYNPUT_EXIT } { - break; - } - match rx.recv_timeout(d) { - Ok((msg, is_press)) => { - let msg = match msg { - PyMsg::Char(chr) => { - format!("{}{}", if is_press { 'p' } else { 'r' }, chr) - } - PyMsg::Str(s) => format!("{}{}", if is_press { 'p' } else { 'r' }, s), - }; - let n = msg.len(); - buf[0] = n as _; - buf[1..(n + 1)].copy_from_slice(msg.as_bytes()); - if let Err(err) = conn.write_all(&buf[..n + 1]) { - log::error!("Failed to write to ipc: {}", err); - break; - } - } - Err(err) => match err { - mpsc::RecvTimeoutError::Disconnected => { - log::error!("pynput sender disconnecte"); - break; - } - _ => {} - }, - } - } - unsafe { - PYNPUT_REDAY = false; - } - break; - } - }); -} diff --git a/libs/hbb_common/Cargo.toml b/libs/hbb_common/Cargo.toml index bc31223cc..b8db8d508 100644 --- a/libs/hbb_common/Cargo.toml +++ b/libs/hbb_common/Cargo.toml @@ -7,9 +7,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -protobuf = "3.0.0-alpha.2" -tokio = { version = "1.15", features = ["full"] } -tokio-util = { version = "0.6", features = ["full"] } +protobuf = { version = "3.1", features = ["with-bytes"] } +tokio = { version = "1.20", features = ["full"] } +tokio-util = { version = "0.7", features = ["full"] } futures = "0.3" bytes = "1.1" log = "0.4" @@ -23,6 +23,7 @@ directories-next = "2.0" rand = "0.8" serde_derive = "1.0" serde = "1.0" +serde_with = "1.14.0" lazy_static = "1.4" confy = { git = "https://github.com/open-trade/confy" } dirs-next = "2.0" @@ -33,12 +34,13 @@ tokio-socks = { git = "https://github.com/open-trade/tokio-socks" } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] mac_address = "1.1" +machine-uid = "0.2" [features] quic = [] [build-dependencies] -protobuf-codegen-pure = "3.0.0-alpha.2" +protobuf-codegen = { version = "3.1" } [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winuser"] } diff --git a/libs/hbb_common/build.rs b/libs/hbb_common/build.rs index 99dacb7ec..5c1c6af22 100644 --- a/libs/hbb_common/build.rs +++ b/libs/hbb_common/build.rs @@ -1,6 +1,7 @@ fn main() { std::fs::create_dir_all("src/protos").unwrap(); - protobuf_codegen_pure::Codegen::new() + protobuf_codegen::Codegen::new() + .pure() .out_dir("src/protos") .inputs(&["protos/rendezvous.proto", "protos/message.proto"]) .include("protos") diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 2282bf43a..009996655 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -63,6 +63,7 @@ message LoginRequest { PortForward port_forward = 8; } bool video_ack_required = 9; + uint64 session_id = 10; } message ChatMessage { string text = 1; } diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index b4d8eaff3..787ffe5ee 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1,4 +1,11 @@ -use crate::log; +use crate::{ + log, + password_security::config::{ + decrypt_str_or_original, decrypt_vec_or_original, encrypt_str_or_original, + encrypt_vec_or_original, + }, +}; +use anyhow::Result; use directories_next::ProjectDirs; use rand::Rng; use serde_derive::{Deserialize, Serialize}; @@ -17,6 +24,7 @@ pub const CONNECT_TIMEOUT: u64 = 18_000; pub const REG_INTERVAL: i64 = 12_000; pub const COMPRESS_LEVEL: i32 = 3; const SERIAL: i32 = 3; +const PASSWORD_ENC_VERSION: &'static str = "00"; // 128x128 #[cfg(target_os = "macos")] // 128x128 on 160x160 canvas, then shrink to 128, mac looks better with padding pub const ICON: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAyVBMVEUAAAAAcf8Acf8Acf8Acv8Acf8Acf8Acf8Acf8AcP8Acf8Ab/8AcP8Acf////8AaP/z+f/o8v/k7v/5/v/T5f8AYP/u9v/X6f+hx/+Kuv95pP8Aef/B1/+TwP9xoP8BdP/g6P+Irv9ZmP8Bgf/E3f98q/9sn/+01f+Es/9nm/9Jif8hhv8off/M4P+syP+avP86iP/c7f+xy/9yqf9Om/9hk/9Rjv+60P99tv9fpf88lv8yjf8Tgf8deP+kvP8BiP8NeP8hkP80gP8oj2VLAAAADXRSTlMA7o7qLvnaxZ1FOxYPjH9HWgAABHJJREFUeNrtm+tW4jAQgBfwuu7MtIUWsOUiCCioIIgLiqvr+z/UHq/LJKVkmwTcc/r9E2nzlU4mSTP9lpGRkZGR8VX5cZjfL+yCEXYL+/nDH//U/Pd8DgyTy39Xbv7oIAcWyB0cqbW/sweW2NtRaj8H1sgpGOwUIAH7Bkd7YJW9dXFwAJY5WNP/cmCZQnJvzIN18on5LwfWySXlxEPYAIcad8D6PdiHDbCfIFCADVBIENiFDbCbIACKPPXrZ+cP8E6/0znvP4EymgIEravIRcTxu8HxNSJ60a8W0AYECKrlAN+YwAthCd9wm1Ug6wKzIn5SgRduXfwkqDasCjx0XFzi9PV6zwNcIuhcWBOg+ikySq8C9UD4dEKWBCoOcspvAuLHTo9sCDQiFPHotRM48j8G5gVur1FdAN2uaYEuiz7xFsgEJ2RUoMUakXuBTHHoGxQYOBhHjeUBAefEnMAowFhaLBOKuOemBBbxLRQrH2PBCgMvNCPQGMeevTb9zLrPxz2Mo+QbEaijzPUcOOHMQZkKGRAIPem39+bypREMPTkQW/oCfk866zAkiIFG4yIKRE/aAnfiSd0WrORY6pFdXQEqi9mvAQm0RIOSnoCcZ8vJoz3diCnjRk+g8VP4/fuQDJ2Lxr6WwG0gXs9aTpDzW0vgDBlVUpixR8gYk44AD8FrUKHr8JQJGgIDnoDqoALxmWPQSi9AVVzm8gKUuEPGr/QCvptwJkbSYT/TC4S8C96DGjTj86aHtAI0x2WaBIq0eSYYpRa4EsdWVVwWu9O0Aj6f6dyBMnwEraeOgSYu0wZlauzA47QCbT7DgAQSE+hZWoEBF/BBmWOewNMK3BsSqKUW4MGcWqCSVmDkbvkXGKQOwg6PAUO9oL3xXhA20yaiCjuwYygRVQlUOTWTCf2SuNJTxeFjgaHByGuAIvd8ItdPLTDhS7IuqEE1YSKVOgbayLhSFQhMzYh8hwfBs1r7c505YVIQYEdNoKwxK06MJiyrpUFHiF0NAfCQUVHoiRclIXJIR6C2fqG37pBHvcWpgwzvAtYwkR5UGV2e42UISdBJETl3mg8ouo54Rcnti1/vaT+iuUQBt500Cgo4U10BeHSkk57FB0JjWkKRMWgLUA0lLodtImAQdaMiiri3+gIAPZQoutHNsgKF1aaDMhMyIdBf8Th+Bh8MTjGWCpl5Wv43tDmnF+IUVMrcZgRoiAxhtrloYizNkZaAnF5leglbNhj0wYCAbCDvGb0mP4nib7O7ZlcYQ2m1gPtIZgVgGNNMeaVAaWR+57TrqgtUnm3sHQ+kYeE6fufUubG1ez50FXbPnWgBlgSABmN3TTcsRl2yWkHRrwbiunvk/W2+Mg1hPZplPDeXRbZzStFH15s1QIVd3UImP5z/bHpeeQLvRJ7XLFUffQIlCvqlXETQbgN9/rlYABGosv+Vi9m2Xs639YLGrZd0br+odetlvdsvbN56abfd4vbCzv9Q3v/ygoOV21A4OPpfXvH4Ai+5ZGRkZGRkbJA/t/I0QMzoMiEAAAAASUVORK5CYII= @@ -114,7 +122,7 @@ pub struct Config2 { pub options: HashMap, } -#[derive(Debug, Default, Serialize, Deserialize, Clone)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] pub struct PeerConfig { #[serde(default)] pub password: Vec, @@ -168,7 +176,7 @@ pub struct PeerInfoSerde { pub platform: String, } -#[derive(Debug, Default, Serialize, Deserialize, Clone)] +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)] pub struct TransferSerde { #[serde(default)] pub write_jobs: Vec, @@ -207,7 +215,16 @@ fn patch(path: PathBuf) -> PathBuf { impl Config2 { fn load() -> Config2 { - Config::load_::("2") + let mut config = Config::load_::("2"); + if let Some(mut socks) = config.socks { + let (password, store) = decrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION); + socks.password = password; + config.socks = Some(socks); + if store { + config.store(); + } + } + config } pub fn file() -> PathBuf { @@ -215,7 +232,12 @@ impl Config2 { } fn store(&self) { - Config::store_(self, "2"); + let mut config = self.clone(); + if let Some(mut socks) = config.socks { + socks.password = encrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION); + config.socks = Some(socks); + } + Config::store_(&config, "2"); } pub fn get() -> Config2 { @@ -267,11 +289,19 @@ impl Config { } fn load() -> Config { - Config::load_::("") + let mut config = Config::load_::(""); + let (password, store) = decrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION); + config.password = password; + if store { + config.store(); + } + config } fn store(&self) { - Config::store_(self, ""); + let mut config = self.clone(); + config.password = encrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION); + Config::store_(&config, ""); } pub fn file() -> PathBuf { @@ -627,7 +657,7 @@ impl Config { log::info!("id updated from {} to {}", id, new_id); } - pub fn set_password(password: &str) { + pub fn set_security_password(password: &str) { let mut config = CONFIG.write().unwrap(); if password == config.password { return; @@ -636,13 +666,8 @@ impl Config { config.store(); } - pub fn get_password() -> String { - let mut password = CONFIG.read().unwrap().password.clone(); - if password.is_empty() { - password = Config::get_auto_password(); - Config::set_password(&password); - } - password + pub fn get_security_password() -> String { + CONFIG.read().unwrap().password.clone() } pub fn set_salt(salt: &str) { @@ -714,7 +739,28 @@ impl PeerConfig { pub fn load(id: &str) -> PeerConfig { let _ = CONFIG.read().unwrap(); // for lock match confy::load_path(&Self::path(id)) { - Ok(config) => config, + Ok(config) => { + let mut config: PeerConfig = config; + let mut store = false; + let (password, store2) = + decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); + config.password = password; + store = store || store2; + config.options.get_mut("rdp_password").map(|v| { + let (password, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + *v = password; + store = store || store2; + }); + config.options.get_mut("os-password").map(|v| { + let (password, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION); + *v = password; + store = store || store2; + }); + if store { + config.store(id); + } + config + } Err(err) => { log::error!("Failed to load config: {}", err); Default::default() @@ -724,7 +770,17 @@ impl PeerConfig { pub fn store(&self, id: &str) { let _ = CONFIG.read().unwrap(); // for lock - if let Err(err) = confy::store_path(Self::path(id), self) { + let mut config = self.clone(); + config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION); + config + .options + .get_mut("rdp_password") + .map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)); + config + .options + .get_mut("os-password") + .map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)); + if let Err(err) = confy::store_path(Self::path(id), config) { log::error!("Failed to store config: {}", err); } } @@ -856,10 +912,26 @@ impl LocalConfig { } } +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct DiscoveryPeer { + pub id: String, + #[serde(with = "serde_with::rust::map_as_tuple_list")] + pub ip_mac: HashMap, + pub username: String, + pub hostname: String, + pub platform: String, + pub online: bool, +} + +impl DiscoveryPeer { + pub fn is_same_peer(&self, other: &DiscoveryPeer) -> bool { + self.id == other.id && self.username == other.username + } +} + #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct LanPeers { - #[serde(default)] - pub peers: String, + pub peers: Vec, } impl LanPeers { @@ -874,8 +946,10 @@ impl LanPeers { } } - pub fn store(peers: String) { - let f = LanPeers { peers }; + pub fn store(peers: &Vec) { + let f = LanPeers { + peers: peers.clone(), + }; if let Err(err) = confy::store_path(Config::file_("_lan_peers"), f) { log::error!("Failed to store lan peers: {}", err); } diff --git a/libs/hbb_common/src/fs.rs b/libs/hbb_common/src/fs.rs index 4512ce940..4880b4622 100644 --- a/libs/hbb_common/src/fs.rs +++ b/libs/hbb_common/src/fs.rs @@ -573,7 +573,7 @@ impl TransferJob { log::info!("file num truncated, ignoring"); } else { match r.union { - Some(file_transfer_send_confirm_request::Union::skip(s)) => { + Some(file_transfer_send_confirm_request::Union::Skip(s)) => { if s { log::debug!("skip file id:{}, file_num:{}", r.id, r.file_num); self.skip_current_file(); @@ -581,7 +581,7 @@ impl TransferJob { self.set_file_confirmed(true); } } - Some(file_transfer_send_confirm_request::Union::offset_blk(_offset)) => { + Some(file_transfer_send_confirm_request::Union::OffsetBlk(_offset)) => { self.set_file_confirmed(true); } _ => {} diff --git a/libs/hbb_common/src/lib.rs b/libs/hbb_common/src/lib.rs index 0a9dace0c..a5443db0f 100644 --- a/libs/hbb_common/src/lib.rs +++ b/libs/hbb_common/src/lib.rs @@ -1,9 +1,10 @@ pub mod compress; -#[path = "./protos/message.rs"] -pub mod message_proto; -#[path = "./protos/rendezvous.rs"] -pub mod rendezvous_proto; +pub mod protos; +pub mod platform; +pub use protos::message as message_proto; +pub use protos::rendezvous as rendezvous_proto; pub use bytes; +use config::Config; pub use futures; pub use protobuf; use std::{ @@ -27,6 +28,7 @@ pub use anyhow::{self, bail}; pub use futures_util; pub mod config; pub mod fs; +pub use lazy_static; #[cfg(not(any(target_os = "android", target_os = "ios")))] pub use mac_address; pub use rand; @@ -35,7 +37,7 @@ pub use sodiumoxide; pub use tokio_socks; pub use tokio_socks::IntoTargetAddr; pub use tokio_socks::TargetAddr; -pub use lazy_static; +pub mod password_security; #[cfg(feature = "quic")] pub type Stream = quic::Connection; @@ -200,6 +202,14 @@ pub fn get_modified_time(path: &std::path::Path) -> SystemTime { .unwrap_or(UNIX_EPOCH) } +pub fn get_uuid() -> Vec { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if let Ok(id) = machine_uid::get() { + return id.into(); + } + Config::get_key_pair().1 +} + #[cfg(test)] mod tests { use super::*; diff --git a/libs/hbb_common/src/password_security.rs b/libs/hbb_common/src/password_security.rs new file mode 100644 index 000000000..ed6376ff9 --- /dev/null +++ b/libs/hbb_common/src/password_security.rs @@ -0,0 +1,330 @@ +pub mod password { + use crate::config::Config; + use std::{ + fmt::Display, + str::FromStr, + sync::{Arc, RwLock}, + }; + + lazy_static::lazy_static! { + pub static ref RANDOM_PASSWORD:Arc> = Arc::new(RwLock::new(Config::get_auto_password())); + } + + const SECURITY_ENABLED: &'static str = "security-password-enabled"; + const RANDOM_ENABLED: &'static str = "random-password-enabled"; + const ONETIME_ENABLED: &'static str = "onetime-password-enabled"; + const ONETIME_ACTIVATED: &'static str = "onetime-password-activated"; + const UPDATE_METHOD: &'static str = "random-password-update-method"; + + #[derive(Debug, Clone, PartialEq, Eq)] + pub enum UpdateMethod { + KEEP, + UPDATE, + DISABLE, + } + + impl FromStr for UpdateMethod { + type Err = (); + + fn from_str(s: &str) -> Result { + if s == "KEEP" { + Ok(Self::KEEP) + } else if s == "UPDATE" { + Ok(Self::UPDATE) + } else if s == "DISABLE" { + Ok(Self::DISABLE) + } else { + Err(()) + } + } + } + + impl Display for UpdateMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + UpdateMethod::KEEP => write!(f, "KEEP"), + UpdateMethod::UPDATE => write!(f, "UPDATE"), + UpdateMethod::DISABLE => write!(f, "DISABLE"), + } + } + } + + pub fn set_random_password(password: &str) { + *RANDOM_PASSWORD.write().unwrap() = password.to_owned(); + } + + pub fn random_password() -> String { + let mut password = RANDOM_PASSWORD.read().unwrap().clone(); + if password.is_empty() { + password = Config::get_auto_password(); + set_random_password(&password); + } + password + } + + pub fn random_password_valid() -> bool { + if random_enabled() { + onetime_password_activated() || !onetime_password_enabled() + } else { + false + } + } + + pub fn passwords() -> Vec { + let mut v = vec![]; + if random_password_valid() { + v.push(random_password()); + } + if security_enabled() { + v.push(Config::get_security_password()); + } + v + } + + pub fn after_session(authorized: bool) { + if authorized && random_enabled() { + UpdateMethod::from_str(&update_method()) + .map(|method| match method { + UpdateMethod::KEEP => {} + UpdateMethod::UPDATE => set_random_password(&Config::get_auto_password()), + UpdateMethod::DISABLE => set_random_enabled(false), + }) + .ok(); + } + } + + pub fn update_method() -> String { + let mut method = Config::get_option(UPDATE_METHOD); + if UpdateMethod::from_str(&method).is_err() { + method = UpdateMethod::KEEP.to_string(); // default is keep + set_update_method(&method); + } + method + } + + pub fn set_update_method(method: &str) { + Config::set_option(UPDATE_METHOD.to_owned(), method.to_owned()); + } + + pub fn random_enabled() -> bool { + str2bool(RANDOM_ENABLED, true, || { + set_onetime_password_activated(false); + set_random_password(&Config::get_auto_password()); + }) + } + + pub fn set_random_enabled(enabled: bool) { + if enabled != random_enabled() { + Config::set_option(RANDOM_ENABLED.to_owned(), bool2str(enabled)); + set_onetime_password_activated(false); + if enabled { + set_random_password(&Config::get_auto_password()); + } + } + } + + pub fn security_enabled() -> bool { + str2bool(SECURITY_ENABLED, true, || {}) + } + + pub fn set_security_enabled(enabled: bool) { + if enabled != security_enabled() { + Config::set_option(SECURITY_ENABLED.to_owned(), bool2str(enabled)); + } + } + + pub fn onetime_password_enabled() -> bool { + str2bool(ONETIME_ENABLED, false, || { + set_onetime_password_activated(false); + set_random_password(&Config::get_auto_password()); + }) + } + + pub fn set_onetime_password_enabled(enabled: bool) { + if enabled != onetime_password_enabled() { + Config::set_option(ONETIME_ENABLED.to_owned(), bool2str(enabled)); + set_onetime_password_activated(false); + set_random_password(&Config::get_auto_password()); + } + } + + pub fn onetime_password_activated() -> bool { + str2bool(ONETIME_ACTIVATED, false, || {}) + } + + pub fn set_onetime_password_activated(activated: bool) { + if activated != onetime_password_activated() { + Config::set_option(ONETIME_ACTIVATED.to_owned(), bool2str(activated)); + if activated { + set_random_password(&Config::get_auto_password()); + } + } + } + + // notice: Function nesting + fn str2bool(key: &str, default: bool, default_set: impl Fn()) -> bool { + let option = Config::get_option(key); + if option == "Y" { + true + } else if option == "N" { + false + } else { + Config::set_option(key.to_owned(), bool2str(default)); + default_set(); + default + } + } + + fn bool2str(option: bool) -> String { + if option { "Y" } else { "N" }.to_owned() + } +} + +pub mod config { + use super::base64::decrypt as decrypt00; + use super::base64::encrypt as encrypt00; + + const VERSION_LEN: usize = 2; + + pub fn encrypt_str_or_original(s: &str, version: &str) -> String { + if version.len() == VERSION_LEN { + if version == "00" { + if let Ok(s) = encrypt00(s.as_bytes()) { + return version.to_owned() + &s; + } + } + } + + s.to_owned() + } + + // bool: whether should store to re-encrypt when load + pub fn decrypt_str_or_original(s: &str, current_version: &str) -> (String, bool) { + if s.len() > VERSION_LEN { + let version = &s[..VERSION_LEN]; + if version == "00" { + if let Ok(v) = decrypt00(&s[VERSION_LEN..].as_bytes()) { + return ( + String::from_utf8_lossy(&v).to_string(), + version != current_version, + ); + } + } + } + + (s.to_owned(), !s.is_empty()) + } + + pub fn encrypt_vec_or_original(v: &[u8], version: &str) -> Vec { + if version.len() == VERSION_LEN { + if version == "00" { + if let Ok(s) = encrypt00(v) { + let mut version = version.to_owned().into_bytes(); + version.append(&mut s.into_bytes()); + return version; + } + } + } + + v.to_owned() + } + + // bool: whether should store to re-encrypt when load + pub fn decrypt_vec_or_original(v: &[u8], current_version: &str) -> (Vec, bool) { + if v.len() > VERSION_LEN { + let version = String::from_utf8_lossy(&v[..VERSION_LEN]); + if version == "00" { + if let Ok(v) = decrypt00(&v[VERSION_LEN..]) { + return (v, version != current_version); + } + } + } + + (v.to_owned(), !v.is_empty()) + } + + mod test { + + #[test] + fn test() { + use crate::password_security::config::*; + + println!("test str"); + let data = "Hello World"; + let encrypted = encrypt_str_or_original(data, "00"); + let (decrypted, store) = decrypt_str_or_original(&encrypted, "00"); + println!("data: {}", data); + println!("encrypted: {}", encrypted); + println!("decrypted: {}", decrypted); + assert_eq!(data, decrypted); + assert_eq!("00", &encrypted[..2]); + assert_eq!(store, false); + let (_, store2) = decrypt_str_or_original(&encrypted, "01"); + assert_eq!(store2, true); + + println!("test vec"); + let data: Vec = vec![1, 2, 3, 4]; + let encrypted = encrypt_vec_or_original(&data, "00"); + let (decrypted, store) = decrypt_vec_or_original(&encrypted, "00"); + println!("data: {:?}", data); + println!("encrypted: {:?}", encrypted); + println!("decrypted: {:?}", decrypted); + assert_eq!(data, decrypted); + assert_eq!("00".as_bytes(), &encrypted[..2]); + assert_eq!(store, false); + let (_, store2) = decrypt_vec_or_original(&encrypted, "01"); + assert_eq!(store2, true); + + println!("test old"); + let data = "00Hello World"; + let (decrypted, store) = decrypt_str_or_original(&data, "00"); + assert_eq!(data, decrypted); + assert_eq!(store, true); + let data: Vec = vec!['0' as u8, '0' as u8, 1, 2, 3, 4]; + let (decrypted, store) = decrypt_vec_or_original(&data, "00"); + assert_eq!(data, decrypted); + assert_eq!(store, true); + let (_, store) = decrypt_str_or_original("", "00"); + assert_eq!(store, false); + let (_, store) = decrypt_vec_or_original(&vec![], "00"); + assert_eq!(store, false); + } + } +} + +mod base64 { + use super::symmetric_crypt; + use sodiumoxide::base64; + + pub fn encrypt(v: &[u8]) -> Result { + if v.len() > 0 { + symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original)) + } else { + Err(()) + } + } + + pub fn decrypt(v: &[u8]) -> Result, ()> { + if v.len() > 0 { + base64::decode(v, base64::Variant::Original).and_then(|v| symmetric_crypt(&v, false)) + } else { + Err(()) + } + } +} + +fn symmetric_crypt(data: &[u8], encrypt: bool) -> Result, ()> { + use sodiumoxide::crypto::secretbox; + use std::convert::TryInto; + + let mut keybuf = crate::get_uuid(); + keybuf.resize(secretbox::KEYBYTES, 0); + let key = secretbox::Key(keybuf.try_into().map_err(|_| ())?); + let nonce = secretbox::Nonce([0; secretbox::NONCEBYTES]); + + if encrypt { + Ok(secretbox::seal(data, &nonce, &key)) + } else { + secretbox::open(data, &nonce, &key) + } +} diff --git a/libs/hbb_common/src/platform/linux.rs b/libs/hbb_common/src/platform/linux.rs new file mode 100644 index 000000000..da79e9e39 --- /dev/null +++ b/libs/hbb_common/src/platform/linux.rs @@ -0,0 +1,102 @@ +use crate::ResultType; + +pub fn get_display_server() -> String { + let session = get_value_of_seat0(0); + get_display_server_of_session(&session) +} + +fn get_display_server_of_session(session: &str) -> String { + if let Ok(output) = std::process::Command::new("loginctl") + .args(vec!["show-session", "-p", "Type", session]) + .output() + // Check session type of the session + { + let display_server = String::from_utf8_lossy(&output.stdout) + .replace("Type=", "") + .trim_end() + .into(); + if display_server == "tty" { + // If the type is tty... + if let Ok(output) = std::process::Command::new("loginctl") + .args(vec!["show-session", "-p", "TTY", session]) + .output() + // Get the tty number + { + let tty: String = String::from_utf8_lossy(&output.stdout) + .replace("TTY=", "") + .trim_end() + .into(); + if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty)) + // And check if Xorg is running on that tty + { + if xorg_results.trim_end().to_string() != "" { + // If it is, manually return "x11", otherwise return tty + "x11".to_owned() + } else { + display_server + } + } else { + // If any of these commands fail just fall back to the display server + display_server + } + } else { + display_server + } + } else { + // If the session is not a tty, then just return the type as usual + display_server + } + } else { + "".to_owned() + } +} + +pub fn get_value_of_seat0(i: usize) -> String { + if let Ok(output) = std::process::Command::new("loginctl").output() { + for line in String::from_utf8_lossy(&output.stdout).lines() { + if line.contains("seat0") { + if let Some(sid) = line.split_whitespace().nth(0) { + if is_active(sid) { + if let Some(uid) = line.split_whitespace().nth(i) { + return uid.to_owned(); + } + } + } + } + } + } + + // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 + if let Ok(output) = std::process::Command::new("loginctl").output() { + for line in String::from_utf8_lossy(&output.stdout).lines() { + if let Some(sid) = line.split_whitespace().nth(0) { + let d = get_display_server_of_session(sid); + if is_active(sid) && d != "tty" { + if let Some(uid) = line.split_whitespace().nth(i) { + return uid.to_owned(); + } + } + } + } + } + + return "".to_owned(); +} + +fn is_active(sid: &str) -> bool { + if let Ok(output) = std::process::Command::new("loginctl") + .args(vec!["show-session", "-p", "State", sid]) + .output() + { + String::from_utf8_lossy(&output.stdout).contains("active") + } else { + false + } +} + +pub fn run_cmds(cmds: String) -> ResultType { + let output = std::process::Command::new("sh") + .args(vec!["-c", &cmds]) + .output()?; + Ok(String::from_utf8_lossy(&output.stdout).to_string()) +} diff --git a/libs/hbb_common/src/platform/mod.rs b/libs/hbb_common/src/platform/mod.rs new file mode 100644 index 000000000..8daba257f --- /dev/null +++ b/libs/hbb_common/src/platform/mod.rs @@ -0,0 +1,2 @@ +#[cfg(target_os = "linux")] +pub mod linux; diff --git a/libs/hbb_common/src/udp.rs b/libs/hbb_common/src/udp.rs index 4213392a5..3532dd1e0 100644 --- a/libs/hbb_common/src/udp.rs +++ b/libs/hbb_common/src/udp.rs @@ -27,6 +27,8 @@ fn new_socket(addr: SocketAddr, reuse: bool, buf_size: usize) -> Result 0 { socket.set_recv_buffer_size(buf_size).ok(); } diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index e06c0e29a..1d9deba68 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -251,11 +251,11 @@ impl Decoder { rgb: &mut Vec, ) -> ResultType { match frame { - video_frame::Union::vp9s(vp9s) => { + video_frame::Union::Vp9s(vp9s) => { Decoder::handle_vp9s_video_frame(&mut self.vpx, vp9s, rgb) } #[cfg(feature = "hwcodec")] - video_frame::Union::h264s(h264s) => { + video_frame::Union::H264s(h264s) => { if let Some(decoder) = &mut self.hw.h264 { Decoder::handle_hw_video_frame(decoder, h264s, rgb, &mut self.i420) } else { @@ -263,7 +263,7 @@ impl Decoder { } } #[cfg(feature = "hwcodec")] - video_frame::Union::h265s(h265s) => { + video_frame::Union::H265s(h265s) => { if let Some(decoder) = &mut self.hw.h265 { Decoder::handle_hw_video_frame(decoder, h265s, rgb, &mut self.i420) } else { diff --git a/libs/scrap/src/common/dxgi.rs b/libs/scrap/src/common/dxgi.rs index 1a8c39885..855ac7ac3 100644 --- a/libs/scrap/src/common/dxgi.rs +++ b/libs/scrap/src/common/dxgi.rs @@ -21,6 +21,10 @@ impl Capturer { }) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.inner.set_use_yuv(use_yuv); + } + pub fn is_gdi(&self) -> bool { self.inner.is_gdi() } @@ -41,8 +45,8 @@ impl Capturer { self.height } - pub fn frame<'a>(&'a mut self, timeout_ms: Duration) -> io::Result> { - match self.inner.frame(timeout_ms.as_millis() as _) { + pub fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { + match self.inner.frame(timeout.as_millis() as _) { Ok(frame) => Ok(Frame(frame)), Err(ref error) if error.kind() == TimedOut => Err(WouldBlock.into()), Err(error) => Err(error), @@ -129,6 +133,11 @@ impl CapturerMag { data: Vec::new(), }) } + + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.inner.set_use_yuv(use_yuv) + } + pub fn exclude(&mut self, cls: &str, name: &str) -> io::Result { self.inner.exclude(cls, name) } diff --git a/libs/scrap/src/common/linux.rs b/libs/scrap/src/common/linux.rs index 50bab092c..8498ab7ff 100644 --- a/libs/scrap/src/common/linux.rs +++ b/libs/scrap/src/common/linux.rs @@ -2,7 +2,7 @@ use crate::common::{ wayland, x11::{self, Frame}, }; -use std::io; +use std::{io, time::Duration}; pub enum Capturer { X11(x11::Capturer), @@ -17,6 +17,13 @@ impl Capturer { }) } + pub 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), + } + } + pub fn width(&self) -> usize { match self { Capturer::X11(d) => d.width(), @@ -31,10 +38,10 @@ impl Capturer { } } - pub fn frame<'a>(&'a mut self, timeout_ms: u32) -> io::Result> { + pub fn frame<'a>(&'a mut self, timeout: Duration) -> io::Result> { match self { - Capturer::X11(d) => d.frame(timeout_ms), - Capturer::WAYLAND(d) => d.frame(timeout_ms), + Capturer::X11(d) => d.frame(timeout), + Capturer::WAYLAND(d) => d.frame(timeout), } } } @@ -45,31 +52,30 @@ pub enum Display { } #[inline] -fn is_wayland() -> bool { - std::env::var("IS_WAYLAND").is_ok() - || std::env::var("XDG_SESSION_TYPE") == Ok("wayland".to_owned()) +pub fn is_x11() -> bool { + "x11" == hbb_common::platform::linux::get_display_server() } impl Display { pub fn primary() -> io::Result { - Ok(if is_wayland() { - Display::WAYLAND(wayland::Display::primary()?) - } else { + Ok(if is_x11() { Display::X11(x11::Display::primary()?) + } else { + Display::WAYLAND(wayland::Display::primary()?) }) } pub fn all() -> io::Result> { - Ok(if is_wayland() { - wayland::Display::all()? - .drain(..) - .map(|x| Display::WAYLAND(x)) - .collect() - } else { + Ok(if is_x11() { x11::Display::all()? .drain(..) .map(|x| Display::X11(x)) .collect() + } else { + wayland::Display::all()? + .drain(..) + .map(|x| Display::WAYLAND(x)) + .collect() }) } diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 792ea14e1..9115bfd3a 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -11,6 +11,7 @@ cfg_if! { mod wayland; mod x11; pub use self::linux::*; + pub use self::x11::Frame; } else { mod x11; pub use self::x11::*; diff --git a/libs/scrap/src/common/wayland.rs b/libs/scrap/src/common/wayland.rs index ff6bf8022..05bb08744 100644 --- a/libs/scrap/src/common/wayland.rs +++ b/libs/scrap/src/common/wayland.rs @@ -1,6 +1,6 @@ use crate::common::x11::Frame; use crate::wayland::{capturable::*, *}; -use std::io; +use std::{io, time::Duration}; pub struct Capturer(Display, Box, bool, Vec); @@ -14,6 +14,10 @@ impl Capturer { Ok(Capturer(display, r, yuv, Default::default())) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.2 = use_yuv; + } + pub fn width(&self) -> usize { self.0.width() } @@ -22,8 +26,8 @@ impl Capturer { self.0.height() } - pub fn frame<'a>(&'a mut self, timeout_ms: u32) -> io::Result> { - match self.1.capture(timeout_ms as _).map_err(map_err)? { + pub 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[..] diff --git a/libs/scrap/src/common/x11.rs b/libs/scrap/src/common/x11.rs index 255819902..c1a25c8d6 100644 --- a/libs/scrap/src/common/x11.rs +++ b/libs/scrap/src/common/x11.rs @@ -8,6 +8,10 @@ impl Capturer { x11::Capturer::new(display.0, yuv).map(Capturer) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.0.set_use_yuv(use_yuv); + } + pub fn width(&self) -> usize { self.0.display().rect().w as usize } diff --git a/libs/scrap/src/dxgi/mag.rs b/libs/scrap/src/dxgi/mag.rs index 9adf26cdb..78f14194c 100644 --- a/libs/scrap/src/dxgi/mag.rs +++ b/libs/scrap/src/dxgi/mag.rs @@ -446,6 +446,10 @@ 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).unwrap(); unsafe { diff --git a/libs/scrap/src/dxgi/mod.rs b/libs/scrap/src/dxgi/mod.rs index 46692535d..6b60b256d 100644 --- a/libs/scrap/src/dxgi/mod.rs +++ b/libs/scrap/src/dxgi/mod.rs @@ -156,6 +156,10 @@ 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() } diff --git a/libs/scrap/src/x11/capturer.rs b/libs/scrap/src/x11/capturer.rs index 890b9db63..ed424c35a 100644 --- a/libs/scrap/src/x11/capturer.rs +++ b/libs/scrap/src/x11/capturer.rs @@ -74,6 +74,10 @@ impl Capturer { Ok(c) } + pub fn set_use_yuv(&mut self, use_yuv: bool) { + self.use_yuv = use_yuv; + } + pub fn display(&self) -> &Display { &self.display } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..05dfa3270 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.62.0" diff --git a/rustdesk.service b/rustdesk.service index af4a3c411..e703b056f 100644 --- a/rustdesk.service +++ b/rustdesk.service @@ -6,7 +6,7 @@ After=systemd-user-sessions.service [Service] Type=simple ExecStart=/usr/bin/rustdesk --service -PIDFile=/var/run/rustdesk.pid +PIDFile=/run/rustdesk.pid KillMode=mixed TimeoutStopSec=30 User=root diff --git a/rustdesk.service.user b/rustdesk.service.user new file mode 100644 index 000000000..f6c7454c9 --- /dev/null +++ b/rustdesk.service.user @@ -0,0 +1,15 @@ +[Unit] +Description=RustDesk user service (--server) + +[Service] +Type=simple +ExecStart=/usr/bin/rustdesk --server +PIDFile=/run/rustdesk.user.pid +KillMode=mixed +TimeoutStopSec=30 +LimitNOFILE=100000 +Restart=on-failure +RestartSec=3 + +[Install] +WantedBy=multi-user.target diff --git a/src/client.rs b/src/client.rs index d63ce970c..28101896e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -28,6 +28,7 @@ use hbb_common::{ log, message_proto::{option_message::BoolOption, *}, protobuf::Message as _, + rand, rendezvous_proto::*, socket_client, sodiumoxide::crypto::{box_, secretbox, sign}, @@ -148,11 +149,25 @@ impl Client { true, )); } - let rendezvous_server = crate::get_rendezvous_server(1_000).await; - log::info!("rendezvous server: {}", rendezvous_server); - + let (mut rendezvous_server, servers, contained) = crate::get_rendezvous_server(1_000).await; let mut socket = - socket_client::connect_tcp(&*rendezvous_server, any_addr, RENDEZVOUS_TIMEOUT).await?; + socket_client::connect_tcp(&*rendezvous_server, any_addr, RENDEZVOUS_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 { + socket = socket_client::connect_tcp(&*server, any_addr, RENDEZVOUS_TIMEOUT).await; + if socket.is_ok() { + rendezvous_server = server; + break; + } + } + crate::refresh_rendezvous_server(); + } else if !contained { + crate::refresh_rendezvous_server(); + } + log::info!("rendezvous server: {}", rendezvous_server); + let mut socket = socket?; let my_addr = socket.local_addr(); let mut signed_id_pk = Vec::new(); let mut relay_server = "".to_owned(); @@ -165,7 +180,7 @@ impl Client { for i in 1..=3 { log::info!("#{} punch attempt with {}, id: {}", i, my_addr, peer); let mut msg_out = RendezvousMessage::new(); - use hbb_common::protobuf::ProtobufEnum; + use hbb_common::protobuf::Enum; let nat_type = NatType::from_i32(my_nat_type).unwrap_or(NatType::UNKNOWN_NAT); msg_out.set_punch_hole_request(PunchHoleRequest { id: peer.to_owned(), @@ -179,7 +194,7 @@ impl Client { if let Some(Ok(bytes)) = socket.next_timeout(i * 6000).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { match msg_in.union { - Some(rendezvous_message::Union::punch_hole_response(ph)) => { + Some(rendezvous_message::Union::PunchHoleResponse(ph)) => { if ph.socket_addr.is_empty() { if !ph.other_failure.is_empty() { bail!(ph.other_failure); @@ -199,8 +214,8 @@ impl Client { } } } else { - peer_nat_type = ph.get_nat_type(); - is_local = ph.get_is_local(); + peer_nat_type = ph.nat_type(); + is_local = ph.is_local(); signed_id_pk = ph.pk; relay_server = ph.relay_server; peer_addr = AddrMangle::decode(&ph.socket_addr); @@ -208,13 +223,13 @@ impl Client { break; } } - Some(rendezvous_message::Union::relay_response(rr)) => { + Some(rendezvous_message::Union::RelayResponse(rr)) => { log::info!( "relay requested from peer, time used: {:?}, relay_server: {}", start.elapsed(), rr.relay_server ); - signed_id_pk = rr.get_pk().into(); + signed_id_pk = rr.pk().into(); let mut conn = Self::create_relay(peer, rr.uuid, rr.relay_server, key, conn_type) .await?; @@ -383,7 +398,7 @@ impl Client { Some(res) => { let bytes = res?; if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { - if let Some(message::Union::signed_id(si)) = msg_in.union { + if let Some(message::Union::SignedId(si)) = msg_in.union { if let Ok((id, their_pk_b)) = decode_id_pk(&si.id, &sign_pk) { if id == peer_id { let their_pk_b = box_::PublicKey(their_pk_b); @@ -466,7 +481,7 @@ impl Client { socket.send(&msg_out).await?; if let Some(Ok(bytes)) = socket.next_timeout(CONNECT_TIMEOUT).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - if let Some(rendezvous_message::Union::relay_response(rs)) = msg_in.union { + if let Some(rendezvous_message::Union::RelayResponse(rs)) = msg_in.union { if !rs.refuse_reason.is_empty() { bail!(rs.refuse_reason); } @@ -768,6 +783,7 @@ pub struct LoginConfigHandler { pub version: i64, pub conn_id: i32, features: Option, + session_id: u64, } impl Deref for LoginConfigHandler { @@ -791,6 +807,7 @@ impl LoginConfigHandler { let config = self.load_config(); self.remember = !config.password.is_empty(); self.config = config; + self.session_id = rand::random(); } pub fn should_auto_login(&self) -> String { @@ -1126,6 +1143,7 @@ impl LoginConfigHandler { my_id, my_name: crate::username(), option: self.get_option_message(true).into(), + session_id: self.session_id, ..Default::default() }; if self.is_file_transfer { diff --git a/src/client/helper.rs b/src/client/helper.rs index ea12cb7ee..26dc37ba4 100644 --- a/src/client/helper.rs +++ b/src/client/helper.rs @@ -69,9 +69,9 @@ pub enum CodecFormat { impl From<&VideoFrame> for CodecFormat { fn from(it: &VideoFrame) -> Self { match it.union { - Some(video_frame::Union::vp9s(_)) => CodecFormat::VP9, - Some(video_frame::Union::h264s(_)) => CodecFormat::H264, - Some(video_frame::Union::h265s(_)) => CodecFormat::H265, + Some(video_frame::Union::Vp9s(_)) => CodecFormat::VP9, + Some(video_frame::Union::H264s(_)) => CodecFormat::H264, + Some(video_frame::Union::H265s(_)) => CodecFormat::H265, _ => CodecFormat::Unknown, } } diff --git a/src/clipboard_file.rs b/src/clipboard_file.rs index 39b2eb766..2f35065c9 100644 --- a/src/clipboard_file.rs +++ b/src/clipboard_file.rs @@ -17,8 +17,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { }); } Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::format_list(CliprdrServerFormatList { + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FormatList(CliprdrServerFormatList { conn_id, formats, ..Default::default() @@ -29,8 +29,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { } } ClipbaordFile::ServerFormatListResponse { conn_id, msg_flags } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::format_list_response( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FormatListResponse( CliprdrServerFormatListResponse { conn_id, msg_flags, @@ -45,8 +45,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { conn_id, requested_format_id, } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::format_data_request( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FormatDataRequest( CliprdrServerFormatDataRequest { conn_id, requested_format_id, @@ -62,8 +62,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { msg_flags, format_data, } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::format_data_response( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FormatDataResponse( CliprdrServerFormatDataResponse { conn_id, msg_flags, @@ -86,8 +86,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { have_clip_data_id, clip_data_id, } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::file_contents_request( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FileContentsRequest( CliprdrFileContentsRequest { conn_id, stream_id, @@ -111,8 +111,8 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { stream_id, requested_data, } => Message { - union: Some(message::Union::cliprdr(Cliprdr { - union: Some(cliprdr::Union::file_contents_response( + union: Some(message::Union::Cliprdr(Cliprdr { + union: Some(cliprdr::Union::FileContentsResponse( CliprdrFileContentsResponse { conn_id, msg_flags, @@ -130,7 +130,7 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message { pub fn msg_2_clip(msg: Cliprdr) -> Option { match msg.union { - Some(cliprdr::Union::format_list(data)) => { + Some(cliprdr::Union::FormatList(data)) => { let mut format_list: Vec<(i32, String)> = Vec::new(); for v in data.formats.iter() { format_list.push((v.id, v.format.clone())); @@ -140,26 +140,26 @@ pub fn msg_2_clip(msg: Cliprdr) -> Option { format_list, }) } - Some(cliprdr::Union::format_list_response(data)) => { + Some(cliprdr::Union::FormatListResponse(data)) => { Some(ClipbaordFile::ServerFormatListResponse { conn_id: data.conn_id, msg_flags: data.msg_flags, }) } - Some(cliprdr::Union::format_data_request(data)) => { + Some(cliprdr::Union::FormatDataRequest(data)) => { Some(ClipbaordFile::ServerFormatDataRequest { conn_id: data.conn_id, requested_format_id: data.requested_format_id, }) } - Some(cliprdr::Union::format_data_response(data)) => { + Some(cliprdr::Union::FormatDataResponse(data)) => { Some(ClipbaordFile::ServerFormatDataResponse { conn_id: data.conn_id, msg_flags: data.msg_flags, format_data: data.format_data, }) } - Some(cliprdr::Union::file_contents_request(data)) => { + Some(cliprdr::Union::FileContentsRequest(data)) => { Some(ClipbaordFile::FileContentsRequest { conn_id: data.conn_id, stream_id: data.stream_id, @@ -172,7 +172,7 @@ pub fn msg_2_clip(msg: Cliprdr) -> Option { clip_data_id: data.clip_data_id, }) } - Some(cliprdr::Union::file_contents_response(data)) => { + Some(cliprdr::Union::FileContentsResponse(data)) => { Some(ClipbaordFile::FileContentsResponse { conn_id: data.conn_id, msg_flags: data.msg_flags, diff --git a/src/common.rs b/src/common.rs index 667dd3e7e..d9eab5d99 100644 --- a/src/common.rs +++ b/src/common.rs @@ -8,11 +8,10 @@ use hbb_common::{ get_version_number, log, message_proto::*, protobuf::Message as _, - protobuf::ProtobufEnum, + protobuf::Enum, rendezvous_proto::*, sleep, socket_client, tokio, ResultType, }; -#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] use hbb_common::{config::RENDEZVOUS_PORT, futures::future::join_all}; use std::sync::{Arc, Mutex}; @@ -32,7 +31,7 @@ lazy_static::lazy_static! { #[inline] pub fn valid_for_numlock(evt: &KeyEvent) -> bool { - if let Some(key_event::Union::control_key(ck)) = evt.union { + if let Some(key_event::Union::ControlKey(ck)) = evt.union { let v = ck.value(); (v >= ControlKey::Numpad0.value() && v <= ControlKey::Numpad9.value()) || v == ControlKey::Decimal.value() @@ -247,7 +246,7 @@ async fn test_nat_type_() -> ResultType { return Ok(true); } let start = std::time::Instant::now(); - let rendezvous_server = get_rendezvous_server(1_000).await; + let (rendezvous_server, _, _) = get_rendezvous_server(1_000).await; let server1 = rendezvous_server; let tmp: Vec<&str> = server1.split(":").collect(); if tmp.len() != 2 { @@ -284,7 +283,7 @@ async fn test_nat_type_() -> ResultType { socket.send(&msg_out).await?; if let Some(Ok(bytes)) = socket.next_timeout(RENDEZVOUS_TIMEOUT).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - if let Some(rendezvous_message::Union::test_nat_response(tnr)) = msg_in.union { + if let Some(rendezvous_message::Union::TestNatResponse(tnr)) = msg_in.union { if i == 0 { port1 = tnr.port; } else { @@ -316,31 +315,62 @@ async fn test_nat_type_() -> ResultType { Ok(ok) } -#[cfg(any(target_os = "android", target_os = "ios"))] -pub async fn get_rendezvous_server(_ms_timeout: u64) -> String { - Config::get_rendezvous_server() +pub async fn get_rendezvous_server(ms_timeout: u64) -> (String, Vec, bool) { + #[cfg(any(target_os = "android", target_os = "ios"))] + let (mut a, mut b) = get_rendezvous_server_(ms_timeout); + #[cfg(not(any(target_os = "android", target_os = "ios")))] + let (mut a, mut b) = get_rendezvous_server_(ms_timeout).await; + let mut b: Vec = b + .drain(..) + .map(|x| { + if !x.contains(":") { + format!("{}:{}", x, config::RENDEZVOUS_PORT) + } else { + x + } + }) + .collect(); + let c = if b.contains(&a) { + b = b.drain(..).filter(|x| x != &a).collect(); + true + } else { + a = b.pop().unwrap_or(a); + false + }; + (a, b, c) } +#[inline] +#[cfg(any(target_os = "android", target_os = "ios"))] +fn get_rendezvous_server_(_ms_timeout: u64) -> (String, Vec) { + ( + Config::get_rendezvous_server(), + Config::get_rendezvous_servers(), + ) +} + +#[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] -pub async fn get_rendezvous_server(ms_timeout: u64) -> String { +async fn get_rendezvous_server_(ms_timeout: u64) -> (String, Vec) { crate::ipc::get_rendezvous_server(ms_timeout).await } +#[inline] #[cfg(any(target_os = "android", target_os = "ios"))] pub async fn get_nat_type(_ms_timeout: u64) -> i32 { Config::get_nat_type() } +#[inline] #[cfg(not(any(target_os = "android", target_os = "ios")))] pub async fn get_nat_type(ms_timeout: u64) -> i32 { crate::ipc::get_nat_type(ms_timeout).await } -#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] #[tokio::main(flavor = "current_thread")] async fn test_rendezvous_server_() { let servers = Config::get_rendezvous_servers(); - hbb_common::config::ONLINE.lock().unwrap().clear(); + Config::reset_online(); let mut futs = Vec::new(); for host in servers { futs.push(tokio::spawn(async move { @@ -363,11 +393,21 @@ async fn test_rendezvous_server_() { join_all(futs).await; } -#[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] pub fn test_rendezvous_server() { std::thread::spawn(test_rendezvous_server_); } +pub fn refresh_rendezvous_server() { + #[cfg(any(target_os = "android", target_os = "ios", feature = "cli"))] + test_rendezvous_server(); + #[cfg(not(any(target_os = "android", target_os = "ios", feature = "cli")))] + std::thread::spawn(|| { + if crate::ipc::test_rendezvous_server().is_err() { + test_rendezvous_server(); + } + }); +} + #[inline] pub fn get_time() -> i64 { std::time::SystemTime::now() @@ -412,7 +452,7 @@ pub const POSTFIX_SERVICE: &'static str = "_service"; #[inline] pub fn is_control_key(evt: &KeyEvent, key: &ControlKey) -> bool { - if let Some(key_event::Union::control_key(ck)) = evt.union { + if let Some(key_event::Union::ControlKey(ck)) = evt.union { ck.value() == key.value() } else { false @@ -421,7 +461,7 @@ pub fn is_control_key(evt: &KeyEvent, key: &ControlKey) -> bool { #[inline] pub fn is_modifier(evt: &KeyEvent) -> bool { - if let Some(key_event::Union::control_key(ck)) = evt.union { + if let Some(key_event::Union::ControlKey(ck)) = evt.union { let v = ck.value(); v == ControlKey::Alt.value() || v == ControlKey::Shift.value() @@ -437,14 +477,15 @@ pub fn is_modifier(evt: &KeyEvent) -> bool { } pub fn check_software_update() { - std::thread::spawn(move || allow_err!(_check_software_update())); + std::thread::spawn(move || allow_err!(check_software_update_())); } #[tokio::main(flavor = "current_thread")] -async fn _check_software_update() -> hbb_common::ResultType<()> { +async fn check_software_update_() -> hbb_common::ResultType<()> { sleep(3.).await; - let rendezvous_server = socket_client::get_target_addr(&get_rendezvous_server(1_000).await)?; + let rendezvous_server = + socket_client::get_target_addr(&format!("rs-sg.rustdesk.com:{}", config::RENDEZVOUS_PORT))?; let mut socket = socket_client::new_udp(Config::get_any_listen_addr(), RENDEZVOUS_TIMEOUT).await?; @@ -457,7 +498,7 @@ async fn _check_software_update() -> hbb_common::ResultType<()> { use hbb_common::protobuf::Message; if let Some(Ok((bytes, _))) = socket.next_timeout(30_000).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - if let Some(rendezvous_message::Union::software_update(su)) = msg_in.union { + if let Some(rendezvous_message::Union::SoftwareUpdate(su)) = msg_in.union { let version = hbb_common::get_version_from_url(&su.url); if get_version_number(&version) > get_version_number(crate::VERSION) { *SOFTWARE_UPDATE_URL.lock().unwrap() = su.url; @@ -496,14 +537,6 @@ pub fn is_setup(name: &str) -> bool { name.to_lowercase().ends_with("setdown.exe") || name.to_lowercase().ends_with("安装.exe") } -pub fn get_uuid() -> Vec { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Ok(id) = machine_uid::get() { - return id.into(); - } - Config::get_key_pair().1 -} - pub fn get_custom_rendezvous_server(custom: String) -> String { if !custom.is_empty() { return custom; diff --git a/src/ipc.rs b/src/ipc.rs index 24a156eba..a5615ce6a 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -7,7 +7,9 @@ use hbb_common::{ config::{self, Config, Config2}, futures::StreamExt as _, futures_util::sink::SinkExt, - log, timeout, tokio, + log, + password_security::password, + timeout, tokio, tokio::io::{AsyncRead, AsyncWrite}, tokio_util::codec::Framed, ResultType, @@ -20,6 +22,16 @@ use std::{collections::HashMap, sync::atomic::Ordering}; #[cfg(not(windows))] use std::{fs::File, io::prelude::*}; +const STR_RANDOM_PASSWORD: &'static str = "random-password"; +const STR_SECURITY_PASSWORD: &'static str = "security-password"; +const STR_RANDOM_PASSWORD_UPDATE_METHOD: &'static str = "random-password-update-method"; +const STR_RANDOM_PASSWORD_ENABLED: &'static str = "random-password-enabled"; +const STR_SECURITY_PASSWORD_ENABLED: &'static str = "security-password-enabled"; +const STR_ONETIME_PASSWORD_ENABLED: &'static str = "onetime-password-enabled"; +const STR_ONETIME_PASSWORD_ACTIVATED: &'static str = "onetime-password-activated"; +const STR_RANDOM_PASSWORD_VALID: &'static str = "random-password-valid"; +pub const STR_PASSWORD_DESCRIPTION: &'static str = "password-description"; + // State with timestamp, because std::time::Instant cannot be serialized #[derive(Debug, Serialize, Deserialize, Copy, Clone)] #[serde(tag = "t", content = "c")] @@ -84,6 +96,45 @@ pub enum FS { }, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataKeyboard { + Sequence(String), + KeyDown(enigo::Key), + KeyUp(enigo::Key), + KeyClick(enigo::Key), + GetKeyState(enigo::Key), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataKeyboardResponse { + GetKeyState(bool), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataMouse { + MoveTo(i32, i32), + MoveRelative(i32, i32), + Down(enigo::MouseButton), + Up(enigo::MouseButton), + Click(enigo::MouseButton), + ScrollX(i32), + ScrollY(i32), +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum DataControl { + Resolution { + minx: i32, + maxx: i32, + miny: i32, + maxy: i32, + }, +} + #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(tag = "t", content = "c")] pub enum Data { @@ -127,6 +178,13 @@ pub enum Data { ClipbaordFile(ClipbaordFile), ClipboardFileEnabled(bool), PrivacyModeState((i32, PrivacyModeState)), + TestRendezvousServer, + Bool((String, Option)), + Keyboard(DataKeyboard), + KeyboardResponse(DataKeyboardResponse), + Mouse(DataMouse), + Control(DataControl), + Empty, } #[tokio::main(flavor = "current_thread")] @@ -281,12 +339,28 @@ async fn handle(data: Data, stream: &mut Connection) { let value; if name == "id" { value = Some(Config::get_id()); - } else if name == "password" { - value = Some(Config::get_password()); + } else if name == STR_RANDOM_PASSWORD { + value = Some(password::random_password()); + } else if name == STR_SECURITY_PASSWORD { + value = Some(Config::get_security_password()); + } else if name == STR_RANDOM_PASSWORD_UPDATE_METHOD { + value = Some(password::update_method().to_string()); + } else if name == STR_PASSWORD_DESCRIPTION { + value = Some( + password::random_password() + + &password::security_enabled().to_string() + + &password::random_enabled().to_string() + + &password::onetime_password_enabled().to_string() + + &password::onetime_password_activated().to_string(), + ); } else if name == "salt" { value = Some(Config::get_salt()); } else if name == "rendezvous_server" { - value = Some(Config::get_rendezvous_server()); + value = Some(format!( + "{},{}", + Config::get_rendezvous_server(), + Config::get_rendezvous_servers().join(",") + )); } else if name == "rendezvous_servers" { value = Some(Config::get_rendezvous_servers().join(",")); } else { @@ -298,8 +372,12 @@ async fn handle(data: Data, stream: &mut Connection) { if name == "id" { Config::set_key_confirmed(false); Config::set_id(&value); - } else if name == "password" { - Config::set_password(&value); + } else if name == STR_RANDOM_PASSWORD { + password::set_random_password(&value); + } else if name == STR_SECURITY_PASSWORD { + Config::set_security_password(&value); + } else if name == STR_RANDOM_PASSWORD_UPDATE_METHOD { + password::set_update_method(&value); } else if name == "salt" { Config::set_salt(&value); } else { @@ -336,7 +414,39 @@ async fn handle(data: Data, stream: &mut Connection) { .await ); } - + Data::TestRendezvousServer => { + crate::test_rendezvous_server(); + } + Data::Bool((name, value)) => match value { + None => { + let value; + if name == STR_SECURITY_PASSWORD_ENABLED { + value = Some(password::security_enabled()); + } else if name == STR_RANDOM_PASSWORD_ENABLED { + value = Some(password::random_enabled()); + } else if name == STR_ONETIME_PASSWORD_ENABLED { + value = Some(password::onetime_password_enabled()); + } else if name == STR_ONETIME_PASSWORD_ACTIVATED { + value = Some(password::onetime_password_activated()); + } else if name == STR_RANDOM_PASSWORD_VALID { + value = Some(password::random_password_valid()); + } else { + return; + } + allow_err!(stream.send(&Data::Bool((name, value))).await); + } + Some(value) => { + if name == STR_SECURITY_PASSWORD_ENABLED { + password::set_security_enabled(value); + } else if name == STR_RANDOM_PASSWORD_ENABLED { + password::set_random_enabled(value); + } else if name == STR_ONETIME_PASSWORD_ENABLED { + password::set_onetime_password_enabled(value); + } else if name == STR_ONETIME_PASSWORD_ACTIVATED { + password::set_onetime_password_activated(value); + } + } + }, _ => {} } } @@ -419,6 +529,10 @@ where .await } + async fn send_bool(&mut self, name: &str, value: bool) -> ResultType<()> { + self.send(&Data::Bool((name.to_owned(), Some(value)))).await + } + pub async fn next_timeout(&mut self, ms_timeout: u64) -> ResultType> { Ok(timeout(ms_timeout, self.next()).await??) } @@ -490,9 +604,128 @@ pub async fn set_config(name: &str, value: String) -> ResultType<()> { set_config_async(name, value).await } -pub fn set_password(v: String) -> ResultType<()> { - Config::set_password(&v); - set_config("password", v) +#[tokio::main(flavor = "current_thread")] +async fn get_bool(name: &str) -> ResultType> { + get_bool_async(name, 1_000).await +} + +async fn get_bool_async(name: &str, ms_timeout: u64) -> ResultType> { + let mut c = connect(ms_timeout, "").await?; + c.send(&Data::Bool((name.to_owned(), None))).await?; + if let Some(Data::Bool((name2, value))) = c.next_timeout(ms_timeout).await? { + if name == name2 { + return Ok(value); + } + } + return Ok(None); +} + +pub async fn set_bool_async(name: &str, value: bool) -> ResultType<()> { + let mut c = connect(1000, "").await?; + c.send_bool(name, value).await?; + Ok(()) +} + +#[tokio::main(flavor = "current_thread")] +pub async fn set_bool(name: &str, value: bool) -> ResultType<()> { + set_bool_async(name, value).await +} + +pub fn get_random_password() -> String { + if let Ok(Some(password)) = get_config(STR_RANDOM_PASSWORD) { + password::set_random_password(&password); + password + } else { + password::random_password() + } +} + +pub fn set_random_password(v: String) -> ResultType<()> { + password::set_random_password(&v); + set_config(STR_RANDOM_PASSWORD, v) +} + +pub fn set_security_password(v: String) -> ResultType<()> { + Config::set_security_password(&v); + set_config(STR_SECURITY_PASSWORD, v) +} + +pub fn random_password_update_method() -> String { + if let Ok(Some(method)) = get_config(STR_RANDOM_PASSWORD_UPDATE_METHOD) { + password::set_update_method(&method); + method + } else { + password::update_method() + } +} + +pub fn set_random_password_update_method(method: String) -> ResultType<()> { + password::set_update_method(&method); + set_config(STR_RANDOM_PASSWORD_UPDATE_METHOD, method) +} + +pub fn is_random_password_enabled() -> bool { + if let Ok(Some(enabled)) = get_bool(STR_RANDOM_PASSWORD_ENABLED) { + password::set_random_enabled(enabled); + enabled + } else { + password::random_enabled() + } +} + +pub fn set_random_password_enabled(enabled: bool) -> ResultType<()> { + password::set_random_enabled(enabled); + set_bool(STR_RANDOM_PASSWORD_ENABLED, enabled) +} + +pub fn is_security_password_enabled() -> bool { + if let Ok(Some(enabled)) = get_bool(STR_SECURITY_PASSWORD_ENABLED) { + password::set_security_enabled(enabled); + enabled + } else { + password::security_enabled() + } +} + +pub fn set_security_password_enabled(enabled: bool) -> ResultType<()> { + password::set_security_enabled(enabled); + set_bool(STR_SECURITY_PASSWORD_ENABLED, enabled) +} + +pub fn is_onetime_password_enabled() -> bool { + if let Ok(Some(enabled)) = get_bool(STR_ONETIME_PASSWORD_ENABLED) { + password::set_onetime_password_enabled(enabled); + enabled + } else { + password::onetime_password_enabled() + } +} + +pub fn set_onetime_password_enabled(enabled: bool) -> ResultType<()> { + password::set_onetime_password_enabled(enabled); + set_bool(STR_ONETIME_PASSWORD_ENABLED, enabled) +} + +pub fn is_onetime_password_activated() -> bool { + if let Ok(Some(activated)) = get_bool(STR_ONETIME_PASSWORD_ACTIVATED) { + password::set_onetime_password_activated(activated); + activated + } else { + password::onetime_password_activated() + } +} + +pub fn set_onetime_password_activated(activated: bool) -> ResultType<()> { + password::set_onetime_password_activated(activated); + set_bool(STR_ONETIME_PASSWORD_ACTIVATED, activated) +} + +pub fn is_random_password_valid() -> bool { + if let Ok(Some(valid)) = get_bool(STR_RANDOM_PASSWORD_VALID) { + valid + } else { + password::random_password_valid() + } } pub fn get_id() -> String { @@ -511,20 +744,17 @@ pub fn get_id() -> String { } } -pub fn get_password() -> String { - if let Ok(Some(v)) = get_config("password") { - Config::set_password(&v); - v - } else { - Config::get_password() - } -} - -pub async fn get_rendezvous_server(ms_timeout: u64) -> String { +pub async fn get_rendezvous_server(ms_timeout: u64) -> (String, Vec) { if let Ok(Some(v)) = get_config_async("rendezvous_server", ms_timeout).await { - v + let mut urls = v.split(","); + let a = urls.next().unwrap_or_default().to_owned(); + let b: Vec = urls.map(|x| x.to_owned()).collect(); + (a, b) } else { - Config::get_rendezvous_server() + ( + Config::get_rendezvous_server(), + Config::get_rendezvous_servers(), + ) } } @@ -636,3 +866,10 @@ pub async fn set_socks(value: config::Socks5Server) -> ResultType<()> { .await?; Ok(()) } + +#[tokio::main(flavor = "current_thread")] +pub async fn test_rendezvous_server() -> ResultType<()> { + let mut c = connect(1000, "").await?; + c.send(&Data::TestRendezvousServer).await?; + Ok(()) +} diff --git a/src/lan.rs b/src/lan.rs new file mode 100644 index 000000000..733e271a9 --- /dev/null +++ b/src/lan.rs @@ -0,0 +1,291 @@ +use hbb_common::{ + allow_err, + anyhow::bail, + config::{self, Config, RENDEZVOUS_PORT}, + log, + protobuf::Message as _, + rendezvous_proto::*, + tokio::{ + self, + sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, + }, + ResultType, +}; +use std::{ + collections::{HashMap, HashSet}, + net::{IpAddr, Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket}, + time::Instant, +}; + +type Message = RendezvousMessage; + +pub(super) fn start_listening() -> ResultType<()> { + let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); + let socket = std::net::UdpSocket::bind(addr)?; + socket.set_read_timeout(Some(std::time::Duration::from_millis(1000)))?; + log::info!("lan discovery listener started"); + loop { + let mut buf = [0; 2048]; + if let Ok((len, addr)) = socket.recv_from(&mut buf) { + if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { + match msg_in.union { + Some(rendezvous_message::Union::PeerDiscovery(p)) => { + if p.cmd == "ping" { + if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + let mut msg_out = Message::new(); + let peer = PeerDiscovery { + cmd: "pong".to_owned(), + mac: get_mac(&self_addr), + id: Config::get_id(), + hostname: whoami::hostname(), + username: crate::platform::get_active_username(), + platform: whoami::platform().to_string(), + ..Default::default() + }; + msg_out.set_peer_discovery(peer); + socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); + } + } + } + _ => {} + } + } + } + } +} + +#[tokio::main(flavor = "current_thread")] +pub async fn discover() -> ResultType<()> { + let sockets = send_query()?; + let rx = spawn_wait_responses(sockets); + handle_received_peers(rx).await?; + + log::info!("discover ping done"); + Ok(()) +} + +pub fn send_wol(id: String) { + let interfaces = default_net::get_interfaces(); + for peer in &config::LanPeers::load().peers { + if peer.id == id { + for (ip, mac) in peer.ip_mac.iter() { + if let Ok(mac_addr) = mac.parse() { + if let Ok(IpAddr::V4(ip)) = ip.parse() { + for interface in &interfaces { + for ipv4 in &interface.ipv4 { + if (u32::from(ipv4.addr) & u32::from(ipv4.netmask)) + == (u32::from(ip) & u32::from(ipv4.netmask)) + { + allow_err!(wol::send_wol( + mac_addr, + None, + Some(IpAddr::V4(ipv4.addr)) + )); + } + } + } + } + } + } + break; + } + } +} + +#[inline] +fn get_broadcast_port() -> u16 { + (RENDEZVOUS_PORT + 3) as _ +} + +fn get_mac(ip: &IpAddr) -> String { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if let Ok(mac) = get_mac_by_ip(ip) { + mac.to_string() + } else { + "".to_owned() + } + #[cfg(any(target_os = "android", target_os = "ios"))] + "".to_owned() +} + +fn get_all_ipv4s() -> ResultType> { + let mut ipv4s = Vec::new(); + for interface in default_net::get_interfaces() { + for ipv4 in &interface.ipv4 { + ipv4s.push(ipv4.addr.clone()); + } + } + Ok(ipv4s) +} + +fn get_mac_by_ip(ip: &IpAddr) -> ResultType { + for interface in default_net::get_interfaces() { + match ip { + IpAddr::V4(local_ipv4) => { + if interface.ipv4.iter().any(|x| x.addr == *local_ipv4) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + IpAddr::V6(local_ipv6) => { + if interface.ipv6.iter().any(|x| x.addr == *local_ipv6) { + if let Some(mac_addr) = interface.mac_addr { + return Ok(mac_addr.address()); + } + } + } + } + } + bail!("No interface found for ip: {:?}", ip); +} + +// Mainly from https://github.com/shellrow/default-net/blob/cf7ca24e7e6e8e566ed32346c9cfddab3f47e2d6/src/interface/shared.rs#L4 +fn get_ipaddr_by_peer(peer: A) -> Option { + let socket = match UdpSocket::bind("0.0.0.0:0") { + Ok(s) => s, + Err(_) => return None, + }; + + match socket.connect(peer) { + Ok(()) => (), + Err(_) => return None, + }; + + match socket.local_addr() { + Ok(addr) => return Some(addr.ip()), + Err(_) => return None, + }; +} + +fn create_broadcast_sockets() -> ResultType> { + let mut sockets = Vec::new(); + for v4_addr in get_all_ipv4s()? { + if v4_addr.is_private() { + let s = UdpSocket::bind(SocketAddr::from((v4_addr, 0)))?; + s.set_broadcast(true)?; + log::debug!("Bind socket to {}", &v4_addr); + sockets.push(s) + } + } + Ok(sockets) +} + +fn send_query() -> ResultType> { + let sockets = create_broadcast_sockets()?; + if sockets.is_empty() { + bail!("Found no ipv4 addresses"); + } + + let mut msg_out = Message::new(); + let peer = PeerDiscovery { + cmd: "ping".to_owned(), + ..Default::default() + }; + msg_out.set_peer_discovery(peer); + let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port())); + for socket in &sockets { + socket.send_to(&msg_out.write_to_bytes()?, maddr)?; + } + log::info!("discover ping sent"); + Ok(sockets) +} + +fn wait_response( + socket: UdpSocket, + timeout: Option, + tx: UnboundedSender, +) -> ResultType<()> { + let mut last_recv_time = Instant::now(); + + socket.set_read_timeout(timeout)?; + loop { + let mut buf = [0; 2048]; + if let Ok((len, addr)) = socket.recv_from(&mut buf) { + if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { + match msg_in.union { + Some(rendezvous_message::Union::PeerDiscovery(p)) => { + last_recv_time = Instant::now(); + if p.cmd == "pong" { + let mac = if let Some(self_addr) = get_ipaddr_by_peer(&addr) { + get_mac(&self_addr) + } else { + "".to_owned() + }; + + if mac != p.mac { + allow_err!(tx.send(config::DiscoveryPeer { + id: p.id.clone(), + ip_mac: HashMap::from([ + (addr.ip().to_string(), p.mac.clone(),) + ]), + username: p.username.clone(), + hostname: p.hostname.clone(), + platform: p.platform.clone(), + online: true, + })); + } + } + } + _ => {} + } + } + } + if last_recv_time.elapsed().as_millis() > 3_000 { + break; + } + } + Ok(()) +} + +fn spawn_wait_responses(sockets: Vec) -> UnboundedReceiver { + let (tx, rx) = unbounded_channel::<_>(); + for socket in sockets { + let tx_clone = tx.clone(); + std::thread::spawn(move || { + allow_err!(wait_response( + socket, + Some(std::time::Duration::from_millis(10)), + tx_clone + )); + }); + } + rx +} + +async fn handle_received_peers(mut rx: UnboundedReceiver) -> ResultType<()> { + let mut peers = config::LanPeers::load().peers; + peers.iter_mut().for_each(|peer| { + peer.online = false; + }); + + let mut response_set = HashSet::new(); + let mut last_write_time = Instant::now() - std::time::Duration::from_secs(4); + loop { + tokio::select! { + data = rx.recv() => match data { + Some(mut peer) => { + let in_response_set = !response_set.insert(peer.id.clone()); + if let Some(pos) = peers.iter().position(|x| x.is_same_peer(&peer) ) { + let peer1 = peers.remove(pos); + if in_response_set { + peer.ip_mac.extend(peer1.ip_mac); + peer.online = true; + } + } + peers.insert(0, peer); + if last_write_time.elapsed().as_millis() > 300 { + config::LanPeers::store(&peers); + last_write_time = Instant::now(); + } + } + None => { + break + } + } + } + } + + config::LanPeers::store(&peers); + Ok(()) +} diff --git a/src/lang.rs b/src/lang.rs index b37c4beb6..f01833718 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -8,6 +8,7 @@ mod de; mod en; mod eo; mod es; +mod hu; mod fr; mod id; mod it; @@ -28,6 +29,7 @@ lazy_static::lazy_static! { ("tw", "繁體中文"), ("pt", "Português"), ("es", "Español"), + ("hu", "Magyar"), ("ru", "Русский"), ("sk", "Slovenčina"), ("id", "Indonesia"), @@ -68,6 +70,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "tw" => tw::T.deref(), "de" => de::T.deref(), "es" => es::T.deref(), + "hu" => hu::T.deref(), "ru" => ru::T.deref(), "eo" => eo::T.deref(), "id" => id::T.deref(), diff --git a/src/lang/cn.rs b/src/lang/cn.rs index ba8e6d178..6e3d4d067 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "进入隐私模式"), ("Out privacy mode", "退出隐私模式"), ("Language", "语言"), + ("Keep RustDesk background service", "保持RustDesk后台服务"), + ("Ignore Battery Optimizations", "忽略电池优化"), + ("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"), + ("Random Password After Session", "会话结束更新随机密码"), + ("Keep", "保持"), + ("Update", "更新"), + ("Disable", "禁用"), + ("Onetime Password", "一次性口令"), + ("Verification Method", "密码验证方式"), + ("Enable security password", "启用安全密码"), + ("Enable random password", "启用随机密码"), + ("Enable onetime password", "启用一次性访问功能"), + ("Disable onetime password", "禁用一次性访问功能"), + ("Activate onetime password", "激活一次性访问功能"), + ("Set security password", "设置安全密码"), + ("Connection not allowed", "对方不允许连接"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 3c92ee71b..d9ff10416 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "v režimu soukromí"), ("Out privacy mode", "mimo režim soukromí"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 16b98359b..f9776e687 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "I databeskyttelsestilstand"), ("Out privacy mode", "Databeskyttelsestilstand fra"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index c2fd56002..c9cd243fb 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "im Datenschutzmodus"), ("Out privacy mode", "Datenschutzmodus aus"), ("Language", "Sprache"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/en.rs b/src/lang/en.rs index a4d11d415..a4923eb19 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -27,5 +27,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), ("doc_fix_wayland", "https://rustdesk.com/docs/en/manual/linux/#x11-required"), ("server_not_support", "Not yet supported by the server"), + ("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]"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index c9701d63a..a21833559 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", ""), ("Out privacy mode", ""), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 60cc05341..14ba0ab57 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "En modo de privacidad"), ("Out privacy mode", "Fuera del modo de privacidad"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index f447c5acd..f387b550b 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "en mode privé"), ("Out privacy mode", "hors mode de confidentialité"), ("Language", "Langue"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs new file mode 100644 index 000000000..e35ab8195 --- /dev/null +++ b/src/lang/hu.rs @@ -0,0 +1,304 @@ +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Státusz"), + ("Your Desktop", "A te asztalod"), + ("desk_tip", "Az asztalod ezzel az ID-vel, és jelszóval érhető el."), + ("Password", "Jelszó"), + ("Ready", "Kész"), + ("Established", "Létrejött"), + ("connecting_status", "Kapcsolódás a RustDesk hálózatához..."), + ("Enable Service", "A szolgáltatás bekapcsolása"), + ("Start Service", "Szolgáltatás Elindítása"), + ("Service is running", "A szolgáltatás fut"), + ("Service is not running", "A szolgáltatás nem fut"), + ("not_ready_status", "A RustDesk nem áll készen. Kérlek nézd meg a hálózati beállításaidat."), + ("Control Remote Desktop", "Távoli Asztal Kontrollálása"), + ("Transfer File", "Fájl Transzfer"), + ("Connect", "Kapcsolódás"), + ("Recent Sessions", "Korábbi Sessionök"), + ("Address Book", "Címköny"), + ("Confirmation", "Megerősít"), + ("TCP Tunneling", "TCP Tunneling"), + ("Remove", "Eltávolít"), + ("Refresh random password", "Véletlenszerű jelszó frissítése"), + ("Set your own password", "Saját jelszó beállítása"), + ("Enable Keyboard/Mouse", "Billentyűzet/Egér bekapcsolása"), + ("Enable Clipboard", "Megosztott vágólap bekapcsolása"), + ("Enable File Transfer", "Fájl transzer bekapcsolása"), + ("Enable TCP Tunneling", "TCP Tunneling bekapcsolása"), + ("IP Whitelisting", "IP Fehérlista"), + ("ID/Relay Server", "ID/Relay Szerver"), + ("Stop service", "Szolgáltatás Kikapcsolása"), + ("Change ID", "ID Megváltoztatása"), + ("Website", "Weboldal"), + ("About", "Rólunk: "), + ("Mute", "Némítás"), + ("Audio Input", "Audo Bemenet"), + ("Enhancements", "Javítások"), + ("Hardware Codec", "Hardware Kodek"), + ("Adaptive Bitrate", "Adaptív Bitrate"), + ("ID Server", "ID Szerver"), + ("Relay Server", "Relay Szerver"), + ("API Server", "API Szerver"), + ("invalid_http", "A címnek mindenképpen http(s)://-el kell kezdődnie."), + ("Invalid IP", "A megadott íp cím helytelen."), + ("id_change_tip", "Csak a-z, A-Z, 0-9 csoportokba tartozó karakterek, illetve a _ karakter van engedélyezve. Az első karakternek mindenképpen a-z, A-Z csoportokba kell esnie. Az ID hosszúsága 6-tól, 16 karakter."), + ("Invalid format", "Érvénytelen formátum"), + ("server_not_support", "Még nem támogatott a szerver által"), + ("Not available", "Nem érhető el"), + ("Too frequent", "Túl gyakori"), + ("Cancel", "Mégsem"), + ("Skip", "Kihagy"), + ("Close", "Bezár"), + ("Retry", "Újrapróbálkozás"), + ("OK", "OK"), + ("Password Required", "A jelszó megadása kötelező"), + ("Please enter your password", "Kérlek írd be a jelszavad"), + ("Remember password", "Kérlek emlékezz a jelszóra"), + ("Wrong Password", "Hibás jelszó"), + ("Do you want to enter again?", "Újra szeretnéd próbálni?"), + ("Connection Error", "Kapcsolódási Hiba"), + ("Error", "Hiba"), + ("Reset by the peer", "A kapcsolatot alaphelyzetbe állt"), + ("Connecting...", "Kapcsolódás..."), + ("Connection in progress. Please wait.", "A kapcsolódás folyamatban van. Kérlek várj."), + ("Please try 1 minute later", "Kérlek próbáld újra 1 perc múlva."), + ("Login Error", "Belépési Hiba"), + ("Successful", "Sikeres"), + ("Connected, waiting for image...", "Kapcsolódva, várakozás a képre..."), + ("Name", "Név"), + ("Type", "Fajta"), + ("Modified", "Módosított"), + ("Size", "Méret"), + ("Show Hidden Files", "Rejtett Fájlok Mutatása"), + ("Receive", "Kapni"), + ("Send", "Küldeni"), + ("Refresh File", "Fájlok Frissítése"), + ("Local", "Lokális"), + ("Remote", "Távoli"), + ("Remote Computer", "Távoli Számítógép"), + ("Local Computer", "Lokális Számítógép"), + ("Confirm Delete", "Törlés Megerősítése"), + ("Delete", "Törlés"), + ("Properties", "Tulajdonságok"), + ("Multi Select", "Több fájl kiválasztása"), + ("Empty Directory", "Üres Könyvtár"), + ("Not an empty directory", "Nem egy üres könyvtár"), + ("Are you sure you want to delete this file?", "Biztosan törölni szeretnéd ezt a fájlt?"), + ("Are you sure you want to delete this empty directory?", "Biztosan törölni szeretnéd ezt az üres könyvtárat?"), + ("Are you sure you want to delete the file of this directory?", "Biztosan törölni szeretnéd a fájlokat ebben a könyvtárban?"), + ("Do this for all conflicts", "Ezt tedd az összes konfliktussal"), + ("This is irreversible!", "Ez a folyamat visszafordíthatatlan!"), + ("Deleting", "A törlés folyamatban"), + ("files", "fájlok"), + ("Waiting", "Várunk"), + ("Finished", "Végzett"), + ("Speed", "Gyorsaság"), + ("Custom Image Quality", "Egyedi Képminőség"), + ("Privacy mode", "Inkognító mód"), + ("Block user input", "Felhasználói input blokkokolása"), + ("Unblock user input", "Felhasználói input blokkolásának feloldása"), + ("Adjust Window", "Ablakméret beállítása"), + ("Original", "Eredeti"), + ("Shrink", "Zsugorított"), + ("Stretch", "Nyújtott"), + ("Good image quality", "Jó képminőség"), + ("Balanced", "Balanszolt"), + ("Optimize reaction time", "Válaszidő optimializálása"), + ("Custom", "Egyedi"), + ("Show remote cursor", "Távoli kurzor mutatása"), + ("Show quality monitor", "Minőségi monitor mutatása"), + ("Disable clipboard", "Vágólap Kikapcsolása"), + ("Lock after session end", "Lezárás a session végén"), + ("Insert", "Beszúrás"), + ("Insert Lock", "Beszúrási Zároló"), + ("Refresh", "Frissítés"), + ("ID does not exist", "Ez az ID nem létezik"), + ("Failed to connect to rendezvous server", "A randevú szerverhez való kapcsolódás sikertelen"), + ("Please try later", "Kérlek próbád később"), + ("Remote desktop is offline", "A távoli asztal offline"), + ("Key mismatch", "Eltérés a kulcsokban"), + ("Timeout", "Időtúllépés"), + ("Failed to connect to relay server", "A relay szerverhez való kapcsolódás sikertelen"), + ("Failed to connect via rendezvous server", "A randevú szerverrel való kapcsolódás sikertelen"), + ("Failed to connect via relay server", "A relay szerverrel való kapcsolódás sikertelen"), + ("Failed to make direct connection to remote desktop", "A távoli asztalhoz való direkt kapcsolódás sikertelen"), + ("Set Password", "Jelszó Beállítása"), + ("OS Password", "Operációs Rendszer Jelszavának Beállítása"), + ("install_tip", "Az UAC (Felhasználói Fiók Felügyelet) miatt, a RustDesk nem fog rendesen funkcionálni mint távoli oldal néhány esetben. Hogy ezt kikerüld, vagy kikapcsold, kérlek nyomj rá a gombra ezalatt az üzenet alatt, hogy feltelepítsd a RustDesket a rendszerre."), + ("Click to upgrade", "Kattints a frissítés telepítéséhez"), + ("Click to download", "Kattints a letöltéshez"), + ("Click to update", "Kattints a frissítés letöltéséhez"), + ("Configure", "Beállítás"), + ("config_acc", "Ahhoz hogy a RustDesket távolról irányítani tudd, \"Elérhetőségi\" jogokat kell adnod a RustDesk-nek."), + ("config_screen", "Ahhoz hogy a RustDesket távolról irányítani tudd, \"Képernyőfelvételi\" jogokat kell adnod a RustDesk-nek."), + ("Installing ...", "Telepítés..."), + ("Install", "Telepítés"), + ("Installation", "Telepítés"), + ("Installation Path", "Telepítési útvonal"), + ("Create start menu shortcuts", "Start menu parancsikon létrehozása"), + ("Create desktop icon", "Asztali icon létrehozása"), + ("agreement_tip", "Azzal hogy elindítod a telepítést, elfogadod a licenszszerződést."), + ("Accept and Install", "Elfogadás és Telepítés"), + ("End-user license agreement", "Felhasználói licencszerződés"), + ("Generating ...", "Generálás..."), + ("Your installation is lower version.", "A jelenleg feltelepített verzió régebbi."), + ("not_close_tcp_tip", "Ne zárd be ezt az ablakot miközben a tunnelt használod"), + ("Listening ...", "Halgazózás..."), + ("Remote Host", "Távoli Host"), + ("Remote Port", "Távoli Port"), + ("Action", "Akció"), + ("Add", "Add"), + ("Local Port", "Lokális Port"), + ("setup_server_tip", "Egy gyorsabb kapcsolatért, kérlek hostolj egy saját szervert"), + ("Too short, at least 6 characters.", "Túl rövid, legalább 6 karakter"), + ("The confirmation is not identical.", "A megerősítés nem volt azonos"), + ("Permissions", "Jogok"), + ("Accept", "Elfogad"), + ("Dismiss", "Elutasít"), + ("Disconnect", "Szétkapcsolás"), + ("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"), + ("Connected", "Kapcsolódva"), + ("Direct and encrypted connection", "Direkt, és titkosított kapcsolat"), + ("Relayed and encrypted connection", "Relayelt, és titkosított kapcsolat"), + ("Direct and unencrypted connection", "Direkt, és nem titkosított kapcsolat"), + ("Relayed and unencrypted connection", "Rekayelt, és nem titkosított kapcsolat"), + ("Enter Remote ID", "Kérlek írd be a távoli ID-t"), + ("Enter your password", "Kérlek írd be a jelszavadat"), + ("Logging in...", "A belépés folyamatban..."), + ("Enable RDP session sharing", "Az RDP session megosztás engedélyezése"), + ("Auto Login", "Automatikus Login"), + ("Enable Direct IP Access", "Direkt IP elérés engedélyezése"), + ("Rename", "Átnevezés"), + ("Space", "Hely"), + ("Create Desktop Shortcut", "Asztali Parancsikon Lértehozása"), + ("Change Path", "Útvonal Megváltoztatása"), + ("Create Folder", "Mappa Készítése"), + ("Please enter the folder name", "Kérlek írd be a mappa nevét"), + ("Fix it", "Kérlek javísd meg"), + ("Warning", "Figyelem"), + ("Login screen using Wayland is not supported", "A belépési kijelzővel a Wayland használata nem támogatott"), + ("Reboot required", "Újraindítás szükséges"), + ("Unsupported display server ", "Nem támogatott kijelző szerver"), + ("x11 expected", "x11-re számítottt"), + ("Port", "Port"), + ("Settings", "Beállítások"), + ("Username", "Felhasználónév"), + ("Invalid port", "Érvénytelen port"), + ("Closed manually by the peer", "A kapcsolat manuálisan be lett zárva a másik fél álltal"), + ("Enable remote configuration modification", "Távoli konfiguráció módosítás engedélyezése"), + ("Run without install", "Futtatás feltelepítés nélkül"), + ("Always connected via relay", "Mindig relay által kapcsolódott"), + ("Always connect via relay", "Mindig relay által kapcsolódik"), + ("whitelist_tip", "Csak a fehérlistán lévő címek érhetnek el"), + ("Login", "Belépés"), + ("Logout", "Kilépés"), + ("Tags", "Tagok"), + ("Search ID", "ID keresés"), + ("Current Wayland display server is not supported", "Jelenleg a Wayland display szerver nem támogatott"), + ("whitelist_sep", "Ide jönnek a címek, vesző, pontosvessző, space, vagy új sorral elválasztva"), + ("Add ID", "ID Hozzáadása"), + ("Add Tag", "Tag Hozzáadása"), + ("Unselect all tags", "Az összes tag kiválasztásának törlése"), + ("Network error", "Hálózati hiba"), + ("Username missed", "A felhasználónév kimaradt"), + ("Password missed", "A jelszó kimaradt"), + ("Wrong credentials", "Hibás felhasználónév vagy jelszó"), + ("Edit Tag", "A tag(ok) szerkeztése"), + ("Unremember Password", "A jelszó megjegyzésének törlése"), + ("Favorites", "Kedvencek"), + ("Add to Favorites", "Hozzáadás a kedvencekhez"), + ("Remove from Favorites", "Eltávolítás a kedvencektől"), + ("Empty", "Üres"), + ("Invalid folder name", "Helytelen fájlnév"), + ("Socks5 Proxy", "Socks5-ös Proxy"), + ("Hostname", "Hostnév"), + ("Discovered", "Felfedezés"), + ("install_daemon_tip", "Ahhoz hogy a RustDesk bootkor elinduljon, telepítened kell a rendszer szolgáltatást."), + ("Remote ID", "Távoli ID"), + ("Paste", "Beillesztés"), + ("Paste here?", "Beillesztés ide?"), + ("Are you sure to close the connection?", "Biztos vagy benne hogy be szeretnéd zárni a kapcsolatot?"), + ("Download new version", "Új verzó letöltése"), + ("Touch mode", "Érintési mód bekapcsolása"), + ("Mouse mode", "Egérhasználati mód bekapcsolása"), + ("One-Finger Tap", "Egyújas érintés"), + ("Left Mouse", "Baloldali Egér"), + ("One-Long Tap", "Egy hosszú érintés"), + ("Two-Finger Tap", "Két újas érintés"), + ("Right Mouse", "Jobboldali Egér"), + ("One-Finger Move", "Egyújas mozgatás"), + ("Double Tap & Move", "Kétszeri érintés, és Mozgatás"), + ("Mouse Drag", "Egérrel való húzás"), + ("Three-Finger vertically", "Három ujj függőlegesen"), + ("Mouse Wheel", "Egérgörgő"), + ("Two-Finger Move", "Kátújas mozgatás"), + ("Canvas Move", "Nézet Mozgatása"), + ("Pinch to Zoom", "Húzd össze a nagyításhoz"), + ("Canvas Zoom", "Nézet Nagyítása"), + ("Reset canvas", "Nézet visszaállítása"), + ("No permission of file transfer", "Nincs jogod fájl transzer indításához"), + ("Note", "Megyjegyzés"), + ("Connection", "Kapcsolat"), + ("Share Screen", "Képernyőmegosztás"), + ("CLOSE", "LETILT"), + ("OPEN", "ENGEDÉLYEZ"), + ("Chat", "Chat"), + ("Total", "Összes"), + ("items", "Tárgyak"), + ("Selected", "Kiválasztott"), + ("Screen Capture", "Képernyőrögzítés"), + ("Input Control", "Input Kontrol"), + ("Audio Capture", "Audió Rögzítés"), + ("File Connection", "Fájlkapcsolat"), + ("Screen Connection", "Új Vizuális Kapcsolat"), + ("Do you accept?", "Elfogadod?"), + ("Open System Setting", "Rendszer beállítások megnyitása"), + ("How to get Android input permission?", "Hogyan állíthatok be Android input jogokat?"), + ("android_input_permission_tip1", "Ahhoz hogy egy távoli eszköz kontolálhassa az Android eszközödet egérrel vagy érintéssel, jogot kell adnod a RustDesk-nek, hogy használja az \"Elérhetőségi\" szolgáltatást."), + ("android_input_permission_tip2", "Kérlek navigálj a rendszer beállításaihoz, keresd meg vagy írd be hogy [Feltelepített Szolgáltatások], és kapcsold be a [RustDesk Input] szolgáltatást."), + ("android_new_connection_tip", "Új kontrollálási kérés érkezett, amely irányítani szeretné az eszközöded."), + ("android_service_will_start_tip", "A \"Képernyőrögzítés\" engedélyezése automatikusan elindítja majd a szolgáltatást, amely megengedi más eszközöknek hogy kérést kezdeményezzenek az eszköz felé."), + ("android_stop_service_tip", "A szolgáltatás bezárása automatikusan szétkapcsol minden létező kapcsolatot."), + ("android_version_audio_tip", "A jelenlegi Android verzió nem támogatja a hangrögzítést, kérlek frissíts legalább Android 10-re, vagy egy újabb verzióra."), + ("android_start_service_tip", "Nyomj a [Szolgáltatás Indítása] opcióra, vagy adj [Képernyőrözítési] jogot az applikációnak hogy elindítsd a képernyőmegosztó szolgáltatást."), + ("Account", "Fiók"), + ("Overwrite", "Felülírás"), + ("This file exists, skip or overwrite this file?", "Ez a fájl már létezik, skippeljünk, vagy felülírjuk ezt a fájlt?"), + ("Quit", "Kilépés"), + ("doc_mac_permission", "https://rustdesk.com/docs/hu/manual/mac/#enable-permissions"), + ("Help", "Segítség"), + ("Failed", "Sikertelen"), + ("Succeeded", "Sikeres"), + ("Someone turns on privacy mode, exit", "Valaki bekacsolta a privát módot, lépj ki"), + ("Unsupported", "Nem támogatott"), + ("Peer denied", "Elutasítva a távoli fél álltal"), + ("Please install plugins", "Kérlek telepítsd a pluginokat"), + ("Peer exit", "A távoli fél kilépett"), + ("Failed to turn off", "Nem tudtuk kikapcsolni"), + ("Turned off", "Kikapcsolva"), + ("In privacy mode", "Belépés a privát módba"), + ("Out privacy mode", "Kilépés a privát módból"), + ("Language", "Nyelv"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), + ].iter().cloned().collect(); +} diff --git a/src/lang/id.rs b/src/lang/id.rs index b1c6aaa1e..6bf69e476 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "Dalam mode privasi"), ("Out privacy mode", "Keluar dari mode privasi"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index c756d2218..778313d5b 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "In modalità privacy"), ("Out privacy mode", "Fuori modalità privacy"), ("Language", "Linguaggio"), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 946e47d4c..176833501 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "No modo de privacidade"), ("Out privacy mode", "Fora do modo de privacidade"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index d421bd85e..55d33c7b3 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -222,7 +222,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Remote ID", "Удаленный идентификатор"), ("Paste", "Вставить"), ("Paste here?", "Вставить сюда?"), - ("Are you sure to close the connection?", "Вы уверены, что хотите закрыть соединение?"), + ("Are you sure to close the connection?", "Вы уверены, что хотите завершить подключение?"), ("Download new version", "Скачать новую версию"), ("Touch mode", "Сенсорный режим"), ("Mouse mode", "Режим мыши"), @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "В режиме конфиденциальности"), ("Out privacy mode", "Выход из режима конфиденциальности"), ("Language", "Язык"), + ("Keep RustDesk background service", "Сохранить фоновый службу RustDesk"), + ("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"), + ("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек "), + ("Random Password After Session", "Случайный пароль после сеанса"), + ("Keep", "Оставить"), + ("Update", "Обновить"), + ("Disable", "Отключить"), + ("Onetime Password", "Одноразовый пароль"), + ("Verification Method", "Метод верификации"), + ("Enable security password", "Включить пароль безопасности"), + ("Enable random password", "Включить случайный пароль"), + ("Enable onetime password", "Включить одноразовый пароль"), + ("Disable onetime password", "Отключить одноразовый пароль"), + ("Activate onetime password", "Активировать одноразовый пароль"), + ("Set security password", "Задать пароль безопасности"), + ("Connection not allowed", "Подключение не разрешено"), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 8c60abaa9..332336007 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("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", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index ad963b323..7b39a2876 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", ""), ("Out privacy mode", ""), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index d12b2881e..02468201e 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "Gizlilik modunda"), ("Out privacy mode", "Gizlilik modu dışında"), ("Language", ""), + ("Keep RustDesk background service", ""), + ("Ignore Battery Optimizations", ""), + ("android_open_battery_optimizations_tip", ""), + ("Random Password After Session", ""), + ("Keep", ""), + ("Update", ""), + ("Disable", ""), + ("Onetime Password", ""), + ("Verification Method", ""), + ("Enable security password", ""), + ("Enable random password", ""), + ("Enable onetime password", ""), + ("Disable onetime password", ""), + ("Activate onetime password", ""), + ("Set security password", ""), + ("Connection not allowed", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 4f9813943..ea94d159c 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -284,5 +284,21 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("In privacy mode", "開啟隱私模式"), ("Out privacy mode", "退出隱私模式"), ("Language", "語言"), + ("Keep RustDesk background service", "保持RustDesk後台服務"), + ("Ignore Battery Optimizations", "忽略電池優化"), + ("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"), + ("Random Password After Session", "會話結束更新隨機密碼"), + ("Keep", "保持"), + ("Update", "更新"), + ("Disable", "禁用"), + ("Onetime Password", "一次性口令"), + ("Verification Method", "密碼驗證方式"), + ("Enable security password", "啟用安全密碼"), + ("Enable random password", "啟用隨機密碼"), + ("Enable onetime password", "啟用一次性訪問功能"), + ("Disable onetime password", "禁用一次性訪問功能"), + ("Activate onetime password", "激活一次性訪問功能"), + ("Set security password", "設置安全密碼"), + ("Connection not allowed", "對方不允許連接"), ].iter().cloned().collect(); } diff --git a/src/lib.rs b/src/lib.rs index 93cd67738..715a59b5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,8 @@ mod client; #[cfg(not(any(target_os = "ios")))] mod rendezvous_mediator; #[cfg(not(any(target_os = "ios")))] +mod lan; +#[cfg(not(any(target_os = "ios")))] pub use self::rendezvous_mediator::*; /// cbindgen:ignore pub mod common; diff --git a/src/main.rs b/src/main.rs index 2e2e4c8ac..c8f76cbd7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -108,6 +108,10 @@ fn main() { args.len() > 1, )); return; + } else if args[0] == "--extract" { + #[cfg(feature = "with_rc")] + hbb_common::allow_err!(crate::rc::extract_resources(&args[1])); + return; } } if args[0] == "--remove" { @@ -148,7 +152,7 @@ fn main() { return; } else if args[0] == "--password" { if args.len() == 2 { - ipc::set_password(args[1].to_owned()).unwrap(); + ipc::set_security_password(args[1].to_owned()).unwrap(); } return; } else if args[0] == "--check-hwcodec-config" { diff --git a/src/mobile.rs b/src/mobile.rs index 5b9651e54..d3adaaa79 100644 --- a/src/mobile.rs +++ b/src/mobile.rs @@ -591,7 +591,6 @@ impl Connection { log::debug!("Exit io_loop of id={}", session.id); } Err(err) => { - crate::common::test_rendezvous_server(); session.msgbox("error", "Connection Error", &err.to_string()); } } @@ -600,7 +599,7 @@ impl Connection { async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { if let Ok(msg_in) = Message::parse_from_bytes(&data) { match msg_in.union { - Some(message::Union::video_frame(vf)) => { + Some(message::Union::VideoFrame(vf)) => { if !self.first_frame { self.first_frame = true; } @@ -611,21 +610,21 @@ impl Connection { s.add(ZeroCopyBuffer(self.video_handler.rgb.clone())); } } - Some(message::Union::hash(hash)) => { + Some(message::Union::Hash(hash)) => { self.session.handle_hash(hash, peer).await; } - Some(message::Union::login_response(lr)) => match lr.union { - Some(login_response::Union::error(err)) => { + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { if !self.session.handle_login_error(&err) { return false; } } - Some(login_response::Union::peer_info(pi)) => { + Some(login_response::Union::PeerInfo(pi)) => { self.session.handle_peer_info(pi); } _ => {} }, - Some(message::Union::clipboard(cb)) => { + Some(message::Union::Clipboard(cb)) => { if !self.session.lc.read().unwrap().disable_clipboard { let content = if cb.compress { decompress(&cb.content) @@ -638,7 +637,7 @@ impl Connection { } } } - Some(message::Union::cursor_data(cd)) => { + Some(message::Union::CursorData(cd)) => { let colors = hbb_common::compress::decompress(&cd.colors); self.session.push_event( "cursor_data", @@ -655,18 +654,18 @@ impl Connection { ], ); } - Some(message::Union::cursor_id(id)) => { + Some(message::Union::CursorId(id)) => { self.session .push_event("cursor_id", vec![("id", &id.to_string())]); } - Some(message::Union::cursor_position(cp)) => { + Some(message::Union::CursorPosition(cp)) => { self.session.push_event( "cursor_position", vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())], ); } - Some(message::Union::file_response(fr)) => match fr.union { - Some(file_response::Union::dir(fd)) => { + Some(message::Union::FileResponse(fr)) => match fr.union { + Some(file_response::Union::Dir(fd)) => { let mut entries = fd.entries.to_vec(); if self.session.peer_platform() == "Windows" { fs::transform_windows_path(&mut entries); @@ -680,7 +679,7 @@ impl Connection { job.set_files(entries); } } - Some(file_response::Union::block(block)) => { + Some(file_response::Union::Block(block)) => { if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { if let Err(_err) = job.write(block, None).await { // to-do: add "skip" for writing job @@ -688,17 +687,17 @@ impl Connection { self.update_jobs_status(); } } - Some(file_response::Union::done(d)) => { + Some(file_response::Union::Done(d)) => { if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { job.modify_time(); fs::remove_job(d.id, &mut self.write_jobs); } self.handle_job_status(d.id, d.file_num, None); } - Some(file_response::Union::error(e)) => { + Some(file_response::Union::Error(e)) => { self.handle_job_status(e.id, e.file_num, Some(e.error)); } - Some(file_response::Union::digest(digest)) => { + Some(file_response::Union::Digest(digest)) => { if digest.is_upload { if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { if let Some(file) = job.files().get(digest.file_num as usize) { @@ -709,9 +708,9 @@ impl Connection { id: digest.id, file_num: digest.file_num, union: Some(if overwrite { - file_transfer_send_confirm_request::Union::offset_blk(0) + file_transfer_send_confirm_request::Union::OffsetBlk(0) } else { - file_transfer_send_confirm_request::Union::skip( + file_transfer_send_confirm_request::Union::Skip( true, ) }), @@ -741,7 +740,7 @@ impl Connection { let msg= new_send_confirm(FileTransferSendConfirmRequest { id: digest.id, file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::skip(true)), + union: Some(file_transfer_send_confirm_request::Union::Skip(true)), ..Default::default() }); self.session.send_msg(msg); @@ -753,9 +752,9 @@ impl Connection { id: digest.id, file_num: digest.file_num, union: Some(if overwrite { - file_transfer_send_confirm_request::Union::offset_blk(0) + file_transfer_send_confirm_request::Union::OffsetBlk(0) } else { - file_transfer_send_confirm_request::Union::skip(true) + file_transfer_send_confirm_request::Union::Skip(true) }), ..Default::default() }, @@ -775,7 +774,7 @@ impl Connection { FileTransferSendConfirmRequest { id: digest.id, file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)), + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), ..Default::default() }, ); @@ -792,15 +791,15 @@ impl Connection { } _ => {} }, - Some(message::Union::misc(misc)) => match misc.union { - Some(misc::Union::audio_format(f)) => { + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::AudioFormat(f)) => { self.audio_handler.handle_format(f); // } - Some(misc::Union::chat_message(c)) => { + Some(misc::Union::ChatMessage(c)) => { self.session .push_event("chat_client_mode", vec![("text", &c.text)]); } - Some(misc::Union::permission_info(p)) => { + Some(misc::Union::PermissionInfo(p)) => { log::info!("Change permission {:?} -> {}", p.permission, p.enabled); use permission_info::Permission; self.session.push_event( @@ -816,7 +815,7 @@ impl Connection { )], ); } - Some(misc::Union::switch_display(s)) => { + Some(misc::Union::SwitchDisplay(s)) => { self.video_handler.reset(); self.session.push_event( "switch_display", @@ -829,22 +828,22 @@ impl Connection { ], ); } - Some(misc::Union::close_reason(c)) => { + Some(misc::Union::CloseReason(c)) => { self.session.msgbox("error", "Connection Error", &c); return false; } _ => {} }, - Some(message::Union::test_delay(t)) => { + Some(message::Union::TestDelay(t)) => { self.session.handle_test_delay(t, peer).await; } - Some(message::Union::audio_frame(frame)) => { + Some(message::Union::AudioFrame(frame)) => { if !self.session.lc.read().unwrap().disable_audio { self.audio_handler.handle_frame(frame); } } - Some(message::Union::file_action(action)) => match action.union { - Some(file_action::Union::send_confirm(c)) => { + Some(message::Union::FileAction(action)) => match action.union { + Some(file_action::Union::SendConfirm(c)) => { if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { job.confirm(&c); } @@ -1030,9 +1029,9 @@ impl Connection { id, file_num, union: if need_override { - Some(file_transfer_send_confirm_request::Union::offset_blk(0)) + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) } else { - Some(file_transfer_send_confirm_request::Union::skip(true)) + Some(file_transfer_send_confirm_request::Union::Skip(true)) }, ..Default::default() }); @@ -1048,9 +1047,9 @@ impl Connection { id, file_num, union: if need_override { - Some(file_transfer_send_confirm_request::Union::offset_blk(0)) + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) } else { - Some(file_transfer_send_confirm_request::Union::skip(true)) + Some(file_transfer_send_confirm_request::Union::Skip(true)) }, ..Default::default() }); @@ -1452,7 +1451,7 @@ pub mod connection_manager { let mut req = FileTransferSendConfirmRequest { id, file_num, - union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)), + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), ..Default::default() }; let digest = FileTransferDigest { diff --git a/src/platform/linux.rs b/src/platform/linux.rs index efd6476b6..5a12f5ac0 100644 --- a/src/platform/linux.rs +++ b/src/platform/linux.rs @@ -1,4 +1,5 @@ use super::{CursorData, ResultType}; +pub use hbb_common::platform::linux::*; use hbb_common::{allow_err, bail, log}; use libc::{c_char, c_int, c_void}; use std::{ @@ -8,6 +9,7 @@ use std::{ Arc, }, }; + type Xdo = *const c_void; pub const PA_SAMPLE_RATE: u32 = 48000; @@ -143,7 +145,75 @@ pub fn get_cursor_data(hcursor: u64) -> ResultType { } } +fn start_uinput_service() { + use crate::server::uinput::service; + std::thread::spawn(|| { + service::start_service_control(); + }); + std::thread::spawn(|| { + service::start_service_keyboard(); + }); + std::thread::spawn(|| { + service::start_service_mouse(); + }); +} + +fn try_start_user_service(username: &str) { + if username == "" || username == "root" { + return; + } + + if let Ok(mut cur_username) = + run_cmds("ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1".to_owned()) + { + cur_username = cur_username.trim().to_owned(); + if cur_username != "root" && cur_username != username { + let _ = run_cmds(format!( + "systemctl --machine={}@.host --user stop rustdesk", + &cur_username + )); + } else if cur_username == username { + return; + } + } + + let _ = run_cmds(format!( + "systemctl --machine={}@.host --user start rustdesk", + username + )); +} + +fn try_stop_user_service() { + if let Ok(mut username) = + run_cmds("ps -ef | grep -E 'rustdesk +--server' | awk '{print $1}' | head -1".to_owned()) + { + username = username.trim().to_owned(); + if username != "root" { + let _ = run_cmds(format!( + "systemctl --machine={}@.host --user stop rustdesk", + &username + )); + } + } +} + +fn stop_server(server: &mut Option) { + if let Some(mut ps) = server.take() { + allow_err!(ps.kill()); + std::thread::sleep(std::time::Duration::from_millis(30)); + match ps.try_wait() { + Ok(Some(_status)) => {} + Ok(None) => { + let _res = ps.wait(); + } + Err(e) => log::error!("error attempting to wait: {e}"), + } + } +} + pub fn start_os_service() { + start_uinput_service(); + let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); let mut uid = "".to_owned(); @@ -157,85 +227,106 @@ pub fn start_os_service() { let mut cm0 = false; let mut last_restart = std::time::Instant::now(); while running.load(Ordering::SeqCst) { - let cm = get_cm(); - let tmp = get_active_userid(); - let mut start_new = false; - if tmp != uid && !tmp.is_empty() { - uid = tmp; - log::info!("uid of seat0: {}", uid); - let gdm = format!("/run/user/{}/gdm/Xauthority", uid); - let mut auth = get_env_tries("XAUTHORITY", &uid, 10); - if auth.is_empty() { - auth = if std::path::Path::new(&gdm).exists() { - gdm - } else { - let username = get_active_username(); - if username == "root" { - format!("/{}/.Xauthority", username) + let username = get_active_username(); + let is_wayland = current_is_wayland(); + + if username == "root" || !is_wayland { + // try stop user service + try_stop_user_service(); + + // try start subprocess "--server" + let cm = get_cm(); + let tmp = get_active_userid(); + let mut start_new = false; + if tmp != uid && !tmp.is_empty() { + uid = tmp; + log::info!("uid of seat0: {}", uid); + let gdm = format!("/run/user/{}/gdm/Xauthority", uid); + let mut auth = get_env_tries("XAUTHORITY", &uid, 10); + if auth.is_empty() { + auth = if std::path::Path::new(&gdm).exists() { + gdm } else { - let tmp = format!("/home/{}/.Xauthority", username); - if std::path::Path::new(&tmp).exists() { - tmp + let username = get_active_username(); + if username == "root" { + format!("/{}/.Xauthority", username) } else { - format!("/var/lib/{}/.Xauthority", username) + let tmp = format!("/home/{}/.Xauthority", username); + if std::path::Path::new(&tmp).exists() { + tmp + } else { + format!("/var/lib/{}/.Xauthority", username) + } } - } - }; - } - let mut d = get_env("DISPLAY", &uid); - if d.is_empty() { - d = get_display(); - } - if d.is_empty() { - d = ":0".to_owned(); - } - d = d.replace(&whoami::hostname(), "").replace("localhost", ""); - log::info!("DISPLAY: {}", d); - log::info!("XAUTHORITY: {}", auth); - std::env::set_var("XAUTHORITY", auth); - std::env::set_var("DISPLAY", d); - if let Some(ps) = server.as_mut() { - allow_err!(ps.kill()); - std::thread::sleep(std::time::Duration::from_millis(30)); - last_restart = std::time::Instant::now(); - } - } else if !cm - && ((cm0 && last_restart.elapsed().as_secs() > 60) - || last_restart.elapsed().as_secs() > 3600) - { - // restart server if new connections all closed, or every one hour, - // as a workaround to resolve "SpotUdp" (dns resolve) - // and x server get displays failure issue - if let Some(ps) = server.as_mut() { - allow_err!(ps.kill()); - std::thread::sleep(std::time::Duration::from_millis(30)); - last_restart = std::time::Instant::now(); - log::info!("restart server"); - } - } - if let Some(ps) = server.as_mut() { - match ps.try_wait() { - Ok(Some(_)) => { - server = None; - start_new = true; + }; } - _ => {} + let mut d = get_env("DISPLAY", &uid); + if d.is_empty() { + d = get_display(); + } + if d.is_empty() { + d = ":0".to_owned(); + } + d = d.replace(&whoami::hostname(), "").replace("localhost", ""); + log::info!("DISPLAY: {}", d); + log::info!("XAUTHORITY: {}", auth); + std::env::set_var("XAUTHORITY", auth); + std::env::set_var("DISPLAY", d); + if let Some(ps) = server.as_mut() { + allow_err!(ps.kill()); + std::thread::sleep(std::time::Duration::from_millis(30)); + last_restart = std::time::Instant::now(); + } + } else if !cm + && ((cm0 && last_restart.elapsed().as_secs() > 60) + || last_restart.elapsed().as_secs() > 3600) + { + // restart server if new connections all closed, or every one hour, + // as a workaround to resolve "SpotUdp" (dns resolve) + // and x server get displays failure issue + if let Some(ps) = server.as_mut() { + allow_err!(ps.kill()); + std::thread::sleep(std::time::Duration::from_millis(30)); + last_restart = std::time::Instant::now(); + log::info!("restart server"); + } + } + if let Some(ps) = server.as_mut() { + match ps.try_wait() { + Ok(Some(_)) => { + server = None; + start_new = true; + } + _ => {} + } + } else { + start_new = true; + } + if start_new { + match crate::run_me(vec!["--server"]) { + Ok(ps) => server = Some(ps), + Err(err) => { + log::error!("Failed to start server: {}", err); + } + } + } + cm0 = cm; + } else if username != "" { + if username != "gdm" { + // try kill subprocess "--server" + stop_server(&mut server); + + // try start user service + try_start_user_service(&username); } } else { - start_new = true; + try_stop_user_service(); + stop_server(&mut server); } - if start_new { - match crate::run_me(vec!["--server"]) { - Ok(ps) => server = Some(ps), - Err(err) => { - log::error!("Failed to start server: {}", err); - } - } - } - cm0 = cm; std::thread::sleep(std::time::Duration::from_millis(super::SERVICE_INTERVAL)); } + try_stop_user_service(); if let Some(ps) = server.take().as_mut() { allow_err!(ps.kill()); } @@ -246,17 +337,6 @@ pub fn get_active_userid() -> String { get_value_of_seat0(1) } -fn is_active(sid: &str) -> bool { - if let Ok(output) = std::process::Command::new("loginctl") - .args(vec!["show-session", "-p", "State", sid]) - .output() - { - String::from_utf8_lossy(&output.stdout).contains("active") - } else { - false - } -} - fn get_cm() -> bool { if let Ok(output) = std::process::Command::new("ps").args(vec!["aux"]).output() { for line in String::from_utf8_lossy(&output.stdout).lines() { @@ -312,89 +392,6 @@ fn get_display() -> String { last } -fn get_value_of_seat0(i: usize) -> String { - if let Ok(output) = std::process::Command::new("loginctl").output() { - for line in String::from_utf8_lossy(&output.stdout).lines() { - if line.contains("seat0") { - if let Some(sid) = line.split_whitespace().nth(0) { - if is_active(sid) { - if let Some(uid) = line.split_whitespace().nth(i) { - return uid.to_owned(); - } - } - } - } - } - } - - // some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73 - if let Ok(output) = std::process::Command::new("loginctl").output() { - for line in String::from_utf8_lossy(&output.stdout).lines() { - if let Some(sid) = line.split_whitespace().nth(0) { - let d = get_display_server_of_session(sid); - if is_active(sid) && d != "tty" { - if let Some(uid) = line.split_whitespace().nth(i) { - return uid.to_owned(); - } - } - } - } - } - - return "".to_owned(); -} - -pub fn get_display_server() -> String { - let session = get_value_of_seat0(0); - get_display_server_of_session(&session) -} - -fn get_display_server_of_session(session: &str) -> String { - if let Ok(output) = std::process::Command::new("loginctl") - .args(vec!["show-session", "-p", "Type", session]) - .output() - // Check session type of the session - { - let display_server = String::from_utf8_lossy(&output.stdout) - .replace("Type=", "") - .trim_end() - .into(); - if display_server == "tty" { - // If the type is tty... - if let Ok(output) = std::process::Command::new("loginctl") - .args(vec!["show-session", "-p", "TTY", session]) - .output() - // Get the tty number - { - let tty: String = String::from_utf8_lossy(&output.stdout) - .replace("TTY=", "") - .trim_end() - .into(); - if let Ok(xorg_results) = run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty)) - // And check if Xorg is running on that tty - { - if xorg_results.trim_end().to_string() != "" { - // If it is, manually return "x11", otherwise return tty - "x11".to_owned() - } else { - display_server - } - } else { - // If any of these commands fail just fall back to the display server - display_server - } - } else { - display_server - } - } else { - // If the session is not a tty, then just return the type as usual - display_server - } - } else { - "".to_owned() - } -} - pub fn is_login_wayland() -> bool { if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") { contents.contains("#WaylandEnable=false") @@ -601,13 +598,6 @@ pub fn is_installed() -> bool { true } -pub fn run_cmds(cmds: String) -> ResultType { - let output = std::process::Command::new("sh") - .args(vec!["-c", &cmds]) - .output()?; - Ok(String::from_utf8_lossy(&output.stdout).to_string()) -} - fn get_env_tries(name: &str, uid: &str, n: usize) -> String { for _ in 0..n { let x = get_env(name, uid); diff --git a/src/port_forward.rs b/src/port_forward.rs index afc65f236..6d7a25b68 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -120,21 +120,21 @@ async fn connect_and_login( Ok(Some(Ok(bytes))) => { let msg_in = Message::parse_from_bytes(&bytes)?; match msg_in.union { - Some(message::Union::hash(hash)) => { + Some(message::Union::Hash(hash)) => { interface.handle_hash(hash, &mut stream).await; } - Some(message::Union::login_response(lr)) => match lr.union { - Some(login_response::Union::error(err)) => { + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { interface.handle_login_error(&err); return Ok(None); } - Some(login_response::Union::peer_info(pi)) => { + Some(login_response::Union::PeerInfo(pi)) => { interface.handle_peer_info(pi); break; } _ => {} } - Some(message::Union::test_delay(t)) => { + Some(message::Union::TestDelay(t)) => { interface.handle_test_delay(t, &mut stream).await; } _ => {} @@ -183,7 +183,7 @@ async fn run_forward(forward: Framed, stream: Stream) -> }, res = stream.next() => { if let Some(Ok(bytes)) = res { - allow_err!(forward.send(bytes.into()).await); + allow_err!(forward.send(bytes).await); } else { break; } diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index a7f90b977..d4b76c991 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -2,7 +2,7 @@ use crate::server::{check_zombie, new as new_server, ServerPtr}; use hbb_common::{ allow_err, anyhow::bail, - config::{self, Config, REG_INTERVAL, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT}, + config::{Config, REG_INTERVAL, RENDEZVOUS_PORT, RENDEZVOUS_TIMEOUT}, futures::future::join_all, log, protobuf::Message as _, @@ -51,9 +51,12 @@ impl RendezvousMediator { check_zombie(); let server = new_server(); if Config::get_nat_type() == NatType::UNKNOWN_NAT as i32 { - crate::common::test_nat_type(); + crate::test_nat_type(); nat_tested = true; } + if !Config::get_option("stop-service").is_empty() { + crate::test_rendezvous_server(); + } let server_cloned = server.clone(); tokio::spawn(async move { direct_server(server_cloned).await; @@ -61,14 +64,14 @@ impl RendezvousMediator { #[cfg(not(any(target_os = "android", target_os = "ios")))] if crate::platform::is_installed() { std::thread::spawn(move || { - allow_err!(lan_discovery()); + allow_err!(super::lan::start_listening()); }); } loop { Config::reset_online(); if Config::get_option("stop-service").is_empty() { if !nat_tested { - crate::common::test_nat_type(); + crate::test_nat_type(); nat_tested = true; } let mut futs = Vec::new(); @@ -157,7 +160,7 @@ impl RendezvousMediator { Some(Ok((bytes, _))) => { if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { match msg_in.union { - Some(rendezvous_message::Union::register_peer_response(rpr)) => { + Some(rendezvous_message::Union::RegisterPeerResponse(rpr)) => { update_latency(); if rpr.request_pk { log::info!("request_pk received from {}", host); @@ -165,7 +168,7 @@ impl RendezvousMediator { continue; } } - Some(rendezvous_message::Union::register_pk_response(rpr)) => { + Some(rendezvous_message::Union::RegisterPkResponse(rpr)) => { update_latency(); match rpr.result.enum_value_or_default() { register_pk_response::Result::OK => { @@ -179,28 +182,28 @@ impl RendezvousMediator { _ => {} } } - Some(rendezvous_message::Union::punch_hole(ph)) => { + Some(rendezvous_message::Union::PunchHole(ph)) => { let rz = rz.clone(); let server = server.clone(); tokio::spawn(async move { allow_err!(rz.handle_punch_hole(ph, server).await); }); } - Some(rendezvous_message::Union::request_relay(rr)) => { + Some(rendezvous_message::Union::RequestRelay(rr)) => { let rz = rz.clone(); let server = server.clone(); tokio::spawn(async move { allow_err!(rz.handle_request_relay(rr, server).await); }); } - Some(rendezvous_message::Union::fetch_local_addr(fla)) => { + Some(rendezvous_message::Union::FetchLocalAddr(fla)) => { let rz = rz.clone(); let server = server.clone(); tokio::spawn(async move { allow_err!(rz.handle_intranet(fla, server).await); }); } - Some(rendezvous_message::Union::configure_update(cu)) => { + Some(rendezvous_message::Union::ConfigureUpdate(cu)) => { let v0 = Config::get_rendezvous_servers(); Config::set_option("rendezvous-servers".to_owned(), cu.rendezvous_servers.join(",")); Config::set_serial(cu.serial); @@ -367,7 +370,7 @@ impl RendezvousMediator { socket }; let mut msg_out = Message::new(); - use hbb_common::protobuf::ProtobufEnum; + use hbb_common::protobuf::Enum; let nat_type = NatType::from_i32(Config::get_nat_type()).unwrap_or(NatType::UNKNOWN_NAT); msg_out.set_punch_hole_sent(PunchHoleSent { socket_addr: ph.socket_addr, @@ -386,7 +389,7 @@ impl RendezvousMediator { async fn register_pk(&mut self, socket: &mut FramedSocket) -> ResultType<()> { let mut msg_out = Message::new(); let pk = Config::get_key_pair().1; - let uuid = crate::get_uuid(); + let uuid = hbb_common::get_uuid(); let id = Config::get_id(); self.last_id_pk_registry = id.clone(); msg_out.set_register_pk(RegisterPk { @@ -537,103 +540,3 @@ async fn direct_server(server: ServerPtr) { } } } - -#[inline] -pub fn get_broadcast_port() -> u16 { - (RENDEZVOUS_PORT + 3) as _ -} - -pub fn get_mac() -> String { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Ok(Some(mac)) = mac_address::get_mac_address() { - mac.to_string() - } else { - "".to_owned() - } - #[cfg(any(target_os = "android", target_os = "ios"))] - "".to_owned() -} - -fn lan_discovery() -> ResultType<()> { - let addr = SocketAddr::from(([0, 0, 0, 0], get_broadcast_port())); - let socket = std::net::UdpSocket::bind(addr)?; - socket.set_read_timeout(Some(std::time::Duration::from_millis(1000)))?; - log::info!("lan discovery listener started"); - loop { - let mut buf = [0; 2048]; - if let Ok((len, addr)) = socket.recv_from(&mut buf) { - if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { - match msg_in.union { - Some(rendezvous_message::Union::peer_discovery(p)) => { - if p.cmd == "ping" { - let mut msg_out = Message::new(); - let peer = PeerDiscovery { - cmd: "pong".to_owned(), - mac: get_mac(), - id: Config::get_id(), - hostname: whoami::hostname(), - username: crate::platform::get_active_username(), - platform: whoami::platform().to_string(), - ..Default::default() - }; - msg_out.set_peer_discovery(peer); - socket.send_to(&msg_out.write_to_bytes()?, addr).ok(); - } - } - _ => {} - } - } - } - } -} - -pub fn discover() -> ResultType<()> { - let addr = SocketAddr::from(([0, 0, 0, 0], 0)); - let socket = std::net::UdpSocket::bind(addr)?; - socket.set_broadcast(true)?; - let mut msg_out = Message::new(); - let peer = PeerDiscovery { - cmd: "ping".to_owned(), - ..Default::default() - }; - msg_out.set_peer_discovery(peer); - let maddr = SocketAddr::from(([255, 255, 255, 255], get_broadcast_port())); - socket.send_to(&msg_out.write_to_bytes()?, maddr)?; - log::info!("discover ping sent"); - let mut last_recv_time = Instant::now(); - let mut last_write_time = Instant::now(); - let mut last_write_n = 0; - // to-do: load saved peers, and update incrementally (then we can see offline) - let mut peers = Vec::new(); - let mac = get_mac(); - socket.set_read_timeout(Some(std::time::Duration::from_millis(10)))?; - loop { - let mut buf = [0; 2048]; - if let Ok((len, _)) = socket.recv_from(&mut buf) { - if let Ok(msg_in) = Message::parse_from_bytes(&buf[0..len]) { - match msg_in.union { - Some(rendezvous_message::Union::peer_discovery(p)) => { - last_recv_time = Instant::now(); - if p.cmd == "pong" { - if p.mac != mac { - peers.push((p.id, p.username, p.hostname, p.platform)); - } - } - } - _ => {} - } - } - } - if last_write_time.elapsed().as_millis() > 300 && last_write_n != peers.len() { - config::LanPeers::store(serde_json::to_string(&peers)?); - last_write_time = Instant::now(); - last_write_n = peers.len(); - } - if last_recv_time.elapsed().as_millis() > 3_000 { - break; - } - } - log::info!("discover ping done"); - config::LanPeers::store(serde_json::to_string(&peers)?); - Ok(()) -} diff --git a/src/server.rs b/src/server.rs index 3b68fc7dd..a71efaee2 100644 --- a/src/server.rs +++ b/src/server.rs @@ -7,7 +7,7 @@ use hbb_common::{ config::{Config, Config2, CONNECT_TIMEOUT, RELAY_PORT}, log, message_proto::*, - protobuf::{Message as _, ProtobufEnum}, + protobuf::{Enum, Message as _}, rendezvous_proto::*, socket_client, sodiumoxide::crypto::{box_, secretbox, sign}, @@ -24,6 +24,10 @@ pub mod audio_service; cfg_if::cfg_if! { if #[cfg(not(any(target_os = "android", target_os = "ios")))] { mod clipboard_service; +#[cfg(target_os = "linux")] +mod wayland; +#[cfg(target_os = "linux")] +pub mod uinput; pub mod input_service; } else { mod clipboard_service { @@ -140,7 +144,7 @@ pub async fn create_tcp_connection( Some(res) => { let bytes = res?; if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { - if let Some(message::Union::public_key(pk)) = msg_in.union { + if let Some(message::Union::PublicKey(pk)) = msg_in.union { if pk.asymmetric_value.len() == box_::PUBLICKEYBYTES { let nonce = box_::Nonce([0u8; box_::NONCEBYTES]); let mut pk_ = [0u8; box_::PUBLICKEYBYTES]; @@ -279,6 +283,8 @@ impl Drop for Server { for s in self.services.values() { s.join(); } + #[cfg(target_os = "linux")] + wayland::clear(); } } diff --git a/src/server/connection.rs b/src/server/connection.rs index fdeaac106..b8b410e5b 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -8,6 +8,7 @@ use crate::video_service; use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel}; use crate::{ipc, VERSION}; use hbb_common::fs::can_enable_overwrite_detection; +use hbb_common::password_security::password; use hbb_common::{ config::Config, fs, @@ -35,6 +36,7 @@ pub type Sender = mpsc::UnboundedSender<(Instant, Arc)>; lazy_static::lazy_static! { static ref LOGIN_FAILURES: Arc::>> = Default::default(); + static ref SESSIONS: Arc::>> = Default::default(); } pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0); pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0); @@ -53,6 +55,14 @@ enum MessageInput { BlockOff, } +#[derive(Clone, Debug)] +struct Session { + name: String, + session_id: u64, + last_recv_time: Arc>, + random_password: String, +} + pub struct Connection { inner: ConnInner, stream: super::Stream, @@ -80,6 +90,8 @@ pub struct Connection { video_ack_required: bool, peer_info: (String, String), api_server: String, + lr: LoginRequest, + last_recv_time: Arc>, } impl Subscriber for ConnInner { @@ -91,7 +103,7 @@ impl Subscriber for ConnInner { #[inline] fn send(&mut self, msg: Arc) { match &msg.union { - Some(message::Union::video_frame(_)) => { + Some(message::Union::VideoFrame(_)) => { self.tx_video.as_mut().map(|tx| { allow_err!(tx.send((Instant::now(), msg))); }); @@ -111,6 +123,7 @@ const H1: Duration = Duration::from_secs(3600); const MILLI1: Duration = Duration::from_millis(1); const SEND_TIMEOUT_VIDEO: u64 = 12_000; const SEND_TIMEOUT_OTHER: u64 = SEND_TIMEOUT_VIDEO * 10; +const SESSION_TIMEOUT: Duration = Duration::from_secs(30); impl Connection { pub async fn start( @@ -164,6 +177,8 @@ impl Connection { video_ack_required: false, peer_info: Default::default(), api_server: "".to_owned(), + lr: Default::default(), + last_recv_time: Arc::new(Mutex::new(Instant::now())), }; #[cfg(not(any(target_os = "android", target_os = "ios")))] tokio::spawn(async move { @@ -222,7 +237,8 @@ impl Connection { let mut msg_out = Message::new(); msg_out.set_misc(misc); conn.send(msg_out).await; - conn.on_close("Close requested from connection manager", false); + conn.on_close("Close requested from connection manager", false).await; + SESSIONS.lock().unwrap().remove(&conn.lr.my_id); break; } ipc::Data::ChatMessage{text} => { @@ -311,11 +327,12 @@ impl Connection { if let Some(res) = res { match res { Err(err) => { - conn.on_close(&err.to_string(), true); + conn.on_close(&err.to_string(), true).await; break; }, Ok(bytes) => { last_recv_time = Instant::now(); + *conn.last_recv_time.lock().unwrap() = Instant::now(); if let Ok(msg_in) = Message::parse_from_bytes(&bytes) { if !conn.on_message(msg_in).await { break; @@ -324,14 +341,14 @@ impl Connection { } } } else { - conn.on_close("Reset by the peer", true); + conn.on_close("Reset by the peer", true).await; break; } }, _ = conn.timer.tick() => { if !conn.read_jobs.is_empty() { if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut conn.stream).await { - conn.on_close(&err.to_string(), false); + conn.on_close(&err.to_string(), false).await; break; } } else { @@ -344,7 +361,7 @@ impl Connection { video_service::notify_video_frame_feched(id, Some(instant.into())); } if let Err(err) = conn.stream.send(&value as &Message).await { - conn.on_close(&err.to_string(), false); + conn.on_close(&err.to_string(), false).await; break; } }, @@ -354,7 +371,7 @@ impl Connection { if latency > 1000 { match &msg.union { - Some(message::Union::audio_frame(_)) => { + Some(message::Union::AudioFrame(_)) => { // log::info!("audio frame latency {}", instant.elapsed().as_secs_f32()); continue; } @@ -362,13 +379,13 @@ impl Connection { } } if let Err(err) = conn.stream.send(msg).await { - conn.on_close(&err.to_string(), false); + conn.on_close(&err.to_string(), false).await; break; } }, _ = test_delay_timer.tick() => { if last_recv_time.elapsed() >= SEC30 { - conn.on_close("Timeout", true); + conn.on_close("Timeout", true).await; break; } let time = crate::get_time(); @@ -398,8 +415,9 @@ impl Connection { video_service::notify_video_frame_feched(id, None); scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); video_service::VIDEO_QOS.lock().unwrap().reset(); + password::after_session(conn.authorized); if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await { - conn.on_close(&err.to_string(), false); + conn.on_close(&err.to_string(), false).await; } conn.post_audit(json!({ @@ -493,7 +511,7 @@ impl Connection { res = self.stream.next() => { if let Some(res) = res { last_recv_time = Instant::now(); - timeout(SEND_TIMEOUT_OTHER, forward.send(res?.into())).await??; + timeout(SEND_TIMEOUT_OTHER, forward.send(res?)).await??; } else { bail!("Stream reset by the peer"); } @@ -572,7 +590,7 @@ impl Connection { let url = self.api_server.clone(); let mut v = v; v["id"] = json!(Config::get_id()); - v["uuid"] = json!(base64::encode(crate::get_uuid())); + v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); v["Id"] = json!(self.inner.id); tokio::spawn(async move { allow_err!(Self::post_audit_async(url, v).await); @@ -629,9 +647,9 @@ impl Connection { #[cfg(target_os = "linux")] if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() { let dtype = crate::platform::linux::get_display_server(); - if dtype != "x11" { + if dtype != "x11" && dtype != "wayland" { res.set_error(format!( - "Unsupported display server type {}, x11 expected", + "Unsupported display server type {}, x11 or wayland expected", dtype )); let mut msg_out = Message::new(); @@ -667,7 +685,7 @@ impl Connection { res.set_peer_info(pi); } else { try_activate_screen(); - match video_service::get_displays() { + match super::video_service::get_displays().await { Err(err) => { res.set_error(format!("X11 error: {}", err)); } @@ -779,8 +797,77 @@ impl Connection { self.tx_input.send(MessageInput::Key((msg, press))).ok(); } + fn validate_one_password(&self, password: String) -> bool { + if password.len() == 0 { + return false; + } + let mut hasher = Sha256::new(); + hasher.update(password); + hasher.update(&self.hash.salt); + let mut hasher2 = Sha256::new(); + hasher2.update(&hasher.finalize()[..]); + hasher2.update(&self.hash.challenge); + hasher2.finalize()[..] == self.lr.password[..] + } + + fn validate_password(&mut self) -> bool { + if password::security_enabled() { + if self.validate_one_password(Config::get_security_password()) { + return true; + } + } + if password::random_password_valid() { + let password = password::random_password(); + if self.validate_one_password(password.clone()) { + if password::onetime_password_activated() { + password::set_onetime_password_activated(false); + } + SESSIONS.lock().unwrap().insert( + self.lr.my_id.clone(), + Session { + name: self.lr.my_name.clone(), + session_id: self.lr.session_id, + last_recv_time: self.last_recv_time.clone(), + random_password: password, + }, + ); + return true; + } + } + false + } + + fn is_of_recent_session(&mut self) -> bool { + let session = SESSIONS + .lock() + .unwrap() + .get(&self.lr.my_id) + .map(|s| s.to_owned()); + if let Some(session) = session { + if session.name == self.lr.my_name + && session.session_id == self.lr.session_id + && !self.lr.password.is_empty() + && self.validate_one_password(session.random_password.clone()) + && session.last_recv_time.lock().unwrap().elapsed() < SESSION_TIMEOUT + { + SESSIONS.lock().unwrap().insert( + self.lr.my_id.clone(), + Session { + name: self.lr.my_name.clone(), + session_id: self.lr.session_id, + last_recv_time: self.last_recv_time.clone(), + random_password: session.random_password, + }, + ); + return true; + } + } + false + } + async fn on_message(&mut self, msg: Message) -> bool { - if let Some(message::Union::login_request(lr)) = msg.union { + if let Some(message::Union::LoginRequest(lr)) = msg.union { + self.lr = lr.clone(); if let Some(o) = lr.option.as_ref() { self.update_option(o).await; if let Some(q) = o.video_codec_state.clone().take() { @@ -805,7 +892,7 @@ impl Connection { return true; } match lr.union { - Some(login_request::Union::file_transfer(ft)) => { + Some(login_request::Union::FileTransfer(ft)) => { if !Config::get_option("enable-file-transfer").is_empty() { self.send_login_error("No permission of file transfer") .await; @@ -814,7 +901,7 @@ impl Connection { } self.file_transfer = Some((ft.dir, ft.show_hidden)); } - Some(login_request::Union::port_forward(mut pf)) => { + Some(login_request::Union::PortForward(mut pf)) => { if !Config::get_option("enable-tunnel").is_empty() { self.send_login_error("No permission of IP tunneling").await; sleep(1.).await; @@ -851,15 +938,19 @@ impl Connection { } if !crate::is_ip(&lr.username) && lr.username != Config::get_id() { self.send_login_error("Offline").await; + } else if self.is_of_recent_session() { + self.try_start_cm(lr.my_id, lr.my_name, true); + self.send_logon_response().await; + if self.port_forward_socket.is_some() { + return false; + } } else if lr.password.is_empty() { self.try_start_cm(lr.my_id, lr.my_name, false); } else { - let mut hasher = Sha256::new(); - hasher.update(&Config::get_password()); - hasher.update(&self.hash.salt); - let mut hasher2 = Sha256::new(); - hasher2.update(&hasher.finalize()[..]); - hasher2.update(&self.hash.challenge); + if password::passwords().len() == 0 { + self.send_login_error("Connection not allowed").await; + return false; + } let mut failure = LOGIN_FAILURES .lock() .unwrap() @@ -872,7 +963,7 @@ impl Connection { .await; } else if time == failure.0 && failure.1 > 6 { self.send_login_error("Please try 1 minute later").await; - } else if hasher2.finalize()[..] != lr.password[..] { + } else if !self.validate_password() { if failure.0 == time { failure.1 += 1; failure.2 += 1; @@ -898,7 +989,7 @@ impl Connection { } } } - } else if let Some(message::Union::test_delay(t)) = msg.union { + } else if let Some(message::Union::TestDelay(t)) = msg.union { if t.from_client { let mut msg_out = Message::new(); msg_out.set_test_delay(t); @@ -913,7 +1004,7 @@ impl Connection { } } else if self.authorized { match msg.union { - Some(message::Union::mouse_event(me)) => { + Some(message::Union::MouseEvent(me)) => { #[cfg(any(target_os = "android", target_os = "ios"))] if let Err(e) = call_main_service_mouse_input(me.mask, me.x, me.y) { log::debug!("call_main_service_mouse_input fail:{}", e); @@ -928,7 +1019,7 @@ impl Connection { self.input_mouse(me, self.inner.id()); } } - Some(message::Union::key_event(me)) => { + Some(message::Union::KeyEvent(me)) => { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.keyboard { if is_enter(&me) { @@ -944,8 +1035,8 @@ impl Connection { }; if is_press { match me.union { - Some(key_event::Union::unicode(_)) - | Some(key_event::Union::seq(_)) => { + Some(key_event::Union::Unicode(_)) + | Some(key_event::Union::Seq(_)) => { self.input_key(me, false); } _ => { @@ -957,14 +1048,14 @@ impl Connection { } } } - Some(message::Union::clipboard(cb)) => + Some(message::Union::Clipboard(cb)) => { #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.clipboard { update_clipboard(cb, None); } } - Some(message::Union::cliprdr(_clip)) => { + Some(message::Union::Cliprdr(_clip)) => { if self.file_transfer_enabled() { #[cfg(windows)] if let Some(clip) = msg_2_clip(_clip) { @@ -972,13 +1063,13 @@ impl Connection { } } } - Some(message::Union::file_action(fa)) => { + Some(message::Union::FileAction(fa)) => { if self.file_transfer.is_some() { match fa.union { - Some(file_action::Union::read_dir(rd)) => { + Some(file_action::Union::ReadDir(rd)) => { self.read_dir(&rd.path, rd.include_hidden); } - Some(file_action::Union::all_files(f)) => { + Some(file_action::Union::AllFiles(f)) => { match fs::get_recursive_files(&f.path, f.include_hidden) { Err(err) => { self.send(fs::new_error(f.id, err, -1)).await; @@ -988,7 +1079,7 @@ impl Connection { } } } - Some(file_action::Union::send(s)) => { + Some(file_action::Union::Send(s)) => { let id = s.id; let od = can_enable_overwrite_detection(get_version_number(VERSION)); @@ -1013,7 +1104,7 @@ impl Connection { } } } - Some(file_action::Union::receive(r)) => { + Some(file_action::Union::Receive(r)) => { self.send_fs(ipc::FS::NewWrite { path: r.path, id: r.id, @@ -1026,31 +1117,31 @@ impl Connection { .collect(), }); } - Some(file_action::Union::remove_dir(d)) => { + Some(file_action::Union::RemoveDir(d)) => { self.send_fs(ipc::FS::RemoveDir { path: d.path, id: d.id, recursive: d.recursive, }); } - Some(file_action::Union::remove_file(f)) => { + Some(file_action::Union::RemoveFile(f)) => { self.send_fs(ipc::FS::RemoveFile { path: f.path, id: f.id, file_num: f.file_num, }); } - Some(file_action::Union::create(c)) => { + Some(file_action::Union::Create(c)) => { self.send_fs(ipc::FS::CreateDir { path: c.path, id: c.id, }); } - Some(file_action::Union::cancel(c)) => { + Some(file_action::Union::Cancel(c)) => { self.send_fs(ipc::FS::CancelWrite { id: c.id }); fs::remove_job(c.id, &mut self.read_jobs); } - Some(file_action::Union::send_confirm(r)) => { + Some(file_action::Union::SendConfirm(r)) => { if let Some(job) = fs::get_job(r.id, &mut self.read_jobs) { job.confirm(&r); } @@ -1059,8 +1150,8 @@ impl Connection { } } } - Some(message::Union::file_response(fr)) => match fr.union { - Some(file_response::Union::block(block)) => { + Some(message::Union::FileResponse(fr)) => match fr.union { + Some(file_response::Union::Block(block)) => { self.send_fs(ipc::FS::WriteBlock { id: block.id, file_num: block.file_num, @@ -1068,13 +1159,13 @@ impl Connection { compressed: block.compressed, }); } - Some(file_response::Union::done(d)) => { + Some(file_response::Union::Done(d)) => { self.send_fs(ipc::FS::WriteDone { id: d.id, file_num: d.file_num, }); } - Some(file_response::Union::digest(d)) => self.send_fs(ipc::FS::CheckDigest { + Some(file_response::Union::Digest(d)) => self.send_fs(ipc::FS::CheckDigest { id: d.id, file_num: d.file_num, file_size: d.file_size, @@ -1083,27 +1174,32 @@ impl Connection { }), _ => {} }, - Some(message::Union::misc(misc)) => match misc.union { - Some(misc::Union::switch_display(s)) => { - video_service::switch_display(s.display); + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::SwitchDisplay(s)) => { + video_service::switch_display(s.display).await; } - Some(misc::Union::chat_message(c)) => { + Some(misc::Union::ChatMessage(c)) => { self.send_to_cm(ipc::Data::ChatMessage { text: c.text }); } - Some(misc::Union::option(o)) => { + Some(misc::Union::Option(o)) => { self.update_option(&o).await; } - Some(misc::Union::refresh_video(r)) => { + Some(misc::Union::RefreshVideo(r)) => { if r { - video_service::refresh(); + super::video_service::refresh(); } } - Some(misc::Union::video_received(_)) => { + Some(misc::Union::VideoReceived(_)) => { video_service::notify_video_frame_feched( self.inner.id, Some(Instant::now().into()), ); } + Some(misc::Union::CloseReason(_)) => { + self.on_close("Peer close", true).await; + SESSIONS.lock().unwrap().remove(&self.lr.my_id); + return false; + } _ => {} }, _ => {} @@ -1258,14 +1354,14 @@ impl Connection { } } - fn on_close(&mut self, reason: &str, lock: bool) { + async fn on_close(&mut self, reason: &str, lock: bool) { if let Some(s) = self.server.upgrade() { s.write().unwrap().remove_connection(&self.inner); } log::info!("#{} Connection closed: {}", self.inner.id(), reason); if lock && self.lock_after_session_end && self.keyboard { #[cfg(not(any(target_os = "android", target_os = "ios")))] - lock_screen(); + lock_screen().await; } self.tx_to_cm.send(ipc::Data::Close).ok(); self.port_forward_socket.take(); diff --git a/src/server/input_service.rs b/src/server/input_service.rs index 252139adb..6103274ee 100644 --- a/src/server/input_service.rs +++ b/src/server/input_service.rs @@ -2,7 +2,7 @@ use super::*; #[cfg(target_os = "macos")] use dispatch::Queue; use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable}; -use hbb_common::{config::COMPRESS_LEVEL, protobuf::ProtobufEnumOrUnknown}; +use hbb_common::{config::COMPRESS_LEVEL, protobuf::ProtobufEnumOrUnknown, protobuf::EnumOrUnknown}; use rdev::{simulate, EventType, Key as RdevKey}; use std::{ convert::TryFrom, @@ -69,7 +69,7 @@ impl Subscriber for MouseCursorSub { #[inline] fn send(&mut self, msg: Arc) { - if let Some(message::Union::cursor_data(cd)) = &msg.union { + if let Some(message::Union::CursorData(cd)) = &msg.union { if let Some(msg) = self.cached.get(&cd.id) { self.inner.send(msg.clone()); } else { @@ -188,6 +188,26 @@ lazy_static::lazy_static! { static ref IS_SERVER: bool = std::env::args().nth(1) == Some("--server".to_owned()); } +#[cfg(target_os = "linux")] +pub async fn set_uinput() -> ResultType<()> { + // Keyboard and mouse both open /dev/uinput + // TODO: Make sure there's no race + let keyboard = super::uinput::client::UInputKeyboard::new().await?; + log::info!("UInput keyboard created"); + let mouse = super::uinput::client::UInputMouse::new().await?; + log::info!("UInput mouse created"); + + let mut en = ENIGO.lock().unwrap(); + en.set_uinput_keyboard(Some(Box::new(keyboard))); + en.set_uinput_mouse(Some(Box::new(mouse))); + Ok(()) +} + +#[cfg(target_os = "linux")] +pub async fn set_uinput_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> { + super::uinput::client::set_resolution(minx, maxx, miny, maxy).await +} + pub fn is_left_up(evt: &MouseEvent) -> bool { let buttons = evt.mask >> 3; let evt_type = evt.mask & 0x7; @@ -300,12 +320,12 @@ fn fix_key_down_timeout(force: bool) { // e.g. current state of ctrl is down, but ctrl not in modifier, we should change ctrl to up, to make modifier state sync between remote and local #[inline] fn fix_modifier( - modifiers: &[ProtobufEnumOrUnknown], + modifiers: &[EnumOrUnknown], key0: ControlKey, key1: Key, en: &mut Enigo, ) { - if get_modifier_state(key1, en) && !modifiers.contains(&ProtobufEnumOrUnknown::new(key0)) { + if get_modifier_state(key1, en) && !modifiers.contains(&EnumOrUnknown::new(key0)) { #[cfg(windows)] if key0 == ControlKey::Control && get_modifier_state(Key::Alt, en) { // AltGr case @@ -316,7 +336,7 @@ fn fix_modifier( } } -fn fix_modifiers(modifiers: &[ProtobufEnumOrUnknown], en: &mut Enigo, ck: i32) { +fn fix_modifiers(modifiers: &[EnumOrUnknown], en: &mut Enigo, ck: i32) { if ck != ControlKey::Shift.value() { fix_modifier(modifiers, ControlKey::Shift, Key::Shift, en); } @@ -431,7 +451,7 @@ fn handle_mouse_(evt: &MouseEvent, conn: i32) { } pub fn is_enter(evt: &KeyEvent) -> bool { - if let Some(key_event::Union::control_key(ck)) = evt.union { + if let Some(key_event::Union::ControlKey(ck)) = evt.union { if ck.value() == ControlKey::Return.value() || ck.value() == ControlKey::NumpadEnter.value() { return true; @@ -440,7 +460,7 @@ pub fn is_enter(evt: &KeyEvent) -> bool { return false; } -pub fn lock_screen() { +pub async fn lock_screen() { cfg_if::cfg_if! { if #[cfg(target_os = "linux")] { // xdg_screensaver lock not work on Linux from our service somehow @@ -477,7 +497,7 @@ pub fn lock_screen() { crate::platform::lock_screen(); } } - super::video_service::switch_to_primary(); + super::video_service::switch_to_primary().await; } lazy_static::lazy_static! { @@ -556,7 +576,6 @@ lazy_static::lazy_static! { (ControlKey::Equals, Key::Equals), (ControlKey::NumpadEnter, Key::NumpadEnter), (ControlKey::RAlt, Key::RightAlt), - (ControlKey::RWin, Key::RWin), (ControlKey::RControl, Key::RightControl), (ControlKey::RShift, Key::RightShift), ].iter().map(|(a, b)| (a.value(), b.clone())).collect(); @@ -656,7 +675,7 @@ fn legacy_keyboard_map(evt: &KeyEvent) { #[cfg(windows)] let mut has_numlock = false; if evt.down { - let ck = if let Some(key_event::Union::control_key(ck)) = evt.union { + let ck = if let Some(key_event::Union::ControlKey(ck)) = evt.union { ck.value() } else { -1 @@ -711,7 +730,7 @@ fn legacy_keyboard_map(evt: &KeyEvent) { } } match evt.union { - Some(key_event::Union::control_key(ck)) => { + Some(key_event::Union::ControlKey(ck)) => { if let Some(key) = KEY_MAP.get(&ck.value()) { #[cfg(windows)] if let Some(_) = NUMPAD_KEY_MAP.get(&ck.value()) { @@ -737,10 +756,10 @@ fn legacy_keyboard_map(evt: &KeyEvent) { allow_err!(send_sas()); }); } else if ck.value() == ControlKey::LockScreen.value() { - lock_screen(); + lock_screen_2(); } } - Some(key_event::Union::chr(chr)) => { + Some(key_event::Union::Chr(chr)) => { if evt.down { if en.key_down(get_layout(chr)).is_ok() { KEYS_DOWN @@ -766,12 +785,12 @@ fn legacy_keyboard_map(evt: &KeyEvent) { .remove(&(chr as u64 + KEY_CHAR_START)); } } - Some(key_event::Union::unicode(chr)) => { + Some(key_event::Union::Unicode(chr)) => { if let Ok(chr) = char::try_from(chr) { en.key_sequence(&chr.to_string()); } } - Some(key_event::Union::seq(ref seq)) => { + Some(key_event::Union::Seq(ref seq)) => { en.key_sequence(&seq); } _ => {} @@ -805,6 +824,11 @@ fn handle_key_(evt: &KeyEvent) { } } +#[tokio::main(flavor = "current_thread")] +async fn lock_screen_2() { + lock_screen().await; +} + #[tokio::main(flavor = "current_thread")] async fn send_sas() -> ResultType<()> { let mut stream = crate::ipc::connect(1000, crate::POSTFIX_SERVICE).await?; diff --git a/src/server/uinput.rs b/src/server/uinput.rs new file mode 100644 index 000000000..7a6d47cff --- /dev/null +++ b/src/server/uinput.rs @@ -0,0 +1,651 @@ +use crate::ipc::{self, new_listener, Connection, Data, DataKeyboard, DataMouse}; +use enigo::{Key, KeyboardControllable, MouseButton, MouseControllable}; +use evdev::{ + uinput::{VirtualDevice, VirtualDeviceBuilder}, + AttributeSet, EventType, InputEvent, +}; +use hbb_common::{allow_err, bail, log, tokio, ResultType}; + +static IPC_CONN_TIMEOUT: u64 = 1000; +static IPC_REQUEST_TIMEOUT: u64 = 1000; +static IPC_POSTFIX_KEYBOARD: &str = "_uinput_keyboard"; +static IPC_POSTFIX_MOUSE: &str = "_uinput_mouse"; +static IPC_POSTFIX_CONTROL: &str = "_uinput_control"; + +pub mod client { + use super::*; + + pub struct UInputKeyboard { + conn: Connection, + } + + impl UInputKeyboard { + pub async fn new() -> ResultType { + let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_KEYBOARD).await?; + Ok(Self { conn }) + } + + #[tokio::main(flavor = "current_thread")] + async fn send(&mut self, data: Data) -> ResultType<()> { + self.conn.send(&data).await + } + + #[tokio::main(flavor = "current_thread")] + async fn send_get_key_state(&mut self, data: Data) -> ResultType { + self.conn.send(&data).await?; + + match self.conn.next_timeout(IPC_REQUEST_TIMEOUT).await { + Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(state)))) => { + Ok(state) + } + Ok(Some(resp)) => { + // FATAL error!!! + bail!( + "FATAL error, wait keyboard result other response: {:?}", + &resp + ); + } + Ok(None) => { + // FATAL error!!! + // Maybe wait later + bail!("FATAL error, wait keyboard result, receive None",); + } + Err(e) => { + // FATAL error!!! + bail!( + "FATAL error, wait keyboard result timeout {}, {}", + &e, + IPC_REQUEST_TIMEOUT + ); + } + } + } + } + + impl KeyboardControllable for UInputKeyboard { + fn get_key_state(&mut self, key: Key) -> bool { + match self.send_get_key_state(Data::Keyboard(DataKeyboard::GetKeyState(key))) { + Ok(state) => state, + Err(e) => { + // unreachable!() + log::error!("Failed to get key state {}", &e); + false + } + } + } + + fn key_sequence(&mut self, sequence: &str) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::Sequence(sequence.to_string())))); + } + + // TODO: handle error??? + fn key_down(&mut self, key: Key) -> enigo::ResultType { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyDown(key)))); + Ok(()) + } + fn key_up(&mut self, key: Key) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyUp(key)))); + } + fn key_click(&mut self, key: Key) { + allow_err!(self.send(Data::Keyboard(DataKeyboard::KeyClick(key)))); + } + } + + pub struct UInputMouse { + conn: Connection, + } + + impl UInputMouse { + pub async fn new() -> ResultType { + let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_MOUSE).await?; + Ok(Self { conn }) + } + + #[tokio::main(flavor = "current_thread")] + async fn send(&mut self, data: Data) -> ResultType<()> { + self.conn.send(&data).await + } + } + + impl MouseControllable for UInputMouse { + fn mouse_move_to(&mut self, x: i32, y: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::MoveTo(x, y)))); + } + fn mouse_move_relative(&mut self, x: i32, y: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::MoveRelative(x, y)))); + } + // TODO: handle error??? + fn mouse_down(&mut self, button: MouseButton) -> enigo::ResultType { + allow_err!(self.send(Data::Mouse(DataMouse::Down(button)))); + Ok(()) + } + fn mouse_up(&mut self, button: MouseButton) { + allow_err!(self.send(Data::Mouse(DataMouse::Up(button)))); + } + fn mouse_click(&mut self, button: MouseButton) { + allow_err!(self.send(Data::Mouse(DataMouse::Click(button)))); + } + fn mouse_scroll_x(&mut self, length: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::ScrollX(length)))); + } + fn mouse_scroll_y(&mut self, length: i32) { + allow_err!(self.send(Data::Mouse(DataMouse::ScrollY(length)))); + } + } + + pub async fn set_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> { + let mut conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_CONTROL).await?; + conn.send(&Data::Control(ipc::DataControl::Resolution { + minx, + maxx, + miny, + maxy, + })) + .await?; + let _ = conn.next().await?; + Ok(()) + } +} + +pub mod service { + use super::*; + use hbb_common::lazy_static; + use mouce::MouseActions; + use std::{collections::HashMap, sync::Mutex}; + + lazy_static::lazy_static! { + static ref KEY_MAP: HashMap = HashMap::from( + [ + (enigo::Key::Alt, evdev::Key::KEY_LEFTALT), + (enigo::Key::Backspace, evdev::Key::KEY_BACKSPACE), + (enigo::Key::CapsLock, evdev::Key::KEY_CAPSLOCK), + (enigo::Key::Control, evdev::Key::KEY_LEFTCTRL), + (enigo::Key::Delete, evdev::Key::KEY_DELETE), + (enigo::Key::DownArrow, evdev::Key::KEY_DOWN), + (enigo::Key::End, evdev::Key::KEY_END), + (enigo::Key::Escape, evdev::Key::KEY_ESC), + (enigo::Key::F1, evdev::Key::KEY_F1), + (enigo::Key::F10, evdev::Key::KEY_F10), + (enigo::Key::F11, evdev::Key::KEY_F11), + (enigo::Key::F12, evdev::Key::KEY_F12), + (enigo::Key::F2, evdev::Key::KEY_F2), + (enigo::Key::F3, evdev::Key::KEY_F3), + (enigo::Key::F4, evdev::Key::KEY_F4), + (enigo::Key::F5, evdev::Key::KEY_F5), + (enigo::Key::F6, evdev::Key::KEY_F6), + (enigo::Key::F7, evdev::Key::KEY_F7), + (enigo::Key::F8, evdev::Key::KEY_F8), + (enigo::Key::F9, evdev::Key::KEY_F9), + (enigo::Key::Home, evdev::Key::KEY_HOME), + (enigo::Key::LeftArrow, evdev::Key::KEY_LEFT), + (enigo::Key::Meta, evdev::Key::KEY_LEFTMETA), + (enigo::Key::Option, evdev::Key::KEY_OPTION), + (enigo::Key::PageDown, evdev::Key::KEY_PAGEDOWN), + (enigo::Key::PageUp, evdev::Key::KEY_PAGEUP), + (enigo::Key::Return, evdev::Key::KEY_ENTER), + (enigo::Key::RightArrow, evdev::Key::KEY_RIGHT), + (enigo::Key::Shift, evdev::Key::KEY_LEFTSHIFT), + (enigo::Key::Space, evdev::Key::KEY_SPACE), + (enigo::Key::Tab, evdev::Key::KEY_TAB), + (enigo::Key::UpArrow, evdev::Key::KEY_UP), + (enigo::Key::Numpad0, evdev::Key::KEY_KP0), // check if correct? + (enigo::Key::Numpad1, evdev::Key::KEY_KP1), + (enigo::Key::Numpad2, evdev::Key::KEY_KP2), + (enigo::Key::Numpad3, evdev::Key::KEY_KP3), + (enigo::Key::Numpad4, evdev::Key::KEY_KP4), + (enigo::Key::Numpad5, evdev::Key::KEY_KP5), + (enigo::Key::Numpad6, evdev::Key::KEY_KP6), + (enigo::Key::Numpad7, evdev::Key::KEY_KP7), + (enigo::Key::Numpad8, evdev::Key::KEY_KP8), + (enigo::Key::Numpad9, evdev::Key::KEY_KP9), + (enigo::Key::Cancel, evdev::Key::KEY_CANCEL), + (enigo::Key::Clear, evdev::Key::KEY_CLEAR), + (enigo::Key::Alt, evdev::Key::KEY_LEFTALT), + (enigo::Key::Pause, evdev::Key::KEY_PAUSE), + (enigo::Key::Kana, evdev::Key::KEY_KATAKANA), // check if correct? + (enigo::Key::Hangul, evdev::Key::KEY_HANGEUL), // check if correct? + // (enigo::Key::Junja, evdev::Key::KEY_JUNJA), // map? + // (enigo::Key::Final, evdev::Key::KEY_FINAL), // map? + (enigo::Key::Hanja, evdev::Key::KEY_HANJA), + // (enigo::Key::Kanji, evdev::Key::KEY_KANJI), // map? + // (enigo::Key::Convert, evdev::Key::KEY_CONVERT), + (enigo::Key::Select, evdev::Key::KEY_SELECT), + (enigo::Key::Print, evdev::Key::KEY_PRINT), + // (enigo::Key::Execute, evdev::Key::KEY_EXECUTE), + // (enigo::Key::Snapshot, evdev::Key::KEY_SNAPSHOT), + (enigo::Key::Insert, evdev::Key::KEY_INSERT), + (enigo::Key::Help, evdev::Key::KEY_HELP), + (enigo::Key::Sleep, evdev::Key::KEY_SLEEP), + // (enigo::Key::Separator, evdev::Key::KEY_SEPARATOR), + (enigo::Key::Scroll, evdev::Key::KEY_SCROLLLOCK), + (enigo::Key::NumLock, evdev::Key::KEY_NUMLOCK), + (enigo::Key::RWin, evdev::Key::KEY_RIGHTMETA), + (enigo::Key::Apps, evdev::Key::KEY_CONTEXT_MENU), + (enigo::Key::Multiply, evdev::Key::KEY_KPASTERISK), + (enigo::Key::Add, evdev::Key::KEY_KPPLUS), + (enigo::Key::Subtract, evdev::Key::KEY_KPMINUS), + (enigo::Key::Decimal, evdev::Key::KEY_KPCOMMA), // KEY_KPDOT and KEY_KPCOMMA are exchanged? + (enigo::Key::Divide, evdev::Key::KEY_KPSLASH), + (enigo::Key::Equals, evdev::Key::KEY_KPEQUAL), + (enigo::Key::NumpadEnter, evdev::Key::KEY_KPENTER), + (enigo::Key::RightAlt, evdev::Key::KEY_RIGHTALT), + (enigo::Key::RightControl, evdev::Key::KEY_RIGHTCTRL), + (enigo::Key::RightShift, evdev::Key::KEY_RIGHTSHIFT), + ]); + + static ref KEY_MAP_LAYOUT: HashMap = HashMap::from( + [ + ('a', evdev::Key::KEY_A), + ('b', evdev::Key::KEY_B), + ('c', evdev::Key::KEY_C), + ('d', evdev::Key::KEY_D), + ('e', evdev::Key::KEY_E), + ('f', evdev::Key::KEY_F), + ('g', evdev::Key::KEY_G), + ('h', evdev::Key::KEY_H), + ('i', evdev::Key::KEY_I), + ('j', evdev::Key::KEY_J), + ('k', evdev::Key::KEY_K), + ('l', evdev::Key::KEY_L), + ('m', evdev::Key::KEY_M), + ('n', evdev::Key::KEY_N), + ('o', evdev::Key::KEY_O), + ('p', evdev::Key::KEY_P), + ('q', evdev::Key::KEY_Q), + ('r', evdev::Key::KEY_R), + ('s', evdev::Key::KEY_S), + ('t', evdev::Key::KEY_T), + ('u', evdev::Key::KEY_U), + ('v', evdev::Key::KEY_V), + ('w', evdev::Key::KEY_W), + ('x', evdev::Key::KEY_X), + ('y', evdev::Key::KEY_Y), + ('z', evdev::Key::KEY_Z), + ('0', evdev::Key::KEY_0), + ('1', evdev::Key::KEY_1), + ('2', evdev::Key::KEY_2), + ('3', evdev::Key::KEY_3), + ('4', evdev::Key::KEY_4), + ('5', evdev::Key::KEY_5), + ('6', evdev::Key::KEY_6), + ('7', evdev::Key::KEY_7), + ('8', evdev::Key::KEY_8), + ('9', evdev::Key::KEY_9), + ('`', evdev::Key::KEY_GRAVE), + ('-', evdev::Key::KEY_MINUS), + ('=', evdev::Key::KEY_EQUAL), + ('[', evdev::Key::KEY_LEFTBRACE), + (']', evdev::Key::KEY_RIGHTBRACE), + ('\\', evdev::Key::KEY_BACKSLASH), + (',', evdev::Key::KEY_COMMA), + ('.', evdev::Key::KEY_DOT), + ('/', evdev::Key::KEY_SLASH), + (';', evdev::Key::KEY_SEMICOLON), + ('\'', evdev::Key::KEY_APOSTROPHE), + ]); + + // ((minx, maxx), (miny, maxy)) + static ref RESOLUTION: Mutex<((i32, i32), (i32, i32))> = Mutex::new(((0, 0), (0, 0))); + } + + fn create_uinput_keyboard() -> ResultType { + // TODO: ensure keys here + let mut keys = AttributeSet::::new(); + for i in evdev::Key::KEY_ESC.code()..(evdev::Key::BTN_TRIGGER_HAPPY40.code() + 1) { + let key = evdev::Key::new(i); + if !format!("{:?}", &key).contains("unknown key") { + keys.insert(key); + } + } + let mut leds = AttributeSet::::new(); + leds.insert(evdev::LedType::LED_NUML); + leds.insert(evdev::LedType::LED_CAPSL); + leds.insert(evdev::LedType::LED_SCROLLL); + let mut miscs = AttributeSet::::new(); + miscs.insert(evdev::MiscType::MSC_SCAN); + let keyboard = VirtualDeviceBuilder::new()? + .name("RustDesk UInput Keyboard") + .with_keys(&keys)? + .with_leds(&leds)? + .with_miscs(&miscs)? + .build()?; + Ok(keyboard) + } + + fn map_key(key: &enigo::Key) -> ResultType { + if let Some(k) = KEY_MAP.get(&key) { + log::trace!("mapkey {:?}, get {:?}", &key, &k); + return Ok(k.clone()); + } else { + match key { + enigo::Key::Layout(c) => { + if let Some(k) = KEY_MAP_LAYOUT.get(&c) { + log::trace!("mapkey {:?}, get {:?}", &key, k); + return Ok(k.clone()); + } + } + // enigo::Key::Raw(c) => { + // let k = evdev::Key::new(c); + // if !format!("{:?}", &k).contains("unknown key") { + // return Ok(k.clone()); + // } + // } + _ => {} + } + } + bail!("Failed to map key {:?}", &key); + } + + async fn ipc_send_data(stream: &mut Connection, data: &Data) { + allow_err!(stream.send(data).await); + } + + async fn handle_keyboard( + stream: &mut Connection, + keyboard: &mut VirtualDevice, + data: &DataKeyboard, + ) { + log::trace!("handle_keyboard {:?}", &data); + match data { + DataKeyboard::Sequence(_seq) => { + // ignore + } + DataKeyboard::KeyDown(key) => { + if let Ok(k) = map_key(key) { + let down_event = InputEvent::new(EventType::KEY, k.code(), 1); + allow_err!(keyboard.emit(&[down_event])); + } + } + DataKeyboard::KeyUp(key) => { + if let Ok(k) = map_key(key) { + let up_event = InputEvent::new(EventType::KEY, k.code(), 0); + allow_err!(keyboard.emit(&[up_event])); + } + } + DataKeyboard::KeyClick(key) => { + if let Ok(k) = map_key(key) { + let down_event = InputEvent::new(EventType::KEY, k.code(), 1); + let up_event = InputEvent::new(EventType::KEY, k.code(), 0); + allow_err!(keyboard.emit(&[down_event, up_event])); + } + } + DataKeyboard::GetKeyState(key) => { + let key_state = if enigo::Key::CapsLock == *key { + match keyboard.get_led_state() { + Ok(leds) => leds.contains(evdev::LedType::LED_CAPSL), + Err(_e) => { + // log::debug!("Failed to get led state {}", &_e); + false + } + } + } else { + match keyboard.get_key_state() { + Ok(keys) => match key { + enigo::Key::Shift => { + keys.contains(evdev::Key::KEY_LEFTSHIFT) + || keys.contains(evdev::Key::KEY_RIGHTSHIFT) + } + enigo::Key::Control => { + keys.contains(evdev::Key::KEY_LEFTCTRL) + || keys.contains(evdev::Key::KEY_RIGHTCTRL) + } + enigo::Key::Alt => { + keys.contains(evdev::Key::KEY_LEFTALT) + || keys.contains(evdev::Key::KEY_RIGHTALT) + } + enigo::Key::NumLock => keys.contains(evdev::Key::KEY_NUMLOCK), + enigo::Key::Meta => { + keys.contains(evdev::Key::KEY_LEFTMETA) + || keys.contains(evdev::Key::KEY_RIGHTMETA) + } + _ => false, + }, + Err(_e) => { + // log::debug!("Failed to get key state: {}", &_e); + false + } + } + }; + ipc_send_data( + stream, + &Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(key_state)), + ) + .await; + } + } + } + + fn handle_mouse(mouse: &mut mouce::nix::UInputMouseManager, data: &DataMouse) { + log::trace!("handle_mouse {:?}", &data); + match data { + DataMouse::MoveTo(x, y) => { + allow_err!(mouse.move_to(*x as _, *y as _)) + } + DataMouse::MoveRelative(x, y) => { + allow_err!(mouse.move_relative(*x, *y)) + } + DataMouse::Down(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.press_button(&btn)) + } + DataMouse::Up(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.release_button(&btn)) + } + DataMouse::Click(button) => { + let btn = match button { + enigo::MouseButton::Left => mouce::common::MouseButton::Left, + enigo::MouseButton::Middle => mouce::common::MouseButton::Middle, + enigo::MouseButton::Right => mouce::common::MouseButton::Right, + _ => { + return; + } + }; + allow_err!(mouse.click_button(&btn)) + } + DataMouse::ScrollX(_length) => { + // TODO: not supported for now + } + DataMouse::ScrollY(length) => { + let mut length = *length; + + let scroll = if length < 0 { + mouce::common::ScrollDirection::Up + } else { + mouce::common::ScrollDirection::Down + }; + + if length < 0 { + length = -length; + } + + for _ in 0..length { + allow_err!(mouse.scroll_wheel(&scroll)) + } + } + } + } + + fn spawn_keyboard_handler(mut stream: Connection) { + tokio::spawn(async move { + let mut keyboard = match create_uinput_keyboard() { + Ok(keyboard) => keyboard, + Err(e) => { + log::error!("Failed to create keyboard {}", e); + return; + } + }; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::info!("UInput keyboard ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Keyboard(data) => { + handle_keyboard(&mut stream, &mut keyboard, &data).await; + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + fn spawn_mouse_handler(mut stream: ipc::Connection) { + let resolution = RESOLUTION.lock().unwrap(); + if resolution.0 .0 == resolution.0 .1 || resolution.1 .0 == resolution.1 .1 { + return; + } + let rng_x = resolution.0.clone(); + let rng_y = resolution.1.clone(); + tokio::spawn(async move { + log::info!( + "Create uinput mouce with rng_x: ({}, {}), rng_y: ({}, {})", + rng_x.0, + rng_x.1, + rng_y.0, + rng_y.1 + ); + let mut mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) { + Ok(mouse) => mouse, + Err(e) => { + log::error!("Failed to create mouse, {}", e); + return; + } + }; + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(err) => { + log::info!("UInput mouse ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Mouse(data) => { + handle_mouse(&mut mouse, &data); + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + fn spawn_controller_handler(mut stream: ipc::Connection) { + tokio::spawn(async move { + loop { + tokio::select! { + res = stream.next() => { + match res { + Err(_err) => { + // log::info!("UInput controller ipc connection closed: {}", err); + break; + } + Ok(Some(data)) => { + match data { + Data::Control(data) => match data { + ipc::DataControl::Resolution{ + minx, + maxx, + miny, + maxy, + } => { + *RESOLUTION.lock().unwrap() = ((minx, maxx), (miny, maxy)); + allow_err!(stream.send(&Data::Empty).await); + } + } + _ => { + } + } + } + _ => {} + } + } + } + } + }); + } + + /// Start uinput service. + async fn start_service(postfix: &str, handler: F) { + match new_listener(postfix).await { + Ok(mut incoming) => { + while let Some(result) = incoming.next().await { + match result { + Ok(stream) => { + log::debug!("Got new connection of uinput ipc {}", postfix); + handler(Connection::new(stream)); + } + Err(err) => { + log::error!("Couldn't get uinput mouse client: {:?}", err); + } + } + } + } + Err(err) => { + log::error!("Failed to start uinput mouse ipc service: {}", err); + } + } + } + + /// Start uinput keyboard service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_keyboard() { + log::info!("start uinput keyboard service"); + start_service(IPC_POSTFIX_KEYBOARD, spawn_keyboard_handler).await; + } + + /// Start uinput mouse service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_mouse() { + log::info!("start uinput mouse service"); + start_service(IPC_POSTFIX_MOUSE, spawn_mouse_handler).await; + } + + /// Start uinput mouse service. + #[tokio::main(flavor = "current_thread")] + pub async fn start_service_control() { + log::info!("start uinput control service"); + start_service(IPC_POSTFIX_CONTROL, spawn_controller_handler).await; + } + + pub fn stop_service_keyboard() { + log::info!("stop uinput keyboard service"); + } + pub fn stop_service_mouse() { + log::info!("stop uinput mouse service"); + } + pub fn stop_service_control() { + log::info!("stop uinput control service"); + } +} diff --git a/src/server/video_qos.rs b/src/server/video_qos.rs index efad1f9d9..b0e06bc03 100644 --- a/src/server/video_qos.rs +++ b/src/server/video_qos.rs @@ -147,7 +147,6 @@ impl VideoQoS { // handle image_quality change from peer pub fn update_image_quality(&mut self, image_quality: i32) { let image_quality = Self::convert_quality(image_quality) as _; - log::debug!("VideoQoS update_image_quality: {}", image_quality); if self.current_image_quality != image_quality { self.current_image_quality = image_quality; let _ = self.generate_bitrate().ok(); @@ -171,7 +170,7 @@ impl VideoQoS { #[cfg(target_os = "android")] { // fix when andorid screen shrinks - let fix = Display::fix_quality() as u32; + let fix = scrap::Display::fix_quality() as u32; log::debug!("Android screen, fix quality:{}", fix); let base_bitrate = base_bitrate * fix; self.target_bitrate = base_bitrate * self.current_image_quality / 100; diff --git a/src/server/video_service.rs b/src/server/video_service.rs index e64ddd807..e6ea713a1 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -31,6 +31,7 @@ use scrap::{ use std::{ collections::HashSet, io::{ErrorKind::WouldBlock, Result}, + ops::{Deref, DerefMut}, time::{self, Duration, Instant}, }; #[cfg(windows)] @@ -127,9 +128,11 @@ impl VideoFrameController { } } -trait TraitCapturer { +pub(super) trait TraitCapturer { fn frame<'a>(&'a mut self, timeout: Duration) -> Result>; + fn set_use_yuv(&mut self, use_yuv: bool); + #[cfg(windows)] fn is_gdi(&self) -> bool; #[cfg(windows)] @@ -141,6 +144,10 @@ impl TraitCapturer for Capturer { self.frame(timeout) } + fn set_use_yuv(&mut self, use_yuv: bool) { + self.set_use_yuv(use_yuv); + } + #[cfg(windows)] fn is_gdi(&self) -> bool { self.is_gdi() @@ -158,6 +165,10 @@ impl TraitCapturer for scrap::CapturerMag { self.frame(_timeout_ms) } + fn set_use_yuv(&mut self, use_yuv: bool) { + self.set_use_yuv(use_yuv); + } + fn is_gdi(&self) -> bool { false } @@ -179,6 +190,14 @@ fn check_display_changed( last_width: usize, last_hegiht: usize, ) -> bool { + #[cfg(target_os = "linux")] + { + // wayland do not support changing display for now + if !scrap::is_x11() { + return false; + } + } + let displays = match try_get_displays() { Ok(d) => d, _ => return false, @@ -293,6 +312,7 @@ fn ensure_close_virtual_device() -> ResultType<()> { Ok(()) } +// This function works on privacy mode. Windows only for now. pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool { let test_begin = Instant::now(); while test_begin.elapsed().as_millis() < timeout_millis as _ { @@ -321,9 +341,38 @@ fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> Res Ok(()) } -fn run(sp: GenericService) -> ResultType<()> { - #[cfg(windows)] - ensure_close_virtual_device()?; +pub(super) struct CapturerInfo { + pub origin: (i32, i32), + pub width: usize, + pub height: usize, + pub ndisplay: usize, + pub current: usize, + pub privacy_mode_id: i32, + pub _captuerer_privacy_mode_id: i32, + pub capturer: Box, +} + +impl Deref for CapturerInfo { + type Target = Box; + + fn deref(&self) -> &Self::Target { + &self.capturer + } +} + +impl DerefMut for CapturerInfo { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.capturer + } +} + +fn get_capturer(use_yuv: bool) -> ResultType { + #[cfg(target_os = "linux")] + { + if !scrap::is_x11() { + return super::wayland::get_capturer(); + } + } let (ndisplay, current, display) = get_current_display()?; let (origin, width, height) = (display.origin(), display.width(), display.height()); @@ -338,38 +387,6 @@ fn run(sp: GenericService) -> ResultType<()> { num_cpus::get(), ); - let mut video_qos = VIDEO_QOS.lock().unwrap(); - - video_qos.set_size(width as _, height as _); - let mut spf = video_qos.spf(); - let bitrate = video_qos.generate_bitrate()?; - let abr = video_qos.check_abr_config(); - drop(video_qos); - log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); - - let encoder_cfg = match Encoder::current_hw_encoder_name() { - Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { - codec_name, - width, - height, - bitrate: bitrate as _, - }), - None => EncoderCfg::VPX(VpxEncoderConfig { - width: width as _, - height: height as _, - timebase: [1, 1000], // Output timestamp precision - bitrate, - codec: VpxVideoCodecId::VP9, - num_threads: (num_cpus::get() / 2) as _, - }), - }; - - let mut encoder; - match Encoder::new(encoder_cfg) { - Ok(x) => encoder = x, - Err(err) => bail!("Failed to create encoder: {}", err), - } - let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap(); #[cfg(not(windows))] let captuerer_privacy_mode_id = privacy_mode_id; @@ -389,17 +406,67 @@ fn run(sp: GenericService) -> ResultType<()> { } else { log::info!("In privacy mode, the peer side cannot watch the screen"); } - let mut c = create_capturer(captuerer_privacy_mode_id, display, encoder.use_yuv())?; + let capturer = create_capturer(captuerer_privacy_mode_id, display, use_yuv)?; + Ok(CapturerInfo { + origin, + width, + height, + ndisplay, + current, + privacy_mode_id, + _captuerer_privacy_mode_id: captuerer_privacy_mode_id, + capturer, + }) +} + +fn run(sp: GenericService) -> ResultType<()> { + #[cfg(windows)] + ensure_close_virtual_device()?; + + let mut c = get_capturer(true)?; + + let mut video_qos = VIDEO_QOS.lock().unwrap(); + + video_qos.set_size(c.width as _, c.height as _); + let mut spf = video_qos.spf(); + let bitrate = video_qos.generate_bitrate()?; + let abr = video_qos.check_abr_config(); + drop(video_qos); + log::info!("init bitrate={}, abr enabled:{}", bitrate, abr); + + let encoder_cfg = match Encoder::current_hw_encoder_name() { + Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { + codec_name, + width: c.width, + height: c.height, + bitrate: bitrate as _, + }), + None => EncoderCfg::VPX(VpxEncoderConfig { + width: c.width as _, + height: c.height as _, + timebase: [1, 1000], // Output timestamp precision + bitrate, + codec: VpxVideoCodecId::VP9, + num_threads: (num_cpus::get() / 2) as _, + }), + }; + + let mut encoder; + match Encoder::new(encoder_cfg) { + Ok(x) => encoder = x, + Err(err) => bail!("Failed to create encoder: {}", err), + } + c.set_use_yuv(encoder.use_yuv()); if *SWITCH.lock().unwrap() { log::debug!("Broadcasting display switch"); let mut misc = Misc::new(); misc.set_switch_display(SwitchDisplay { - display: current as _, - x: origin.0 as _, - y: origin.1 as _, - width: width as _, - height: height as _, + display: c.current as _, + x: c.origin.0 as _, + y: c.origin.1 as _, + width: c.width as _, + height: c.height as _, ..Default::default() }); let mut msg_out = Message::new(); @@ -419,7 +486,7 @@ fn run(sp: GenericService) -> ResultType<()> { while sp.ok() { #[cfg(windows)] - check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?; + check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?; { let mut video_qos = VIDEO_QOS.lock().unwrap(); @@ -437,11 +504,11 @@ fn run(sp: GenericService) -> ResultType<()> { if *SWITCH.lock().unwrap() { bail!("SWITCH"); } - if current != *CURRENT_DISPLAY.lock().unwrap() { + if c.current != *CURRENT_DISPLAY.lock().unwrap() { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } - check_privacy_mode_changed(&sp, privacy_mode_id)?; + check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] { if crate::platform::windows::desktop_changed() { @@ -451,7 +518,7 @@ fn run(sp: GenericService) -> ResultType<()> { let now = time::Instant::now(); if last_check_displays.elapsed().as_millis() > 1000 { last_check_displays = now; - if ndisplay != get_display_num() { + if c.ndisplay != get_display_num() { log::info!("Displays changed"); *SWITCH.lock().unwrap() = true; bail!("SWITCH"); @@ -515,7 +582,7 @@ fn run(sp: GenericService) -> ResultType<()> { } } Err(err) => { - if check_display_changed(ndisplay, current, width, height) { + if check_display_changed(c.ndisplay, c.current, c.width, c.height) { log::info!("Displays changed"); *SWITCH.lock().unwrap() = true; bail!("SWITCH"); @@ -537,9 +604,9 @@ fn run(sp: GenericService) -> ResultType<()> { let timeout_millis = 3_000u64; let wait_begin = Instant::now(); while wait_begin.elapsed().as_millis() < timeout_millis as _ { - check_privacy_mode_changed(&sp, privacy_mode_id)?; + check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] - check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?; + check_uac_switch(c.privacy_mode_id, c._captuerer_privacy_mode_id)?; frame_controller.try_wait_next(&mut fetched_conn_ids, 300); // break if all connections have received current frame if fetched_conn_ids.len() >= frame_controller.send_conn_ids.len() { @@ -633,6 +700,17 @@ pub fn handle_one_frame_encoded( } fn get_display_num() -> usize { + #[cfg(target_os = "linux")] + { + if !scrap::is_x11() { + return if let Ok(n) = super::wayland::get_display_num() { + n + } else { + 0 + }; + } + } + if let Ok(d) = try_get_displays() { d.len() } else { @@ -640,14 +718,10 @@ fn get_display_num() -> usize { } } -pub fn get_displays() -> ResultType<(usize, Vec)> { - // switch to primary display if long time (30 seconds) no users - if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 { - *CURRENT_DISPLAY.lock().unwrap() = usize::MAX; - } +pub(super) fn get_displays_2(all: &Vec) -> (usize, Vec) { let mut displays = Vec::new(); let mut primary = 0; - for (i, d) in try_get_displays()?.iter().enumerate() { + for (i, d) in all.iter().enumerate() { if d.is_primary() { primary = i; } @@ -665,12 +739,26 @@ pub fn get_displays() -> ResultType<(usize, Vec)> { if *lock >= displays.len() { *lock = primary } - Ok((*lock, displays)) + (*lock, displays) } -pub fn switch_display(i: i32) { +pub async fn get_displays() -> ResultType<(usize, Vec)> { + #[cfg(target_os = "linux")] + { + if !scrap::is_x11() { + return super::wayland::get_displays().await; + } + } + // switch to primary display if long time (30 seconds) no users + if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 { + *CURRENT_DISPLAY.lock().unwrap() = usize::MAX; + } + Ok(get_displays_2(&try_get_displays()?)) +} + +pub async fn switch_display(i: i32) { let i = i as usize; - if let Ok((_, displays)) = get_displays() { + if let Ok((_, displays)) = get_displays().await { if i < displays.len() { *CURRENT_DISPLAY.lock().unwrap() = i; } @@ -684,6 +772,16 @@ pub fn refresh() { } fn get_primary() -> usize { + #[cfg(target_os = "linux")] + { + if !scrap::is_x11() { + return match super::wayland::get_primary() { + Ok(n) => n, + Err(_) => 0, + }; + } + } + if let Ok(all) = try_get_displays() { for (i, d) in all.iter().enumerate() { if d.is_primary() { @@ -694,8 +792,8 @@ fn get_primary() -> usize { 0 } -pub fn switch_to_primary() { - switch_display(get_primary() as _); +pub async fn switch_to_primary() { + switch_display(get_primary() as _).await; } #[cfg(not(windows))] @@ -733,16 +831,15 @@ fn try_get_displays() -> ResultType> { Ok(displays) } -fn get_current_display() -> ResultType<(usize, usize, Display)> { +pub(super) fn get_current_display_2(mut all: Vec) -> ResultType<(usize, usize, Display)> { let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize; - let mut displays = try_get_displays()?; - if displays.len() == 0 { + if all.len() == 0 { bail!("No displays"); } - let n = displays.len(); + let n = all.len(); if current >= n { current = 0; - for (i, d) in displays.iter().enumerate() { + for (i, d) in all.iter().enumerate() { if d.is_primary() { current = i; break; @@ -750,5 +847,9 @@ fn get_current_display() -> ResultType<(usize, usize, Display)> { } *CURRENT_DISPLAY.lock().unwrap() = current; } - return Ok((n, current, displays.remove(current))); + return Ok((n, current, all.remove(current))); +} + +fn get_current_display() -> ResultType<(usize, usize, Display)> { + get_current_display_2(try_get_displays()?) } diff --git a/src/server/wayland.rs b/src/server/wayland.rs new file mode 100644 index 000000000..e84be3f7c --- /dev/null +++ b/src/server/wayland.rs @@ -0,0 +1,179 @@ +use super::*; +use hbb_common::allow_err; +use scrap::{Capturer, Display, Frame}; +use std::{io::Result, time::Duration}; + +lazy_static::lazy_static! { + static ref CAP_DISPLAY_INFO: RwLock = RwLock::new(0); +} +struct CapDisplayInfo { + rects: Vec<((i32, i32), usize, usize)>, + displays: Vec, + num: usize, + primary: usize, + current: usize, + capturer: *mut Capturer, +} + +impl super::video_service::TraitCapturer for *mut Capturer { + fn frame<'a>(&'a mut self, timeout: Duration) -> Result> { + unsafe { (**self).frame(timeout) } + } + + fn set_use_yuv(&mut self, use_yuv: bool) { + unsafe { + (**self).set_use_yuv(use_yuv); + } + } +} + +async fn check_init() -> ResultType<()> { + if !scrap::is_x11() { + let mut minx = 0; + let mut maxx = 0; + let mut miny = 0; + let mut maxy = 0; + + if *CAP_DISPLAY_INFO.read().unwrap() == 0 { + let mut lock = CAP_DISPLAY_INFO.write().unwrap(); + if *lock == 0 { + let all = Display::all()?; + let num = all.len(); + let (primary, displays) = super::video_service::get_displays_2(&all); + + let mut rects: Vec<((i32, i32), usize, usize)> = Vec::new(); + for d in &all { + rects.push((d.origin(), d.width(), d.height())); + } + + let (ndisplay, current, display) = + super::video_service::get_current_display_2(all)?; + let (origin, width, height) = (display.origin(), display.width(), display.height()); + log::debug!( + "#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}", + ndisplay, + current, + &origin, + width, + height, + num_cpus::get_physical(), + num_cpus::get(), + ); + + minx = origin.0; + maxx = origin.0 + width as i32; + miny = origin.1; + maxy = origin.1 + height as i32; + + let capturer = Box::into_raw(Box::new( + Capturer::new(display, true).with_context(|| "Failed to create capturer")?, + )); + let cap_display_info = Box::into_raw(Box::new(CapDisplayInfo { + rects, + displays, + num, + primary, + current, + capturer, + })); + *lock = cap_display_info as _; + } + } + + if minx != maxx && miny != maxy { + log::info!( + "send uinput resolution: ({}, {}), ({}, {})", + minx, + maxx, + miny, + maxy + ); + allow_err!(input_service::set_uinput_resolution(minx, maxx, miny, maxy).await); + allow_err!(input_service::set_uinput().await); + } + } + Ok(()) +} + +pub fn clear() { + if scrap::is_x11() { + return; + } + + let mut lock = CAP_DISPLAY_INFO.write().unwrap(); + if *lock != 0 { + unsafe { + let cap_display_info = Box::from_raw(*lock as *mut CapDisplayInfo); + let _ = Box::from_raw(cap_display_info.capturer); + } + *lock = 0; + } +} + +pub(super) async fn get_displays() -> ResultType<(usize, Vec)> { + check_init().await?; + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + let primary = cap_display_info.primary; + let displays = cap_display_info.displays.clone(); + Ok((primary, displays)) + } + } else { + bail!("Failed to get capturer display info"); + } +} + +pub(super) fn get_primary() -> ResultType { + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + Ok(cap_display_info.primary) + } + } else { + bail!("Failed to get capturer display info"); + } +} + +pub(super) fn get_display_num() -> ResultType { + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + Ok(cap_display_info.num) + } + } else { + bail!("Failed to get capturer display info"); + } +} + +pub(super) fn get_capturer() -> ResultType { + if scrap::is_x11() { + bail!("Do not call this function if not wayland"); + } + let addr = *CAP_DISPLAY_INFO.read().unwrap(); + if addr != 0 { + let cap_display_info: *const CapDisplayInfo = addr as _; + unsafe { + let cap_display_info = &*cap_display_info; + let rect = cap_display_info.rects[cap_display_info.current]; + Ok(super::video_service::CapturerInfo { + origin: rect.0, + width: rect.1, + height: rect.2, + ndisplay: cap_display_info.num, + current: cap_display_info.current, + privacy_mode_id: 0, + _captuerer_privacy_mode_id: 0, + capturer: Box::new(cap_display_info.capturer), + }) + } + } else { + bail!("Failed to get capturer display info"); + } +} diff --git a/src/ui.rs b/src/ui.rs index f0134aceb..3fecc33cd 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -43,6 +43,7 @@ struct UI( Arc>>, Arc>, mpsc::UnboundedSender, + Arc>, ); struct UIHostHandler; @@ -169,7 +170,7 @@ pub fn start(args: &mut [String]) { impl UI { fn new(childs: Childs) -> Self { let res = check_connect_status(true); - Self(childs, res.0, res.1, Default::default(), res.2) + Self(childs, res.0, res.1, Default::default(), res.2, res.3) } fn recent_sessions_updated(&mut self) -> bool { @@ -186,16 +187,16 @@ impl UI { ipc::get_id() } - fn get_password(&mut self) -> String { - ipc::get_password() + fn get_random_password(&self) -> String { + ipc::get_random_password() } - fn update_password(&mut self, password: String) { - if password.is_empty() { - allow_err!(ipc::set_password(Config::get_auto_password())); - } else { - allow_err!(ipc::set_password(password)); - } + fn update_random_password(&self) { + allow_err!(ipc::set_random_password(Config::get_auto_password())); + } + + fn set_security_password(&self, password: String) { + allow_err!(ipc::set_security_password(password)); } fn get_remote_id(&mut self) -> String { @@ -541,6 +542,16 @@ impl UI { PeerConfig::remove(&id); } + fn remove_discovered(&mut self, id: String) { + let mut peers = config::LanPeers::load().peers; + peers.retain(|x| x.id != id); + config::LanPeers::store(&peers); + } + + fn send_wol(&mut self, id: String) { + crate::lan::send_wol(id) + } + fn new_remote(&mut self, id: String, remote_type: String) { let mut lock = self.0.lock().unwrap(); let args = vec![format!("--{}", remote_type), id.clone()]; @@ -685,16 +696,16 @@ impl UI { fn discover(&self) { std::thread::spawn(move || { - allow_err!(crate::rendezvous_mediator::discover()); + allow_err!(crate::lan::discover()); }); } fn get_lan_peers(&self) -> String { - config::LanPeers::load().peers + serde_json::to_string(&config::LanPeers::load().peers).unwrap_or_default() } fn get_uuid(&self) -> String { - base64::encode(crate::get_uuid()) + base64::encode(hbb_common::get_uuid()) } fn open_url(&self, url: String) { @@ -764,6 +775,54 @@ impl UI { fn get_langs(&self) -> String { crate::lang::LANGS.to_string() } + + fn random_password_update_method(&self) -> String { + ipc::random_password_update_method() + } + + fn set_random_password_update_method(&self, method: String) { + allow_err!(ipc::set_random_password_update_method(method)); + } + + fn is_random_password_enabled(&self) -> bool { + ipc::is_random_password_enabled() + } + + fn set_random_password_enabled(&self, enabled: bool) { + allow_err!(ipc::set_random_password_enabled(enabled)); + } + + fn is_security_password_enabled(&self) -> bool { + ipc::is_security_password_enabled() + } + + fn set_security_password_enabled(&self, enabled: bool) { + allow_err!(ipc::set_security_password_enabled(enabled)); + } + + fn is_onetime_password_enabled(&self) -> bool { + ipc::is_onetime_password_enabled() + } + + fn set_onetime_password_enabled(&self, enabled: bool) { + allow_err!(ipc::set_onetime_password_enabled(enabled)); + } + + fn is_onetime_password_activated(&self) -> bool { + ipc::is_onetime_password_activated() + } + + fn set_onetime_password_activated(&self, activated: bool) { + allow_err!(ipc::set_onetime_password_activated(activated)); + } + + fn is_random_password_valid(&self) -> bool { + ipc::is_random_password_valid() + } + + fn password_description(&mut self) -> String { + self.5.lock().unwrap().clone() + } } impl sciter::EventHandler for UI { @@ -773,14 +832,17 @@ impl sciter::EventHandler for UI { fn is_xfce(); fn using_public_server(); fn get_id(); - fn get_password(); - fn update_password(String); + fn get_random_password(); + fn update_random_password(); + fn set_security_password(String); fn get_remote_id(); fn set_remote_id(String); fn closing(i32, i32, i32, i32); fn get_size(); fn new_remote(String, bool); + fn send_wol(String); fn remove_peer(String); + fn remove_discovered(String); fn get_connect_status(); fn get_mouse_time(); fn check_mouse_time(); @@ -842,6 +904,18 @@ impl sciter::EventHandler for UI { fn get_uuid(); fn has_hwcodec(); fn get_langs(); + fn random_password_update_method(); + fn set_random_password_update_method(String); + fn is_random_password_enabled(); + fn set_random_password_enabled(bool); + fn is_security_password_enabled(); + fn set_security_password_enabled(bool); + fn is_onetime_password_enabled(); + fn set_onetime_password_enabled(bool); + fn is_onetime_password_activated(); + fn set_onetime_password_activated(bool); + fn is_random_password_valid(); + fn password_description(); } } @@ -881,6 +955,7 @@ async fn check_connect_status_( status: Arc>, options: Arc>>, rx: mpsc::UnboundedReceiver, + password: Arc>, ) { let mut key_confirmed = false; let mut rx = rx; @@ -907,6 +982,8 @@ async fn check_connect_status_( Ok(Some(ipc::Data::Config((name, Some(value))))) => { if name == "id" { id = value; + } else if name == ipc::STR_PASSWORD_DESCRIPTION { + *password.lock().unwrap() = value; } } Ok(Some(ipc::Data::OnlineStatus(Some((mut x, c))))) => { @@ -926,6 +1003,7 @@ async fn check_connect_status_( c.send(&ipc::Data::OnlineStatus(None)).await.ok(); c.send(&ipc::Data::Options(None)).await.ok(); c.send(&ipc::Data::Config(("id".to_owned(), None))).await.ok(); + c.send(&ipc::Data::Config((ipc::STR_PASSWORD_DESCRIPTION.to_owned(), None))).await.ok(); } } } @@ -974,14 +1052,19 @@ fn check_connect_status( Arc>, Arc>>, mpsc::UnboundedSender, + Arc>, ) { let status = Arc::new(Mutex::new((0, false, 0, "".to_owned()))); let options = Arc::new(Mutex::new(Config::get_options())); let cloned = status.clone(); let cloned_options = options.clone(); let (tx, rx) = mpsc::unbounded_channel::(); - std::thread::spawn(move || check_connect_status_(reconnect, cloned, cloned_options, rx)); - (status, options, tx) + let password = Arc::new(Mutex::new(String::default())); + let cloned_password = password.clone(); + std::thread::spawn(move || { + check_connect_status_(reconnect, cloned, cloned_options, rx, cloned_password) + }); + (status, options, tx, password) } const INVALID_FORMAT: &'static str = "Invalid format"; @@ -1045,7 +1128,7 @@ async fn check_id( if let Some(Ok(bytes)) = socket.next_timeout(3_000).await { if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { match msg_in.union { - Some(rendezvous_message::Union::register_pk_response(rpr)) => { + Some(rendezvous_message::Union::RegisterPkResponse(rpr)) => { match rpr.result.enum_value_or_default() { register_pk_response::Result::OK => { ok = true; diff --git a/src/ui/ab.tis b/src/ui/ab.tis index 716ff1ba6..28fa62352 100644 --- a/src/ui/ab.tis +++ b/src/ui/ab.tis @@ -318,9 +318,10 @@ class SessionList: Reactor.Component {
  • {translate('TCP Tunneling')}
  • {false && !handler.using_public_server() &&
  • {svg_checkmark}{translate('Always connect via relay')}
  • }
  • RDP
  • +
  • {translate('WOL')}
  • -
  • {translate('Rename')}
  • - {this.type != "fav" && this.type != "lan" &&
  • {translate('Remove')}
  • } + {this.type != "lan" &&
  • {translate('Rename')}
  • } + {this.type != "fav" &&
  • {translate('Remove')}
  • } {is_win &&
  • {translate('Create Desktop Shortcut')}
  • }
  • {translate('Unremember Password')}
  • {(!this.type || this.type == "fav") &&
  • {translate('Add to Favorites')}
  • } @@ -419,6 +420,8 @@ class SessionList: Reactor.Component { createNewConnect(id, "connect"); } else if (action == "transfer") { createNewConnect(id, "file-transfer"); + } else if (action == "wol") { + handler.send_wol(id); } else if (action == "remove") { if (this.type == "ab") { for (var i = 0; i < ab.peers.length; ++i) { @@ -429,6 +432,9 @@ class SessionList: Reactor.Component { break; } } + } else if (this.type == "lan") { + handler.remove_discovered(id); + app.update(); } else { handler.remove_peer(id); app.update(); diff --git a/src/ui/cm.rs b/src/ui/cm.rs index 90d066d62..c1acb6926 100644 --- a/src/ui/cm.rs +++ b/src/ui/cm.rs @@ -204,7 +204,7 @@ impl ConnectionManager { let mut req = FileTransferSendConfirmRequest { id, file_num, - union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)), + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), ..Default::default() }; let digest = FileTransferDigest { diff --git a/src/ui/common.css b/src/ui/common.css index 39427fc8a..c3f3706ef 100644 --- a/src/ui/common.css +++ b/src/ui/common.css @@ -120,7 +120,7 @@ textarea:empty { @ELLIPSIS; } -div.password svg { +div.password svg:not(.checkmark) { padding-left: 1em; size: 16px; color: #ddd; diff --git a/src/ui/common.tis b/src/ui/common.tis index 46adc3288..aae950c2d 100644 --- a/src/ui/common.tis +++ b/src/ui/common.tis @@ -141,7 +141,7 @@ function adjustBorder() { if (el) el.attributes.toggleClass("active", view.windowState == View.WINDOW_FULL_SCREEN); } -var svg_checkmark = ; +var svg_checkmark = ; var svg_edit = ; diff --git a/src/ui/index.css b/src/ui/index.css index 3c4dc6a47..f4ec4c2f5 100644 --- a/src/ui/index.css +++ b/src/ui/index.css @@ -403,3 +403,18 @@ div.remote-session svg#menu { background: none; color: white; } + +svg#refresh-password { + display: inline-block; + stroke:#ddd; +} + +svg#refresh-password:hover { + stroke:color(text); +} + +li:disabled, li:disabled:hover { + color: color(lighter-text); + background: color(menu); +} + diff --git a/src/ui/index.tis b/src/ui/index.tis index 099ca2af8..4a6004135 100644 --- a/src/ui/index.tis +++ b/src/ui/index.tis @@ -20,6 +20,7 @@ var svg_menu = ; +var svg_refresh_password = ; var my_id = ""; function get_id() { @@ -520,10 +521,6 @@ class App: Reactor.Component var is_can_screen_recording = handler.is_can_screen_recording(false); return
    - -
  • {translate('Refresh random password')}
  • -
  • {translate('Set your own password')}
  • -
    {translate('Your Desktop')}
    @@ -533,8 +530,7 @@ class App: Reactor.Component {key_confirmed ? : translate("Generating ...")}
    -
    {translate('Password')}
    - +
    {!is_win || handler.is_installed() ? "": } @@ -806,44 +802,151 @@ function watch_screen_recording() { class PasswordEyeArea : Reactor.Component { render() { + var show = handler.is_random_password_valid(); + var value = show ? handler.get_random_password() : "-"; return
    - - {svg_eye} + + {svg_refresh_password}
    ; } - - event mouseenter { - var me = this; - me.leaved = false; - me.timer(300ms, function() { - if (me.leaved) return; - me.input.value = handler.get_password(); - }); - } - event mouseleave { - this.leaved = true; - this.input.value = "******"; + event click $(svg#refresh-password) (_, me) { + if (handler.is_random_password_valid()) handler.update_random_password(); + this.update(); } } -class Password: Reactor.Component { +var verificationMethodMenu; +class VerificationMethodMenu: Reactor.Component { + function this() { + verificationMethodMenu = this; + } + function render() { - return
    - - {svg_edit} + if (!this.show) return
  • ; + var me = this; + self.timer(1ms, function() { me.toggleMenuState() }); + return
  • {translate('Verification Method')} + +
  • {svg_checkmark}{translate('Enable security password')}
  • +
  • {svg_checkmark}{translate('Enable random password')}
  • +
    +
  • ; + } + + function toggleMenuState() { + var security_enabled = handler.is_security_password_enabled(); + var random_enabled = handler.is_random_password_enabled(); + var onetime_enabled = handler.is_onetime_password_enabled(); + for (var (index, el) in this.$$(menu#verification-method>li)) { + if (index == 0) el.attributes.toggleClass("selected", security_enabled); + if (index == 1) el.attributes.toggleClass("selected", random_enabled); + } + } + + event click $(menu#verification-method>li) (_, me) { + switch (me.id.substring('verification-method-'.length)) { + case 'security': + { + var security_enabled = handler.is_security_password_enabled(); + handler.set_security_password_enabled(!security_enabled); + } + break; + case 'random': + { + var random_enabled = handler.is_random_password_enabled(); + handler.set_random_password_enabled(!random_enabled); + } + break; + } + + this.toggleMenuState(); + passwordArea.update(); + } +} + +var randomPasswordUpdateMethodMenu; +class RandomPasswordUpdateMethodMenu: Reactor.Component { + function this() { + randomPasswordUpdateMethodMenu = this; + } + + function render() { + if (!this.show) return
  • ; + var me = this; + var random_enabled = handler.is_random_password_enabled(); + self.timer(1ms, function() { me.toggleMenuState() }); + return
  • {translate('Random Password After Session')} + +
  • {svg_checkmark}{translate('Keep')}
  • +
  • {svg_checkmark}{translate('Update')}
  • +
  • {svg_checkmark}{translate('Disable')}
  • +
    +
  • ; + } + + function toggleMenuState() { + var method = handler.random_password_update_method(); + for (var (index, el) in this.$$(menu#random-password-update-method>li)) { + if (index == 0) el.attributes.toggleClass("selected", method == "KEEP"); + if (index == 1) el.attributes.toggleClass("selected", method == "UPDATE"); + if (index == 2) el.attributes.toggleClass("selected", method == "DISABLE"); + } + } + + event click $(menu#random-password-update-method>li) (_, me) { + if (me.id === 'random-password-update-method-keep') handler.set_random_password_update_method("KEEP"); + if (me.id === 'random-password-update-method-update') handler.set_random_password_update_method("UPDATE"); + if (me.id === 'random-password-update-method-disable') handler.set_random_password_update_method("DISABLE"); + this.toggleMenuState(); + passwordArea.update(); + } +} + +var passwordArea; +class PasswordArea: Reactor.Component { + function this() { + passwordArea = this; + } + + function render() { + var onetime_enabled = handler.is_onetime_password_enabled(); + + return +
    +
    {translate(onetime_enabled ? 'Onetime Password' : 'Password')}
    +
    + {this.renderPop()} + + {svg_edit} +
    ; } - event click $(svg#edit) (_, me) { - var menu = $(menu#edit-password-context); - me.popup(menu); + function renderPop() { + var security_enabled = handler.is_security_password_enabled(); + var random_enabled = handler.is_random_password_enabled(); + var onetime_enabled = handler.is_onetime_password_enabled(); + var onetime_activated = handler.is_onetime_password_activated(); + + return +
  • {translate(onetime_enabled ? "Disable onetime password" : "Enable onetime password")}
  • +
  • {translate('Activate onetime password')}
  • +
    + +
    +
  • {translate('Set security password')}
  • +
    + + ; } - event click $(li#refresh-password) { - handler.update_password(""); - this.update(); + event click $(svg#edit) (_, me) { + randomPasswordUpdateMethodMenu.update({show: true }); + verificationMethodMenu.update({show: true }); + var menu = $(menu#edit-password-context); + me.popup(menu); } event click $(li#set-password) { @@ -862,12 +965,36 @@ class Password: Reactor.Component { if (p0 != p1) { return translate("The confirmation is not identical."); } - handler.update_password(p0); + handler.set_security_password(p0); me.update(); }); } + + event click $(li#enable-onetime-password) { + var onetime_enabled = handler.is_onetime_password_enabled(); + handler.set_onetime_password_enabled(!onetime_enabled); + passwordArea.update(); + } + + event click $(li#activate-onetime-password) { + handler.set_onetime_password_activated(true); + passwordArea.update(); + } } +var last_password_description = ""; +function updatePasswordArea() { + self.timer(1s, function() { + var description = handler.password_description(); + if (last_password_description != description) { + last_password_description = description + passwordArea.update(); + } + updatePasswordArea(); + }); +} +updatePasswordArea(); + class ID: Reactor.Component { function render() { return String { if let Some(key_event) = self.get_key_event(1, &name, code) { match key_event.union { - Some(key_event::Union::chr(chr)) => { + Some(key_event::Union::Chr(chr)) => { if let Some(chr) = std::char::from_u32(chr as _) { return chr.to_string(); } @@ -1763,6 +1763,11 @@ impl Remote { // log::info!("new msg from ui, {}",data); match data { Data::Close => { + let mut misc = Misc::new(); + misc.set_close_reason("".to_owned()); + let mut msg = Message::new(); + msg.set_misc(misc); + allow_err!(peer.send(&msg).await); return false; } Data::Login((password, remember)) => { @@ -1940,9 +1945,9 @@ impl Remote { id, file_num, union: if need_override { - Some(file_transfer_send_confirm_request::Union::offset_blk(0)) + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) } else { - Some(file_transfer_send_confirm_request::Union::skip(true)) + Some(file_transfer_send_confirm_request::Union::Skip(true)) }, ..Default::default() }); @@ -1958,9 +1963,9 @@ impl Remote { id, file_num, union: if need_override { - Some(file_transfer_send_confirm_request::Union::offset_blk(0)) + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) } else { - Some(file_transfer_send_confirm_request::Union::skip(true)) + Some(file_transfer_send_confirm_request::Union::Skip(true)) }, ..Default::default() }); @@ -2147,7 +2152,7 @@ impl Remote { async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { if let Ok(msg_in) = Message::parse_from_bytes(&data) { match msg_in.union { - Some(message::Union::video_frame(vf)) => { + Some(message::Union::VideoFrame(vf)) => { if !self.first_frame { self.first_frame = true; self.handler.call2("closeSuccess", &make_args!()); @@ -2163,16 +2168,16 @@ impl Remote { }; self.video_sender.send(MediaData::VideoFrame(vf)).ok(); } - Some(message::Union::hash(hash)) => { + Some(message::Union::Hash(hash)) => { self.handler.handle_hash(hash, peer).await; } - Some(message::Union::login_response(lr)) => match lr.union { - Some(login_response::Union::error(err)) => { + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { if !self.handler.handle_login_error(&err) { return false; } } - Some(login_response::Union::peer_info(pi)) => { + Some(login_response::Union::PeerInfo(pi)) => { self.handler.handle_peer_info(pi); self.check_clipboard_file_context(); if !(self.handler.is_file_transfer() @@ -2199,22 +2204,22 @@ impl Remote { } _ => {} }, - Some(message::Union::cursor_data(cd)) => { + Some(message::Union::CursorData(cd)) => { self.handler.set_cursor_data(cd); } - Some(message::Union::cursor_id(id)) => { + Some(message::Union::CursorId(id)) => { self.handler.set_cursor_id(id.to_string()); } - Some(message::Union::cursor_position(cp)) => { + Some(message::Union::CursorPosition(cp)) => { self.handler.set_cursor_position(cp); } - Some(message::Union::clipboard(cb)) => { + Some(message::Union::Clipboard(cb)) => { if !self.handler.lc.read().unwrap().disable_clipboard { update_clipboard(cb, Some(&self.old_clipboard)); } } #[cfg(windows)] - Some(message::Union::cliprdr(clip)) => { + Some(message::Union::Cliprdr(clip)) => { if !self.handler.lc.read().unwrap().disable_clipboard { if let Some(context) = &mut self.clipboard_file_context { if let Some(clip) = msg_2_clip(clip) { @@ -2223,9 +2228,9 @@ impl Remote { } } } - Some(message::Union::file_response(fr)) => { + Some(message::Union::FileResponse(fr)) => { match fr.union { - Some(file_response::Union::dir(fd)) => { + Some(file_response::Union::Dir(fd)) => { #[cfg(windows)] let entries = fd.entries.to_vec(); #[cfg(not(windows))] @@ -2248,7 +2253,7 @@ impl Remote { job.files = entries; } } - Some(file_response::Union::digest(digest)) => { + Some(file_response::Union::Digest(digest)) => { if digest.is_upload { if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { if let Some(file) = job.files().get(digest.file_num as usize) { @@ -2259,9 +2264,9 @@ impl Remote { id: digest.id, file_num: digest.file_num, union: Some(if overwrite { - file_transfer_send_confirm_request::Union::offset_blk(0) + file_transfer_send_confirm_request::Union::OffsetBlk(0) } else { - file_transfer_send_confirm_request::Union::skip( + file_transfer_send_confirm_request::Union::Skip( true, ) }), @@ -2294,7 +2299,7 @@ impl Remote { let msg= new_send_confirm(FileTransferSendConfirmRequest { id: digest.id, file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::skip(true)), + union: Some(file_transfer_send_confirm_request::Union::Skip(true)), ..Default::default() }); allow_err!(peer.send(&msg).await); @@ -2306,9 +2311,9 @@ impl Remote { id: digest.id, file_num: digest.file_num, union: Some(if overwrite { - file_transfer_send_confirm_request::Union::offset_blk(0) + file_transfer_send_confirm_request::Union::OffsetBlk(0) } else { - file_transfer_send_confirm_request::Union::skip(true) + file_transfer_send_confirm_request::Union::Skip(true) }), ..Default::default() }, @@ -2331,7 +2336,7 @@ impl Remote { FileTransferSendConfirmRequest { id: digest.id, file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)), + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), ..Default::default() }, ); @@ -2346,7 +2351,7 @@ impl Remote { } } } - Some(file_response::Union::block(block)) => { + Some(file_response::Union::Block(block)) => { log::info!( "file response block, file id:{}, file num: {}", block.id, @@ -2359,27 +2364,27 @@ impl Remote { self.update_jobs_status(); } } - Some(file_response::Union::done(d)) => { + Some(file_response::Union::Done(d)) => { if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { job.modify_time(); fs::remove_job(d.id, &mut self.write_jobs); } self.handle_job_status(d.id, d.file_num, None); } - Some(file_response::Union::error(e)) => { + Some(file_response::Union::Error(e)) => { self.handle_job_status(e.id, e.file_num, Some(e.error)); } _ => {} } } - Some(message::Union::misc(misc)) => match misc.union { - Some(misc::Union::audio_format(f)) => { + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::AudioFormat(f)) => { self.audio_sender.send(MediaData::AudioFormat(f)).ok(); } - Some(misc::Union::chat_message(c)) => { + Some(misc::Union::ChatMessage(c)) => { self.handler.call("newMessage", &make_args!(c.text)); } - Some(misc::Union::permission_info(p)) => { + Some(misc::Union::PermissionInfo(p)) => { log::info!("Change permission {:?} -> {}", p.permission, p.enabled); match p.permission.enum_value_or_default() { Permission::Keyboard => { @@ -2407,7 +2412,7 @@ impl Remote { } } } - Some(misc::Union::switch_display(s)) => { + Some(misc::Union::SwitchDisplay(s)) => { self.handler.call("switchDisplay", &make_args!(s.display)); self.video_sender.send(MediaData::Reset).ok(); if s.width > 0 && s.height > 0 { @@ -2423,27 +2428,27 @@ impl Remote { self.handler.set_display(s.x, s.y, s.width, s.height); } } - Some(misc::Union::close_reason(c)) => { + Some(misc::Union::CloseReason(c)) => { self.handler.msgbox("error", "Connection Error", &c); return false; } - Some(misc::Union::back_notification(notification)) => { + Some(misc::Union::BackNotification(notification)) => { if !self.handle_back_notification(notification).await { return false; } } _ => {} }, - Some(message::Union::test_delay(t)) => { + Some(message::Union::TestDelay(t)) => { self.handler.handle_test_delay(t, peer).await; } - Some(message::Union::audio_frame(frame)) => { + Some(message::Union::AudioFrame(frame)) => { if !self.handler.lc.read().unwrap().disable_audio { self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); } } - Some(message::Union::file_action(action)) => match action.union { - Some(file_action::Union::send_confirm(c)) => { + Some(message::Union::FileAction(action)) => match action.union { + Some(file_action::Union::SendConfirm(c)) => { if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { job.confirm(&c); } @@ -2458,13 +2463,13 @@ impl Remote { async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { match notification.union { - Some(back_notification::Union::block_input_state(state)) => { + Some(back_notification::Union::BlockInputState(state)) => { self.handle_back_msg_block_input( state.enum_value_or(back_notification::BlockInputState::StateUnknown), ) .await; } - Some(back_notification::Union::privacy_mode_state(state)) => { + Some(back_notification::Union::PrivacyModeState(state)) => { if !self .handle_back_msg_privacy_mode( state.enum_value_or(back_notification::PrivacyModeState::StateUnknown),