diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml new file mode 100644 index 000000000..82243264c --- /dev/null +++ b/.github/workflows/winget.yml @@ -0,0 +1,12 @@ +name: Publish to WinGet +on: + release: + types: [released] +jobs: + publish: + runs-on: windows-latest # action can only be run on windows + steps: + - uses: vedantmgoyal2009/winget-releaser@latest + with: + identifier: RustDesk.RustDesk + token: ${{ secrets.WINGET_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index 418d517b6..7a4629e4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,9 +123,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" [[package]] name = "arboard" @@ -455,7 +455,7 @@ version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6358dedf60f4d9b8db43ad187391afe959746101346fe51bb978126bec61dfb" dependencies = [ - "clap 3.1.18", + "clap 3.2.6", "heck 0.4.0", "indexmap", "log", @@ -553,9 +553,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.18" +version = "3.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" +checksum = "9f1fe12880bae935d142c8702d500c63a4e8634b6c3c57ad72bf978fc7b6249a" dependencies = [ "atty", "bitflags", @@ -568,9 +568,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +checksum = "87eba3c8c7f42ef17f6c659fc7416d0f4758cd3e58861ee63c5fa4a4dde649e4" dependencies = [ "os_str_bytes", ] @@ -876,9 +876,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -897,15 +897,15 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" dependencies = [ "autocfg 1.1.0", "cfg-if 1.0.0", "crossbeam-utils", - "lazy_static", "memoffset", + "once_cell", "scopeguard", ] @@ -921,12 +921,12 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" dependencies = [ "cfg-if 1.0.0", - "lazy_static", + "once_cell", ] [[package]] @@ -1751,13 +1751,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] @@ -2123,9 +2123,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" [[package]] name = "hbb_common" @@ -2335,9 +2335,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.2" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg 1.1.0", "hashbrown", @@ -2425,9 +2425,9 @@ checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" dependencies = [ "wasm-bindgen", ] @@ -2584,9 +2584,9 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "lock_api" @@ -2757,13 +2757,13 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.36.1", ] @@ -3021,9 +3021,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fbc387afefefd5e9e39493299f3069e14a140dd34dc19b4c1c1a8fddb6a790" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" dependencies = [ "num-traits 0.2.15", ] @@ -3517,9 +3517,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ "unicode-ident", ] @@ -3648,9 +3648,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] @@ -3925,9 +3925,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.10" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a1f7aa4f35e5e8b4160449f51afc758f0ce6454315a9fa7d0d113e958c41eb" +checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" dependencies = [ "base64", "bytes", @@ -3947,12 +3947,13 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls", - "rustls-pemfile 0.3.0", + "rustls-pemfile 1.0.0", "serde 1.0.137", "serde_json 1.0.81", "serde_urlencoded", "tokio", "tokio-rustls", + "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", @@ -4061,7 +4062,7 @@ dependencies = [ "base64", "cc", "cfg-if 1.0.0", - "clap 3.1.18", + "clap 3.2.6", "clipboard", "cocoa 0.24.0", "core-foundation 0.9.3", @@ -4164,15 +4165,6 @@ dependencies = [ "base64", ] -[[package]] -name = "rustls-pemfile" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee86d63972a7c661d1536fefe8c3c8407321c3df668891286de28abcd087360" -dependencies = [ - "base64", -] - [[package]] name = "rustls-pemfile" version = "1.0.0" @@ -4194,9 +4186,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" +checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" [[package]] name = "ryu" @@ -4485,9 +4477,9 @@ checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "smallvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" [[package]] name = "smithay-client-toolkit" @@ -4627,9 +4619,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.96" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", @@ -4650,13 +4642,15 @@ dependencies = [ [[package]] name = "sys-locale" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3913c5a3d30054d7f77cf07cdd800c8103ace15c6e44437c5db66a43dd3a92cf" +checksum = "658ee915b6c7b73ec4c1ffcd838506b5c5a4087eadc1ec8f862f1066cf2c8132" dependencies = [ "cc", "cstr_core", + "js-sys", "libc", + "wasm-bindgen", "web-sys", "winapi 0.3.9", ] @@ -4804,9 +4798,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" dependencies = [ "itoa 1.0.2", "libc", @@ -4844,7 +4838,7 @@ dependencies = [ "bytes", "libc", "memchr", - "mio 0.8.3", + "mio 0.8.4", "num_cpus", "once_cell", "parking_lot 0.12.1", @@ -4880,7 +4874,7 @@ dependencies = [ [[package]] name = "tokio-socks" version = "0.5.1" -source = "git+https://github.com/open-trade/tokio-socks#3de8300fbce37e2cdaef042e016aa95058d007cf" +source = "git+https://github.com/open-trade/tokio-socks#c34272f219b24dc6508f13fa81eff9850e616ce2" dependencies = [ "bytes", "either", @@ -4934,9 +4928,9 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" @@ -4963,9 +4957,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" dependencies = [ "once_cell", ] @@ -5040,15 +5034,15 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "unicode-normalization" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd" dependencies = [ "tinyvec", ] @@ -5161,12 +5155,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5175,9 +5163,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -5185,9 +5173,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" dependencies = [ "bumpalo", "lazy_static", @@ -5200,9 +5188,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5212,9 +5200,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5222,9 +5210,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" dependencies = [ "proc-macro2", "quote", @@ -5235,9 +5223,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" [[package]] name = "wayland-client" @@ -5314,9 +5302,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/README-AR.md b/README-AR.md new file mode 100644 index 000000000..055a654d2 --- /dev/null +++ b/README-AR.md @@ -0,0 +1,190 @@ +

+ RustDesk - Your remote desktop
+ Servers • + Build • + Docker • + Structure • + Snapshot
+ [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
+ لغتك الأم, Doc و RustDesk UI, README نحن بحاجة إلى مساعدتك لترجمة هذا +

+ +[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) + +.Rustبرنامج آخر لسطح المكتب عن بعد، مكتوب بـ +يعمل خارج الصندوق، لا حاجة إلى إعدادات. لديك سيطرة كاملة على بياناتك، دون مخاوف بشأن الأمن. يمكنك استخدام خادم + الخاص بنا rendezvous/relay +[جهز لنفسك واحدا](https://rustdesk.com/server), أو +[خاص بك rendezvous/relay أكتب خادم](https://github.com/rustdesk/rustdesk-server-demo). + +![image](https://user-images.githubusercontent.com/71636191/171661982-430285f0-2e12-4b1d-9957-4a58e375304d.png) + +لمساعدتك على ذلك [`CONTRIBUTING.md`](CONTRIBUTING.md) يرحب بمساهمة الجميع. اطلع على RustDesk. + +[**؟ RustDesk كيفية يعمل**](https://github.com/rustdesk/rustdesk/wiki/How-does-RustDesk-work%3F) + +[**BINARY تنزيل**](https://github.com/rustdesk/rustdesk/releases) + +## خوادم مفتوحة ومجانية + +فيما يلي الخوادم التي تستخدمها مجانًا، وقد تتغير طوال الوقت. إذا لم تكن قريبًا من أحد هؤلاء، فقد تكون شبكتك بطيئة. +| الموقع | المورد | المواصفات | +| --------- | ------------- | ------------------ | +| Seoul | AWS lightsail | 1 VCPU / 0.5GB RAM | +| Singapore | Vultr | 1 VCPU / 1GB RAM | +| Dallas | Vultr | 1 VCPU / 1GB RAM | | + +## التبعيات + + لواجهة المستخدم الرسومية [sciter](https://sciter.com/) نسخة سطح المكتب تستخدم + بنفسك sciter dynamic library عليك تحميل + +[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) + + Sciter إلى Flutter سنقوم بترحيل نسخة سطح المكتب من .Flutter تستخدم إصدارات الهاتف المحمول. + +## خطوات البناء + +- C++ build env و Rust development env قم بإعداد + +- بطريقة صحيحة `VCPKG_ROOT` env variable وأعد [vcpkg](https://github.com/microsoft/vcpkg) ثبت + + - Windows: `vcpkg install libvpx:x64-windows-static libyuv:x64-windows-static opus:x64-windows-static` + - Linux/MacOS: `vcpkg install libvpx libyuv opus` + +- run `cargo run` + +## [البناء](https://rustdesk.com/docs/en/dev/build/) + +## Linux + +### 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 +``` + +### pynput package تثبيت + +```sh +pip3 install pynput +``` + +### vcpkg تثبيت + +```sh +git clone https://github.com/microsoft/vcpkg +cd vcpkg +git checkout 2021.12.01 +cd .. +vcpkg/bootstrap-vcpkg.sh +export VCPKG_ROOT=$HOME/vcpkg +vcpkg/vcpkg install libvpx libyuv opus +``` + +### Fix libvpx (For Fedora) + +```sh +cd vcpkg/buildtrees/libvpx/src +cd * +./configure +sed -i 's/CFLAGS+=-I/CFLAGS+=-fPIC -I/g' Makefile +sed -i 's/CXXFLAGS+=-I/CXXFLAGS+=-fPIC -I/g' Makefile +make +cp libvpx.a $HOME/vcpkg/installed/x64-linux/lib/ +cd +``` + +### البناء + +```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 +``` + +### X11 (Xorg) إلى Wayland تغيير + +افتراضية GNOME session ك Xorg إتبع [هذه](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/) الخطوات لإعداد Wayland لا تدعم RustDesk + +## Docker طريقة البناء باستخدام + +ابدأ باستنساخ المستودع وبناء الكونتاينر: + +```sh +git clone https://github.com/rustdesk/rustdesk +cd rustdesk +docker build -t "rustdesk-builder" . +``` + +ثم، في كل مرة تحتاج إلى بناء التطبيق، قم بتشغيل الأمر التالي: + +```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 +``` + +لاحظ أن البناء الأول قد يستغرق وقتًا أطول قبل تخزين التبعيات، وسيكون البناء اللاحق أسرع. بالإضافة إلى ذلك، إذا كنت بحاجة إلى تحديد وسائط مختلفة لأمر البناء، فيمكنك القيام بذلك في نهاية الأمر بوضع +`` +على سبيل المثال، إذا كنت ترغب في بناء إصدار محسن، فستقوم بتشغيل الأمر أعلاه متبوعًا بـ +`--release` +:سيكون الملف القابل للتنفيذ الناتج متاحًا في مجلد تارغت، ويمكن تشغيله باستخدام + +```sh +target/debug/rustdesk +``` + +:أو في حال قمت ببناء إصدار محسن + +```sh +target/release/rustdesk +``` + +RustDesk يرجى التأكد من أنك تنفذ هذه الأوامر من جذر مستودع +وإلا فقد لا يتمكن التطبيق من العثور على الموارد المطلوبة. لاحظ أيضًا أن الأوامر الفرعية الأخرى مثل +`install` أو `run` +لا يتم دعمها حاليًا عبر هذه الطريقة لأنها ستقوم بتثبيت أو تشغيل البرنامج داخل الكونتاينر بدلاً من الهوست. + +## هيكل الملف + +- **[libs/hbb_common](https://github.com/rustdesk/rustdesk/tree/master/libs/hbb_common)**: وظائف لنقل الملفات، وبعض وظائف المرافق الأخرى tcp/udp، protobuf ترميز الفيديو، إعدادات + +- **[libs/scrap](https://github.com/rustdesk/rustdesk/tree/master/libs/scrap)**: التقاط الشاشة +- **[libs/enigo](https://github.com/rustdesk/rustdesk/tree/master/libs/enigo)**: التحكم في لوحة المفاتيح/الماوس الخاصة بكل منصة +- **[src/ui](https://github.com/rustdesk/rustdesk/tree/master/src/ui)**: واجهة المستخدم الرسومية +- **[src/server](https://github.com/rustdesk/rustdesk/tree/master/src/server)**: خدمات الصوت/الحافظة/المدخلات/الفيديو، ووصلات الشبكة +- **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: بدء اتصال متقارن +- **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: أو المنقول عن بُعد (TCP hole punching) انتظر الاتصال المباشر [rustdesk-server](https://github.com/rustdesk/rustdesk-server) الإتصال ب +- **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: رمز خاص بكل منصة +- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: رمز الهاتف المحمول +- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**:Flutter لعميل الويب الخاص ب Javascript + +## لقطات + +![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-CS.md b/README-CS.md index 2606e2def..1dd5463a1 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] | [한국어]
+ [中文] | [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 217fbc76a..3f770d226 100644 --- a/README-DE.md +++ b/README-DE.md @@ -5,7 +5,7 @@ DockerDateistrukturScreenshots
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Wir brauchen deine Hilfe um diese README Datei zu verbessern und aktualisieren

diff --git a/README-EO.md b/README-EO.md index 224caa064..21a4f9521 100644 --- a/README-EO.md +++ b/README-EO.md @@ -5,7 +5,7 @@ DockerStrukturoEkrankopio
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Ni bezonas helpon traduki tiun README kaj la interfacon al via denaska lingvo

diff --git a/README-ES.md b/README-ES.md index 19a0484ef..ce8601fa0 100644 --- a/README-ES.md +++ b/README-ES.md @@ -5,11 +5,11 @@ DockerEstructuraCaptura de pantalla
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Necesitamos tu ayuda para traducir este README a tu idioma

-Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Reddit](https://www.reddit.com/r/rustdesk) +Chatea con nosotros: [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) @@ -23,9 +23,11 @@ RustDesk agradece la contribución de todo el mundo. Ve [`CONTRIBUTING.md`](CONT A continuación se muestran los servidores que está utilizando de forma gratuita, puede cambiar en algún momento. Si no estás cerca de uno de ellos, tu red puede ser lenta. -- Seoul, AWS lightsail, 1 VCPU/0.5G RAM -- Singapore, Vultr, 1 VCPU/1G RAM -- Dallas, Vultr, 1 VCPU/1G RAM +| Ubicación | Vendedor | Especificación | +| --------- | ------------- | ------------------ | +| Seoul | AWS lightsail | 1 VCPU / 0.5GB RAM | +| Singapore | Vultr | 1 VCPU / 1GB RAM | +| Dallas | Vultr | 1 VCPU / 1GB RAM | | ## Dependencies @@ -37,7 +39,7 @@ La versión Desktop usa [sciter](https://sciter.com/) para GUI, por favor bajate ## Pasos para compilar desde el inicio -- Prepara el entono de desarrollode Rust y el entorno de compilación de C++ y Rust. +- Prepara el entono de desarrollo de Rust y el entorno de compilación de C++ y Rust. - Instala [vcpkg](https://github.com/microsoft/vcpkg), y configura la variable de entono `VCPKG_ROOT` correctamente. @@ -78,7 +80,7 @@ export VCPKG_ROOT=$HOME/vcpkg vcpkg/vcpkg install libvpx libyuv opus ``` -### Soluciona libvpx (For Fedora) +### Soluciona libvpx (Para Fedora) ```sh cd vcpkg/buildtrees/libvpx/src @@ -124,7 +126,7 @@ Entonces, cada vez que necesites compilar una modificación, ejecuta el siguient 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 ``` -Ten en cuenta que la primera compilación puede tardar más tiempo antes de que las dependencias se almacenen en la caché, las siguientes compilaciones serán más rápidas. Además, si necesitas especificar diferentes argumentos a la orden de compilación, puede hacerlo al final de la linea de comandos en el apartado``. Por ejemplo, si desea compilar una versión optimizada para publicación, deberá ejecutar el comando anterior seguido de `--release`. El ejecutable resultante estará disponible en la carpeta de destino en su sistema, y puede ser ejecutado con: +Ten en cuenta que la primera compilación puede tardar más tiempo antes de que las dependencias se almacenen en la caché, las siguientes compilaciones serán más rápidas. Además, si necesitas especificar diferentes argumentos a la orden de compilación, puede hacerlo al final de la linea de comandos en el apartado ``. Por ejemplo, si desea compilar una versión optimizada para publicación, deberá ejecutar el comando anterior seguido de `--release`. El ejecutable resultante estará disponible en la carpeta de destino en su sistema, y puede ser ejecutado con: ```sh target/debug/rustdesk @@ -148,6 +150,8 @@ Por favor, asegurate de que estás ejecutando estos comandos desde la raíz del - **[src/client.rs](https://github.com/rustdesk/rustdesk/tree/master/src/client.rs)**: iniciar una conexión "peer to peer" - **[src/rendezvous_mediator.rs](https://github.com/rustdesk/rustdesk/tree/master/src/rendezvous_mediator.rs)**: Comunicación con [rustdesk-server](https://github.com/rustdesk/rustdesk-server), esperar la conexión remota directa ("TCP hole punching") o conexión indirecta ("relayed") - **[src/platform](https://github.com/rustdesk/rustdesk/tree/master/src/platform)**: código específico de cada plataforma +- **[flutter](https://github.com/rustdesk/rustdesk/tree/master/flutter)**: Flutter, código para moviles +- **[flutter/web/js](https://github.com/rustdesk/rustdesk/tree/master/flutter/web/js)**: Javascript para cliente web Flutter ## Captura de pantalla diff --git a/README-FA.md b/README-FA.md index 33b9f29bd..0f7ca1a95 100644 --- a/README-FA.md +++ b/README-FA.md @@ -5,7 +5,7 @@ داکرساختسرور
- [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
برای ترجمه این RustDesk UI ،README و Doc به زبان مادری شما به کمکتون نیاز داریم

diff --git a/README-FI.md b/README-FI.md index ea923170e..a2d7534e0 100644 --- a/README-FI.md +++ b/README-FI.md @@ -5,7 +5,7 @@ DockerRakenneTilannevedos
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Tarvitsemme apua tämän README-tiedoston kääntämiseksi äidinkielellesi

diff --git a/README-FR.md b/README-FR.md index 9dbeaecf2..b1f8e3670 100644 --- a/README-FR.md +++ b/README-FR.md @@ -5,7 +5,7 @@ Docker - Structure - Images
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Nous avons besoin de votre aide pour traduire ce README dans votre langue maternelle.

diff --git a/README-ID.md b/README-ID.md index 31400a945..624336f45 100644 --- a/README-ID.md +++ b/README-ID.md @@ -5,7 +5,7 @@ DockerStructureSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [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

diff --git a/README-IT.md b/README-IT.md index 6a0df6ac6..7eba7860a 100644 --- a/README-IT.md +++ b/README-IT.md @@ -5,7 +5,7 @@ DockerStrutturaScreenshots
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Abbiamo bisogno del tuo aiuto per tradurre questo README e la RustDesk UI nella tua lingua nativa

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

diff --git a/README-NL.md b/README-NL.md index 0b355a273..2d87504db 100644 --- a/README-NL.md +++ b/README-NL.md @@ -5,7 +5,7 @@ DockerStructuurSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
We hebben je hulp nodig om deze README te vertalen naar jouw moedertaal

diff --git a/README-PL.md b/README-PL.md index 2922eae0d..162ca7648 100644 --- a/README-PL.md +++ b/README-PL.md @@ -5,7 +5,7 @@ DockerStrukturaSnapshot
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Potrzebujemy twojej pomocy w tłumaczeniu README na twój ojczysty język

diff --git a/README-PTBR.md b/README-PTBR.md index 7eff79566..76b360283 100644 --- a/README-PTBR.md +++ b/README-PTBR.md @@ -5,7 +5,7 @@ DockerEstruturaScreenshots
- [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [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

diff --git a/README-RU.md b/README-RU.md index ffc1256d4..54c161cf0 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] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어] | [العربي]
Нам нужна ваша помощь для перевода этого README и RustDesk UI на ваш родной язык

diff --git a/README-ZH.md b/README-ZH.md index 00176a1c4..8d4203b16 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] | [한국어]
+ [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [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 a9ddc6071..2166073a7 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,12 @@ DockerStructureSnapshot
- [česky] | [中文] | [Español] | [فارسی] | [Français] | [Deutsch] | [Polski] | [Indonesian] | [Suomi] | [മലയാളം] | [日本語] | [Nederlands] | [Italiano] | [Русский] | [Português (Brasil)] | [Esperanto] | [한국어]
+ [česky] | [中文] | [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

Chat with us: [Discord](https://discord.gg/nDceKgxnkV) | [Twitter](https://twitter.com/rustdesk) | [Reddit](https://www.reddit.com/r/rustdesk) - [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I04VU09) Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, [set up your own](https://rustdesk.com/server), or [write your own rendezvous/relay server](https://github.com/rustdesk/rustdesk-server-demo). @@ -24,6 +23,10 @@ RustDesk welcomes contribution from everyone. See [`CONTRIBUTING.md`](CONTRIBUTI [**BINARY DOWNLOAD**](https://github.com/rustdesk/rustdesk/releases) +[Get it on F-Droid](https://f-droid.org/en/packages/com.carriez.flutter_hbb) + ## Free Public Servers Below are the servers you are using for free, it may change along the time. If you are not close to one of these, your network may be slow. diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index e32132ee7..966ad3df8 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -1 +1,11 @@ -Yet another remote desktop software, written in Rust. Works out of the box, no configuration required. You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, set up your own, or write your own rendezvous/relay server. \ No newline at end of file +An open-source remote desktop application, the open source TeamViewer alternative. +Source code: https://github.com/rustdesk/rustdesk +Doc: https://rustdesk.com/docs/en/manual/mobile/ + +In order for a remote device to control your Android device via mouse or touch, you need to allow RustDesk to use the "Accessibility" service, RustDesk uses AccessibilityService API to implement Addroid remote control. + +In addtion to remote control, you can also transfer files between Android devices and PCs easily with RustDesk. + +You have full control of your data, with no concerns about security. You can use our rendezvous/relay server, or self-hosting, or write your own rendezvous/relay server. Self-hosting server is free and open source: https://github.com/rustdesk/rustdesk-server + +Please download and install desktop version from: https://rustdesk.com, then you can access and control your desktop from your mobile, or control your mobile from desktop. diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt index b88c8e1d7..357fb37ab 100644 --- a/fastlane/metadata/android/en-US/short_description.txt +++ b/fastlane/metadata/android/en-US/short_description.txt @@ -1 +1 @@ -Yet another remote desktop software \ No newline at end of file +An open-source remote desktop application, the open source TeamViewer alternative. diff --git a/fastlane/metadata/android/zh-CN/full_description.txt b/fastlane/metadata/android/zh-CN/full_description.txt index 51fad180b..f1f44057e 100644 --- a/fastlane/metadata/android/zh-CN/full_description.txt +++ b/fastlane/metadata/android/zh-CN/full_description.txt @@ -1,3 +1,12 @@ -远程桌面软件,开箱即用,无需任何配置。您完全掌控数据,不用担心安全问题。您可以使用我们的注册/中继服务器, -或者自己设置, -亦或者开发您的版本。 \ No newline at end of file +开源远程桌面应用,开源 TeamViewer 替代方案。 +源代码:https://github.com/rustdesk/rustdesk +文档:https://rustdesk.com/docs/en/manual/mobile/ + +为了让远程设备通过鼠标或触摸控制您的 Android 设备,您需要允许 RustDesk 使用“Accessibility”服务,RustDesk 使用 AccessibilityService API 来实现 Addroid 远程控制。 + +除了远程控制,您还可以使用 RustDesk 在 Android 设备和 PC 之间轻松传输文件。 + +您完全掌控数据,不用担心安全问题。您可以使用我们的注册/中继服务器,或者自建,亦或者开发您的版本。 +自托管服务器是免费和开源的:https://github.com/rustdesk/rustdesk-server + +请从:https://rustdesk.com 下载并安装桌面版,然后您可以通过手机访问和控制您的桌面,或从桌面控制您的手机。 diff --git a/fastlane/metadata/android/zh-CN/short_description.txt b/fastlane/metadata/android/zh-CN/short_description.txt index ac41efbee..69a4a5b52 100644 --- a/fastlane/metadata/android/zh-CN/short_description.txt +++ b/fastlane/metadata/android/zh-CN/short_description.txt @@ -1 +1 @@ -远程桌面软件 \ No newline at end of file +开源远程桌面应用,开源 TeamViewer 替代方案 diff --git a/flutter/build_android_deps.sh b/flutter/build_android_deps.sh index 566196d52..f120346cf 100755 --- a/flutter/build_android_deps.sh +++ b/flutter/build_android_deps.sh @@ -4,7 +4,7 @@ # Required: # 1. set VCPKG_ROOT / ANDROID_NDK path environment variables # 2. vcpkg initialized -# 3. ndk >= 22 (if ndk< 22 you need to change LD as `export LD=$TOOLCHAIN/bin/aarch64-linux-android-ld`) +# 3. ndk, version: 22 (if ndk < 22 you need to change LD as `export LD=$TOOLCHAIN/bin/$NDK_LLVM_TARGET-ld`) if [ -z "$ANDROID_NDK" ]; then echo "Failed! Please set ANDROID_NDK" @@ -16,66 +16,110 @@ if [ -z "$VCPKG_ROOT" ]; then exit 1 fi -PREFIX=$VCPKG_ROOT/installed/arm64-android/ +API_LEVEL="21" -echo "*** [Start] Build opus / libyuv from vcpkg" -export ANDROID_NDK_HOME=$ANDROID_NDK -pushd $VCPKG_ROOT -$VCPKG_ROOT/vcpkg install opus --triplet arm64-android -$VCPKG_ROOT/vcpkg install libyuv --triplet arm64-android -popd -echo "*** [Finished] Build opus / libyuv from vcpkg" +# NDK llvm toolchain +HOST_TAG="linux-x86_64" # current platform, set as `ls $ANDROID_NDK/toolchains/llvm/prebuilt/` +TOOLCHAIN=$ANDROID_NDK/toolchains/llvm/prebuilt/$HOST_TAG + +function build { + ANDROID_ABI=$1 + VCPKG_TARGET=$2 + NDK_LLVM_TARGET=$3 + LIBVPX_TARGET=$4 + + PREFIX=$VCPKG_ROOT/installed/$VCPKG_TARGET/ + + # 1 + echo "*** [$ANDROID_ABI][Start] Build opus / libyuv from vcpkg" + export ANDROID_NDK_HOME=$ANDROID_NDK + pushd $VCPKG_ROOT + $VCPKG_ROOT/vcpkg install opus --triplet $VCPKG_TARGET + $VCPKG_ROOT/vcpkg install libyuv --triplet $VCPKG_TARGET + popd + echo "*** [$ANDROID_ABI][Finished] Build opus / libyuv from vcpkg" + + # 2 + echo "*** [$ANDROID_ABI][Start] Build libvpx" + pushd build/libvpx + export AR=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ar + export AS=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-as + export LD=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ld.gold # if ndk < 22, use aarch64-linux-android-ld + export RANLIB=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-ranlib + export STRIP=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}-strip + + if [ $NDK_LLVM_TARGET == "arm-linux-androideabi" ] + then + export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi${API_LEVEL}-clang + export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi${API_LEVEL}-clang++ + else + export CC=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}${API_LEVEL}-clang + export CXX=$TOOLCHAIN/bin/${NDK_LLVM_TARGET}${API_LEVEL}-clang++ + fi + make clean + ./configure --target=$LIBVPX_TARGET \ + --enable-pic --disable-vp8 \ + --disable-webm-io \ + --disable-unit-tests \ + --disable-examples \ + --disable-libyuv \ + --disable-postproc \ + --disable-vp8 \ + --disable-tools \ + --disable-docs \ + --prefix=$PREFIX + make -j5 + make install + + popd + echo "*** [$ANDROID_ABI][Finished] Build libvpx" + + # 3 + echo "*** [$ANDROID_ABI][Start] Build oboe" + pushd build/oboe + make clean + cmake -DBUILD_SHARED_LIBS=true \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DANDROID_TOOLCHAIN=clang \ + -DANDROID_STL=c++_shared \ + -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ + -DCMAKE_INSTALL_PREFIX=$PREFIX \ + -DANDROID_ABI=$ANDROID_ABI \ + -DANDROID_PLATFORM=android-$API_LEVEL + make -j5 + make install + mv $PREFIX/lib/$ANDROID_ABI/liboboe.a $PREFIX/lib/ + popd + echo "*** [$ANDROID_ABI][Finished] Build oboe" + + echo "*** [$ANDROID_ABI][All Finished]" +} -echo "*** [Start] Build libvpx" git clone -b v1.11.0 --depth=1 https://github.com/webmproject/libvpx.git build/libvpx -pushd build/libvpx -export NDK=$ANDROID_NDK -export HOST_TAG=linux-x86_64 -export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG -export AR=$TOOLCHAIN/bin/aarch64-linux-android-ar -export AS=$TOOLCHAIN/bin/aarch64-linux-android-as -export CC=$TOOLCHAIN/bin/aarch64-linux-android21-clang -export CXX=$TOOLCHAIN/bin/aarch64-linux-android21-clang++ -export LD=$TOOLCHAIN/bin/aarch64-linux-android-ld.gold # if ndk < 22, use aarch64-linux-android-ld -export RANLIB=$TOOLCHAIN/bin/aarch64-linux-android-ranlib -export STRIP=$TOOLCHAIN/bin/aarch64-linux-android-strip - -./configure --target=arm64-android-gcc \ - --enable-pic --disable-vp8 \ - --disable-webm-io \ - --disable-unit-tests \ - --disable-examples \ - --disable-libyuv \ - --disable-postproc \ - --disable-vp8 \ - --disable-tools \ - --disable-docs \ - --prefix=$PREFIX -make -j5 -make install - -popd -echo "*** [Finished] Build libvpx" - - -echo "*** [Start] Build oboe" git clone -b 1.6.1 --depth=1 https://github.com/google/oboe build/oboe -patch -d build/oboe -p1 < ../src/oboe.patch -pushd build/oboe -cmake -DBUILD_SHARED_LIBS=true \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DANDROID_TOOLCHAIN=clang \ - -DANDROID_STL=c++_shared \ - -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \ - -DCMAKE_INSTALL_PREFIX=$PREFIX \ - -DANDROID_ABI=arm64-v8a \ - -DANDROID_PLATFORM=android-21 -make -j5 -make install -mv $PREFIX/lib/arm64-v8a/liboboe.a $PREFIX/lib/ -popd -echo "*** [Finished] Build oboe" -echo "*** [All Finished]" +patch -N -d build/oboe -p1 < ../src/oboe.patch + +# VCPKG_TARGET ANDROID_ABI +# arm64-android arm64-v8a +# arm-android armeabi-v7a +# x64-android x86_64 +# x86-android x86 + +# NDK_LLVM_TARGET +# aarch64-linux-android +# arm-linux-androideabi +# x86_64-linux-android +# i686-linux-android + +# LIBVPX_TARGET : +# arm64-android-gcc +# armv7-android-gcc +# x86_64-android-gcc +# x86-android-gcc + +# args: ANDROID_ABI VCPKG_TARGET NDK_LLVM_TARGET LIBVPX_TARGET +build arm64-v8a arm64-android aarch64-linux-android arm64-android-gcc +build armeabi-v7a arm-android arm-linux-androideabi armv7-android-gcc # rm -rf build/libvpx # rm -rf build/oboe \ No newline at end of file diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index c6a70460d..d6be51986 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -233,7 +233,7 @@ class AccessibilityListener extends StatelessWidget { } }, onPointerUp: (evt) { - if (evt.size == 1 && GestureBinding.instance != null) { + if (evt.size == 1) { GestureBinding.instance.handlePointerEvent(PointerUpEvent( pointer: evt.pointer + offset, size: 0.1, @@ -243,7 +243,7 @@ class AccessibilityListener extends StatelessWidget { } }, onPointerMove: (evt) { - if (evt.size == 1 && GestureBinding.instance != null) { + if (evt.size == 1) { GestureBinding.instance.handlePointerEvent(PointerMoveEvent( pointer: evt.pointer + offset, size: 0.1, diff --git a/flutter/lib/models/file_model.dart b/flutter/lib/models/file_model.dart index 49184cf5b..e4afc892f 100644 --- a/flutter/lib/models/file_model.dart +++ b/flutter/lib/models/file_model.dart @@ -593,15 +593,7 @@ class FileFetcher { tryCompleteTask(String? msg, String? isLocalStr) { if (msg == null || isLocalStr == null) return; - late final isLocal; late final tasks; - if (isLocalStr == "true") { - isLocal = true; - } else if (isLocalStr == "false") { - isLocal = false; - } else { - return; - } try { final fd = FileDirectory.fromJson(jsonDecode(msg)); if (fd.id > 0) { diff --git a/flutter/lib/models/native_model.dart b/flutter/lib/models/native_model.dart index f6824dda8..a600e372e 100644 --- a/flutter/lib/models/native_model.dart +++ b/flutter/lib/models/native_model.dart @@ -21,7 +21,6 @@ typedef F2 = Pointer Function(Pointer, Pointer); typedef F3 = void Function(Pointer, Pointer); class PlatformFFI { - static Pointer? _lastRgbaFrame; static String _dir = ''; static String _homeDir = ''; static F2? _getByName; diff --git a/flutter/lib/models/web_model.dart b/flutter/lib/models/web_model.dart index d9668272a..3ec6c9b9f 100644 --- a/flutter/lib/models/web_model.dart +++ b/flutter/lib/models/web_model.dart @@ -1,3 +1,5 @@ +// ignore_for_file: avoid_web_libraries_in_flutter + import 'dart:convert'; import 'dart:typed_data'; import 'dart:js'; diff --git a/flutter/lib/pages/connection_page.dart b/flutter/lib/pages/connection_page.dart index 2cfbaa63b..90f290136 100644 --- a/flutter/lib/pages/connection_page.dart +++ b/flutter/lib/pages/connection_page.dart @@ -100,8 +100,8 @@ class _ConnectionPageState extends State { : InkWell( onTap: () async { final url = _updateUrl + '.apk'; - if (await canLaunch(url)) { - await launch(url); + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(Uri.parse(url)); } }, child: Container( diff --git a/flutter/lib/pages/remote_page.dart b/flutter/lib/pages/remote_page.dart index 9feef9577..b93069203 100644 --- a/flutter/lib/pages/remote_page.dart +++ b/flutter/lib/pages/remote_page.dart @@ -126,7 +126,7 @@ class _RemotePageState extends State { common < oldValue.length && common < newValue.length && newValue[common] == oldValue[common]; - ++common); + ++common) {} for (i = 0; i < oldValue.length - common; ++i) { FFI.inputKey('VK_BACK'); } diff --git a/flutter/lib/pages/settings_page.dart b/flutter/lib/pages/settings_page.dart index 90ff0d564..2c8b7fe9a 100644 --- a/flutter/lib/pages/settings_page.dart +++ b/flutter/lib/pages/settings_page.dart @@ -68,8 +68,8 @@ class _SettingsState extends State { tiles: [ SettingsTile.navigation( onPressed: (context) async { - if (await canLaunch(url)) { - await launch(url); + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(Uri.parse(url)); } }, title: Text(translate("Version: ") + version), @@ -105,8 +105,8 @@ void showAbout() { InkWell( onTap: () async { const url = 'https://rustdesk.com/'; - if (await canLaunch(url)) { - await launch(url); + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(Uri.parse(url)); } }, child: Padding( @@ -149,7 +149,7 @@ fetch('http://localhost:21114/api/login', { 'uuid': FFI.getByName('uuid') }; try { - final response = await http.post(Uri.parse('${url}/api/login'), + final response = await http.post(Uri.parse('$url/api/login'), headers: {"Content-Type": "application/json"}, body: json.encode(body)); return parseResp(response.body); } catch (e) { @@ -186,7 +186,7 @@ void refreshCurrentUser() async { 'uuid': FFI.getByName('uuid') }; try { - final response = await http.post(Uri.parse('${url}/api/currentUser'), + final response = await http.post(Uri.parse('$url/api/currentUser'), headers: { "Content-Type": "application/json", "Authorization": "Bearer $token" @@ -212,7 +212,7 @@ void logout() async { 'uuid': FFI.getByName('uuid') }; try { - await http.post(Uri.parse('${url}/api/logout'), + await http.post(Uri.parse('$url/api/logout'), headers: { "Content-Type": "application/json", "Authorization": "Bearer $token" @@ -242,7 +242,7 @@ String getUrl() { url = 'http://${tmp[0]}:$port'; } } else { - url = 'http://${url}:21114'; + url = 'http://$url:21114'; } } } diff --git a/flutter/lib/widgets/gestures.dart b/flutter/lib/widgets/gestures.dart index d70fe05e6..960439678 100644 --- a/flutter/lib/widgets/gestures.dart +++ b/flutter/lib/widgets/gestures.dart @@ -594,10 +594,7 @@ class _TapTracker { required this.entry, required Duration doubleTapMinTime, required this.gestureSettings, - }) : assert(doubleTapMinTime != null), - assert(event != null), - assert(event.buttons != null), - pointer = event.pointer, + }) : pointer = event.pointer, _initialGlobalPosition = event.position, initialButtons = event.buttons, _doubleTapMinTimeCountdown = @@ -643,7 +640,7 @@ class _TapTracker { /// CountdownZoned tracks whether the specified duration has elapsed since /// creation, honoring [Zone]. class _CountdownZoned { - _CountdownZoned({required Duration duration}) : assert(duration != null) { + _CountdownZoned({required Duration duration}) { Timer(duration, _onTimeout); } diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 10e04d404..273e7a6c5 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -447,9 +447,9 @@ message PermissionInfo { enum ImageQuality { NotSet = 0; - Low = 2; - Balanced = 3; - Best = 4; + Low = 50; + Balanced = 66; + Best = 100; } message VideoCodecState { @@ -481,6 +481,8 @@ message OptionMessage { message TestDelay { int64 time = 1; bool from_client = 2; + uint32 last_delay = 3; + uint32 target_bitrate = 4; } message PublicKey { diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index f01ac36de..2ddfc5d0d 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -16,7 +16,7 @@ pub const RENDEZVOUS_TIMEOUT: u64 = 12_000; pub const CONNECT_TIMEOUT: u64 = 18_000; pub const REG_INTERVAL: i64 = 12_000; pub const COMPRESS_LEVEL: i32 = 3; -const SERIAL: i32 = 1; +const SERIAL: i32 = 3; // 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= @@ -140,6 +140,8 @@ pub struct PeerConfig { pub disable_clipboard: bool, #[serde(default)] pub enable_file_transfer: bool, + #[serde(default)] + pub show_quality_monitor: bool, // the other scalar value must before this #[serde(default)] diff --git a/libs/scrap/src/common/android.rs b/libs/scrap/src/common/android.rs index 1975a6505..8322da3cd 100644 --- a/libs/scrap/src/common/android.rs +++ b/libs/scrap/src/common/android.rs @@ -3,8 +3,8 @@ use crate::rgba_to_i420; use lazy_static::lazy_static; use serde_json::Value; use std::collections::HashMap; -use std::io; use std::sync::Mutex; +use std::{io, time::Duration}; lazy_static! { static ref SCREEN_SIZE: Mutex<(u16, u16, u16)> = Mutex::new((0, 0, 0)); // (width, height, scale) @@ -33,7 +33,7 @@ impl Capturer { self.display.height() as usize } - pub fn frame<'a>(&'a mut self, _timeout_ms: u32) -> io::Result> { + pub fn frame<'a>(&'a mut self, _timeout: Duration) -> io::Result> { if let Some(buf) = get_video_raw() { crate::would_block_if_equal(&mut self.saved_raw_data, buf)?; rgba_to_i420(self.width(), self.height(), buf, &mut self.bgra); diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index bfc93fabc..f55a4bfd3 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -50,6 +50,8 @@ pub trait EncoderApi { fn encode_to_message(&mut self, frame: &[u8], ms: i64) -> ResultType; fn use_yuv(&self) -> bool; + + fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()>; } pub struct DecoderCfg { diff --git a/libs/scrap/src/common/dxgi.rs b/libs/scrap/src/common/dxgi.rs index c0b4130bb..1a8c39885 100644 --- a/libs/scrap/src/common/dxgi.rs +++ b/libs/scrap/src/common/dxgi.rs @@ -1,5 +1,6 @@ use crate::dxgi; use std::io::ErrorKind::{NotFound, TimedOut, WouldBlock}; +use std::time::Duration; use std::{io, ops}; pub struct Capturer { @@ -40,8 +41,8 @@ impl Capturer { self.height } - pub fn frame<'a>(&'a mut self, timeout_ms: u32) -> io::Result> { - match self.inner.frame(timeout_ms) { + pub fn frame<'a>(&'a mut self, timeout_ms: Duration) -> io::Result> { + match self.inner.frame(timeout_ms.as_millis() as _) { Ok(frame) => Ok(Frame(frame)), Err(ref error) if error.kind() == TimedOut => Err(WouldBlock.into()), Err(error) => Err(error), @@ -135,7 +136,7 @@ impl CapturerMag { pub fn get_rect(&self) -> ((i32, i32), usize, usize) { self.inner.get_rect() } - pub fn frame<'a>(&'a mut self, _timeout_ms: u32) -> io::Result> { + pub fn frame<'a>(&'a mut self, _timeout_ms: Duration) -> io::Result> { self.inner.frame(&mut self.data)?; Ok(Frame(&self.data)) } diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index ec4e7082c..076304067 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -140,6 +140,10 @@ impl EncoderApi for HwEncoder { fn use_yuv(&self) -> bool { false } + + fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()> { + todo!() + } } impl HwEncoder { diff --git a/libs/scrap/src/common/vpxcodec.rs b/libs/scrap/src/common/vpxcodec.rs index 4067996d0..2943419e4 100644 --- a/libs/scrap/src/common/vpxcodec.rs +++ b/libs/scrap/src/common/vpxcodec.rs @@ -119,17 +119,6 @@ impl EncoderApi for VpxEncoder { c.rc_target_bitrate = config.bitrate; c.rc_undershoot_pct = 95; c.rc_dropframe_thresh = 25; - if config.rc_min_quantizer > 0 { - c.rc_min_quantizer = config.rc_min_quantizer; - } - if config.rc_max_quantizer > 0 { - c.rc_max_quantizer = config.rc_max_quantizer; - } - let mut speed = config.speed; - if speed <= 0 { - speed = 6; - } - c.g_threads = if config.num_threads == 0 { num_cpus::get() as _ } else { @@ -174,7 +163,7 @@ impl EncoderApi for VpxEncoder { Higher numbers (7 or 8) will be lower quality but more manageable for lower latency use cases and also for lower CPU power devices such as mobile. */ - call_vpx!(vpx_codec_control_(&mut ctx, VP8E_SET_CPUUSED as _, speed,)); + call_vpx!(vpx_codec_control_(&mut ctx, VP8E_SET_CPUUSED as _, 7,)); // set row level multi-threading /* as some people in comments and below have already commented, @@ -232,6 +221,13 @@ impl EncoderApi for VpxEncoder { fn use_yuv(&self) -> bool { true } + + fn set_bitrate(&mut self, bitrate: u32) -> ResultType<()> { + let mut new_enc_cfg = unsafe { *self.ctx.config.enc.to_owned() }; + new_enc_cfg.rc_target_bitrate = bitrate; + call_vpx!(vpx_codec_enc_config_set(&mut self.ctx, &new_enc_cfg)); + return Ok(()); + } } impl VpxEncoder { @@ -336,9 +332,6 @@ pub struct VpxEncoderConfig { pub bitrate: c_uint, /// The codec pub codec: VpxVideoCodecId, - pub rc_min_quantizer: u32, - pub rc_max_quantizer: u32, - pub speed: i32, pub num_threads: u32, } diff --git a/libs/scrap/src/common/x11.rs b/libs/scrap/src/common/x11.rs index f8217e3b7..255819902 100644 --- a/libs/scrap/src/common/x11.rs +++ b/libs/scrap/src/common/x11.rs @@ -1,5 +1,5 @@ use crate::x11; -use std::{io, ops}; +use std::{io, ops, time::Duration}; pub struct Capturer(x11::Capturer); @@ -16,7 +16,7 @@ impl Capturer { self.0.display().rect().h as usize } - pub fn frame<'a>(&'a mut self, _timeout_ms: u32) -> io::Result> { + pub fn frame<'a>(&'a mut self, _timeout: Duration) -> io::Result> { Ok(Frame(self.0.frame()?)) } } diff --git a/pynput_service.py b/pynput_service.py index d8df09e96..90b8741ce 100644 --- a/pynput_service.py +++ b/pynput_service.py @@ -1,65 +1,32 @@ from pynput.keyboard import Key, Controller from pynput.keyboard._xorg import KeyCode from pynput._util.xorg import display_manager -import Xlib import os import sys import socket +from Xlib.ext.xtest import fake_input +from Xlib import X +import Xlib KeyCode._from_symbol("\0") # test + class MyController(Controller): def _handle(self, key, is_press): """Resolves a key identifier and sends a keyboard event. :param event: The *X* keyboard event. :param int keysym: The keysym to handle. """ - event = Xlib.display.event.KeyPress if is_press \ - else Xlib.display.event.KeyRelease keysym = self._keysym(key) + keycode = self._display.keysym_to_keycode(keysym) - # Make sure to verify that the key was resolved - if keysym is None: - raise self.InvalidKeyException(key) + with display_manager(self._display) as dm: + Xlib.ext.xtest.fake_input( + dm, + Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, + keycode) - # If the key has a virtual key code, use that immediately with - # fake_input; fake input,being an X server extension, has access to - # more internal state that we do - if key.vk is not None: - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input( - dm, - Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, - dm.keysym_to_keycode(key.vk)) - # Otherwise use XSendEvent; we need to use this in the general case to - # work around problems with keyboard layouts - else: - try: - keycode, shift_state = self.keyboard_mapping[keysym] - with self.modifiers as modifiers: - alt_gr = Key.alt_gr in modifiers - if alt_gr: - self._send_key(event, keycode, shift_state) - else: - with display_manager(self._display) as dm: - Xlib.ext.xtest.fake_input( - dm, - Xlib.X.KeyPress if is_press else Xlib.X.KeyRelease, - keycode) - - except KeyError: - with self._borrow_lock: - keycode, index, count = self._borrows[keysym] - self._send_key( - event, - keycode, - index_to_shift(self._display, index)) - count += 1 if is_press else -1 - self._borrows[keysym] = (keycode, index, count) - - # Notify any running listeners - self._emit('_on_fake_event', key, is_press) keyboard = MyController() @@ -77,7 +44,7 @@ server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) server.bind(server_address) server.listen(1) clientsocket, address = server.accept() -os.system('chmod a+rw %s'%server_address) +os.system('chmod a+rw %s' % server_address) print("Got pynput connection") @@ -121,4 +88,3 @@ def loop(): loop() clientsocket.close() server.close() - diff --git a/src/client.rs b/src/client.rs index 6354a1078..ca5352ae4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -880,6 +880,8 @@ impl LoginConfigHandler { option.block_input = BoolOption::Yes.into(); } else if name == "unblock-input" { option.block_input = BoolOption::No.into(); + } else if name == "show-quality-monitor" { + config.show_quality_monitor = !config.show_quality_monitor; } else { let v = self.options.get(&name).is_some(); if v { @@ -912,15 +914,8 @@ impl LoginConfigHandler { n += 1; } else if q == "custom" { let config = PeerConfig::load(&self.id); - let mut it = config.custom_image_quality.iter(); - let bitrate = it.next(); - let quantizer = it.next(); - if let Some(bitrate) = bitrate { - if let Some(quantizer) = quantizer { - msg.custom_image_quality = bitrate << 8 | quantizer; - n += 1; - } - } + msg.custom_image_quality = config.custom_image_quality[0] as _; + n += 1; } if self.get_toggle_option("show-remote-cursor") { msg.show_remote_cursor = BoolOption::Yes.into(); @@ -987,6 +982,8 @@ impl LoginConfigHandler { self.config.disable_audio } else if name == "disable-clipboard" { self.config.disable_clipboard + } else if name == "show-quality-monitor" { + self.config.show_quality_monitor } else { !self.get_option(name).is_empty() } @@ -1008,17 +1005,17 @@ impl LoginConfigHandler { msg_out } - pub fn save_custom_image_quality(&mut self, bitrate: i32, quantizer: i32) -> Message { + pub fn save_custom_image_quality(&mut self, custom_image_quality: i32) -> Message { let mut misc = Misc::new(); misc.set_option(OptionMessage { - custom_image_quality: bitrate << 8 | quantizer, + custom_image_quality, ..Default::default() }); let mut msg_out = Message::new(); msg_out.set_misc(misc); let mut config = self.load_config(); config.image_quality = "custom".to_owned(); - config.custom_image_quality = vec![bitrate, quantizer]; + config.custom_image_quality = vec![custom_image_quality as _]; self.save_config(config); msg_out } @@ -1215,14 +1212,6 @@ where return (video_sender, audio_sender); } -pub async fn handle_test_delay(t: TestDelay, peer: &mut Stream) { - if !t.from_client { - let mut msg_out = Message::new(); - msg_out.set_test_delay(t); - allow_err!(peer.send(&msg_out).await); - } -} - // mask = buttons << 3 | type // type, 1: down, 2: up, 3: wheel // buttons, 1: left, 2: right, 4: middle diff --git a/src/lang.rs b/src/lang.rs index c6e522940..4b4998fc1 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -6,6 +6,7 @@ mod da; mod sk; mod de; mod en; +mod es; mod eo; mod fr; mod id; @@ -44,6 +45,7 @@ pub fn translate_locale(name: String, locale: &str) -> String { "it" => it::T.deref(), "tw" => tw::T.deref(), "de" => de::T.deref(), + "es" => es::T.deref(), "ru" => ru::T.deref(), "eo" => eo::T.deref(), "id" => id::T.deref(), diff --git a/src/lang/es.rs b/src/lang/es.rs new file mode 100644 index 000000000..ba4c671a8 --- /dev/null +++ b/src/lang/es.rs @@ -0,0 +1,283 @@ +lazy_static::lazy_static! { +pub static ref T: std::collections::HashMap<&'static str, &'static str> = + [ + ("Status", "Estado"), + ("Your Desktop", "Tu escritorio"), + ("desk_tip", "Puoi accedere al tuo desktop usando l'ID e la password riportati qui."), + ("Password", "Contraseña"), + ("Ready", "Listo"), + ("Established", "Establecido"), + ("connecting_status", "Conexión a la red RustDesk en progreso..."), + ("Enable Service", "Habilitar Servicio"), + ("Start Service", "Iniciar Servicio"), + ("Service is running", "Servicio se está ejecutando"), + ("Service is not running", "Servicio no se está ejecutando"), + ("not_ready_status", "No está listo. Comprueba tu conexión"), + ("Control Remote Desktop", "Controlar Escritorio Remoto"), + ("Transfer File", "Transferir archivo"), + ("Connect", "Conectar"), + ("Recent Sessions", "Sesiones recientes"), + ("Address Book", "Directorio"), + ("Confirmation", "Confirmación"), + ("TCP Tunneling", "Tunel TCP"), + ("Remove", "Remover"), + ("Refresh random password", "Actualizar contraseña aleatoria"), + ("Set your own password", "Establece tu propia contraseña"), + ("Enable Keyboard/Mouse", "Habilitar teclado/ratón"), + ("Enable Clipboard", "Habilitar portapapeles"), + ("Enable File Transfer", "Habilitar transferencia de archivos"), + ("Enable TCP Tunneling", "Habilitar tunel TCP"), + ("IP Whitelisting", "Lista blanca IP"), + ("ID/Relay Server", "Servidor de ID/Relay"), + ("Stop service", "Parar servicio"), + ("Change ID", "Cambiar identificación"), + ("Website", "Sitio web"), + ("About", "Sobre"), + ("Mute", "Silencio"), + ("Audio Input", "Entrada de audio"), + ("ID Server", "ID server"), + ("Relay Server", "Server relay"), + ("API Server", "Server API"), + ("invalid_http", "debe comenzar con http:// o https://"), + ("Invalid IP", "IP inválida"), + ("id_change_tip", "Solo puedes usar caracteres a-z, A-Z, 0-9 e _ (guion bajo). El primer carácter debe ser a-z o A-Z. La longitud debe estar entre 6 a 16 caracteres."), + ("Invalid format", "Formato inválido"), + ("server_not_support", "Aún no es compatible con el servidor"), + ("Not available", "Indisponible"), + ("Too frequent", "Demasiado frecuente"), + ("Cancel", "Cancelar"), + ("Skip", "Saltar"), + ("Close", "Cerrar"), + ("Retry", "Volver"), + ("OK", "OK"), + ("Password Required", "Se requiere contraseña"), + ("Please enter your password", "Por favor, introduzca su contraseña"), + ("Remember password", "Recordar contraseña"), + ("Wrong Password", "Contraseña incorrecta"), + ("Do you want to enter again?", "Quieres volver a entrar?"), + ("Connection Error", "Error de conexión"), + ("Error", "Error"), + ("Reset by the peer", "Restablecido por el par"), + ("Connecting...", "Conectando..."), + ("Connection in progress. Please wait.", "Conexión en curso. Espere por favor."), + ("Please try 1 minute later", "Intente 1 minuto más tarde"), + ("Login Error", "Error de inicio de sesión"), + ("Successful", "Exitoso"), + ("Connected, waiting for image...", "Conectado, esperando imagen..."), + ("Name", "Nombre"), + ("Type", "Tipo"), + ("Modified", "Modificado"), + ("Size", "Tamaño"), + ("Show Hidden Files", "Mostrar archivos ocultos"), + ("Receive", "Recibir"), + ("Send", "Enviar"), + ("Refresh File", "Actualizar archivo"), + ("Local", "Local"), + ("Remote", "Remoto"), + ("Remote Computer", "Computadora remota"), + ("Local Computer", "Computadora local"), + ("Confirm Delete", "Confirmar eliminación"), + ("Delete", "Borrar"), + ("Properties", "Propiedades"), + ("Multi Select", "Selección múltiple"), + ("Empty Directory", "Directorio vacío"), + ("Not an empty directory", "No es un directorio vacío"), + ("Are you sure you want to delete this file?", "Estás seguro de que quieres eliminar este archivo?"), + ("Are you sure you want to delete this empty directory?", "Está seguro de que desea eliminar este directorio vacío?"), + ("Are you sure you want to delete the file of this directory?", "Está seguro de que desea eliminar el archivo de este directorio?"), + ("Do this for all conflicts", "Haga esto para todos los conflictos"), + ("This is irreversible!", "Esto es irreversible!"), + ("Deleting", "Borrando"), + ("files", "archivos"), + ("Waiting", "Esperando"), + ("Finished", "Acabado"), + ("Speed", "Velocidad"), + ("Custom Image Quality", "Calidad de imagen personalizada"), + ("Privacy mode", "Modo privado"), + ("Block user input", "Bloquear entrada de usuario"), + ("Unblock user input", "Desbloquear entrada de usuario"), + ("Adjust Window", "Ajustar ventana"), + ("Original", "Original"), + ("Shrink", "Encogerse"), + ("Stretch", "Estirar"), + ("Good image quality", "Buena calidad de imagen"), + ("Balanced", "Equilibrado"), + ("Optimize reaction time", "Optimizar el tiempo de reacción"), + ("Custom", "Personalizado"), + ("Show remote cursor", "Mostrar cursor remoto"), + ("Disable clipboard", "Deshabilitar portapapeles"), + ("Lock after session end", "Bloquear después del final de la sesión"), + ("Insert", "Insertar"), + ("Insert Lock", "Insertar bloqueo"), + ("Refresh", "Actualizar"), + ("ID does not exist", "ID no existe"), + ("Failed to connect to rendezvous server", "No se pudo conectar al servidor de encuentro"), + ("Please try later", "Por favor intente mas tarde"), + ("Remote desktop is offline", "El escritorio remoto está fuera de línea"), + ("Key mismatch", "La clave no coincide"), + ("Timeout", "Timeout"), + ("Failed to connect to relay server", "No se pudo conectar al servidor de retransmisión"), + ("Failed to connect via rendezvous server", "No se pudo conectar a través del servidor de encuentro"), + ("Failed to connect via relay server", "No se pudo conectar a través del servidor de retransmisión"), + ("Failed to make direct connection to remote desktop", "No se pudo establecer la conexión directa con el escritorio remoto"), + ("Set Password", "Configurar la clave"), + ("OS Password", "Contraseña del sistema operativo"), + ("install_tip", "Debido al Control de cuentas de usuario, es posible que RustDesk no funcione correctamente como escritorio remoto. Para evitar este problema, haga clic en el botón de abajo para instalar RustDesk a nivel de sistema."), + ("Click to upgrade", "Clic para actualizar"), + ("Click to download", "Clic para descargar"), + ("Click to update", "Fare clic per aggiornare"), + ("Configure", "Configurar"), + ("config_acc", "Para controlar su escritorio desde el exterior, debe otorgar permiso a RustDesk de \"Accesibilidad\"."), + ("config_screen", "Para controlar su escritorio desde el exterior, debe otorgar permiso a RustDesk de \"Grabación de pantalla\"."), + ("Installing ...", "Instalando ..."), + ("Install", "Instalar"), + ("Installation", "Instalación"), + ("Installation Path", "Ruta de instalación"), + ("Create start menu shortcuts", "Crear accesos directos al menú de inicio"), + ("Create desktop icon", "Crear icono de escritorio"), + ("agreement_tip", "Al iniciar la instalación, acepta los términos del acuerdo de licencia."), + ("Accept and Install", "Aceptar e instalar"), + ("End-user license agreement", "Acuerdo de licencia de usuario final"), + ("Generating ...", "Generando ..."), + ("Your installation is lower version.", "Su instalación es una versión inferior."), + ("not_close_tcp_tip", "No cierre esta ventana mientras esté usando el túnel"), + ("Listening ...", "Escuchando ..."), + ("Remote Host", "Servidor remoto"), + ("Remote Port", "Puerto remoto"), + ("Action", "Acción"), + ("Add", "Agregar"), + ("Local Port", "Puerto local"), + ("setup_server_tip", "Para una conexión más rápida, configure su propio servidor"), + ("Too short, at least 6 characters.", "Demasiado corto, al menos 6 caracteres."), + ("The confirmation is not identical.", "La confirmación no es idéntica."), + ("Permissions", "Permisos"), + ("Accept", "Aceptar"), + ("Dismiss", "Cancelar"), + ("Disconnect", "Desconectar"), + ("Allow using keyboard and mouse", "Permitir el uso del teclado y el mouse"), + ("Allow using clipboard", "Permitir usar portapapeles"), + ("Allow hearing sound", "Permitir escuchar sonido"), + ("Allow file copy and paste", "Permitir copiar y pegar archivos"), + ("Connected", "Conectado"), + ("Direct and encrypted connection", "Conexión directa y encriptada"), + ("Relayed and encrypted connection", "Conexión retransmitida y cifrada"), + ("Direct and unencrypted connection", "Conexión directa y sin cifrar"), + ("Relayed and unencrypted connection", "Conexión retransmitida y sin cifrar"), + ("Enter Remote ID", "Ingrese el ID remoto"), + ("Enter your password", "Ingrese su contraseña"), + ("Logging in...", "Iniciando sesión..."), + ("Enable RDP session sharing", "Habilitar el uso compartido de sesiones RDP"), + ("Auto Login", "Ingreso automático"), + ("Enable Direct IP Access", "Habilitar acceso IP directo"), + ("Rename", "Renombrar"), + ("Space", "Espacio"), + ("Create Desktop Shortcut", "Crear acceso directo del escritorio"), + ("Change Path", "Cambiar ruta"), + ("Create Folder", "Crear carpeta"), + ("Please enter the folder name", "Por favor ingrese el nombre de la carpeta"), + ("Fix it", "Resolver"), + ("Warning", "Aviso"), + ("Login screen using Wayland is not supported", "La pantalla de inicio de sesión con Wayland no es compatible"), + ("Reboot required", "Reinicio requerido"), + ("Unsupported display server ", "Servidor de visualización no compatible"), + ("x11 expected", "x11 necesario"), + ("Port", "Puerto"), + ("Settings", "Ajustes"), + ("Username", " Nombre de usuario"), + ("Invalid port", "Puerto inválido"), + ("Closed manually by the peer", "Cerrado manualmente por el par"), + ("Enable remote configuration modification", "Habilitar modificación de configuración remota"), + ("Run without install", "Ejecutar sin instalar"), + ("Always connected via relay", "Siempre conectado a través de relay"), + ("Always connect via relay", "Conéctese siempre a través de relay"), + ("whitelist_tip", "Solo las direcciones IP autorizadas pueden conectarse a este escritorio"), + ("Login", "Iniciar sesión"), + ("Logout", "Salir"), + ("Tags", "Tags"), + ("Search ID", "Buscar ID"), + ("Current Wayland display server is not supported", "El servidor de visualización actual de Wayland no es compatible"), + ("whitelist_sep", "Separados por coma, punto y coma, espacio o nueva línea"), + ("Add ID", "Agregar ID"), + ("Add Tag", "Agregar tag"), + ("Unselect all tags", "Deseleccionar todos los tags"), + ("Network error", "Error de red"), + ("Username missed", "Olvidó su nombre de usuario"), + ("Password missed", "Olvidó su contraseña"), + ("Wrong credentials", "Credenciales incorrectas"), + ("Edit Tag", "Editar tag"), + ("Unremember Password", "Olvidaste tu contraseña"), + ("Favorites", "Favoritos"), + ("Add to Favorites", "Agregar a favoritos"), + ("Remove from Favorites", "Quitar de favoritos"), + ("Empty", "Vacío"), + ("Invalid folder name", "Nombre de carpeta no válido"), + ("Socks5 Proxy", "Proxy Socks5"), + ("Hostname", "Nombre de host"), + ("Discovered", "Descubierto"), + ("install_daemon_tip", "Para comenzar en el encendido, debe instalar el servicio del sistema."), + ("Remote ID", "ID remoto"), + ("Paste", "Pegar"), + ("Paste here?", "Pegar aqui?"), + ("Are you sure to close the connection?", "Estás seguro de cerrar la conexión?"), + ("Download new version", "Descargar nueva versión"), + ("Touch mode", "Modo táctil"), + ("Mouse mode", "Modo ratón"), + ("One-Finger Tap", "Toque con un dedo"), + ("Left Mouse", "Ratón izquierdo"), + ("One-Long Tap", "Un toque largo"), + ("Two-Finger Tap", "Toque con dos dedos"), + ("Right Mouse", "Botón derecho"), + ("One-Finger Move", "Movimiento con un dedo"), + ("Double Tap & Move", "Toca dos veces y mueve"), + ("Mouse Drag", "Arrastre de ratón"), + ("Three-Finger vertically", "Tres dedos verticalmente"), + ("Mouse Wheel", "Rueda de ratón"), + ("Two-Finger Move", "Movimiento con dos dedos"), + ("Canvas Move", "Movimiento de lienzo"), + ("Pinch to Zoom", "Pellizcar para ampliar"), + ("Canvas Zoom", "Ampliar lienzo"), + ("Reset canvas", "Restablecer lienzo"), + ("No permission of file transfer", "Sin permiso de transferencia de archivos"), + ("Note", "Nota"), + ("Connection", "Conexión"), + ("Share Screen", "Compartir pantalla"), + ("CLOSE", "CERRAR"), + ("OPEN", "ABRIR"), + ("Chat", "Chat"), + ("Total", "Total"), + ("items", "items"), + ("Selected", "Seleccionado"), + ("Screen Capture", "Captura de pantalla"), + ("Input Control", "Control de entrada"), + ("Audio Capture", "Captura de audio"), + ("File Connection", "Conexión de archivos"), + ("Screen Connection", "Conexión de pantalla"), + ("Do you accept?", "Aceptas?"), + ("Open System Setting", "Configuración del sistema abierto"), + ("How to get Android input permission?", "Cómo obtener el permiso de entrada de Android?"), + ("android_input_permission_tip1", "Para que un dispositivo remoto controle su dispositivo Android a través del mouse o toque, debe permitir que RustDesk use el servicio de \"Accesibilidad\"."), + ("android_input_permission_tip2", "Vaya a la página de configuración del sistema que se abrirá a continuación, busque y acceda a [Servicios instalados], active el servicio [RustDesk Input]."), + ("android_new_connection_tip", "Se recibió una nueva solicitud de control para el dispositivo actual."), + ("android_service_will_start_tip", "Habilitar la captura de pantalla iniciará automáticamente el servicio, lo que permitirá que otros dispositivos soliciten una conexión desde este dispositivo."), + ("android_stop_service_tip", "Cerrar el servicio cerrará automáticamente todas las conexiones establecidas."), + ("android_version_audio_tip", "La versión actual de Android no admite la captura de audio, actualice a Android 10 o posterior."), + ("android_start_service_tip", "Toque el permiso [Iniciar servicio] o ABRIR [Captura de pantalla] para iniciar el servicio de uso compartido de pantalla."), + ("Account", "Cuenta"), + ("Overwrite", "Sobrescribir"), + ("This file exists, skip or overwrite this file?", "Este archivo existe, ¿omitir o sobrescribir este archivo?"), + ("Quit", "Salir"), + ("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"), + ("Help", "Ayuda"), + ("Failed", "Fallido"), + ("Succeeded", "Logrado"), + ("Someone turns on privacy mode, exit", "Alguien active el modo privacidad, salga"), + ("Unsupported", "No soportado"), + ("Peer denied", "Par negado"), + ("Please install plugins", "Instale complementos"), + ("Peer exit", "Par salio"), + ("Failed to turn off", "Error al apagar"), + ("Turned off", "Apagado"), + ("In privacy mode", "En modo de privacidad"), + ("Out privacy mode", "Fuera del modo de privacidad"), + ].iter().cloned().collect(); +} diff --git a/src/main.rs b/src/main.rs index 1e63704fb..9158cab32 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,6 +106,7 @@ fn main() { "desktopicon startmenu", "".to_owned(), false, + false, )); return; } else if args[0] == "--silent-install" { @@ -113,11 +114,9 @@ fn main() { "desktopicon startmenu", "".to_owned(), true, + args.len() > 1, )); return; - } else if args[0] == "--extract" { - #[cfg(feature = "with_rc")] - hbb_common::allow_err!(crate::rc::extract_resources(&args[1])); } else if args[0] == "--check-hwcodec-config" { #[cfg(feature = "hwcodec")] ipc::check_hwcodec_config(); @@ -209,7 +208,7 @@ fn main() { .about("RustDesk command line tool") .args_from_usage(&args) .get_matches(); - use hbb_common::{config::LocalConfig, env_logger::*}; + use hbb_common::env_logger::*; init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); if let Some(p) = matches.value_of("port-forward") { let options: Vec = p.split(":").map(|x| x.to_owned()).collect(); @@ -236,14 +235,6 @@ fn main() { remote_host = options[3].clone(); } let key = matches.value_of("key").unwrap_or("").to_owned(); - let token = LocalConfig::get_option("access_token"); - cli::start_one_port_forward( - options[0].clone(), - port, - remote_host, - remote_port, - key, - token, - ); + cli::start_one_port_forward(options[0].clone(), port, remote_host, remote_port, key); } } diff --git a/src/mobile.rs b/src/mobile.rs index fe02513a0..5b9651e54 100644 --- a/src/mobile.rs +++ b/src/mobile.rs @@ -436,7 +436,12 @@ impl Interface for Session { let mut displays = Vec::new(); let mut current = pi.current_display as usize; - if !lc.is_file_transfer { + if lc.is_file_transfer { + if pi.username.is_empty() { + self.msgbox("error", "Error", "No active console user logged on, please connect and logon first."); + return; + } + } else { if pi.displays.is_empty() { self.msgbox("error", "Remote Error", "No Display"); } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index cc3b4a050..4e8f7e16a 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -698,7 +698,7 @@ pub fn set_share_rdp(enable: bool) { subkey, if enable { "true" } else { "false" } ); - run_cmds(cmd, false).ok(); + run_cmds(cmd, false, "share_rdp").ok(); } pub fn get_active_username() -> String { @@ -835,7 +835,7 @@ pub fn check_update_broker_process() -> ResultType<()> { origin_process_exe = origin_process_exe, cur_exe = cur_exe.to_string_lossy().to_string(), ); - run_cmds(cmds, false)?; + run_cmds(cmds, false, "update_broker")?; Ok(()) } @@ -876,7 +876,7 @@ pub fn update_me() -> ResultType<()> { lic = register_licence(), ); std::thread::sleep(std::time::Duration::from_millis(1000)); - run_cmds(cmds, false)?; + run_cmds(cmds, false, "update")?; std::thread::sleep(std::time::Duration::from_millis(2000)); std::process::Command::new(&exe).arg("--tray").spawn().ok(); std::process::Command::new(&exe).spawn().ok(); @@ -905,7 +905,7 @@ fn get_after_install(exe: &str) -> String { ", ext=ext, exe=exe, app_name=app_name) } -pub fn install_me(options: &str, path: String, silent: bool) -> ResultType<()> { +pub fn install_me(options: &str, path: String, silent: bool, debug: bool) -> ResultType<()> { let uninstall_str = get_uninstall(); let mut path = path.trim_end_matches('\\').to_owned(); let (subkey, _path, start_menu, exe) = get_default_install_info(); @@ -929,7 +929,7 @@ pub fn install_me(options: &str, path: String, silent: bool) -> ResultType<()> { version_build = versions[2]; } - let tmp_path = "C:\\Windows\\temp"; + let tmp_path = std::env::temp_dir().to_string_lossy().to_string(); let mk_shortcut = write_cmds( format!( " @@ -945,6 +945,7 @@ oLink.Save exe = exe, ), "vbs", + "mk_shortcut", )? .to_str() .unwrap_or("") @@ -966,6 +967,7 @@ oLink.Save exe = exe, ), "vbs", + "uninstall_shortcut", )? .to_str() .unwrap_or("") @@ -986,6 +988,7 @@ oLink.Save exe = exe, ), "vbs", + "tray_shortcut", )? .to_str() .unwrap_or("") @@ -1042,7 +1045,7 @@ reg add {subkey} /f /v WindowsInstaller /t REG_DWORD /d 0 cscript \"{mk_shortcut}\" cscript \"{uninstall_shortcut}\" cscript \"{tray_shortcut}\" -copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\" +copy /Y \"{tmp_path}\\{app_name} Tray.lnk\" \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\\" {shortcuts} copy /Y \"{tmp_path}\\Uninstall {app_name}.lnk\" \"{path}\\\" del /f \"{mk_shortcut}\" @@ -1079,7 +1082,7 @@ sc delete {app_name} lic=register_licence(), after_install=get_after_install(&exe), ); - run_cmds(cmds, false)?; + run_cmds(cmds, debug, "install")?; std::thread::sleep(std::time::Duration::from_millis(2000)); if !silent { std::process::Command::new(&exe).spawn()?; @@ -1091,11 +1094,11 @@ sc delete {app_name} pub fn run_after_install() -> ResultType<()> { let (_, _, _, exe) = get_install_info(); - run_cmds(get_after_install(&exe), true) + run_cmds(get_after_install(&exe), true, "after_install") } pub fn run_before_uninstall() -> ResultType<()> { - run_cmds(get_before_uninstall(), true) + run_cmds(get_before_uninstall(), true, "before_install") } fn get_before_uninstall() -> String { @@ -1126,7 +1129,7 @@ fn get_uninstall() -> String { rd /s /q \"{path}\" rd /s /q \"{start_menu}\" del /f /q \"%PUBLIC%\\Desktop\\{app_name}*\" - del /f /q \"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" + del /f /q \"%PROGRAMDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\{app_name} Tray.lnk\" ", before_uninstall=get_before_uninstall(), subkey=subkey, @@ -1137,17 +1140,12 @@ fn get_uninstall() -> String { } pub fn uninstall_me() -> ResultType<()> { - run_cmds(get_uninstall(), true) + run_cmds(get_uninstall(), true, "uninstall") } -fn write_cmds(cmds: String, ext: &str) -> ResultType { +fn write_cmds(cmds: String, ext: &str, tip: &str) -> ResultType { let mut tmp = std::env::temp_dir(); - tmp.push(format!( - "{}_{:?}.{}", - crate::get_app_name(), - cmds.as_ptr(), - ext - )); + tmp.push(format!("{}_{}.{}", crate::get_app_name(), tip, ext)); let mut file = std::fs::File::create(&tmp)?; // in case cmds mixed with \r\n and \n, make sure all ending with \r\n // in some windows, \r\n required for cmd file to run @@ -1170,8 +1168,8 @@ fn to_le(v: &mut [u16]) -> &[u8] { unsafe { v.align_to().1 } } -fn run_cmds(cmds: String, show: bool) -> ResultType<()> { - let tmp = write_cmds(cmds, "bat")?; +fn run_cmds(cmds: String, show: bool, tip: &str) -> ResultType<()> { + let tmp = write_cmds(cmds, "bat", tip)?; let tmp_fn = tmp.to_str().unwrap_or(""); let res = runas::Command::new("cmd") .args(&["/C", &tmp_fn]) @@ -1348,6 +1346,7 @@ oLink.Save id = id, ), "vbs", + "connect_shortcut", )? .to_str() .unwrap_or("") diff --git a/src/rendezvous_mediator.rs b/src/rendezvous_mediator.rs index f575f684f..a7f90b977 100644 --- a/src/rendezvous_mediator.rs +++ b/src/rendezvous_mediator.rs @@ -216,7 +216,7 @@ impl RendezvousMediator { }, Some(Err(e)) => bail!("Failed to receive next {}", e), // maybe socks5 tcp disconnected None => { - // unreachable!() + bail!("Socket receive none. Maybe socks5 server is down."); }, } }, @@ -231,7 +231,7 @@ impl RendezvousMediator { } last_timer = now; let elapsed_resp = last_register_resp.map(|x| x.elapsed().as_millis() as i64).unwrap_or(REG_INTERVAL); - let timeout = (last_register_sent.map(|x| x.elapsed().as_millis() as i64).unwrap_or(REG_INTERVAL) - elapsed_resp) > REG_TIMEOUT; + let timeout = (elapsed_resp - last_register_sent.map(|x| x.elapsed().as_millis() as i64).unwrap_or(REG_INTERVAL)) > REG_TIMEOUT; if timeout || elapsed_resp >= REG_INTERVAL { allow_err!(rz.register_peer(&mut socket).await); last_register_sent = now; diff --git a/src/server/connection.rs b/src/server/connection.rs index e09a60bb8..77d41d4e0 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -3,6 +3,7 @@ use super::{input_service::*, *}; use crate::clipboard_file::*; #[cfg(not(any(target_os = "android", target_os = "ios")))] use crate::common::update_clipboard; +use crate::video_service; #[cfg(any(target_os = "android", target_os = "ios"))] use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel}; use crate::{ipc, VERSION}; @@ -69,7 +70,6 @@ pub struct Connection { audio: bool, file: bool, last_test_delay: i64, - image_quality: i32, lock_after_session_end: bool, show_remote_cursor: bool, // by peer ip: String, @@ -105,7 +105,7 @@ impl Subscriber for ConnInner { } } -const TEST_DELAY_TIMEOUT: Duration = Duration::from_secs(3); +const TEST_DELAY_TIMEOUT: Duration = Duration::from_secs(1); const SEC30: Duration = Duration::from_secs(30); const H1: Duration = Duration::from_secs(3600); const MILLI1: Duration = Duration::from_millis(1); @@ -154,7 +154,6 @@ impl Connection { audio: Config::get_option("enable-audio").is_empty(), file: Config::get_option("enable-file-transfer").is_empty(), last_test_delay: 0, - image_quality: ImageQuality::Balanced.value(), lock_after_session_end: false, show_remote_cursor: false, ip: "".to_owned(), @@ -376,8 +375,11 @@ impl Connection { if time > 0 && conn.last_test_delay == 0 { conn.last_test_delay = time; let mut msg_out = Message::new(); + let qos = video_service::VIDEO_QOS.lock().unwrap(); msg_out.set_test_delay(TestDelay{ time, + last_delay:qos.current_delay, + target_bitrate:qos.target_bitrate, ..Default::default() }); conn.inner.send(msg_out.into()); @@ -394,9 +396,8 @@ impl Connection { let _ = privacy_mode::turn_off_privacy(0); } video_service::notify_video_frame_feched(id, None); - video_service::update_test_latency(id, 0); - video_service::update_image_quality(id, None); scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove); + video_service::VIDEO_QOS.lock().unwrap().reset(); if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await { conn.on_close(&err.to_string(), false); } @@ -665,7 +666,7 @@ impl Connection { res.set_peer_info(pi); } else { try_activate_screen(); - match super::video_service::get_displays() { + match video_service::get_displays() { Err(err) => { res.set_error(format!("X11 error: {}", err)); } @@ -903,10 +904,11 @@ impl Connection { self.inner.send(msg_out.into()); } else { self.last_test_delay = 0; - let latency = crate::get_time() - t.time; - if latency > 0 { - super::video_service::update_test_latency(self.inner.id(), latency); - } + let new_delay = (crate::get_time() - t.time) as u32; + video_service::VIDEO_QOS + .lock() + .unwrap() + .update_network_delay(new_delay); } } else if self.authorized { match msg.union { @@ -1082,7 +1084,7 @@ impl Connection { }, Some(message::Union::misc(misc)) => match misc.union { Some(misc::Union::switch_display(s)) => { - super::video_service::switch_display(s.display); + video_service::switch_display(s.display); } Some(misc::Union::chat_message(c)) => { self.send_to_cm(ipc::Data::ChatMessage { text: c.text }); @@ -1092,7 +1094,7 @@ impl Connection { } Some(misc::Union::refresh_video(r)) => { if r { - super::video_service::refresh(); + video_service::refresh(); } } Some(misc::Union::video_received(_)) => { @@ -1112,13 +1114,18 @@ impl Connection { async fn update_option(&mut self, o: &OptionMessage) { log::info!("Option update: {:?}", o); if let Ok(q) = o.image_quality.enum_value() { - self.image_quality = q.value(); - super::video_service::update_image_quality(self.inner.id(), Some(q.value())); - } - let q = o.custom_image_quality; - if q > 0 { - self.image_quality = q; - super::video_service::update_image_quality(self.inner.id(), Some(q)); + let mut image_quality = None; + if let ImageQuality::NotSet = q { + if o.custom_image_quality > 0 { + image_quality = Some(o.custom_image_quality as _); + } + } else { + image_quality = Some(q.value() as _) + } + video_service::VIDEO_QOS + .lock() + .unwrap() + .update_image_quality(image_quality); } if let Ok(q) = o.lock_after_session_end.enum_value() { if q != BoolOption::NotSet { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 62e3a5138..8113ffee3 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -37,19 +37,249 @@ use std::{ use virtual_display; pub const NAME: &'static str = "video"; +const FPS: u8 = 30; lazy_static::lazy_static! { static ref CURRENT_DISPLAY: Arc> = Arc::new(Mutex::new(usize::MAX)); static ref LAST_ACTIVE: Arc> = Arc::new(Mutex::new(Instant::now())); static ref SWITCH: Arc> = Default::default(); - static ref TEST_LATENCIES: Arc>> = Default::default(); - static ref IMAGE_QUALITIES: Arc>> = Default::default(); static ref FRAME_FETCHED_NOTIFIER: (UnboundedSender<(i32, Option)>, Arc)>>>) = { let (tx, rx) = unbounded_channel(); (tx, Arc::new(TokioMutex::new(rx))) }; static ref PRIVACY_MODE_CONN_ID: Mutex = Mutex::new(0); static ref IS_CAPTURER_MAGNIFIER_SUPPORTED: bool = is_capturer_mag_supported(); + pub static ref VIDEO_QOS: Arc> = Default::default(); +} + +pub struct VideoQoS { + width: u32, + height: u32, + user_image_quality: u32, + current_image_quality: u32, + enable_abr: bool, + + pub current_delay: u32, + pub fps: u8, // abr + pub target_bitrate: u32, // abr + updated: bool, + + state: AdaptiveState, + last_delay: u32, + count: u32, +} + +#[derive(Debug)] +enum AdaptiveState { + Normal, + LowDelay, + HeightDelay, +} + +impl Default for VideoQoS { + fn default() -> Self { + VideoQoS { + fps: FPS, + user_image_quality: ImageQuality::Balanced.value() as _, + current_image_quality: ImageQuality::Balanced.value() as _, + enable_abr: false, + width: 0, + height: 0, + current_delay: 0, + target_bitrate: 0, + updated: false, + state: AdaptiveState::Normal, + last_delay: 0, + count: 0, + } + } +} + +const MAX: f32 = 1.2; +const MIN: f32 = 0.8; +const MAX_COUNT: u32 = 3; +const MAX_DELAY: u32 = 500; +const MIN_DELAY: u32 = 50; + +impl VideoQoS { + pub fn set_size(&mut self, width: u32, height: u32) { + if width == 0 || height == 0 { + return; + } + self.width = width; + self.height = height; + } + + pub fn spf(&mut self) -> Duration { + if self.fps <= 0 { + self.fps = FPS; + } + time::Duration::from_secs_f32(1. / (self.fps as f32)) + } + + // abr + pub fn update_network_delay(&mut self, delay: u32) { + if self.current_delay.eq(&0) { + self.current_delay = delay; + return; + } + let current_delay = self.current_delay as f32; + + self.current_delay = delay / 2 + self.current_delay / 2; + log::debug!( + "update_network_delay:{}, {}, state:{:?},count:{}", + self.current_delay, + delay, + self.state, + self.count + ); + + if self.current_delay < MIN_DELAY { + if self.fps != 30 && self.current_image_quality != self.user_image_quality { + log::debug!("current_delay is normal, set to user_image_quality"); + self.fps = 30; + self.current_image_quality = self.user_image_quality; + let _ = self.generate_bitrate().ok(); + self.updated = true; + } + self.state = AdaptiveState::Normal; + } else if self.current_delay > MAX_DELAY { + if self.fps != 5 && self.current_image_quality != 25 { + log::debug!("current_delay is very height, set fps to 5, image_quality to 25"); + self.fps = 5; + self.current_image_quality = 25; + let _ = self.generate_bitrate().ok(); + self.updated = true; + } + } else { + let delay = delay as f32; + let last_delay = self.last_delay as f32; + match self.state { + AdaptiveState::Normal => { + if delay > current_delay * MAX { + self.state = AdaptiveState::HeightDelay; + } else if delay < current_delay * MIN + && self.current_image_quality < self.user_image_quality + { + self.state = AdaptiveState::LowDelay; + } + self.count = 1; + self.last_delay = self.current_delay + } + AdaptiveState::HeightDelay => { + if delay > last_delay { + if self.count > MAX_COUNT { + self.decrease_quality(); + self.reset_state(); + return; + } + self.count += 1; + } else { + self.reset_state(); + } + } + AdaptiveState::LowDelay => { + if delay < last_delay * MIN { + if self.count > MAX_COUNT { + self.increase_quality(); + self.reset_state(); + return; + } + self.count += 1; + } else { + self.reset_state(); + } + } + } + } + } + + fn reset_state(&mut self) { + self.count = 0; + self.state = AdaptiveState::Normal; + } + + fn increase_quality(&mut self) { + log::debug!("Adaptive increase quality"); + if self.fps < FPS { + log::debug!("increase fps {} -> {}", self.fps, FPS); + self.fps = FPS; + } else { + self.current_image_quality += self.current_image_quality / 2; + let _ = self.generate_bitrate().ok(); + log::debug!("increase quality:{}", self.current_image_quality); + } + self.updated = true; + } + + fn decrease_quality(&mut self) { + log::debug!("Adaptive decrease quality"); + if self.fps < 15 { + log::debug!("fps is low enough :{}", self.fps); + return; + } + if self.current_image_quality < ImageQuality::Low.value() as _ { + self.fps = self.fps / 2; + log::debug!("decrease fps:{}", self.fps); + } else { + self.current_image_quality -= self.current_image_quality / 2; + let _ = self.generate_bitrate().ok(); + log::debug!("decrease quality:{}", self.current_image_quality); + }; + self.updated = true; + } + + pub fn update_image_quality(&mut self, image_quality: Option) { + if let Some(image_quality) = image_quality { + if image_quality < 10 || image_quality > 200 { + self.current_image_quality = ImageQuality::Balanced.value() as _; + } + if self.current_image_quality != image_quality { + self.current_image_quality = image_quality; + let _ = self.generate_bitrate().ok(); + self.updated = true; + } + } else { + self.current_image_quality = ImageQuality::Balanced.value() as _; + } + self.user_image_quality = self.current_image_quality; + } + + pub fn generate_bitrate(&mut self) -> ResultType { + // https://www.nvidia.com/en-us/geforce/guides/broadcasting-guide/ + if self.width == 0 || self.height == 0 { + bail!("Fail to generate_bitrate, width or height is not set"); + } + if self.current_image_quality == 0 { + self.current_image_quality = ImageQuality::Balanced.value() as _; + } + + let base_bitrate = ((self.width * self.height) / 800) as u32; + + #[cfg(target_os = "android")] + { + // fix when andorid screen shrinks + let fix = Display::fix_quality() as u32; + log::debug!("Android screen, fix quality:{}", fix); + let base_bitrate = base_bitrate * fix; + self.target_bitrate = base_bitrate * self.image_quality / 100; + Ok(self.target_bitrate) + } + self.target_bitrate = base_bitrate * self.current_image_quality / 100; + Ok(self.target_bitrate) + } + + pub fn check_if_updated(&mut self) -> bool { + if self.updated { + self.updated = false; + return true; + } + return false; + } + + pub fn reset(&mut self) { + *self = Default::default(); + } } fn is_capturer_mag_supported() -> bool { @@ -129,7 +359,7 @@ impl VideoFrameController { } trait TraitCapturer { - fn frame<'a>(&'a mut self, timeout_ms: u32) -> Result>; + fn frame<'a>(&'a mut self, timeout: Duration) -> Result>; #[cfg(windows)] fn is_gdi(&self) -> bool; @@ -138,8 +368,8 @@ trait TraitCapturer { } impl TraitCapturer for Capturer { - fn frame<'a>(&'a mut self, timeout_ms: u32) -> Result> { - self.frame(timeout_ms) + fn frame<'a>(&'a mut self, timeout: Duration) -> Result> { + self.frame(timeout) } #[cfg(windows)] @@ -155,7 +385,7 @@ impl TraitCapturer for Capturer { #[cfg(windows)] impl TraitCapturer for scrap::CapturerMag { - fn frame<'a>(&'a mut self, _timeout_ms: u32) -> Result> { + fn frame<'a>(&'a mut self, _timeout_ms: Duration) -> Result> { self.frame(_timeout_ms) } @@ -326,9 +556,6 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] ensure_close_virtual_device()?; - let fps = 30; - let wait = 1000 / fps; - let spf = time::Duration::from_secs_f32(1. / (fps as f32)); let (ndisplay, current, display) = get_current_display()?; let (origin, width, height) = (display.origin(), display.width(), display.height()); log::debug!( @@ -342,16 +569,21 @@ fn run(sp: GenericService) -> ResultType<()> { num_cpus::get(), ); - let q = get_image_quality(); - let (bitrate, rc_min_quantizer, rc_max_quantizer, speed) = get_quality(width, height, q); - log::info!("bitrate={}, rc_min_quantizer={}", bitrate, rc_min_quantizer); + 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()?; + drop(video_qos); + + log::info!("init bitrate={}", bitrate); let encoder_cfg = match Encoder::current_hw_encoder_name() { Some(codec_name) => EncoderCfg::HW(HwEncoderConfig { codec_name, width, height, - bitrate_ratio: q >> 8, + bitrate_ratio: bitrate as _, }), None => EncoderCfg::VPX(VpxEncoderConfig { width: width as _, @@ -359,9 +591,6 @@ fn run(sp: GenericService) -> ResultType<()> { timebase: [1, 1000], // Output timestamp precision bitrate, codec: VpxVideoCodecId::VP9, - rc_min_quantizer, - rc_max_quantizer, - speed, num_threads: (num_cpus::get() / 2) as _, }), }; @@ -418,10 +647,24 @@ fn run(sp: GenericService) -> ResultType<()> { let mut try_gdi = 1; #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); + while sp.ok() { #[cfg(windows)] check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?; + { + let mut video_qos = VIDEO_QOS.lock().unwrap(); + if video_qos.check_if_updated() { + log::debug!( + "qos is updated, target_bitrate:{}, fps:{}", + video_qos.target_bitrate, + video_qos.fps + ); + encoder.set_bitrate(video_qos.target_bitrate).unwrap(); + spf = video_qos.spf(); + } + } + if *SWITCH.lock().unwrap() { bail!("SWITCH"); } @@ -430,9 +673,6 @@ fn run(sp: GenericService) -> ResultType<()> { bail!("SWITCH"); } check_privacy_mode_changed(&sp, privacy_mode_id)?; - if get_image_quality() != q { - bail!("SWITCH"); - } #[cfg(windows)] { if crate::platform::windows::desktop_changed() { @@ -454,7 +694,7 @@ fn run(sp: GenericService) -> ResultType<()> { frame_controller.reset(); #[cfg(any(target_os = "android", target_os = "ios"))] - let res = match (*c).frame(wait as _) { + let res = match c.frame(spf) { Ok(frame) => { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; @@ -477,7 +717,7 @@ fn run(sp: GenericService) -> ResultType<()> { }; #[cfg(not(any(target_os = "android", target_os = "ios")))] - let res = match (*c).frame(wait as _) { + let res = match c.frame(spf) { Ok(frame) => { let time = now - start; let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64; @@ -743,82 +983,3 @@ fn get_current_display() -> ResultType<(usize, usize, Display)> { } return Ok((n, current, displays.remove(current))); } - -#[inline] -fn update_latency(id: i32, latency: i64, latencies: &mut HashMap) { - if latency <= 0 { - latencies.remove(&id); - } else { - latencies.insert(id, latency); - } -} - -pub fn update_test_latency(id: i32, latency: i64) { - update_latency(id, latency, &mut *TEST_LATENCIES.lock().unwrap()); -} - -fn convert_quality(q: i32) -> i32 { - let q = { - if q == ImageQuality::Balanced.value() { - (100 * 2 / 3, 12) - } else if q == ImageQuality::Low.value() { - (100 / 2, 18) - } else if q == ImageQuality::Best.value() { - (100, 12) - } else { - let bitrate = q >> 8 & 0xFF; - let quantizer = q & 0xFF; - (bitrate * 2, (100 - quantizer) * 36 / 100) - } - }; - if q.0 <= 0 { - 0 - } else { - q.0 << 8 | q.1 - } -} - -pub fn update_image_quality(id: i32, q: Option) { - match q { - Some(q) => { - let q = convert_quality(q); - if q > 0 { - IMAGE_QUALITIES.lock().unwrap().insert(id, q); - } else { - IMAGE_QUALITIES.lock().unwrap().remove(&id); - } - } - None => { - IMAGE_QUALITIES.lock().unwrap().remove(&id); - } - } -} - -fn get_image_quality() -> i32 { - IMAGE_QUALITIES - .lock() - .unwrap() - .values() - .min() - .unwrap_or(&convert_quality(ImageQuality::Balanced.value())) - .clone() -} - -#[inline] -fn get_quality(w: usize, h: usize, q: i32) -> (u32, u32, u32, i32) { - // https://www.nvidia.com/en-us/geforce/guides/broadcasting-guide/ - let bitrate = q >> 8 & 0xFF; - let quantizer = q & 0xFF; - let b = ((w * h) / 1000) as u32; - - #[cfg(target_os = "android")] - { - // fix when andorid screen shrinks - let fix = Display::fix_quality() as u32; - log::debug!("Android screen, fix quality:{}", fix); - let b = b * fix; - return (bitrate as u32 * b / 100, quantizer as _, 56, 7); - } - - (bitrate as u32 * b / 100, quantizer as _, 56, 7) -} diff --git a/src/ui.rs b/src/ui.rs index 76ba23226..b93c11d44 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -213,7 +213,9 @@ impl UI { fn install_me(&mut self, _options: String, _path: String) { #[cfg(windows)] std::thread::spawn(move || { - allow_err!(crate::platform::windows::install_me(&_options, _path, false)); + allow_err!(crate::platform::windows::install_me( + &_options, _path, false, false + )); std::process::exit(0); }); } diff --git a/src/ui/header.tis b/src/ui/header.tis index 4b2615a45..88ee8b6e6 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -159,6 +159,7 @@ class Header: Reactor.Component {
  • {svg_checkmark}{translate('Custom')}
  • {svg_checkmark}{translate('Show remote cursor')}
  • +
  • {svg_checkmark}{translate('Show quality monitor')}
  • {audio_enabled ?
  • {svg_checkmark}{translate('Mute')}
  • : ""} {is_win && pi.platform == 'Windows' && file_enabled ?
  • {svg_checkmark}{translate('Allow file copy and paste')}
  • : ""} {keyboard_enabled && clipboard_enabled ?
  • {svg_checkmark}{translate('Disable clipboard')}
  • : ""} @@ -315,7 +316,9 @@ class Header: Reactor.Component { handle_custom_image_quality(); } else if (me.id == "privacy-mode") { togglePrivacyMode(me.id); - } else if (me.attributes.hasClass("toggle-option")) { + } else if (me.id == "show-quality-monitor") { + toggleQualityMonitor(me.id); + }else if (me.attributes.hasClass("toggle-option")) { handler.toggle_option(me.id); toggleMenuState(); } else if (!me.attributes.hasClass("selected")) { @@ -332,16 +335,13 @@ class Header: Reactor.Component { } function handle_custom_image_quality() { - var tmp = handler.get_custom_image_quality(); - var bitrate0 = tmp[0] || 50; - var quantizer0 = tmp.length > 1 ? tmp[1] : 100; + var bitrate = handler.get_custom_image_quality()[0] / 2; msgbox("custom", "Custom Image Quality", "
    \ -
    x% bitrate
    \ -
    x% quantizer
    \ +
    x% Bitrate
    \
    ", function(res=null) { if (!res) return; if (!res.bitrate) return; - handler.save_custom_image_quality(res.bitrate, res.quantizer); + handler.save_custom_image_quality(res.bitrate * 2); toggleMenuState(); }); } @@ -357,7 +357,7 @@ function toggleMenuState() { for (var el in $$(menu#display-options>li)) { el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0); } - for (var id in ["show-remote-cursor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) { + for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) { var el = self.select('#' + id); if (el) { var value = handler.get_toggle_option(id); @@ -425,6 +425,17 @@ function togglePrivacyMode(privacy_id) { } } +function toggleQualityMonitor(name) { + var show = handler.get_toggle_option(name); + if (show) { + $(#quality-monitor).style.set{ display: "none" }; + } else { + $(#quality-monitor).style.set{ display: "block" }; + } + handler.toggle_option(name); + toggleMenuState(); +} + handler.updateBlockInputState = function(input_blocked) { if (!input_blocked) { handler.toggle_option("block-input"); diff --git a/src/ui/remote.css b/src/ui/remote.css index 617285e9c..66c5ce80f 100644 --- a/src/ui/remote.css +++ b/src/ui/remote.css @@ -9,6 +9,16 @@ div#video-wrapper { background: #212121; } +div#quality-monitor { + top: 20px; + right: 20px; + background: #7571719c; + padding: 5px; + min-width: 150px; + color: azure; + border: solid azure; +} + video#handler { behavior: native-remote video; size: *; @@ -24,7 +34,7 @@ img#cursor { } .goup { - transform: rotate(90deg); + transform: rotate(90deg); } table#remote-folder-view { @@ -33,4 +43,4 @@ table#remote-folder-view { table#local-folder-view { context-menu: selector(menu#local-folder-view); -} +} \ No newline at end of file diff --git a/src/ui/remote.html b/src/ui/remote.html index 32c1409e2..d58c3449b 100644 --- a/src/ui/remote.html +++ b/src/ui/remote.html @@ -1,12 +1,13 @@ - - - - -
    -
    + +
    + + + -
    - -
    - -
    -
    -
    -
    - - + + + +
    +